diff options
author | Srinivas Visvanathan <sriniv@google.com> | 2017-04-17 16:57:51 -0700 |
---|---|---|
committer | Srinivas Visvanathan <sriniv@google.com> | 2017-04-21 09:52:43 -0700 |
commit | 6ed89513a64aeaadc55268607e556c8299d4d6b4 (patch) | |
tree | 4e2781390e4b8e06b5c937fa7a6f2215e2874105 | |
parent | de4cf995d62280d139c05a9639832a9bf781b480 (diff) | |
download | platform_packages_apps_Car_Dialer-6ed89513a64aeaadc55268607e556c8299d4d6b4.tar.gz platform_packages_apps_Car_Dialer-6ed89513a64aeaadc55268607e556c8299d4d6b4.tar.bz2 platform_packages_apps_Car_Dialer-6ed89513a64aeaadc55268607e556c8299d4d6b4.zip |
CallManager improvements
- Merging TelecomUiCallManager code into UiCallManager. Also
UiCallManager is no longer a singleton and is constructed/owned by
TelecomActivity. TelecomActivity passes it to Fragments.
- Moved InCallServiceImpl into parent telecom package and killed
embedded sub-package.
Bug: 37251324
Test: Tested incoming, outgoing calls, including conference.
Change-Id: I2817a3a93f8314f303e53394d665266dc39a04b5
-rw-r--r-- | AndroidManifest.xml | 2 | ||||
-rw-r--r-- | src/com/android/car/dialer/DialerFragment.java | 19 | ||||
-rw-r--r-- | src/com/android/car/dialer/OngoingCallFragment.java | 11 | ||||
-rw-r--r-- | src/com/android/car/dialer/StrequentsAdapter.java | 10 | ||||
-rw-r--r-- | src/com/android/car/dialer/StrequentsFragment.java | 12 | ||||
-rw-r--r-- | src/com/android/car/dialer/TelecomActivity.java | 11 | ||||
-rw-r--r-- | src/com/android/car/dialer/telecom/InCallServiceImpl.java (renamed from src/com/android/car/dialer/telecom/embedded/InCallServiceImpl.java) | 4 | ||||
-rw-r--r-- | src/com/android/car/dialer/telecom/UiCallManager.java | 457 | ||||
-rw-r--r-- | src/com/android/car/dialer/telecom/embedded/TelecomUiCallManager.java | 490 |
9 files changed, 450 insertions, 566 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 0d3c89a9..646cc002 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -57,7 +57,7 @@ android:resource="@xml/searchable" /> </activity> - <service android:name="com.android.car.dialer.telecom.embedded.InCallServiceImpl" + <service android:name="com.android.car.dialer.telecom.InCallServiceImpl" android:permission="android.permission.BIND_INCALL_SERVICE" android:exported="true"> <meta-data android:name="android.telecom.IN_CALL_SERVICE_UI" android:value="true" /> diff --git a/src/com/android/car/dialer/DialerFragment.java b/src/com/android/car/dialer/DialerFragment.java index edf163a4..e84f1891 100644 --- a/src/com/android/car/dialer/DialerFragment.java +++ b/src/com/android/car/dialer/DialerFragment.java @@ -63,6 +63,7 @@ public class DialerFragment extends Fragment { } private Context mContext; + private UiCallManager mUiCallManager; private final StringBuffer mNumber = new StringBuffer(MAX_DIAL_NUMBER); private AudioManager mAudioManager; private ToneGenerator mToneGenerator; @@ -86,6 +87,12 @@ public class DialerFragment extends Fragment { void onDialerBackClick(); } + public static DialerFragment newInstance(UiCallManager callManager) { + DialerFragment fragment = new DialerFragment(); + fragment.mUiCallManager = callManager; + return fragment; + } + /** * Sets the given {@link DialerBackButtonListener} to be notified whenever the back button * on the dialer has been clicked. Passing {@code null} to this method will clear all listeners. @@ -145,7 +152,7 @@ public class DialerFragment extends Fragment { } if (!TextUtils.isEmpty(mNumber.toString())) { - getUiCallManager().safePlaceCall(mNumber.toString(), false); + mUiCallManager.safePlaceCall(mNumber.toString(), false); } }); View deleteButton = view.findViewById(R.id.delete); @@ -207,7 +214,7 @@ public class DialerFragment extends Fragment { mToneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC, TONE_RELATIVE_VOLUME); } } - UiCallManager.getInstance(mContext).addListener(mCallListener); + mUiCallManager.addListener(mCallListener); if (mPendingRunnable != null) { mPendingRunnable.run(); @@ -218,7 +225,7 @@ public class DialerFragment extends Fragment { @Override public void onPause() { super.onPause(); - UiCallManager.getInstance(mContext).removeListener(mCallListener); + mUiCallManager.removeListener(mCallListener); stopTone(); synchronized (mToneGeneratorLock) { if (mToneGenerator != null) { @@ -272,10 +279,6 @@ public class DialerFragment extends Fragment { } }; - private UiCallManager getUiCallManager() { - return UiCallManager.getInstance(mContext); - } - private String getFormattedNumber(String number) { return TelecomUtils.getFormattedNumber(mContext, number); } @@ -286,7 +289,7 @@ public class DialerFragment extends Fragment { if (event.getKeyCode() == KeyEvent.KEYCODE_CALL && event.getAction() == KeyEvent.ACTION_UP && !TextUtils.isEmpty(mNumber.toString())) { - getUiCallManager().safePlaceCall(mNumber.toString(), false); + mUiCallManager.safePlaceCall(mNumber.toString(), false); } } }; diff --git a/src/com/android/car/dialer/OngoingCallFragment.java b/src/com/android/car/dialer/OngoingCallFragment.java index 2eb659e2..a083b802 100644 --- a/src/com/android/car/dialer/OngoingCallFragment.java +++ b/src/com/android/car/dialer/OngoingCallFragment.java @@ -105,15 +105,17 @@ public class OngoingCallFragment extends Fragment { mAccelerateDecelerateInterpolator = new AccelerateDecelerateInterpolator(); private final Interpolator mAccelerateInterpolator = new AccelerateInterpolator(10); - // Should be set soon after construction. - void setUiBluetoothMonitor(UiBluetoothMonitor uiBluetoothMonitor) { - mUiBluetoothMonitor = uiBluetoothMonitor; + public static OngoingCallFragment newInstance( + UiCallManager callManager, UiBluetoothMonitor btMonitor) { + OngoingCallFragment fragment = new OngoingCallFragment(); + fragment.mUiCallManager = callManager; + fragment.mUiBluetoothMonitor = btMonitor; + return fragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mUiCallManager = UiCallManager.getInstance(getContext()); mHandler = new Handler(); } @@ -123,7 +125,6 @@ public class OngoingCallFragment extends Fragment { mHandler.removeCallbacks(mUpdateDurationRunnable); mHandler.removeCallbacks(mStopDtmfToneRunnable); mHandler = null; - mUiCallManager = null; mLoadedNumber = null; } diff --git a/src/com/android/car/dialer/StrequentsAdapter.java b/src/com/android/car/dialer/StrequentsAdapter.java index f2719a73..78f4d3e6 100644 --- a/src/com/android/car/dialer/StrequentsAdapter.java +++ b/src/com/android/car/dialer/StrequentsAdapter.java @@ -56,6 +56,7 @@ public class StrequentsAdapter extends RecyclerView.Adapter<CallLogViewHolder> private static final int VIEW_TYPE_STREQUENT = 2; private final Context mContext; + private final UiCallManager mUiCallManager; private List<ContactEntry> mData; private LastCallData mLastCallData; @@ -73,8 +74,9 @@ public class StrequentsAdapter extends RecyclerView.Adapter<CallLogViewHolder> private int mMaxItems = -1; private boolean mIsEmpty; - public StrequentsAdapter(Context context) { + public StrequentsAdapter(Context context, UiCallManager callManager) { mContext = context; + mUiCallManager = callManager; mContentResolver = context.getContentResolver(); } @@ -315,7 +317,7 @@ public class StrequentsAdapter extends RecyclerView.Adapter<CallLogViewHolder> secondaryText.append(relativeDate); } - int[] callTypes = getCarTelecomManager().getCallTypes(cursor, 1); + int[] callTypes = mUiCallManager.getCallTypes(cursor, 1); return new LastCallData(number, nameSb.toString(), secondaryText.toString(), callTypes); } @@ -394,10 +396,6 @@ public class StrequentsAdapter extends RecyclerView.Adapter<CallLogViewHolder> DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE); } - private UiCallManager getCarTelecomManager() { - return UiCallManager.getInstance(mContext); - } - /** * A container for data relating to a last call entry. */ diff --git a/src/com/android/car/dialer/StrequentsFragment.java b/src/com/android/car/dialer/StrequentsFragment.java index 87bb668e..364c25db 100644 --- a/src/com/android/car/dialer/StrequentsFragment.java +++ b/src/com/android/car/dialer/StrequentsFragment.java @@ -48,6 +48,7 @@ public class StrequentsFragment extends Fragment { public static final String KEY_MAX_CLICKS = "max_clicks"; public static final int DEFAULT_MAX_CLICKS = 6; + private UiCallManager mUiCallManager; private StrequentsAdapter mAdapter; private CursorLoader mSpeedialCursorLoader; private CursorLoader mCallLogCursorLoader; @@ -57,6 +58,12 @@ public class StrequentsFragment extends Fragment { private Cursor mCallLogCursor; private boolean mHasLoadedData; + public static StrequentsFragment newInstance(UiCallManager callManager) { + StrequentsFragment fragment = new StrequentsFragment(); + fragment.mUiCallManager = callManager; + return fragment; + } + @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -120,14 +127,13 @@ public class StrequentsFragment extends Fragment { mListView.removeDefaultItemDecoration(); mListView.setLightMode(); - mAdapter = new StrequentsAdapter(mContext); + mAdapter = new StrequentsAdapter(mContext, mUiCallManager); mAdapter.setStrequentsListener(viewHolder -> { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "onContactedClicked"); } - UiCallManager.getInstance(mContext).safePlaceCall( - (String) viewHolder.itemView.getTag(), false); + mUiCallManager.safePlaceCall((String) viewHolder.itemView.getTag(), false); }); mListView.setMaxPages(maxPages); mListView.setAdapter(mAdapter); diff --git a/src/com/android/car/dialer/TelecomActivity.java b/src/com/android/car/dialer/TelecomActivity.java index 499f6f16..43fce86a 100644 --- a/src/com/android/car/dialer/TelecomActivity.java +++ b/src/com/android/car/dialer/TelecomActivity.java @@ -91,7 +91,7 @@ public class TelecomActivity extends CarDrawerActivity implements getWindow().getDecorView().setBackgroundColor(getColor(R.color.phone_theme)); setTitle(getString(R.string.phone_app_name)); - mUiCallManager = UiCallManager.getInstance(this); + mUiCallManager = new UiCallManager(this); mUiBluetoothMonitor = new UiBluetoothMonitor(this); if (savedInstanceState != null) { @@ -308,7 +308,7 @@ public class TelecomActivity extends CarDrawerActivity implements } if (mSpeedDialFragment == null) { - mSpeedDialFragment = new StrequentsFragment(); + mSpeedDialFragment = StrequentsFragment.newInstance(mUiCallManager); Bundle args = new Bundle(); mSpeedDialFragment.setArguments(args); } @@ -330,9 +330,8 @@ public class TelecomActivity extends CarDrawerActivity implements } if (mOngoingCallFragment == null) { - OngoingCallFragment fragment = new OngoingCallFragment(); - fragment.setUiBluetoothMonitor(mUiBluetoothMonitor); - mOngoingCallFragment = fragment; + mOngoingCallFragment = + OngoingCallFragment.newInstance(mUiCallManager, mUiBluetoothMonitor); } setContentFragmentWithFadeAnimation(mOngoingCallFragment); @@ -356,7 +355,7 @@ public class TelecomActivity extends CarDrawerActivity implements Log.v(TAG, "showDialer: creating dialer"); } - mDialerFragment = new DialerFragment(); + mDialerFragment = DialerFragment.newInstance(mUiCallManager); mDialerFragment.setDialerBackButtonListener(this); } diff --git a/src/com/android/car/dialer/telecom/embedded/InCallServiceImpl.java b/src/com/android/car/dialer/telecom/InCallServiceImpl.java index 26e748cf..48688963 100644 --- a/src/com/android/car/dialer/telecom/embedded/InCallServiceImpl.java +++ b/src/com/android/car/dialer/telecom/InCallServiceImpl.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.car.dialer.telecom.embedded; +package com.android.car.dialer.telecom; import android.content.Intent; import android.os.Binder; @@ -28,7 +28,7 @@ import java.util.concurrent.CopyOnWriteArrayList; /** * An implementation of {@link InCallService}. This service is bounded by android telecom and - * {@link TelecomUiCallManager}. For incoming calls it will launch Dialer app. + * {@link UiCallManager}. For incoming calls it will launch Dialer app. */ public class InCallServiceImpl extends InCallService { private static final String TAG = "Em.InCallService"; diff --git a/src/com/android/car/dialer/telecom/UiCallManager.java b/src/com/android/car/dialer/telecom/UiCallManager.java index 8b24f2a2..95e5139b 100644 --- a/src/com/android/car/dialer/telecom/UiCallManager.java +++ b/src/com/android/car/dialer/telecom/UiCallManager.java @@ -15,43 +15,49 @@ */ package com.android.car.dialer.telecom; +import android.content.ComponentName; import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; import android.database.Cursor; +import android.net.Uri; +import android.os.IBinder; import android.provider.CallLog; import android.telecom.Call; +import android.telecom.CallAudioState; +import android.telecom.DisconnectCause; +import android.telecom.GatewayInfo; +import android.telecom.InCallService; +import android.telecom.TelecomManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; import android.view.KeyEvent; import com.android.car.dialer.R; -import com.android.car.dialer.telecom.embedded.TelecomUiCallManager; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; /** * The entry point for all interactions between UI and telecom. */ -public abstract class UiCallManager { +public class UiCallManager { private static String TAG = "Em.TelecomMgr"; - private static Object sInstanceLock = new Object(); - private static UiCallManager sInstance; - - private long mLastPlacedCallTimeMs = 0; - // Rate limit how often you can place outgoing calls. private static final long MIN_TIME_BETWEEN_CALLS_MS = 3000; - private static final List<Integer> sCallStateRank = new ArrayList<>(); - protected Context mContext; - - protected TelephonyManager mTelephonyManager; + // Used to assign id's to UiCall objects as they're created. + private static int nextCarPhoneCallId = 0; static { // States should be added from lowest rank to highest @@ -66,69 +72,365 @@ public abstract class UiCallManager { sCallStateRank.add(Call.STATE_RINGING); } - public static UiCallManager getInstance(Context context) { - synchronized (sInstanceLock) { - if (sInstance == null) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Creating an instance of CarTelecomManager"); - } - sInstance = new TelecomUiCallManager(); - sInstance.setUp(context.getApplicationContext()); - } - } - return sInstance; - } + private Context mContext; + private TelephonyManager mTelephonyManager; + private long mLastPlacedCallTimeMs; - protected UiCallManager() {} + private TelecomManager mTelecomManager; + private InCallServiceImpl mInCallService; + private final Map<UiCall, Call> mCallMapping = new HashMap<>(); + private final List<CallListener> mCallListeners = new CopyOnWriteArrayList<>(); - protected void setUp(Context context) { + public UiCallManager(Context context) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "SetUp"); } mContext = context; mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); + + mTelecomManager = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); + Intent intent = new Intent(context, InCallServiceImpl.class); + intent.setAction(InCallServiceImpl.ACTION_LOCAL_BIND); + context.bindService(intent, mInCallServiceConnection, Context.BIND_AUTO_CREATE); } - public abstract void tearDown(); + private final ServiceConnection mInCallServiceConnection = new ServiceConnection() { - public abstract void addListener(CallListener listener); + @Override + public void onServiceConnected(ComponentName name, IBinder binder) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "onServiceConnected: " + name + ", service: " + binder); + } + mInCallService = ((InCallServiceImpl.LocalBinder) binder).getService(); + mInCallService.registerCallback(mInCallServiceCallback); + + // The InCallServiceImpl could be bound when we already have some active calls, let's + // notify UI about these calls. + for (Call telecomCall : mInCallService.getCalls()) { + UiCall uiCall = doTelecomCallAdded(telecomCall); + onStateChanged(uiCall, uiCall.getState()); + } + } - public abstract void removeListener(CallListener listener); + @Override + public void onServiceDisconnected(ComponentName name) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "onServiceDisconnected: " + name); + } + mInCallService.unregisterCallback(mInCallServiceCallback); + } - protected abstract void placeCall(String number); + private InCallServiceImpl.Callback mInCallServiceCallback = + new InCallServiceImpl.Callback() { + @Override + public void onTelecomCallAdded(Call telecomCall) { + doTelecomCallAdded(telecomCall); + } + + @Override + public void onTelecomCallRemoved(Call telecomCall) { + doTelecomCallRemoved(telecomCall); + } + + @Override + public void onCallAudioStateChanged(CallAudioState audioState) { + doCallAudioStateChanged(audioState); + } + }; + }; + + public void tearDown() { + if (mInCallService != null) { + mContext.unbindService(mInCallServiceConnection); + mInCallService = null; + } + mCallMapping.clear(); + } - public abstract void answerCall(UiCall call); + public void addListener(CallListener listener) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "addListener: " + listener); + } + mCallListeners.add(listener); + } - public abstract void rejectCall(UiCall call, boolean rejectWithMessage, String textMessage); + public void removeListener(CallListener listener) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "removeListener: " + listener); + } + mCallListeners.remove(listener); + } - public abstract void disconnectCall(UiCall call); + protected void placeCall(String number) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "placeCall: " + number); + } + Uri uri = Uri.fromParts("tel", number, null); + Log.d(TAG, "android.telecom.TelecomManager#placeCall: " + uri); + mTelecomManager.placeCall(uri, null); + } - public abstract List<UiCall> getCalls(); + public void answerCall(UiCall uiCall) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "answerCall: " + uiCall); + } - public abstract boolean getMuted(); + Call telecomCall = mCallMapping.get(uiCall); + if (telecomCall != null) { + telecomCall.answer(0); + } + } - public abstract void setMuted(boolean muted); + public void rejectCall(UiCall uiCall, boolean rejectWithMessage, String textMessage) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "rejectCall: " + uiCall + ", rejectWithMessage: " + rejectWithMessage + + "textMessage: " + textMessage); + } - public abstract int getSupportedAudioRouteMask(); + Call telecomCall = mCallMapping.get(uiCall); + if (telecomCall != null) { + telecomCall.reject(rejectWithMessage, textMessage); + } + } - public abstract int getAudioRoute(); + public void disconnectCall(UiCall uiCall) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "disconnectCall: " + uiCall); + } - public abstract void setAudioRoute(int audioRoute); + Call telecomCall = mCallMapping.get(uiCall); + if (telecomCall != null) { + telecomCall.disconnect(); + } + } - public abstract void holdCall(UiCall call); + public List<UiCall> getCalls() { + return new ArrayList<>(mCallMapping.keySet()); + } - public abstract void unholdCall(UiCall call); + public boolean getMuted() { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "getMuted"); + } + if (mInCallService == null) { + return false; + } + CallAudioState audioState = mInCallService.getCallAudioState(); + return audioState != null && audioState.isMuted(); + } - public abstract void playDtmfTone(UiCall call, char digit); + public void setMuted(boolean muted) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "setMuted: " + muted); + } + if (mInCallService == null) { + return; + } + mInCallService.setMuted(muted); + } - public abstract void stopDtmfTone(UiCall call); + public int getSupportedAudioRouteMask() { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "getSupportedAudioRouteMask"); + } - public abstract void postDialContinue(UiCall call, boolean proceed); + CallAudioState audioState = getCallAudioStateOrNull(); + return audioState != null ? audioState.getSupportedRouteMask() : 0; + } - public abstract void conference(UiCall call, UiCall otherCall); + public int getAudioRoute() { + CallAudioState audioState = getCallAudioStateOrNull(); + int audioRoute = audioState != null ? audioState.getRoute() : 0; + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "getAudioRoute " + audioRoute); + } + return audioRoute; + } + + public void setAudioRoute(int audioRoute) { + // In case of embedded where the CarKitt is always connected to one kind of speaker we + // should simply ignore any setAudioRoute requests. + Log.w(TAG, "setAudioRoute ignoring request " + audioRoute); + } + + public void holdCall(UiCall uiCall) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "holdCall: " + uiCall); + } + + Call telecomCall = mCallMapping.get(uiCall); + if (telecomCall != null) { + telecomCall.hold(); + } + } - public abstract void splitFromConference(UiCall call); + public void unholdCall(UiCall uiCall) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "unholdCall: " + uiCall); + } + + Call telecomCall = mCallMapping.get(uiCall); + if (telecomCall != null) { + telecomCall.unhold(); + } + } + + public void playDtmfTone(UiCall uiCall, char digit) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "playDtmfTone: call: " + uiCall + ", digit: " + digit); + } + + Call telecomCall = mCallMapping.get(uiCall); + if (telecomCall != null) { + telecomCall.playDtmfTone(digit); + } + } + + public void stopDtmfTone(UiCall uiCall) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "stopDtmfTone: call: " + uiCall); + } + + Call telecomCall = mCallMapping.get(uiCall); + if (telecomCall != null) { + telecomCall.stopDtmfTone(); + } + } + + public void postDialContinue(UiCall uiCall, boolean proceed) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "postDialContinue: call: " + uiCall + ", proceed: " + proceed); + } + + Call telecomCall = mCallMapping.get(uiCall); + if (telecomCall != null) { + telecomCall.postDialContinue(proceed); + } + } + + public void conference(UiCall uiCall, UiCall otherUiCall) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "conference: call: " + uiCall + ", otherCall: " + otherUiCall); + } + + Call telecomCall = mCallMapping.get(uiCall); + Call otherTelecomCall = mCallMapping.get(otherUiCall); + if (telecomCall != null) { + telecomCall.conference(otherTelecomCall); + } + } + + public void splitFromConference(UiCall uiCall) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "splitFromConference: call: " + uiCall); + } + + Call telecomCall = mCallMapping.get(uiCall); + if (telecomCall != null) { + telecomCall.splitFromConference(); + } + } + + private UiCall doTelecomCallAdded(final Call telecomCall) { + Log.d(TAG, "doTelecomCallAdded: " + telecomCall); + + UiCall uiCall = getOrCreateCallContainer(telecomCall); + telecomCall.registerCallback(new TelecomCallListener(this, uiCall)); + for (CallListener listener : mCallListeners) { + listener.onCallAdded(uiCall); + } + Log.d(TAG, "Call backs registered"); + + if (telecomCall.getState() == Call.STATE_SELECT_PHONE_ACCOUNT) { + // TODO(b/26189994): need to show Phone Account picker to let user choose a phone + // account. It should be an account from TelecomManager#getCallCapablePhoneAccounts + // list. + Log.w(TAG, "Need to select phone account for the given call: " + telecomCall + ", " + + "but this feature is not implemented yet."); + telecomCall.disconnect(); + } + return uiCall; + } + + private void doTelecomCallRemoved(Call telecomCall) { + UiCall uiCall = getOrCreateCallContainer(telecomCall); + + mCallMapping.remove(uiCall); + + for (CallListener listener : mCallListeners) { + listener.onCallRemoved(uiCall); + } + } + + private void doCallAudioStateChanged(CallAudioState audioState) { + for (CallListener listener : mCallListeners) { + listener.onAudioStateChanged(audioState.isMuted(), audioState.getRoute(), + audioState.getSupportedRouteMask()); + } + } + + private void onStateChanged(UiCall uiCall, int state) { + for (CallListener listener : mCallListeners) { + listener.onStateChanged(uiCall, state); + } + } + + private void onCallUpdated(UiCall uiCall) { + for (CallListener listener : mCallListeners) { + listener.onCallUpdated(uiCall); + } + } + + private UiCall getOrCreateCallContainer(Call telecomCall) { + for (Map.Entry<UiCall, Call> entry : mCallMapping.entrySet()) { + if (entry.getValue() == telecomCall) { + return entry.getKey(); + } + } + + UiCall uiCall = new UiCall(nextCarPhoneCallId++); + updateCallContainerFromTelecom(uiCall, telecomCall); + mCallMapping.put(uiCall, telecomCall); + return uiCall; + } + + private static void updateCallContainerFromTelecom(UiCall uiCall, Call telecomCall) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "updateCallContainerFromTelecom: call: " + uiCall + ", telecomCall: " + + telecomCall); + } + + uiCall.setState(telecomCall.getState()); + uiCall.setHasChildren(!telecomCall.getChildren().isEmpty()); + uiCall.setHasParent(telecomCall.getParent() != null); + + Call.Details details = telecomCall.getDetails(); + if (details == null) { + return; + } + + uiCall.setConnectTimeMillis(details.getConnectTimeMillis()); + + DisconnectCause cause = details.getDisconnectCause(); + uiCall.setDisconnectCause(cause == null ? null : cause.getLabel()); + + GatewayInfo gatewayInfo = details.getGatewayInfo(); + uiCall.setGatewayInfoOriginalAddress( + gatewayInfo == null ? null : gatewayInfo.getOriginalAddress()); + + String number = ""; + if (gatewayInfo != null) { + number = gatewayInfo.getOriginalAddress().getSchemeSpecificPart(); + } else if (details.getHandle() != null) { + number = details.getHandle().getSchemeSpecificPart(); + } + uiCall.setNumber(number); + } + + private CallAudioState getCallAudioStateOrNull() { + return mInCallService != null ? mInCallService.getCallAudioState() : null; + } public static class CallListener { @SuppressWarnings("unused") @@ -308,4 +610,69 @@ public abstract class UiCallManager { } }; } + + private static class TelecomCallListener extends Call.Callback { + private final WeakReference<UiCallManager> mCarTelecomMangerRef; + private final WeakReference<UiCall> mCallContainerRef; + + TelecomCallListener(UiCallManager carTelecomManager, UiCall uiCall) { + mCarTelecomMangerRef = new WeakReference<>(carTelecomManager); + mCallContainerRef = new WeakReference<>(uiCall); + } + + @Override + public void onStateChanged(Call telecomCall, int state) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "onStateChanged: " + state); + } + UiCallManager manager = mCarTelecomMangerRef.get(); + UiCall call = mCallContainerRef.get(); + if (manager != null && call != null) { + call.setState(state); + manager.onStateChanged(call, state); + } + } + + @Override + public void onParentChanged(Call telecomCall, Call parent) { + doCallUpdated(telecomCall); + } + + @Override + public void onCallDestroyed(Call telecomCall) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "onCallDestroyed"); + } + } + + @Override + public void onDetailsChanged(Call telecomCall, Call.Details details) { + doCallUpdated(telecomCall); + } + + @Override + public void onVideoCallChanged(Call telecomCall, InCallService.VideoCall videoCall) { + doCallUpdated(telecomCall); + } + + @Override + public void onCannedTextResponsesLoaded(Call telecomCall, + List<String> cannedTextResponses) { + doCallUpdated(telecomCall); + } + + @Override + public void onChildrenChanged(Call telecomCall, List<Call> children) { + doCallUpdated(telecomCall); + } + + private void doCallUpdated(Call telecomCall) { + UiCallManager manager = mCarTelecomMangerRef.get(); + UiCall uiCall = mCallContainerRef.get(); + if (manager != null && uiCall != null) { + updateCallContainerFromTelecom(uiCall, telecomCall); + manager.onCallUpdated(uiCall); + } + } + } } diff --git a/src/com/android/car/dialer/telecom/embedded/TelecomUiCallManager.java b/src/com/android/car/dialer/telecom/embedded/TelecomUiCallManager.java deleted file mode 100644 index 357512f7..00000000 --- a/src/com/android/car/dialer/telecom/embedded/TelecomUiCallManager.java +++ /dev/null @@ -1,490 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * 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.android.car.dialer.telecom.embedded; - -import com.android.car.dialer.telecom.UiCall; -import com.android.car.dialer.telecom.UiCallManager; - -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.net.Uri; -import android.os.IBinder; -import android.telecom.Call; -import android.telecom.Call.Details; -import android.telecom.CallAudioState; -import android.telecom.DisconnectCause; -import android.telecom.GatewayInfo; -import android.telecom.InCallService.VideoCall; -import android.telecom.TelecomManager; -import android.util.Log; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * An implementation of {@link UiCallManager} that uses {@code android.telecom.*} stack. - */ -public class TelecomUiCallManager extends UiCallManager { - - private static final String TAG = "Em.TelecomUiCallMgr"; - - // Used to assign id's to UiCall objects as they're created. - private static int nextCarPhoneCallId = 0; - - private TelecomManager mTelecomManager; - private InCallServiceImpl mInCallService; - private Map<UiCall, Call> mCallMapping = new HashMap<>(); - - private List<CallListener> mCallListeners = new CopyOnWriteArrayList<>(); - - @Override - protected void setUp(Context context) { - super.setUp(context); - mTelecomManager = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); - Intent intent = new Intent(context, InCallServiceImpl.class); - intent.setAction(InCallServiceImpl.ACTION_LOCAL_BIND); - context.bindService(intent, mInCallServiceConnection, Context.BIND_AUTO_CREATE); - } - - public void tearDown() { - if (mInCallService != null) { - mContext.unbindService(mInCallServiceConnection); - mInCallService = null; - } - mCallMapping.clear(); - } - - @Override - public void addListener(CallListener listener) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "addListener: " + listener); - } - mCallListeners.add(listener); - } - - @Override - public void removeListener(CallListener listener) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "removeListener: " + listener); - } - mCallListeners.remove(listener); - } - - @Override - public void placeCall(String number) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "placeCall: " + number); - } - Uri uri = Uri.fromParts("tel", number, null); - Log.d(TAG, "android.telecom.TelecomManager#placeCall: " + uri); - mTelecomManager.placeCall(uri, null); - } - - @Override - public void answerCall(UiCall uiCall) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "answerCall: " + uiCall); - } - - Call telecomCall = mCallMapping.get(uiCall); - if (telecomCall != null) { - telecomCall.answer(0); - } - } - - @Override - public void rejectCall(UiCall uiCall, boolean rejectWithMessage, String textMessage) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "rejectCall: " + uiCall + ", rejectWithMessage: " + rejectWithMessage - + "textMessage: " + textMessage); - } - - Call telecomCall = mCallMapping.get(uiCall); - if (telecomCall != null) { - telecomCall.reject(rejectWithMessage, textMessage); - } - } - - @Override - public void disconnectCall(UiCall uiCall) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "disconnectCall: " + uiCall); - } - - Call telecomCall = mCallMapping.get(uiCall); - if (telecomCall != null) { - telecomCall.disconnect(); - } - } - - @Override - public List<UiCall> getCalls() { - return new ArrayList<>(mCallMapping.keySet()); - } - - @Override - public boolean getMuted() { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "getMuted"); - } - if (mInCallService == null) { - return false; - } - CallAudioState audioState = mInCallService.getCallAudioState(); - return audioState != null && audioState.isMuted(); - } - - @Override - public void setMuted(boolean muted) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "setMuted: " + muted); - } - if (mInCallService == null) { - return; - } - mInCallService.setMuted(muted); - } - - @Override - public int getSupportedAudioRouteMask() { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "getSupportedAudioRouteMask"); - } - - CallAudioState audioState = getCallAudioStateOrNull(); - return audioState != null ? audioState.getSupportedRouteMask() : 0; - } - - @Override - public int getAudioRoute() { - CallAudioState audioState = getCallAudioStateOrNull(); - int audioRoute = audioState != null ? audioState.getRoute() : 0; - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "getAudioRoute " + audioRoute); - } - return audioRoute; - } - - @Override - public void setAudioRoute(int audioRoute) { - // In case of embedded where the CarKitt is always connected to one kind of speaker we - // should simply ignore any setAudioRoute requests. - Log.w(TAG, "setAudioRoute ignoring request " + audioRoute); - } - - @Override - public void holdCall(UiCall uiCall) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "holdCall: " + uiCall); - } - - Call telecomCall = mCallMapping.get(uiCall); - if (telecomCall != null) { - telecomCall.hold(); - } - } - - @Override - public void unholdCall(UiCall uiCall) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "unholdCall: " + uiCall); - } - - Call telecomCall = mCallMapping.get(uiCall); - if (telecomCall != null) { - telecomCall.unhold(); - } - } - - @Override - public void playDtmfTone(UiCall uiCall, char digit) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "playDtmfTone: call: " + uiCall + ", digit: " + digit); - } - - Call telecomCall = mCallMapping.get(uiCall); - if (telecomCall != null) { - telecomCall.playDtmfTone(digit); - } - } - - @Override - public void stopDtmfTone(UiCall uiCall) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "stopDtmfTone: call: " + uiCall); - } - - Call telecomCall = mCallMapping.get(uiCall); - if (telecomCall != null) { - telecomCall.stopDtmfTone(); - } - } - - @Override - public void postDialContinue(UiCall uiCall, boolean proceed) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "postDialContinue: call: " + uiCall + ", proceed: " + proceed); - } - - Call telecomCall = mCallMapping.get(uiCall); - if (telecomCall != null) { - telecomCall.postDialContinue(proceed); - } - } - - @Override - public void conference(UiCall uiCall, UiCall otherUiCall) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "conference: call: " + uiCall + ", otherCall: " + otherUiCall); - } - - Call telecomCall = mCallMapping.get(uiCall); - Call otherTelecomCall = mCallMapping.get(otherUiCall); - if (telecomCall != null) { - telecomCall.conference(otherTelecomCall); - } - } - - @Override - public void splitFromConference(UiCall uiCall) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "splitFromConference: call: " + uiCall); - } - - Call telecomCall = mCallMapping.get(uiCall); - if (telecomCall != null) { - telecomCall.splitFromConference(); - } - } - - private UiCall doTelecomCallAdded(final Call telecomCall) { - Log.d(TAG, "doTelecomCallAdded: " + telecomCall); - - UiCall uiCall = getOrCreateCallContainer(telecomCall); - telecomCall.registerCallback(new TelecomCallListener(this, uiCall)); - for (CallListener listener : mCallListeners) { - listener.onCallAdded(uiCall); - } - Log.d(TAG, "Call backs registered"); - - if (telecomCall.getState() == Call.STATE_SELECT_PHONE_ACCOUNT) { - // TODO(b/26189994): need to show Phone Account picker to let user choose a phone - // account. It should be an account from TelecomManager#getCallCapablePhoneAccounts - // list. - Log.w(TAG, "Need to select phone account for the given call: " + telecomCall + ", " - + "but this feature is not implemented yet."); - telecomCall.disconnect(); - } - return uiCall; - } - - private void doTelecomCallRemoved(Call telecomCall) { - UiCall uiCall = getOrCreateCallContainer(telecomCall); - - mCallMapping.remove(uiCall); - - for (CallListener listener : mCallListeners) { - listener.onCallRemoved(uiCall); - } - } - - private void doCallAudioStateChanged(CallAudioState audioState) { - for (CallListener listener : mCallListeners) { - listener.onAudioStateChanged(audioState.isMuted(), audioState.getRoute(), - audioState.getSupportedRouteMask()); - } - } - - private void onStateChanged(UiCall uiCall, int state) { - for (CallListener listener : mCallListeners) { - listener.onStateChanged(uiCall, state); - } - } - - private void onCallUpdated(UiCall uiCall) { - for (CallListener listener : mCallListeners) { - listener.onCallUpdated(uiCall); - } - } - - private static class TelecomCallListener extends Call.Callback { - private final WeakReference<TelecomUiCallManager> mCarTelecomMangerRef; - private final WeakReference<UiCall> mCallContainerRef; - - TelecomCallListener(TelecomUiCallManager carTelecomManager, UiCall uiCall) { - mCarTelecomMangerRef = new WeakReference<>(carTelecomManager); - mCallContainerRef = new WeakReference<>(uiCall); - } - - @Override - public void onStateChanged(Call telecomCall, int state) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "onStateChanged: " + state); - } - TelecomUiCallManager manager = mCarTelecomMangerRef.get(); - UiCall call = mCallContainerRef.get(); - if (manager != null && call != null) { - call.setState(state); - manager.onStateChanged(call, state); - } - } - - @Override - public void onParentChanged(Call telecomCall, Call parent) { - doCallUpdated(telecomCall); - } - - @Override - public void onCallDestroyed(Call telecomCall) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "onCallDestroyed"); - } - } - - @Override - public void onDetailsChanged(Call telecomCall, Details details) { - doCallUpdated(telecomCall); - } - - @Override - public void onVideoCallChanged(Call telecomCall, VideoCall videoCall) { - doCallUpdated(telecomCall); - } - - @Override - public void onCannedTextResponsesLoaded(Call telecomCall, - List<String> cannedTextResponses) { - doCallUpdated(telecomCall); - } - - @Override - public void onChildrenChanged(Call telecomCall, List<Call> children) { - doCallUpdated(telecomCall); - } - - private void doCallUpdated(Call telecomCall) { - TelecomUiCallManager manager = mCarTelecomMangerRef.get(); - UiCall uiCall = mCallContainerRef.get(); - if (manager != null && uiCall != null) { - updateCallContainerFromTelecom(uiCall, telecomCall); - manager.onCallUpdated(uiCall); - } - } - } - - private ServiceConnection mInCallServiceConnection = new ServiceConnection() { - - @Override - public void onServiceConnected(ComponentName name, IBinder binder) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "onServiceConnected: " + name + ", service: " + binder); - } - mInCallService = ((InCallServiceImpl.LocalBinder) binder).getService(); - mInCallService.registerCallback(mInCallServiceCallback); - - // The InCallServiceImpl could be bound when we already have some active calls, let's - // notify UI about these calls. - for (Call telecomCall : mInCallService.getCalls()) { - UiCall uiCall = doTelecomCallAdded(telecomCall); - onStateChanged(uiCall, uiCall.getState()); - } - } - - @Override - public void onServiceDisconnected(ComponentName name) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "onServiceDisconnected: " + name); - } - mInCallService.unregisterCallback(mInCallServiceCallback); - } - - private InCallServiceImpl.Callback mInCallServiceCallback = - new InCallServiceImpl.Callback() { - - @Override - public void onTelecomCallAdded(Call telecomCall) { - doTelecomCallAdded(telecomCall); - } - - @Override - public void onTelecomCallRemoved(Call telecomCall) { - doTelecomCallRemoved(telecomCall); - } - - @Override - public void onCallAudioStateChanged(CallAudioState audioState) { - doCallAudioStateChanged(audioState); - } - }; - }; - - private UiCall getOrCreateCallContainer(Call telecomCall) { - for (Map.Entry<UiCall, Call> entry : mCallMapping.entrySet()) { - if (entry.getValue() == telecomCall) { - return entry.getKey(); - } - } - - UiCall uiCall = new UiCall(nextCarPhoneCallId++); - updateCallContainerFromTelecom(uiCall, telecomCall); - mCallMapping.put(uiCall, telecomCall); - return uiCall; - } - - private static void updateCallContainerFromTelecom(UiCall uiCall, Call telecomCall) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "updateCallContainerFromTelecom: call: " + uiCall + ", telecomCall: " - + telecomCall); - } - - uiCall.setState(telecomCall.getState()); - uiCall.setHasChildren(!telecomCall.getChildren().isEmpty()); - uiCall.setHasParent(telecomCall.getParent() != null); - - Call.Details details = telecomCall.getDetails(); - if (details == null) { - return; - } - - uiCall.setConnectTimeMillis(details.getConnectTimeMillis()); - - DisconnectCause cause = details.getDisconnectCause(); - uiCall.setDisconnectCause(cause == null ? null : cause.getLabel()); - - GatewayInfo gatewayInfo = details.getGatewayInfo(); - uiCall.setGatewayInfoOriginalAddress( - gatewayInfo == null ? null : gatewayInfo.getOriginalAddress()); - - String number = ""; - if (gatewayInfo != null) { - number = gatewayInfo.getOriginalAddress().getSchemeSpecificPart(); - } else if (details.getHandle() != null) { - number = details.getHandle().getSchemeSpecificPart(); - } - uiCall.setNumber(number); - - } - - private CallAudioState getCallAudioStateOrNull() { - return mInCallService != null ? mInCallService.getCallAudioState() : null; - } -} |