From ecc708d3c1417b60f316a0387d17f2cf4c67c2b1 Mon Sep 17 00:00:00 2001 From: Stephen Bird Date: Thu, 31 Mar 2016 15:03:11 -0700 Subject: [1/4] Refactor CallMethodHelper Change-Id: I3b24eedffddbf0960023dc2f1429038e5353de2f --- src-ambient/ambient/AmbientConnection.java | 66 -- src-ambient/ambient/SingletonHolder.java | 50 - .../phone/common/ambient/AmbientConnection.java | 65 + .../common/ambient/AmbientDataSubscription.java | 326 +++++ .../phone/common/ambient/SingletonHolder.java | 56 + .../phone/common/ambient/TypedPendingResult.java | 53 + .../phone/common/ambient/utils/PluginUtils.java | 26 + .../phone/common/incall/CallMethodInfo.java | 357 ++++++ .../common/incall/CallMethodSpinnerAdapter.java | 304 +++++ .../common/incall/ContactsDataSubscription.java | 123 ++ .../phone/common/incall/CreditBarHelper.java | 105 ++ .../common/incall/DialerDataSubscription.java | 193 +++ .../common/incall/StartInCallCallReceiver.java | 62 + .../android/phone/common/incall/api/ApiHelper.java | 38 + .../phone/common/incall/api/InCallListeners.java | 84 ++ .../phone/common/incall/api/InCallQueries.java | 230 ++++ .../phone/common/incall/api/InCallResults.java | 283 +++++ .../listeners/AuthenticationListenerImpl.java | 58 + .../incall/listeners/CallCreditListenerImpl.java | 53 + .../common/incall/utils/CallMethodFilters.java | 94 ++ .../phone/common/incall/utils/CallMethodUtils.java | 220 ++++ .../phone/common/incall/utils/MimeTypeUtils.java | 143 +++ .../android/phone/common/nudge/api/ApiHelper.java | 31 + .../phone/common/nudge/api/NudgeQueries.java | 55 + .../phone/common/nudge/api/NudgeTypedResult.java | 51 + .../phone/common/nudge/utils/NudgeUtils.java | 47 + src-ambient/incall/AuthenticationListenerImpl.java | 27 - src-ambient/incall/CallCreditListenerImpl.java | 28 - src-ambient/incall/CallMethodHelper.java | 1250 -------------------- src-ambient/incall/CallMethodInfo.java | 353 ------ src-ambient/incall/CallMethodSpinnerAdapter.java | 303 ----- src-ambient/incall/CallMethodUtils.java | 151 --- src-ambient/incall/CreditBarHelper.java | 107 -- .../phone/common/util/StartInCallCallReceiver.java | 42 - 34 files changed, 3057 insertions(+), 2377 deletions(-) delete mode 100644 src-ambient/ambient/AmbientConnection.java delete mode 100644 src-ambient/ambient/SingletonHolder.java create mode 100644 src-ambient/com/android/phone/common/ambient/AmbientConnection.java create mode 100644 src-ambient/com/android/phone/common/ambient/AmbientDataSubscription.java create mode 100644 src-ambient/com/android/phone/common/ambient/SingletonHolder.java create mode 100644 src-ambient/com/android/phone/common/ambient/TypedPendingResult.java create mode 100644 src-ambient/com/android/phone/common/ambient/utils/PluginUtils.java create mode 100644 src-ambient/com/android/phone/common/incall/CallMethodInfo.java create mode 100644 src-ambient/com/android/phone/common/incall/CallMethodSpinnerAdapter.java create mode 100644 src-ambient/com/android/phone/common/incall/ContactsDataSubscription.java create mode 100644 src-ambient/com/android/phone/common/incall/CreditBarHelper.java create mode 100644 src-ambient/com/android/phone/common/incall/DialerDataSubscription.java create mode 100644 src-ambient/com/android/phone/common/incall/StartInCallCallReceiver.java create mode 100644 src-ambient/com/android/phone/common/incall/api/ApiHelper.java create mode 100644 src-ambient/com/android/phone/common/incall/api/InCallListeners.java create mode 100644 src-ambient/com/android/phone/common/incall/api/InCallQueries.java create mode 100644 src-ambient/com/android/phone/common/incall/api/InCallResults.java create mode 100644 src-ambient/com/android/phone/common/incall/listeners/AuthenticationListenerImpl.java create mode 100644 src-ambient/com/android/phone/common/incall/listeners/CallCreditListenerImpl.java create mode 100644 src-ambient/com/android/phone/common/incall/utils/CallMethodFilters.java create mode 100644 src-ambient/com/android/phone/common/incall/utils/CallMethodUtils.java create mode 100644 src-ambient/com/android/phone/common/incall/utils/MimeTypeUtils.java create mode 100644 src-ambient/com/android/phone/common/nudge/api/ApiHelper.java create mode 100644 src-ambient/com/android/phone/common/nudge/api/NudgeQueries.java create mode 100644 src-ambient/com/android/phone/common/nudge/api/NudgeTypedResult.java create mode 100644 src-ambient/com/android/phone/common/nudge/utils/NudgeUtils.java delete mode 100644 src-ambient/incall/AuthenticationListenerImpl.java delete mode 100644 src-ambient/incall/CallCreditListenerImpl.java delete mode 100644 src-ambient/incall/CallMethodHelper.java delete mode 100644 src-ambient/incall/CallMethodInfo.java delete mode 100644 src-ambient/incall/CallMethodSpinnerAdapter.java delete mode 100644 src-ambient/incall/CallMethodUtils.java delete mode 100644 src-ambient/incall/CreditBarHelper.java delete mode 100644 src/com/android/phone/common/util/StartInCallCallReceiver.java diff --git a/src-ambient/ambient/AmbientConnection.java b/src-ambient/ambient/AmbientConnection.java deleted file mode 100644 index ed4c0c0..0000000 --- a/src-ambient/ambient/AmbientConnection.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.android.phone.common.ambient; - -import android.content.Context; -import android.os.Bundle; -import android.util.Log; - -import com.cyanogen.ambient.analytics.AnalyticsServices; -import com.cyanogen.ambient.callerinfo.CallerInfoServices; -import com.cyanogen.ambient.common.ConnectionResult; -import com.cyanogen.ambient.common.api.AmbientApiClient; -import com.cyanogen.ambient.discovery.DiscoveryManagerServices; -import com.cyanogen.ambient.discovery.NudgeServices; -import com.cyanogen.ambient.incall.InCallServices; - -/** - * Holds Ambient Client for all PIM apps - */ -public class AmbientConnection { - - public static final SingletonHolder CLIENT = - new SingletonHolder() { - private static final String TAG = "Dialer.AmbientSingletonHolder"; - - @Override - protected AmbientApiClient create(Context context) { - AmbientApiClient client = new AmbientApiClient.Builder(context) - .addApi(AnalyticsServices.API) - .addApi(InCallServices.API) - .addApi(CallerInfoServices.API) - .addApi(NudgeServices.API) - .addApi(DiscoveryManagerServices.API) - .build(); - - client.registerConnectionFailedListener( - new AmbientApiClient.OnConnectionFailedListener() { - @Override - public void onConnectionFailed(ConnectionResult result) { - Log.w(TAG, "Ambient connection failed: " + result); - } - }); - client.registerDisconnectionListener( - new AmbientApiClient.OnDisconnectionListener() { - @Override - public void onDisconnection() { - Log.d(TAG, "Ambient connection disconnected"); - } - }); - client.registerConnectionCallbacks( - new AmbientApiClient.ConnectionCallbacks() { - @Override - public void onConnected(Bundle connectionHint) { - Log.d(TAG, "Ambient connection established"); - } - - @Override - public void onConnectionSuspended(int cause) { - Log.d(TAG, "Ambient connection suspended"); - } - }); - client.connect(); - return client; - } - }; - - -} diff --git a/src-ambient/ambient/SingletonHolder.java b/src-ambient/ambient/SingletonHolder.java deleted file mode 100644 index 52890fc..0000000 --- a/src-ambient/ambient/SingletonHolder.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.android.phone.common.ambient; - -/** - * Encapsulates a threadsafe singleton pattern. - * - * This class is designed to be used as a public constant, living within a class that has a private constructor. - * It defines a {@link #create(I)} method that will only ever be called once, upon the first call of {@link #get(I)}. - * That method is responsible for creating the actual singleton instance, and that instance will be returned for all - * future calls of {@link #get(I)}. - * - * Example: - * - * public class FooSingleton { - * public static final SingletonHolder<FooSingleton, ParamObject> HOLDER = - * new SingletonHolder<FooSingleton, ParamObject>() { - * @Override - * protected FooSingleton create(ParamObject param) { - * return new FooSingleton(param); - * } - * }; - * - * private FooSingleton(ParamObject param) { - * - * } - * } - * - * // somewhere else - * FooSingleton.HOLDER.get(params).doStuff(); - * - * @param The type of the class to hold as a singleton. - * @param A parameter object to use during creation of the singleton object. - */ -public abstract class SingletonHolder { - private E mInstance; - private final Object LOCK = new Object(); - - public final E get(I initializer) { - if (null == mInstance) { - synchronized (LOCK) { - if (null == mInstance) { - mInstance = create(initializer); - } - } - } - - return mInstance; - } - - protected abstract E create(I initializer); -} diff --git a/src-ambient/com/android/phone/common/ambient/AmbientConnection.java b/src-ambient/com/android/phone/common/ambient/AmbientConnection.java new file mode 100644 index 0000000..04fe63a --- /dev/null +++ b/src-ambient/com/android/phone/common/ambient/AmbientConnection.java @@ -0,0 +1,65 @@ +package com.android.phone.common.ambient; + +import android.content.Context; +import android.os.Bundle; +import android.util.Log; + +import com.cyanogen.ambient.analytics.AnalyticsServices; +import com.cyanogen.ambient.callerinfo.CallerInfoServices; +import com.cyanogen.ambient.common.ConnectionResult; +import com.cyanogen.ambient.common.api.AmbientApiClient; +import com.cyanogen.ambient.discovery.DiscoveryManagerServices; +import com.cyanogen.ambient.discovery.NudgeServices; +import com.cyanogen.ambient.incall.InCallServices; + +/** + * Holds Ambient Client for all PIM apps + */ +public class AmbientConnection { + + public static final SingletonHolder CLIENT = + new SingletonHolder() { + private static final String TAG = "PhoneCommon.AmbientSingletonHolder"; + + @Override + protected AmbientApiClient create(Context context) { + AmbientApiClient client = new AmbientApiClient.Builder(context) + .addApi(AnalyticsServices.API) + .addApi(InCallServices.API) + .addApi(CallerInfoServices.API) + .addApi(NudgeServices.API) + .addApi(DiscoveryManagerServices.API) + .build(); + + client.registerConnectionFailedListener( + new AmbientApiClient.OnConnectionFailedListener() { + @Override + public void onConnectionFailed(ConnectionResult result) { + Log.w(TAG, "Connection failed: " + result); + } + }); + client.registerDisconnectionListener( + new AmbientApiClient.OnDisconnectionListener() { + @Override + public void onDisconnection() { + Log.d(TAG, "Connection disconnected"); + } + }); + client.registerConnectionCallbacks( + new AmbientApiClient.ConnectionCallbacks() { + @Override + public void onConnected(Bundle connectionHint) { + Log.d(TAG, "Connection established"); + } + + @Override + public void onConnectionSuspended(int cause) { + Log.d(TAG, "Connection suspended"); + } + }); + client.connect(); + return client; + } + }; + +} diff --git a/src-ambient/com/android/phone/common/ambient/AmbientDataSubscription.java b/src-ambient/com/android/phone/common/ambient/AmbientDataSubscription.java new file mode 100644 index 0000000..3b30ccb --- /dev/null +++ b/src-ambient/com/android/phone/common/ambient/AmbientDataSubscription.java @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.phone.common.ambient; + +import android.content.ComponentName; +import android.content.Context; +import android.os.Handler; +import android.util.Log; + +import com.cyanogen.ambient.common.api.AmbientApiClient; +import com.cyanogen.ambient.common.api.PendingResult; +import com.cyanogen.ambient.common.api.Result; +import com.cyanogen.ambient.common.api.ResultCallback; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * Helper method for holding and broadcasting updated plugin data + * + */ +public abstract class AmbientDataSubscription { + + private static final String TAG = AmbientDataSubscription.class.getSimpleName(); + private static final boolean DEBUG = false; + + // Our ambient client + public AmbientApiClient mClient; + public Context mContext; + + // The handler that processes broadcasts + private Handler mMainHandler; + + // Holder for plugin object + private HashMap mPluginInfo; + + private static boolean mDataHasBeenBroadcastPreviously = false; + + // A list of our registered clients, who all register with the CallMethodReceiver + private static HashMap mRegisteredClients = new HashMap<>(); + + // A map of our components and if they have have enabled IInterface listeners. This is to keep + // track of if listeners should be added or removed. + private static HashMap mEnabledListeners = new HashMap<>(); + + // Wait up to 60 seconds for data to return. Mimicks what PluginBinderManager does. + private static final long TIMEOUT_MILLISECONDS = 60000L; + + // Bootstrap is the initial callback to get your installed plugins. + // this is the only item that executes ASAP and has no componentname tied to it + public ResultCallback BOOTSTRAP = new ResultCallback() { + + @Override + public void onResult(Result result) { + List installedPlugins = getPluginComponents(result); + for (ComponentName cn : installedPlugins) { + ArrayList apiCallbacks = new ArrayList<>(); + getPluginInfo().put(cn, getNewModObject(cn)); + requestedModInfo(apiCallbacks, cn); + executeAll(apiCallbacks, cn); + } + } + + }; + + public AmbientDataSubscription(Context context) { + mContext = context; + mClient = AmbientConnection.CLIENT.get(context); + mMainHandler = new Handler(context.getMainLooper()); + mPluginInfo = new HashMap<>(); + } + + public interface PluginChanged { + void onChanged(HashMap pluginInfo); + } + + /** + * OnPostResult is always called on the main thread (like onPostExecute in an asynctask) + * + * @param plugin The object that represents the plugin + * @param result the result of the resultCallback + * @param type the type defined by the TypedPendingResult that was sent to the apiexecutor + */ + protected abstract void onPostResult(M plugin, Result result, int type); + + /** + * Callback that gets the initial list of componentnames that are valid plugin for us to query + * + * @param result result of the initial call + * @return list of componentnames + */ + protected abstract List getPluginComponents(Result result); + + /** + * Gets the required queries for all our plugins + * + * @param queries ArrayList for us to add TypedPendingResults to + * @param componentName ComponentName of the plugin we will be querying + */ + protected abstract void requestedModInfo(ArrayList queries, + ComponentName componentName); + + + /** + * Action that takes place when a refresh is requested. + */ + protected abstract void onRefreshRequested(); + + /** + * Called when dynamic items need to be refreshed. Dynamic items being things that may change + * over time in a plugin. + * + * @param apiCallbacks callbacks to add to the queue + * @param componentName of plugin that needs to be updated + */ + protected abstract void onDynamicRefreshRequested(ArrayList apiCallbacks, + ComponentName componentName); + + /** + * Methods to enable and disable listeners on a plugin + * + * @param plugin object that needs to be listened to. + */ + protected abstract void enableListeners(M plugin); + protected abstract void disableListeners(M plugin); + + /** + * Gets a new plugin object + * + * @param componentName of the plugin + * @return M object + */ + protected abstract M getNewModObject(ComponentName componentName); + + private void enablePluginListeners(ComponentName cn) { + if (!mEnabledListeners.containsKey(cn) || !mEnabledListeners.get(cn)) { + M plugin = getPluginIfExists(cn); + if (plugin != null) { + enableListeners(plugin); + mEnabledListeners.put(cn, true); + } + + } + } + + private void disablePluginListeners(ComponentName cn) { + if (mEnabledListeners.containsKey(cn) && mEnabledListeners.get(cn)) { + M plugin = getPluginIfExists(cn); + disableListeners(plugin); + mEnabledListeners.put(cn, false); + } + } + + /** + * Broadcasts mModInfo to all registered clients on the Main thread. + */ + public void broadcast() { + mMainHandler.post(new Runnable() { + @Override + public void run() { + if (DEBUG) Log.d(TAG, "broadcast"); + for (PluginChanged client : mRegisteredClients.values()) { + client.onChanged(mPluginInfo); + } + } + }); + } + + /*** + * Registers the client, on register returns boolean if plugin info is already collected + * and the initial broadcast has been sent. + * + * @param id unique string for the client + * @param cmr client receiver + * @return boolean isempty + */ + public boolean subscribe(String id, PluginChanged cmr) { + mRegisteredClients.put(id, cmr); + if (DEBUG) Log.v("TAG", "subscribed: " + id); + return mDataHasBeenBroadcastPreviously; + } + + /** + * Unsubscribes the client. All clients must unsubscribe when the client ends. + * + * @param id of the client to remove + */ + public void unsubscribe(String id) { + mRegisteredClients.remove(id); + if (mRegisteredClients.isEmpty()) { + for (ComponentName cn : mPluginInfo.keySet()) { + disablePluginListeners(cn); + } + } + } + + /** + * Refreshes certain items that can constantly change. + */ + public void refreshDynamicItems() { + for (ComponentName cn : mPluginInfo.keySet()) { + ArrayList apiCallbacks = new ArrayList<>(); + onDynamicRefreshRequested(apiCallbacks, cn); + executeAll(apiCallbacks, cn); + } + } + + private void executeAll(final ArrayList apiCallbacks, + final ComponentName componentName) { + + final ArrayList pendingResults = new ArrayList<>(); + + for (final TypedPendingResult pendingResult : apiCallbacks) { + pendingResult.setResultCallback(new ResultCallback() { + @Override + public void onResult(Result result) { + pendingResults.remove(pendingResult.mPendingResult); + if (result == null) { + // Our plugin failed to make this call, log out what and why + Log.e(TAG, "Result from: " + componentName.getPackageName() + " failed"); + } else { + if (result.getStatus().isSuccess()) { + M plugin = getPluginIfExists(componentName); + onPostResult(plugin, result, pendingResult.mType); + + // check to see if our onPostResult removed the plugin. + if (!getPluginInfo().containsKey(componentName)) { + // Our plugin no longer exists for some reason, clear other callbacks + // and broadcast changes. + for (PendingResult pr : pendingResults) { + pr.cancel(); + } + pendingResults.clear(); + } + } else if (result.getStatus().isCanceled()) { + // Our plugin was found invalid or no longer exists. + Log.e(TAG, "Queries to: " + componentName.getPackageName() + + " are cancelled"); + } else { + Log.e(TAG, "Query to: " + componentName.getPackageName() + " " + + result.getClass().getSimpleName() + " failed. Code: " + + result.getStatus().getStatusMessage()); + } + } + maybeBroadcastToSubscribers(pendingResults); + + } + }, TIMEOUT_MILLISECONDS, TimeUnit.MILLISECONDS); + + // Add items that we've already added to our queue for so we can cancel them if + // need be. Set resultcallback will return instantly but onResult will not. + // So we can use this as our "counter" to determine if we should broadcast + pendingResults.add(pendingResult.mPendingResult); + } + enablePluginListeners(componentName); + } + + public void refresh() { + onRefreshRequested(); + } + + public static boolean infoReady() { + return mDataHasBeenBroadcastPreviously; + } + + private void maybeBroadcastToSubscribers(ArrayList apiCallbacks) { + maybeBroadcastToSubscribers(apiCallbacks, false); + } + + /** + * Broadcast to subscribers once we know we've gathered all our data. Do not do this until we + * have everything we need for sure. + * + * This method is called after every callback from ModCore. We will keep track of all of + * the callbacks, once we have accounted for all callbacks from all plugins, we can go ahead + * and update subscribers. + * + * @param apiCallbacks hashmap of our callbacks and pendingresults + * @param critical marks this call as critical to shortcircut our general broadcast and + * broadcast right away. + */ + private void maybeBroadcastToSubscribers(ArrayList apiCallbacks, + boolean critical) { + + if (apiCallbacks.isEmpty() || critical) { + // we are on the last item or we are a critical item. broadcast updated hashmap + broadcast(); + // We don't want to tell providers that our main data has already been broadcast + // if it's just a critical broadcast to prevent extra work from our subscribers + // once our long running calls are broadcast then this will be true. + if (!critical) { + mDataHasBeenBroadcastPreviously = true; + } + } + } + + /** + * In order to speed up the process we make calls for providers that may be invalid + * To prevent this, make sure every resultcallback uses this before filling in the hashmap. + * @param cn componentname + * @return M if valid, otherwise null + */ + public M getPluginIfExists(ComponentName cn) { + return getPluginInfo().get(cn); + } + + public HashMap getPluginInfo() { + return mPluginInfo; + } +} diff --git a/src-ambient/com/android/phone/common/ambient/SingletonHolder.java b/src-ambient/com/android/phone/common/ambient/SingletonHolder.java new file mode 100644 index 0000000..bca6023 --- /dev/null +++ b/src-ambient/com/android/phone/common/ambient/SingletonHolder.java @@ -0,0 +1,56 @@ +package com.android.phone.common.ambient; + +/** + * Encapsulates a threadsafe singleton pattern. + * + * This class is designed to be used as a public constant, living within a class that has a private constructor. + * It defines a {@link #create(I)} method that will only ever be called once, upon the first call of {@link #get(I)}. + * That method is responsible for creating the actual singleton instance, and that instance will be returned for all + * future calls of {@link #get(I)}. + * + * Example: + * + * public class FooSingleton { + * public static final SingletonHolder<FooSingleton, ParamObject> HOLDER = + * new SingletonHolder<FooSingleton, ParamObject>() { + * @Override + * protected FooSingleton create(ParamObject param) { + * return new FooSingleton(param); + * } + * }; + * + * private FooSingleton(ParamObject param) { + * + * } + * } + * + * // somewhere else + * FooSingleton.HOLDER.get(params).doStuff(); + * + * @param The type of the class to hold as a singleton. + * @param A parameter object to use during creation of the singleton object. + */ +public abstract class SingletonHolder { + private E mInstance; + private final Object LOCK = new Object(); + + public final E get(I initializer) { + if (null == mInstance) { + synchronized (LOCK) { + if (null == mInstance) { + mInstance = create(initializer); + } + } + } + + return mInstance; + } + + public final boolean isCreated() { + synchronized (LOCK) { + return mInstance != null; + } + } + + protected abstract E create(I initializer); +} diff --git a/src-ambient/com/android/phone/common/ambient/TypedPendingResult.java b/src-ambient/com/android/phone/common/ambient/TypedPendingResult.java new file mode 100644 index 0000000..783b519 --- /dev/null +++ b/src-ambient/com/android/phone/common/ambient/TypedPendingResult.java @@ -0,0 +1,53 @@ +package com.android.phone.common.ambient; + +import com.cyanogen.ambient.common.api.PendingResult; +import com.cyanogen.ambient.common.api.ResultCallback; + +import java.util.concurrent.TimeUnit; + +/** + * PendingResult wrapped with a Type + * + * Some queries return the same pending result type. We need a way to differentiate these items to + * know how to process them. + */ +public class TypedPendingResult { + + PendingResult mPendingResult; + int mType; + + // None should be used when a unique Result will be returned from our ResultCallback. + public static final int NONE = 0; + + // 1 - 1000 Reserved for INCALL API + public static final int GENERAL_MIME_TYPE = 1; + public static final int IM_MIME_TYPE = 2; + public static final int VIDEO_MIME_TYPE = 3; + + public static final int SETTINGS_INTENT = 4; + public static final int CREDIT_INTENT = 5; + public static final int LOGIN_INTENT = 6; + public static final int DEFAULT_DIRECTORY_SEARCH_INTENT = 7; + + public static final int GENERAL_DATA = 8; + public static final int STATUS = 9; + public static final int AUTHENTICATION = 10; + public static final int CREDIT_INFO = 11; + public static final int ACCOUNT_HANDLE = 12; + public static final int HINT_TEXT = 13; + + // 1001 - 2000 Reserved for Nudges + public static final int INCALL_CREDIT_NUDGE = 1001; + public static final int INCALL_CONTACT_CARD_LOGIN = 1002; + public static final int INCALL_CONTACT_CARD_DOWNLOAD = 1003; + public static final int INCALL_CONTACT_FRAGMENT_LOGIN = 1004; + + public TypedPendingResult(PendingResult pendingResult, int type) { + this.mPendingResult = pendingResult; + this.mType = type; + } + + public void setResultCallback(ResultCallback resultCallback, long length, TimeUnit unit) { + this.mPendingResult.setResultCallback(resultCallback, length, unit); + } +} diff --git a/src-ambient/com/android/phone/common/ambient/utils/PluginUtils.java b/src-ambient/com/android/phone/common/ambient/utils/PluginUtils.java new file mode 100644 index 0000000..471457b --- /dev/null +++ b/src-ambient/com/android/phone/common/ambient/utils/PluginUtils.java @@ -0,0 +1,26 @@ +package com.android.phone.common.ambient.utils; + +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.util.Log; + +/** + * Utilites for Plugins + */ +public class PluginUtils { + + private static final String TAG = PluginUtils.class.getSimpleName(); + + public static Resources getPluginResources(Context context, ComponentName componentName) { + PackageManager packageManager = context.getPackageManager(); + try { + return packageManager.getResourcesForApplication(componentName.getPackageName()); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Plugin isn't installed: " + componentName.flattenToShortString()); + return null; + } + } + +} diff --git a/src-ambient/com/android/phone/common/incall/CallMethodInfo.java b/src-ambient/com/android/phone/common/incall/CallMethodInfo.java new file mode 100644 index 0000000..cbc5242 --- /dev/null +++ b/src-ambient/com/android/phone/common/incall/CallMethodInfo.java @@ -0,0 +1,357 @@ +/* + * Copyright (C) 2015 The CyanogenMod 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.phone.common.incall; + +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.os.UserHandle; +import android.provider.ContactsContract; +import android.telecom.PhoneAccountHandle; +import android.telephony.PhoneNumberUtils; +import android.telephony.SubscriptionManager; +import android.text.TextUtils; +import android.util.Log; +import com.android.phone.common.ambient.AmbientConnection; +import com.android.phone.common.R; +import com.android.phone.common.incall.listeners.AuthenticationListenerImpl; +import com.android.phone.common.incall.listeners.CallCreditListenerImpl; +import com.android.phone.common.incall.utils.CallMethodUtils; +import com.cyanogen.ambient.incall.InCallServices; +import com.cyanogen.ambient.incall.extension.CreditBalance; +import com.cyanogen.ambient.incall.extension.CreditInfo; +import com.cyanogen.ambient.incall.extension.StartCallRequest; +import com.cyanogen.ambient.incall.extension.SubscriptionInfo; +import com.google.common.base.Objects; + +import java.math.BigDecimal; +import java.util.Currency; +import java.util.List; + +public class CallMethodInfo { + + public String mId; + public UserHandle mUserHandle; + public ComponentName mComponent; + public String mName; + public String mSummary; + public int mSlotId; + public int mSubId; + public int mColor; + public int mStatus; + public boolean mIsInCallProvider; + public boolean mIsAuthenticated; + public String mMimeType; + public String mVideoCallableMimeType; + public String mImMimeType; + public String mSubscriptionButtonText; + public String mCreditButtonText; + public String mT9HintDescriptionNoCreditOrSub; + public String mT9HintDescriptionHasCreditOrSub; + /* Plugin's simple brand icon (24dp x 24dp) + Expected format: Vector Drawable (.xml) + 2 colors allowed. */ + public Drawable mBrandIcon; + /* Plugin's single color simple brand icon (24dp x 24dp) + Same as to pluginBrandIcon, but only a single color. + Please keep image single color, it will be tinted in the client application. + Expected format: Vector Drawable (.xml) + 1 color allowed. */ + public Drawable mSingleColorBrandIcon; + /* Plugin's attribution badge (17dp x 17dp) + Similar to pluginBrandIcon. + To make badge pop against (on top of) images, please add a white outline. + Expected format: Vector Drawable (.xml) + 2 colors allowed. */ + public Drawable mBadgeIcon; + /* Plugin's full brand icon (any width (max 360dp) x 60dp height) + May contain full brand name or icon image. + Please keep image single color, it will be tinted in the client application. + Expected format: Vector Drawable (.xml) + 1 color allowed. */ + public Drawable mLoginIcon; + /* Plugin's video call action icon (24dp x 24dp) + Expected format: Vector Drawable (.xml) + 1 color allowed. */ + public Drawable mVideoIcon; + /* Plugin's IM action icon (24dp x 24dp) + Expected format: Vector Drawable (.xml) + 1 color allowed. */ + public Drawable mImIcon; + /* Plugin's voice call action icon (24dp x 24dp) + Expected format: Vector Drawable (.xml) + 1 color allowed. */ + public Drawable mVoiceIcon; + public CreditInfo mProviderCreditInfo; + public float mCreditWarn = 0.0f; + private int mCurrencyAmount; + private CallCreditListenerImpl mCreditListener; + public PendingIntent mManageCreditIntent; + public PendingIntent mLoginIntent; + public PendingIntent mDefaultDirectorySearchIntent; // empty contact Uri + public PendingIntent mDirectorySearchIntent; + public PendingIntent mInviteIntent; + public PendingIntent mSettingsIntent; + private static CallMethodInfo sEmergencyCallMethod; + private AuthenticationListenerImpl mAuthListener; + public String mAccountType; + public String mDependentPackage; + public String mLoginSubtitle; + public String mAccountHandle; + public int mLoginIconId; // resource ID + public int mBrandIconId; // resource ID + // Contact install nudge + public boolean mInstallNudgeEnable; + public String mInstallNudgeSubtitle; + public String mInstallNudgeActionText; + // Contact login nudge + public boolean mLoginNudgeEnable; + public String mLoginNudgeSubtitle; + public String mLoginNudgeActionText; + + @Override + public int hashCode() { + return Objects.hashCode(mId, mComponent, mName, mSlotId, mSubId); + } + + public static final String TAG = "CallMethodInfo"; + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object instanceof CallMethodInfo) { + final CallMethodInfo info = (CallMethodInfo) object; + return Objects.equal(this.mId, info.mId) + && Objects.equal(this.mComponent, info.mComponent) + && Objects.equal(this.mName, info.mName) + && Objects.equal(this.mSlotId, info.mSlotId) + && Objects.equal(this.mSubId, info.mSubId); + } + return false; + } + + /** + * return empty mock call method that represents emergency call only mode + */ + public static CallMethodInfo getEmergencyCallMethod(Context ctx) { + if (sEmergencyCallMethod == null) { + sEmergencyCallMethod = new CallMethodInfo(); + sEmergencyCallMethod.mName = + ctx.getString(R.string.call_method_spinner_item_emergency_call); + sEmergencyCallMethod.mColor = + ctx.getResources().getColor(R.color.emergency_call_icon_color); + sEmergencyCallMethod.mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + sEmergencyCallMethod.mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX; + sEmergencyCallMethod.mIsInCallProvider = false; + } + + return sEmergencyCallMethod; + } + + public static PhoneAccountHandle getPhoneAccountHandleFromCallMethodInfo(Context ctx, + CallMethodInfo callMethodInfo, String number) { + CallMethodInfo emergencyCallMethod = getEmergencyCallMethod(ctx); + // If no sim is selected, or emergency callmethod selected, or number is + // an emergency number, phone account handle should be null, and will use the + // default account. + // Else, create PhoneAccountHandle from selected callmethod components and + // initial call using that account. + PhoneAccountHandle handle = null; + if (callMethodInfo != null && !callMethodInfo.mIsInCallProvider && + !callMethodInfo.equals(emergencyCallMethod) && + (number == null || !PhoneNumberUtils.isEmergencyNumber(number))) { + handle = new PhoneAccountHandle(callMethodInfo.mComponent, + callMethodInfo.mId, + callMethodInfo.mUserHandle); + } + return handle; + } + + public void placeCall(String origin, String number, Context c) { + placeCall(origin, number, c, false); + } + + public void placeCall(String origin, String number, Context c, boolean isVideoCall) { + placeCall(origin, number, c, isVideoCall, false, null, null); + } + + public void placeCall(String origin, String number, Context c, boolean isVideoCall, boolean + forcePSTN) { + placeCall(origin, number, c, isVideoCall, forcePSTN, null, null); + } + + public void placeCall(String origin, String number, Context c, boolean isVideoCall, boolean + forcePSTN, StartInCallCallReceiver.InCallCallListener listener) { + placeCall(origin, number, c, isVideoCall, forcePSTN, null, listener); + } + + public void placeCall(String origin, String number, Context c, boolean isVideoCall, + boolean forcePSTN, String numberMimeType) { + placeCall(origin, number, c, isVideoCall, forcePSTN, numberMimeType, null); + } + + public void placeCall(String origin, String number, Context c, boolean isVideoCall, + boolean forcePSTN, String numberMimeType, + StartInCallCallReceiver.InCallCallListener listener) { + + StartInCallCallReceiver svcrr + = CallMethodUtils.getVoIPResultReceiver(c, this, origin, listener); + StartCallRequest request = new StartCallRequest(number, origin, 0, svcrr); + + if (isVideoCall) { + InCallServices.getInstance().startVideoCall( + AmbientConnection.CLIENT.get(c), this.mComponent, request); + } else { + if (forcePSTN || (numberMimeType != null && numberMimeType.equals(ContactsContract + .CommonDataKinds.Phone.CONTENT_ITEM_TYPE))) { + InCallServices.getInstance().startOutCall( + AmbientConnection.CLIENT.get(c), this.mComponent, request); + } else { + InCallServices.getInstance().startVoiceCall( + AmbientConnection.CLIENT.get(c), this.mComponent, request); + } + } + } + + public String getCreditsDescriptionText(Resources r) { + CreditInfo ci = this.mProviderCreditInfo; + + List subscriptionInfos = ci.subscriptions; + + if (showSubscriptions()) { + int size = subscriptionInfos.size(); + return r.getQuantityString(R.plurals.number_of_incall_subscriptions, size, size); + } else { + CreditBalance balance = ci.balance; + if (balance != null) { + mCurrencyAmount = (int) balance.balance; + try { + if (balance.currencyCode != null) { + Currency currencyCode = Currency.getInstance(balance.currencyCode); + BigDecimal availableCredit = BigDecimal.valueOf(mCurrencyAmount, + currencyCode.getDefaultFractionDigits()); + + + return currencyCode.getSymbol(r.getConfiguration().locale) + + availableCredit.toString(); + } else { + throw new IllegalArgumentException(); + } + } catch (IllegalArgumentException e) { + Log.w(TAG, "Unable to retrieve currency code for plugin: " + + this.mComponent); + + return null; + } + } else { + Log.e(TAG, "Plugin has credit component but the balance and subscriptions are" + + " null. This should never happen. Not showing credit banner due to " + + "failures."); + + return null; + } + } + } + + public boolean hasAvailableCredit() { + boolean hasAvailableCredit = false; + CreditInfo ci = this.mProviderCreditInfo; + if (ci != null) { + CreditBalance balance = ci.balance; + if (balance != null) { + mCurrencyAmount = (int) balance.balance; + hasAvailableCredit = mCurrencyAmount > 0; + } + } + return hasAvailableCredit; + } + + public boolean hasSubscription() { + boolean hasSubscription = false; + CreditInfo ci = this.mProviderCreditInfo; + if (ci != null) { + List subscriptionInfos = ci.subscriptions; + hasSubscription = subscriptionInfos != null && !subscriptionInfos.isEmpty(); + } + return hasSubscription; + } + + public boolean showSubscriptions() { + // If the user has > 0 credits, we don't want to show the subscription. + boolean hasCredits = hasAvailableCredit(); + boolean hasSubscriptions = hasSubscription(); + + return !hasCredits && hasSubscriptions; + } + + public int getCurrencyAmount() { + return mCurrencyAmount; + } + + /** + * Returns hint text based on credit and subscription availability. + * If either is null or empty, use the non-null/non-empty string. + * Null is returned if both are null or empty. + * @return hintText String hint text for the current situation + */ + public String getHintText() { + String hintText = null; + boolean hasAvailableCredit = hasAvailableCredit(); + boolean usesSubscriptions = hasSubscription(); + if (!hasAvailableCredit && !usesSubscriptions) { + // No credits and no subscription + if (!TextUtils.isEmpty(mT9HintDescriptionNoCreditOrSub)) { + // Use no credits or subscription hint + hintText = mT9HintDescriptionNoCreditOrSub; + } else if (!TextUtils.isEmpty(mT9HintDescriptionHasCreditOrSub)) { + // If no credit/sub hint empty, but has credit/sub hint valid, use it + hintText = mT9HintDescriptionHasCreditOrSub; + } + } else { + // Has credits or subscription + if (!TextUtils.isEmpty(mT9HintDescriptionHasCreditOrSub)) { + // Use has credits/sub hint text + hintText = mT9HintDescriptionHasCreditOrSub; + } else if (!TextUtils.isEmpty(mT9HintDescriptionNoCreditOrSub)) { + // If has credit/sub hint empty, but no credit/sub hint valid, use it + hintText = mT9HintDescriptionNoCreditOrSub; + } + } + return hintText; + } + + public CallCreditListenerImpl getCreditListener() { + return mCreditListener; + } + + public AuthenticationListenerImpl getAuthListener() { + return mAuthListener; + } + + public void setCreditListener(CallCreditListenerImpl creditListener) { + mCreditListener = creditListener; + } + + public void setAuthListener(AuthenticationListenerImpl authListener) { + mAuthListener = authListener; + } + +} diff --git a/src-ambient/com/android/phone/common/incall/CallMethodSpinnerAdapter.java b/src-ambient/com/android/phone/common/incall/CallMethodSpinnerAdapter.java new file mode 100644 index 0000000..cb3a8fe --- /dev/null +++ b/src-ambient/com/android/phone/common/incall/CallMethodSpinnerAdapter.java @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2015 The CyanogenMod 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.phone.common.incall; + +import android.content.ComponentName; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.SpinnerAdapter; +import android.widget.TextView; + +import com.android.phone.common.R; +import com.android.phone.common.util.VolteUtils; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.cyanogen.ambient.incall.util.InCallHelper.NO_COLOR; + +public class CallMethodSpinnerAdapter extends ArrayAdapter + implements SpinnerAdapter { + + private static final String TAG = CallMethodSpinnerAdapter.class.getSimpleName(); + public static final int POSITION_UNKNOWN = -1; + + private final Context mContext; + private Map mComponentMap; + private boolean mShowVolte = false; + + public CallMethodSpinnerAdapter(Context context, List objects, boolean + showVolte) { + super(context, 0, objects); + mContext = context.getApplicationContext(); + processCallMethods(objects); + mShowVolte = showVolte; + } + + /** + * Remove all elements from the list. + */ + @Override + public void clear() { + super.clear(); + if (mComponentMap != null) { + mComponentMap.clear(); + } + } + + /** + * Adds the specified call method info at the end of the array. + * + * @param object The call method info entry to add at the end of the array. + */ + public void add(CallMethodInfo object) { + super.add(object); + processCallMethod(object); + } + + /** + * Adds the specified list of call method infos at the end of the array. + * + * @param objects The list of call method info entries to add at the end of the array. + */ + public void addAll(List objects) { + super.addAll(objects); + processCallMethods(objects); + } + + /** + * Adds the specified list of call method infos at the end of the array. + * + * @param objects The list of call method info entries to add at the end of the array. + */ + public void addAll(HashMap objects) { + super.addAll(objects.values()); + processCallMethods(objects.values()); + } + + /** + * Get a View that displays the data at the specified position in the data set. You can either + * create a View manually or inflate it from an XML layout file. When the View is inflated, the + * parent View (GridView, ListView...) will apply default layout parameters unless you use + * {@link LayoutInflater#inflate(int, ViewGroup, boolean)} + * to specify a root view and to prevent attachment to the root. + * + * @param position The position of the item within the adapter's data set of the item whose + * view we want. + * @param convertView The old view to reuse, if possible. Note: You should check that this view + * is non-null and of an appropriate type before using. If it is not possible + * to convert this view to display the correct data, this method can create a + * new view. Heterogeneous lists can specify their number of view types, so + * that this View is always of the right type + * (see {@link #getViewTypeCount()} and {@link #getItemViewType(int)}). + * @param parent The parent that this view will eventually be attached to + * @return A View corresponding to the data at the specified position. + */ + @Override + public View getView(int position, View convertView, ViewGroup parent) { + CallMethodInfo callMethodInfo = getItem(position); + boolean volteInUse = (callMethodInfo.mSubId > 0) && + VolteUtils.isVolteInUse(mContext, callMethodInfo.mSubId); + + SpinnerItemViewHolder holder = null; + // Note: if mVolteInUse changes, it invalidates cached views + if (convertView == null || (holder = (SpinnerItemViewHolder)convertView.getTag()) == null + || holder.volteInUse != volteInUse) + { + convertView = LayoutInflater.from(mContext).inflate(R.layout + .call_method_spinner_item, parent, false); + if (holder == null) holder = new SpinnerItemViewHolder(); + convertView.setTag(holder); + holder.volteInUse = volteInUse; + holder.callTypeIcon = (ImageView) convertView.findViewById(R.id + .call_method_spinner_item_image); + holder.volteIcon = (ImageView) convertView.findViewById(R.id + .call_method_spinner_volte_image); + } + if (mShowVolte && volteInUse) { + holder.volteIcon.setVisibility(View.VISIBLE); + } else { + holder.volteIcon.setVisibility(View.GONE); + } + setIcon(convertView, callMethodInfo); + + return convertView; + } + + /** + *

Get a {@link View} that displays in the drop down popup + * the data at the specified position in the data set.

+ * + * @param position index of the item whose view we want. + * @param convertView the old view to reuse, if possible. Note: You should + * check that this view is non-null and of an appropriate type before + * using. If it is not possible to convert this view to display the + * correct data, this method can create a new view. + * @param parent the parent that this view will eventually be attached to + * @return a {@link View} corresponding to the data at the + * specified position. + */ + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + CallMethodInfo callMethodInfo = getItem(position); + boolean volteInUse = (callMethodInfo.mSubId > 0) && + VolteUtils.isVolteInUse(mContext, callMethodInfo.mSubId); + + SpinnerDropDownItemViewHolder holder = null; + int tag = volteInUse ? R.string.call_method_spinner_volte_drop_down_tag : + R.string.call_method_spinner_drop_down_tag; + if (convertView == null || (holder = (SpinnerDropDownItemViewHolder) convertView.getTag + (tag)) == null) { + int resId = R.layout.call_method_spinner_dropdown_item; + convertView = LayoutInflater.from(mContext).inflate(resId, parent, false); + holder = new SpinnerDropDownItemViewHolder(); + holder.volteInUse = volteInUse; + holder.callTypeIcon = (ImageView) convertView.findViewById(R.id + .call_method_spinner_item_image); + holder.volteIcon = (ImageView) convertView.findViewById(R.id + .call_method_spinner_volte_image); + holder.textView = (TextView) convertView.findViewById(R.id + .call_method_spinner_item_text); + convertView.setTag(tag, holder); + } + if (mShowVolte && volteInUse) { + holder.volteIcon.setVisibility(View.VISIBLE); + } else { + holder.volteIcon.setVisibility(View.GONE); + } + setIcon(convertView, callMethodInfo); + holder.textView.setText(callMethodInfo.mName); + + return convertView; + } + + private void setIcon(View convertView, CallMethodInfo callMethodInfo) { + ImageView icon = (ImageView) convertView.findViewById(R.id.call_method_spinner_item_image); + if (callMethodInfo.mBrandIcon != null) { + icon.setImageDrawable(callMethodInfo.mBrandIcon); + icon.getDrawable().setTintList(null); + icon.setBackground(null); + } else { + int drawableId = getIconForSlot(callMethodInfo.mSlotId); + + Drawable forground = mContext.getDrawable(drawableId); + Drawable background = mContext.getDrawable(R.drawable.ic_sim_backing); + + if (callMethodInfo.mColor != NO_COLOR) { + forground.setTint(callMethodInfo.mColor); + } else { + forground.setTint(mContext.getResources().getColor(R.color.sim_icon_color)); + } + + Drawable[] layers = {background, forground}; + LayerDrawable layerDrawable = new LayerDrawable(layers); + icon.setImageDrawable(layerDrawable); + } + } + + public static int getIconForSlot(int slotId) { + switch (slotId) { + case -1: + // SubscriptionManager.INVALID_SIM_SLOT_INDEX + return R.drawable.ic_nosim; + case 1: + return R.drawable.ic_sim_2; + case 2: + return R.drawable.ic_sim_3; + case 3: + return R.drawable.ic_sim_4; + default: + return R.drawable.ic_sim_1; + } + } + + /** + * Map call method component names to spinner positions. + * @param callMethodInfo new call method + */ + private void processCallMethod(CallMethodInfo callMethodInfo) { + if (mComponentMap == null) { + mComponentMap = new HashMap(); + } + if (callMethodInfo != null) { + String key = getCallMethodKey(callMethodInfo); + mComponentMap.put(key, mComponentMap.size()); + } + } + + /** + * Map call method component names to spinner positions. + * @param callMethodInfoList list of current call methods + */ + private void processCallMethods(Collection callMethodInfoList) { + if (mComponentMap == null) { + mComponentMap = new HashMap(); + } + if (callMethodInfoList != null) { + for (CallMethodInfo info : callMethodInfoList) { + processCallMethod(info); + } + } + } + + /** + * Returns the position of the specified component within the spinner + * @param key String key for call method lookup + * @return Position of specified component, POSITION_UNKNOWN if not found. + */ + public int getPosition(String key) { + int position = POSITION_UNKNOWN; + if (!TextUtils.isEmpty(key)) { + position = mComponentMap.containsKey(key) ? + mComponentMap.get(key) : POSITION_UNKNOWN; + } + return position; + } + + /** + * Returns an ID for the specified CallMethodInfo + * @param info CallMethodInfo to create id for + * @return String key for specified CallMethodInfo. + */ + public static String getCallMethodKey(CallMethodInfo info) { + if (info == null) { + return null; + } + return String.valueOf(info.hashCode()); + } + + private static class SpinnerItemViewHolder { + ImageView callTypeIcon; + ImageView volteIcon; + boolean volteInUse; + } + + private static class SpinnerDropDownItemViewHolder { + ImageView callTypeIcon; + ImageView volteIcon; + TextView textView; + boolean volteInUse; + } +} diff --git a/src-ambient/com/android/phone/common/incall/ContactsDataSubscription.java b/src-ambient/com/android/phone/common/incall/ContactsDataSubscription.java new file mode 100644 index 0000000..2b38b1e --- /dev/null +++ b/src-ambient/com/android/phone/common/incall/ContactsDataSubscription.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.phone.common.incall; + +import android.content.ComponentName; +import android.content.Context; + +import com.android.phone.common.ambient.SingletonHolder; +import com.android.phone.common.ambient.TypedPendingResult; +import com.android.phone.common.incall.api.InCallListeners; +import com.android.phone.common.incall.api.InCallQueries; +import com.android.phone.common.nudge.api.NudgeQueries; +import com.cyanogen.ambient.discovery.util.NudgeKey; +import com.cyanogen.ambient.incall.extension.InCallContactInfo; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +public class ContactsDataSubscription extends DialerDataSubscription { + + public ContactsDataSubscription(Context context) { + super(context); + } + + private static final SingletonHolder sInstance = + new SingletonHolder() { + + @Override + protected ContactsDataSubscription create(Context context) { + // Let's get started here. + return new ContactsDataSubscription(context); + } + }; + + + public static ContactsDataSubscription get(Context context) { + return sInstance.get(context); + } + + public static boolean isCreated() { + return sInstance.isCreated(); + } + + public static void init(Context context) { + ContactsDataSubscription.get(context).refresh(); + } + + @Override + public void onDynamicRefreshRequested(ArrayList queries, + ComponentName componentName) { + queries.add(InCallQueries.getCallMethodAuthenticated(mClient, componentName)); + queries.add(InCallQueries.getCallMethodAccountHandle(mClient, componentName)); + } + + @Override + protected void enableListeners(CallMethodInfo mod) { + InCallListeners.enableAuthListener(this, mod); + } + + @Override + protected void disableListeners(CallMethodInfo mod) { + InCallListeners.disableAuthListener(this, mod); + } + + @Override + protected void requestedModInfo(ArrayList queries, + ComponentName componentName) { + + queries.add(InCallQueries.getCallMethodInfo(mClient, componentName)); + queries.add(InCallQueries.getCallMethodStatus(mClient, componentName)); + queries.add(InCallQueries.getCallMethodMimeType(mClient, componentName)); + queries.add(InCallQueries.getCallMethodVideoCallableMimeType(mClient, componentName)); + queries.add(InCallQueries.getCallMethodAuthenticated(mClient, componentName)); + queries.add(InCallQueries.getLoginIntent(mClient, componentName)); + queries.add(InCallQueries.getDefaultDirectorySearchIntent(mClient, componentName)); + queries.add(InCallQueries.getCallMethodImMimeType(mClient, componentName)); + + TypedPendingResult fragLogin = NudgeQueries.getNudgeConfig(mClient, mContext, componentName, + NudgeKey.INCALL_CONTACT_FRAGMENT_LOGIN); + if (fragLogin != null) { + queries.add(fragLogin); + } + TypedPendingResult cardLogin = NudgeQueries.getNudgeConfig(mClient, mContext, componentName, + NudgeKey.INCALL_CONTACT_CARD_LOGIN); + if (cardLogin != null) { + queries.add(cardLogin); + } + TypedPendingResult cardDownload = NudgeQueries.getNudgeConfig(mClient, mContext, + componentName, NudgeKey.INCALL_CONTACT_CARD_DOWNLOAD); + if (cardDownload != null) { + queries.add(cardDownload); + } + } + + public static void refreshPendingIntents(InCallContactInfo contactInfo) { + // TODO: implement + } + + public Set getAllPluginComponentNames() { + Set names = new HashSet(); + HashMap plugins = this.getPluginInfo(); + for (ComponentName cn : plugins.keySet()) { + names.add(cn.flattenToString()); + } + return names; + } +} diff --git a/src-ambient/com/android/phone/common/incall/CreditBarHelper.java b/src-ambient/com/android/phone/common/incall/CreditBarHelper.java new file mode 100644 index 0000000..7ef7584 --- /dev/null +++ b/src-ambient/com/android/phone/common/incall/CreditBarHelper.java @@ -0,0 +1,105 @@ +package com.android.phone.common.incall; + +import android.app.PendingIntent; +import android.content.res.Resources; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.android.phone.common.R; + +/** + * Helper method used to handle incall credit bars + * + * The credit bar's origin code comes from google. We've added a second credit bar in dialer that + * uses these same helper method. + */ +public class CreditBarHelper { + + private static final String TAG = CreditBarHelper.class.getSimpleName(); + + public interface CreditBarVisibilityListener { + public void creditsBarVisibilityChanged(int visibility); + } + + public static void clearCallRateInformation(ViewGroup v, CreditBarVisibilityListener cpvl) { + setCallRateInformation(null, v, null, null, null, false, cpvl); + } + + public static void setCallRateInformation(Resources res, ViewGroup creditsBar, + String creditText, String buttonText, final PendingIntent buttonIntent, + boolean warnIfLow, CreditBarVisibilityListener cpvl) { + if (TextUtils.isEmpty(creditText) && TextUtils.isEmpty(buttonText) && + buttonIntent == null) { + if (creditsBar.getVisibility() != View.GONE) { + creditsBar.setVisibility(View.GONE); + cpvl.creditsBarVisibilityChanged(View.GONE); + } + return; + } + if (creditsBar.getVisibility() != View.VISIBLE) { + creditsBar.setVisibility(View.VISIBLE); + cpvl.creditsBarVisibilityChanged(View.VISIBLE); + } + + // These views already exist, we are hijacking them. + TextView credit = (TextView) creditsBar.findViewById(R.id.ild_country); + TextView button = (TextView) creditsBar.findViewById(R.id.ild_rate); + + if (res != null) { + int color = warnIfLow ? + res.getColor(R.color.credit_banner_alert_color) : + res.getColor(R.color.credit_banner_text); + credit.setTextColor(color); + } + + credit.setText(creditText); + button.setText(buttonText); + button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + try { + if (buttonIntent != null) { + buttonIntent.send(); + } else { + Log.wtf(TAG, "The intent we attempted to fire was null"); + } + } catch (PendingIntent.CanceledException e) { + e.printStackTrace(); + } + } + }); + } + + public static void callMethodCredits(ViewGroup v, CallMethodInfo cmi, Resources r, + CreditBarVisibilityListener cpvl) { + String creditText = cmi.getCreditsDescriptionText(r); + String buttonText = null; + PendingIntent button; + + boolean warnIfLow = false; + if (TextUtils.isEmpty(creditText)) { + clearCallRateInformation(v, cpvl); + return; + } else { + if (cmi.mIsAuthenticated) { + button = cmi.mManageCreditIntent; + if (cmi.showSubscriptions()) { + buttonText = cmi.mSubscriptionButtonText; + } else { + if (cmi.getCurrencyAmount() <= cmi.mCreditWarn) { + warnIfLow = true; + } + buttonText = cmi.mCreditButtonText; + } + } else { + buttonText = r.getString(R.string.sign_in_credit_banner_text); + creditText = ""; + button = cmi.mLoginIntent; + } + } + setCallRateInformation(r, v, creditText, buttonText, button, warnIfLow, cpvl); + } +} diff --git a/src-ambient/com/android/phone/common/incall/DialerDataSubscription.java b/src-ambient/com/android/phone/common/incall/DialerDataSubscription.java new file mode 100644 index 0000000..d4b6071 --- /dev/null +++ b/src-ambient/com/android/phone/common/incall/DialerDataSubscription.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2015 The CyanogenMod 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.phone.common.incall; + +import android.content.ComponentName; +import android.content.Context; +import android.util.Log; + +import com.android.phone.common.ambient.SingletonHolder; +import com.android.phone.common.ambient.TypedPendingResult; +import com.android.phone.common.incall.api.InCallListeners; +import com.android.phone.common.incall.api.InCallQueries; +import com.android.phone.common.incall.api.InCallResults; +import com.android.phone.common.nudge.api.NudgeQueries; +import com.cyanogen.ambient.common.api.Result; +import com.cyanogen.ambient.discovery.results.BundleResult; +import com.cyanogen.ambient.discovery.util.NudgeKey; +import com.cyanogen.ambient.incall.extension.GetCreditInfoResult; +import com.cyanogen.ambient.incall.results.AccountHandleResult; +import com.cyanogen.ambient.incall.results.AuthenticationStateResult; +import com.cyanogen.ambient.incall.results.GetCreditInfoResultResult; +import com.cyanogen.ambient.incall.results.HintTextResultResult; +import com.cyanogen.ambient.incall.results.InCallProviderInfoResult; +import com.cyanogen.ambient.incall.results.InstalledPluginsResult; +import com.cyanogen.ambient.incall.results.MimeTypeResult; +import com.cyanogen.ambient.incall.results.PendingIntentResult; +import com.cyanogen.ambient.incall.results.PluginStatusResult; +import com.android.phone.common.ambient.AmbientDataSubscription; + +import java.util.ArrayList; +import java.util.List; + +import static com.cyanogen.ambient.incall.util.InCallHelper.NO_COLOR; + +/** + * Call Method Helper - In charge of loading InCall Mod Data + * + * Fragments and Activities can subscribe to changes with subscribe. + */ +public class DialerDataSubscription extends AmbientDataSubscription { + + protected static final String TAG = DialerDataSubscription.class.getSimpleName(); + + private static final int CALL_METHOD_TYPE = -1; + + public DialerDataSubscription(Context context) { + super(context); + } + + public static final SingletonHolder sInstance = + new SingletonHolder() { + + @Override + protected DialerDataSubscription create(Context context) { + return new DialerDataSubscription(context); + } + + }; + + public static DialerDataSubscription get(Context context) { + return sInstance.get(context); + } + + public static boolean isCreated() { + return sInstance.isCreated(); + } + + public static void init(Context context) { + sInstance.get(context).refresh(); + } + + @Override + protected void onRefreshRequested() { + InCallQueries.updateCallPlugins(this); + } + + @Override + protected List getPluginComponents(Result result) { + return InCallResults.gotInstalledPlugins((InstalledPluginsResult)result); + } + + @Override + protected void requestedModInfo(ArrayList queries, + ComponentName componentName) { + + queries.add(InCallQueries.getCallMethodInfo(mClient, componentName)); + queries.add(InCallQueries.getCallMethodStatus(mClient, componentName)); + queries.add(InCallQueries.getCallMethodMimeType(mClient, componentName)); + queries.add(InCallQueries.getCallMethodVideoCallableMimeType(mClient, componentName)); + queries.add(InCallQueries.getCallMethodAuthenticated(mClient, componentName)); + queries.add(InCallQueries.getLoginIntent(mClient, componentName)); + queries.add(InCallQueries.getSettingsIntent(mClient, componentName)); + queries.add(InCallQueries.getCreditInfo(mClient, componentName)); + queries.add(InCallQueries.getHintText(mClient, componentName)); + queries.add(InCallQueries.getManageCreditsIntent(mClient, componentName)); + + TypedPendingResult creditQuery = NudgeQueries.getNudgeConfig(mClient, mContext, + componentName, NudgeKey.INCALL_CREDIT_NUDGE); + if (creditQuery != null) { + queries.add(creditQuery); + } + } + + @Override + protected CallMethodInfo getNewModObject(ComponentName componentName) { + CallMethodInfo callMethodInfo = new CallMethodInfo(); + callMethodInfo.mComponent = componentName; + callMethodInfo.mSlotId = CALL_METHOD_TYPE; + callMethodInfo.mSubId = CALL_METHOD_TYPE; + callMethodInfo.mColor = NO_COLOR; + callMethodInfo.mIsInCallProvider = true; + return callMethodInfo; + } + + @Override + protected void onDynamicRefreshRequested(ArrayList queries, + ComponentName componentName) { + + queries.add(InCallQueries.getCallMethodAuthenticated(mClient, componentName)); + queries.add(InCallQueries.getCreditInfo(mClient, componentName)); + } + + @Override + protected void enableListeners(CallMethodInfo cn) { + InCallListeners.enableCreditListener(this, cn); + InCallListeners.enableAuthListener(this, cn); + } + + @Override + protected void disableListeners(CallMethodInfo cn) { + InCallListeners.disableCreditListener(this, cn); + InCallListeners.disableAuthListener(this, cn); + } + + @Override + protected void onPostResult(CallMethodInfo cmi, Result r, int type) { + switch (type) { + case TypedPendingResult.GENERAL_MIME_TYPE: + case TypedPendingResult.IM_MIME_TYPE: + case TypedPendingResult.VIDEO_MIME_TYPE: + InCallResults.gotMimeType(cmi, (MimeTypeResult)r, type); + break; + case TypedPendingResult.SETTINGS_INTENT: + case TypedPendingResult.CREDIT_INTENT: + case TypedPendingResult.LOGIN_INTENT: + case TypedPendingResult.DEFAULT_DIRECTORY_SEARCH_INTENT: + InCallResults.gotIntent(cmi, (PendingIntentResult)r, type); + break; + case TypedPendingResult.GENERAL_DATA: + InCallResults.gotGeneralInfo(cmi, this, (InCallProviderInfoResult)r); + break; + case TypedPendingResult.STATUS: + InCallResults.gotStatus(cmi, (PluginStatusResult)r); + break; + case TypedPendingResult.AUTHENTICATION: + InCallResults.gotAuthenticationState(cmi, (AuthenticationStateResult)r); + break; + case TypedPendingResult.CREDIT_INFO: + GetCreditInfoResult gcir = ((GetCreditInfoResultResult)r).result; + InCallResults.gotCreditData(cmi, gcir); + break; + case TypedPendingResult.ACCOUNT_HANDLE: + InCallResults.gotAccountHandle(cmi, mContext, (AccountHandleResult)r); + break; + case TypedPendingResult.HINT_TEXT: + InCallResults.gotHintText(cmi, (HintTextResultResult)r); + break; + case TypedPendingResult.INCALL_CONTACT_CARD_DOWNLOAD: + case TypedPendingResult.INCALL_CONTACT_CARD_LOGIN: + case TypedPendingResult.INCALL_CONTACT_FRAGMENT_LOGIN: + case TypedPendingResult.INCALL_CREDIT_NUDGE: + InCallResults.gotNudgeData(mContext, cmi, (BundleResult)r, type); + break; + default: + Log.e(TAG, "Unhandled result type!"); + break; + } + } +} diff --git a/src-ambient/com/android/phone/common/incall/StartInCallCallReceiver.java b/src-ambient/com/android/phone/common/incall/StartInCallCallReceiver.java new file mode 100644 index 0000000..52bfdd0 --- /dev/null +++ b/src-ambient/com/android/phone/common/incall/StartInCallCallReceiver.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 The CyanogenMod 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.phone.common.incall; + +import android.os.Bundle; +import android.os.Handler; +import android.os.ResultReceiver; +import android.util.Log; + +import java.lang.ref.WeakReference; + +public class StartInCallCallReceiver extends ResultReceiver { + private static final String TAG = StartInCallCallReceiver.class.getSimpleName(); + private static final boolean DEBUG = false; + + private WeakReference mReceiver; + + public interface InCallCallListener { + void onResult(int resultCode); + } + + public StartInCallCallReceiver(Handler handler) { + super(handler); + } + + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + if (DEBUG) { + Log.d(TAG, "Result received resultCode: " + resultCode + " resultData: " + resultData); + } + + if (mReceiver != null) { + Receiver receiver = mReceiver.get(); + if (receiver != null) { + receiver.onReceiveResult(resultCode, resultData); + } + } + } + + public interface Receiver { + void onReceiveResult(int resultCode, Bundle resultData); + } + + public void setReceiver(Receiver receiver) { + mReceiver = new WeakReference(receiver); + } + +} \ No newline at end of file diff --git a/src-ambient/com/android/phone/common/incall/api/ApiHelper.java b/src-ambient/com/android/phone/common/incall/api/ApiHelper.java new file mode 100644 index 0000000..8a20aa2 --- /dev/null +++ b/src-ambient/com/android/phone/common/incall/api/ApiHelper.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.phone.common.incall.api; + +import android.content.Context; +import android.util.Log; + +import com.android.phone.common.ambient.AmbientDataSubscription; +import com.android.phone.common.incall.DialerDataSubscription; +import com.android.phone.common.incall.CallMethodInfo; +import com.android.phone.common.incall.ContactsDataSubscription; +import com.cyanogen.ambient.incall.InCallApi; +import com.cyanogen.ambient.incall.InCallServices; + +/** + * Helper for getting instance and InCall Api + */ +public class ApiHelper { + + static InCallApi thisApi() { + return InCallServices.getInstance(); + } + +} diff --git a/src-ambient/com/android/phone/common/incall/api/InCallListeners.java b/src-ambient/com/android/phone/common/incall/api/InCallListeners.java new file mode 100644 index 0000000..a25c1e1 --- /dev/null +++ b/src-ambient/com/android/phone/common/incall/api/InCallListeners.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.phone.common.incall.api; + +import android.content.Context; + +import com.android.phone.common.ambient.AmbientDataSubscription; +import com.android.phone.common.incall.listeners.AuthenticationListenerImpl; +import com.android.phone.common.incall.listeners.CallCreditListenerImpl; +import com.android.phone.common.incall.CallMethodInfo; + +/** + * Data Listeners that incall implements. + */ +public class InCallListeners extends ApiHelper { + + /** + * Enables credit listener on a Mod. + * @param instance us + * @param callMethodInfo CallMethod to add listener to + */ + public static void enableCreditListener(AmbientDataSubscription instance, + CallMethodInfo callMethodInfo) { + CallCreditListenerImpl listener = CallCreditListenerImpl.getInstance(instance.mContext, + callMethodInfo.mComponent); + thisApi().addCreditListener(instance.mClient, callMethodInfo.mComponent, + listener); + callMethodInfo.setCreditListener(listener); + } + + /** + * Disables credit listener on a Mod. + * @param instance us + * @param callMethodInfo CallMethod to remove listener from + */ + public static void disableCreditListener(AmbientDataSubscription instance, + CallMethodInfo callMethodInfo) { + CallCreditListenerImpl listener = callMethodInfo.getCreditListener(); + thisApi().removeCreditListener(instance.mClient, callMethodInfo.mComponent, + listener); + callMethodInfo.setCreditListener(null); + } + + /** + * Enable Auth Listener on a Mod + * @param instance us + * @param callMethodInfo CallMethod to add listener to + */ + public static void enableAuthListener(AmbientDataSubscription instance, + CallMethodInfo callMethodInfo) { + AuthenticationListenerImpl listener = AuthenticationListenerImpl.getInstance( + instance.mContext, callMethodInfo.mComponent); + thisApi().addAuthenticationListener(instance.mClient, callMethodInfo.mComponent, + listener); + callMethodInfo.setAuthListener(listener); + } + + /** + * Disable Auth Listener on a Mod + * @param instance us + * @param callMethodInfo CallMethod to remove listener from + */ + public static void disableAuthListener(AmbientDataSubscription instance, + CallMethodInfo callMethodInfo) { + AuthenticationListenerImpl listener = callMethodInfo.getAuthListener(); + thisApi().removeAuthenticationListener(instance.mClient, + callMethodInfo.mComponent, listener); + callMethodInfo.setAuthListener(null); + } +} diff --git a/src-ambient/com/android/phone/common/incall/api/InCallQueries.java b/src-ambient/com/android/phone/common/incall/api/InCallQueries.java new file mode 100644 index 0000000..384cfdb --- /dev/null +++ b/src-ambient/com/android/phone/common/incall/api/InCallQueries.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.phone.common.incall.api; + +import android.content.ComponentName; +import android.content.Context; +import android.net.Uri; +import android.util.Log; + +import com.android.phone.common.ambient.AmbientDataSubscription; +import com.android.phone.common.ambient.TypedPendingResult; +import com.android.phone.common.incall.CallMethodInfo; +import com.cyanogen.ambient.analytics.Event; +import com.cyanogen.ambient.common.api.AmbientApiClient; +import com.cyanogen.ambient.common.api.Result; +import com.cyanogen.ambient.common.api.ResultCallback; + +/** + * Queries for incall plugins + */ +public class InCallQueries extends ApiHelper { + + private static final String TAG = InCallQueries.class.getSimpleName(); + private static final boolean DEBUG = false; + + /** + * Prepare to query and fire off ModCore calls in all directions + */ + public static void updateCallPlugins(AmbientDataSubscription instance) { + thisApi().getInstalledPlugins(instance.mClient).setResultCallback(instance.BOOTSTRAP); + } + + /** + * Get the plugin info + * @param componentName of mod + * + * @return TypedPendingResult to identify which field to modify in our CallMethodInfo object + */ + public static TypedPendingResult getCallMethodInfo(AmbientApiClient client, + ComponentName componentName) { + + return new TypedPendingResult(thisApi().getProviderInfo(client, componentName), + TypedPendingResult.GENERAL_DATA); + } + + /** + * Get our mod enabled status + * @param componentName of mod + * + * @return TypedPendingResult to identify which field to modify in our CallMethodInfo object + */ + public static TypedPendingResult getCallMethodStatus(AmbientApiClient client, + ComponentName componentName) { + return new TypedPendingResult(thisApi().getPluginStatus(client, componentName), + TypedPendingResult.STATUS); + } + + /** + * Get the mod mimetype + * @param componentName of mod + * + * @return TypedPendingResult to identify which field to modify in our CallMethodInfo object + */ + public static TypedPendingResult getCallMethodMimeType(AmbientApiClient client, + ComponentName componentName) { + return new TypedPendingResult(thisApi().getCallableMimeType(client, componentName), + TypedPendingResult.GENERAL_MIME_TYPE); + } + + /** + * Get the plugin video mimetype + * @param componentName of mod + * + * @return TypedPendingResult to identify which field to modify in our CallMethodInfo object + */ + public static TypedPendingResult getCallMethodVideoCallableMimeType(AmbientApiClient client, + ComponentName componentName) { + return new TypedPendingResult(thisApi().getVideoCallableMimeType(client, componentName), + TypedPendingResult.VIDEO_MIME_TYPE); + } + + /** + * Get the plugin im mime type + * @param componentName of mod + * + * @return TypedPendingResult to identify which field to modify in our CallMethodInfo object + */ + public static TypedPendingResult getCallMethodImMimeType(AmbientApiClient client, + ComponentName componentName) { + return new TypedPendingResult(thisApi().getImMimeType(client, componentName), + TypedPendingResult.IM_MIME_TYPE); + } + + /** + * Get the Authentication state of the callmethod + * @param componentName of mod + * + * @return TypedPendingResult to identify which field to modify in our CallMethodInfo object + */ + public static TypedPendingResult getCallMethodAuthenticated(AmbientApiClient client, + ComponentName componentName) { + return new TypedPendingResult(thisApi().getAuthenticationState(client, componentName), + TypedPendingResult.AUTHENTICATION); + } + + /** + * Get the credits intent of the mod + * @param componentName of mod + * + * @return TypedPendingResult to identify which field to modify in our CallMethodInfo object + */ + public static TypedPendingResult getManageCreditsIntent(AmbientApiClient client, + ComponentName componentName) { + return new TypedPendingResult(thisApi().getManageCreditsIntent(client, componentName), + TypedPendingResult.CREDIT_INTENT); + } + + /** + * Get the login intent of the mod + * @param componentName of mod + * + * @return TypedPendingResult to identify which field to modify in our CallMethodInfo object + */ + public static TypedPendingResult getLoginIntent(AmbientApiClient client, + ComponentName componentName) { + return new TypedPendingResult(thisApi().getLoginIntent(client, componentName), + TypedPendingResult.LOGIN_INTENT); + } + + /** + * Get the settings intent for the callmethod + * @param componentName of mod + * + * @return TypedPendingResult to identify which field to modify in our CallMethodInfo object + */ + public static TypedPendingResult getSettingsIntent(AmbientApiClient client, + ComponentName componentName) { + return new TypedPendingResult(thisApi().getSettingsIntent(client, componentName), + TypedPendingResult.SETTINGS_INTENT); + } + + /** + * Get the credit info of the mod + * @param componentName of mod + * + * @return TypedPendingResult to identify which field to modify in our CallMethodInfo object + */ + public static TypedPendingResult getCreditInfo(AmbientApiClient client, + ComponentName componentName) { + return new TypedPendingResult(thisApi().getCreditInfo(client, componentName), + TypedPendingResult.CREDIT_INFO); + } + + /** + * Get the account handle of the mod + * @param componentName of mod + * + * @return TypedPendingResult to identify which field to modify in our CallMethodInfo object + */ + public static TypedPendingResult getCallMethodAccountHandle(AmbientApiClient client, + ComponentName componentName) { + return new TypedPendingResult(thisApi().getAccountHandle(client, componentName), + TypedPendingResult.ACCOUNT_HANDLE); + } + + /** + * Get the hint texts for the callmethod + * @param componentName of mod + * + * @return TypedPendingResult to identify which field to modify in our CallMethodInfo object + */ + public static TypedPendingResult getHintText(AmbientApiClient client, + ComponentName componentName) { + return new TypedPendingResult(thisApi().getHintText(client, componentName), + TypedPendingResult.HINT_TEXT); + } + + /** + * Get the default search intent for the mod + * @param componentName of mod + * + * @return TypedPendingResult to identify which field to modify in our CallMethodInfo object + */ + public static TypedPendingResult getDefaultDirectorySearchIntent(AmbientApiClient client, + ComponentName componentName) { + return new TypedPendingResult(thisApi().getDirectorySearchIntent(client, componentName, + Uri.parse("")), TypedPendingResult.DEFAULT_DIRECTORY_SEARCH_INTENT); + } + + /** + * Send analytics to our mod + * @param client ambient client + * @param componentName Componentname of the mod to send to + * @param event the event to send. + */ + public static void shipAnalyticsToPlugin(AmbientApiClient client, ComponentName componentName, + Event event) { + if (componentName == null) { + return; + } + if (DEBUG) { + Log.d(TAG, "componentName: " + componentName.toShortString() + + ":Event: " + event.toString()); + } + thisApi().sendAnalyticsEventToPlugin(client, componentName, event) + .setResultCallback(new ResultCallback() { + @Override + public void onResult(Result result) { + if (DEBUG) { + Log.v(TAG, "Event sent with result: " + result.getStatus() + .getStatusMessage()); + } + } + }); + } +} diff --git a/src-ambient/com/android/phone/common/incall/api/InCallResults.java b/src-ambient/com/android/phone/common/incall/api/InCallResults.java new file mode 100644 index 0000000..b636f56 --- /dev/null +++ b/src-ambient/com/android/phone/common/incall/api/InCallResults.java @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.phone.common.incall.api; + +import android.content.ComponentName; +import android.content.Context; +import android.content.res.Resources; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.text.TextUtils; +import android.util.Log; + +import com.android.phone.common.ambient.AmbientDataSubscription; +import com.android.phone.common.ambient.TypedPendingResult; +import com.android.phone.common.incall.CallMethodInfo; +import com.android.phone.common.incall.utils.CallMethodUtils; +import com.android.phone.common.ambient.utils.PluginUtils; +import com.android.phone.common.nudge.api.NudgeTypedResult; +import com.cyanogen.ambient.discovery.results.BundleResult; +import com.cyanogen.ambient.discovery.util.NudgeKey; +import com.cyanogen.ambient.incall.extension.CreditBalance; +import com.cyanogen.ambient.incall.extension.CreditInfo; +import com.cyanogen.ambient.incall.extension.GetCreditInfoResult; +import com.cyanogen.ambient.incall.extension.HintTextResult; +import com.cyanogen.ambient.incall.extension.StatusCodes; +import com.cyanogen.ambient.incall.results.AccountHandleResult; +import com.cyanogen.ambient.incall.results.AuthenticationStateResult; +import com.cyanogen.ambient.incall.results.HintTextResultResult; +import com.cyanogen.ambient.incall.results.InCallProviderInfoResult; +import com.cyanogen.ambient.incall.results.InstalledPluginsResult; +import com.cyanogen.ambient.incall.results.MimeTypeResult; +import com.cyanogen.ambient.incall.results.PendingIntentResult; +import com.cyanogen.ambient.incall.results.PluginStatusResult; +import com.cyanogen.ambient.incall.util.InCallProviderInfo; + +import java.util.ArrayList; +import java.util.List; + +/** + * Class that handles writing results to our CallMethodInfo object + */ +public class InCallResults extends ApiHelper { + + private static final String TAG = InCallResults.class.getSimpleName(); + + public static List gotInstalledPlugins(InstalledPluginsResult result) { + if (result.components == null) { + return new ArrayList(); + } + return result.components; + } + + public static void gotStatus(CallMethodInfo callMethodInfo, PluginStatusResult result) { + callMethodInfo.mStatus = result.status; + } + + public static void gotMimeType(CallMethodInfo callMethodInfo, MimeTypeResult result, int type) { + switch(type) { + case TypedPendingResult.GENERAL_MIME_TYPE: + callMethodInfo.mMimeType = result.mimeType; + break; + case TypedPendingResult.VIDEO_MIME_TYPE: + callMethodInfo.mVideoCallableMimeType = result.mimeType; + break; + case TypedPendingResult.IM_MIME_TYPE: + callMethodInfo.mImMimeType = result.mimeType; + break; + } + } + + public static void gotIntent(CallMethodInfo callMethodInfo, PendingIntentResult result, + int type) { + switch(type) { + case TypedPendingResult.LOGIN_INTENT: + callMethodInfo.mLoginIntent = result.intent; + break; + case TypedPendingResult.CREDIT_INTENT: + callMethodInfo.mManageCreditIntent = result.intent; + break; + case TypedPendingResult.SETTINGS_INTENT: + callMethodInfo.mSettingsIntent = result.intent; + break; + case TypedPendingResult.DEFAULT_DIRECTORY_SEARCH_INTENT: + callMethodInfo.mDefaultDirectorySearchIntent = result.intent; + break; + } + } + + public static void gotAuthenticationState(CallMethodInfo callMethodInfo, + AuthenticationStateResult result) { + callMethodInfo.mIsAuthenticated + = result.result == StatusCodes.AuthenticationState.LOGGED_IN; + } + + public static void gotAccountHandle(CallMethodInfo callMethodInfo, Context context, + AccountHandleResult result) { + gotAccountHandle(callMethodInfo, context, result.accountHandle); + } + + private static void gotAccountHandle(CallMethodInfo callMethodInfo, Context context, + String accountHandle) { + callMethodInfo.mAccountHandle = accountHandle; + if (TextUtils.isEmpty(callMethodInfo.mAccountHandle)) { + callMethodInfo.mAccountHandle + = CallMethodUtils.lookupAccountHandle(context, callMethodInfo.mAccountType); + } + } + + public static void gotGeneralInfo(CallMethodInfo callMethodInfo, + AmbientDataSubscription instance, InCallProviderInfoResult result) { + + ComponentName componentName = callMethodInfo.mComponent; + InCallProviderInfo icpi = result.inCallProviderInfo; + if (icpi == null) { + instance.getPluginInfo().remove(componentName); + return; + } + + Resources resources = PluginUtils.getPluginResources(instance.mContext, componentName); + if (resources == null) { + return; + } + + callMethodInfo.mBrandIconId = icpi.getBrandIcon(); + callMethodInfo.mLoginIconId = icpi.getLoginIcon(); + + try { + callMethodInfo.mSingleColorBrandIcon + = resources.getDrawable(icpi.getSingleColorBrandIcon()); + callMethodInfo.mBrandIcon = resources.getDrawable(callMethodInfo.mBrandIconId); + callMethodInfo.mLoginIcon = resources.getDrawable(callMethodInfo.mLoginIconId); + callMethodInfo.mVoiceIcon = resources.getDrawable(icpi.getVoiceMimeIcon()); + callMethodInfo.mImIcon = resources.getDrawable(icpi.getImMimeIcon()); + callMethodInfo.mBadgeIcon = resources.getDrawable(icpi.getBadgeIcon()); + callMethodInfo.mVideoIcon = resources.getDrawable(icpi.getVideoMimeIcon()); + } catch (Resources.NotFoundException e) { + Log.e(TAG, "Resource Not found: " + componentName.flattenToShortString()); + instance.getPluginInfo().remove(componentName); + return; + } + + callMethodInfo.mSubscriptionButtonText = icpi.getSubscriptionButtonText(); + callMethodInfo.mCreditButtonText = icpi.getCreditsButtonText(); + // If present, use the deprecated attribute defined hint text. + // These values may be overwritten by getHintText. + callMethodInfo.mT9HintDescriptionNoCreditOrSub = icpi.getT9HintDescription(); + callMethodInfo.mT9HintDescriptionHasCreditOrSub = icpi.getT9HintDescription(); + callMethodInfo.mDependentPackage = icpi.getDependentPackage(); + callMethodInfo.mName = icpi.getTitle(); + callMethodInfo.mSummary = icpi.getSummary(); + callMethodInfo.mAccountType = icpi.getAccountType(); + gotAccountHandle(callMethodInfo, instance.mContext, icpi.getAccountHandle()); + } + + public static void gotCreditData(CallMethodInfo callMethodInfo, GetCreditInfoResult result) { + if (result.creditInfo == null) { + // Build zero credit dummy if no result found. + callMethodInfo.mProviderCreditInfo = new CreditInfo(new CreditBalance(0, null), null); + } else { + callMethodInfo.mProviderCreditInfo = result.creditInfo; + } + } + + public static void gotHintText(CallMethodInfo callMethodInfo, + HintTextResultResult resultResult) { + HintTextResult result = resultResult.result; + if (result != null) { + String hintText = result.getNoCreditOrSubscriptionHint(); + if (!TextUtils.isEmpty(hintText)) { + callMethodInfo.mT9HintDescriptionNoCreditOrSub = hintText; + } + hintText = result.getHasCreditOrSubscriptionHint(); + if (!TextUtils.isEmpty(hintText)) { + callMethodInfo.mT9HintDescriptionHasCreditOrSub = hintText; + } + } + } + + public static void updateCreditInfo(AmbientDataSubscription instance, + ComponentName name, GetCreditInfoResult result) { + CallMethodInfo callMethodInfo = instance.getPluginIfExists(name); + if (callMethodInfo != null) { + gotCreditData(callMethodInfo, result); + instance.getPluginInfo().put(name, callMethodInfo); + + // Since a CallMethodInfo object was updated here, we should let the subscribers know + instance.broadcast(); + } else { + Log.wtf(TAG, "Call method does not exist, this should not happen"); + } + } + + public static void updateAuthenticationState(AmbientDataSubscription instance, + ComponentName name, int state) { + CallMethodInfo callMethodInfo = instance.getPluginIfExists(name); + if (callMethodInfo != null) { + callMethodInfo.mIsAuthenticated = state == StatusCodes.AuthenticationState.LOGGED_IN; + instance.getPluginInfo().put(name, callMethodInfo); + + // Since a CallMethodInfo object was updated here, we should let the subscribers know + instance.broadcast(); + } else { + Log.wtf(TAG, "Call method does not exist, this should not happen"); + } + } + + public static void gotNudgeData(Context context, CallMethodInfo callMethodInfo, + BundleResult result, int type) { + + Bundle bundle = result.bundle; + if (bundle == null) { + return; + } + switch (type) { + case NudgeTypedResult.INCALL_CREDIT_NUDGE: + gotCreditWarn(callMethodInfo, bundle); + break; + case NudgeTypedResult.INCALL_CONTACT_FRAGMENT_LOGIN: + gotLoginSubtitle(callMethodInfo, bundle); + break; + case NudgeTypedResult.INCALL_CONTACT_CARD_LOGIN: + gotContactCardLogin(callMethodInfo, NudgeKey.INCALL_CONTACT_CARD_LOGIN, bundle, + context); + break; + case NudgeTypedResult.INCALL_CONTACT_CARD_DOWNLOAD: + gotContactCardDownload(callMethodInfo, NudgeKey.INCALL_CONTACT_CARD_DOWNLOAD, + bundle, context); + break; + } + } + + private static void gotLoginSubtitle(CallMethodInfo callMethodInfo, Bundle bundle) { + callMethodInfo.mLoginSubtitle = bundle.getString(NudgeKey.NUDGE_PARAM_SUBTITLE); + } + + private static void gotContactCardLogin(CallMethodInfo callMethodInfo, String key, + Bundle bundle, Context c) { + callMethodInfo.mLoginNudgeEnable = bundle.getBoolean(NudgeKey.NUDGE_PARAM_ENABLED, true) + && PreferenceManager.getDefaultSharedPreferences(c).getBoolean( + getKey(key, callMethodInfo), true); + callMethodInfo.mLoginNudgeSubtitle = bundle.getString(NudgeKey.NUDGE_PARAM_SUBTITLE); + callMethodInfo.mLoginNudgeActionText = bundle.getString(NudgeKey.NUDGE_PARAM_ACTION_TEXT); + } + + private static void gotCreditWarn(CallMethodInfo callMethodInfo, Bundle bundle) { + Object creditWarn = bundle.get(NudgeKey.INCALL_PARAM_CREDIT_WARN); + if (creditWarn.getClass().equals(Integer.class)) { + callMethodInfo.mCreditWarn = (Integer) creditWarn; + } else if (creditWarn.getClass().equals(Float.class)) { + callMethodInfo.mCreditWarn = (Float) creditWarn; + } else { + Log.e(TAG, "Invalid value for Credit Warn limit: " + creditWarn); + } + } + + private static void gotContactCardDownload(CallMethodInfo callMethodInfo, String key, + Bundle bundle, Context context) { + callMethodInfo.mInstallNudgeEnable = bundle.getBoolean(NudgeKey.NUDGE_PARAM_ENABLED, true) + && PreferenceManager.getDefaultSharedPreferences(context).getBoolean( + getKey(key, callMethodInfo), true); + callMethodInfo.mInstallNudgeSubtitle = bundle.getString(NudgeKey.NUDGE_PARAM_SUBTITLE); + callMethodInfo.mInstallNudgeActionText = bundle.getString(NudgeKey.NUDGE_PARAM_ACTION_TEXT); + } + + + private static String getKey(String key, CallMethodInfo cmi) { + return cmi.mComponent.getClassName() + "." + key; + } +} diff --git a/src-ambient/com/android/phone/common/incall/listeners/AuthenticationListenerImpl.java b/src-ambient/com/android/phone/common/incall/listeners/AuthenticationListenerImpl.java new file mode 100644 index 0000000..8bf3b53 --- /dev/null +++ b/src-ambient/com/android/phone/common/incall/listeners/AuthenticationListenerImpl.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.phone.common.incall.listeners; + +import android.content.ComponentName; +import android.content.Context; +import android.os.RemoteException; +import android.util.Log; + +import com.android.phone.common.incall.DialerDataSubscription; +import com.android.phone.common.incall.api.InCallResults; +import com.cyanogen.ambient.incall.extension.IAuthenticationListener; + +public class AuthenticationListenerImpl extends IAuthenticationListener.Stub { + + private static final String TAG = AuthenticationListenerImpl.class.getSimpleName(); + private static final boolean DEBUG = false; + + private ComponentName mComponentName; + private Context mContext; + + public static AuthenticationListenerImpl getInstance(Context context, ComponentName + componentName) { + + return new AuthenticationListenerImpl(context, componentName); + } + + private AuthenticationListenerImpl(Context context, ComponentName cn) { + mContext = context; + mComponentName = cn; + } + + @Override + public void authenticationStateUpdated(int state) throws RemoteException { + if (DEBUG) { + Log.d(TAG, "Got authenticationStateUpdated for: " + mComponentName + " state: " + + state); + } + + InCallResults.updateAuthenticationState(DialerDataSubscription.get(mContext), + mComponentName, state); + } + +} \ No newline at end of file diff --git a/src-ambient/com/android/phone/common/incall/listeners/CallCreditListenerImpl.java b/src-ambient/com/android/phone/common/incall/listeners/CallCreditListenerImpl.java new file mode 100644 index 0000000..9eef6cc --- /dev/null +++ b/src-ambient/com/android/phone/common/incall/listeners/CallCreditListenerImpl.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.phone.common.incall.listeners; + +import android.content.ComponentName; +import android.content.Context; +import android.os.RemoteException; +import android.util.Log; + +import com.android.phone.common.incall.DialerDataSubscription; +import com.android.phone.common.incall.api.InCallResults; +import com.cyanogen.ambient.incall.extension.GetCreditInfoResult; +import com.cyanogen.ambient.incall.extension.ICallCreditListener; + +public class CallCreditListenerImpl extends ICallCreditListener.Stub { + + private static final String TAG = CallCreditListenerImpl.class.getSimpleName(); + private static final boolean DEBUG = false; + + private Context mContext; + private ComponentName mComponentName; + + public static CallCreditListenerImpl getInstance(Context context, ComponentName componentName) { + return new CallCreditListenerImpl(context, componentName); + } + + private CallCreditListenerImpl(Context context, ComponentName cn) { + mContext = context; + mComponentName = cn; + } + + @Override + public void creditInfoUpdated(GetCreditInfoResult gcir) throws RemoteException { + if (DEBUG) { + Log.d(TAG, "got creditInfoUpdated for: " + mComponentName + " gcir: " + gcir); + } + InCallResults.updateCreditInfo(DialerDataSubscription.get(mContext), mComponentName, gcir); + } +} \ No newline at end of file diff --git a/src-ambient/com/android/phone/common/incall/utils/CallMethodFilters.java b/src-ambient/com/android/phone/common/incall/utils/CallMethodFilters.java new file mode 100644 index 0000000..9a576b7 --- /dev/null +++ b/src-ambient/com/android/phone/common/incall/utils/CallMethodFilters.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.phone.common.incall.utils; + +import android.content.ComponentName; +import android.content.Context; +import android.text.TextUtils; + +import com.android.phone.common.ambient.AmbientDataSubscription; +import com.android.phone.common.incall.CallMethodInfo; +import com.android.phone.common.incall.api.ApiHelper; +import com.cyanogen.ambient.plugin.PluginStatus; + +import java.util.HashMap; +import java.util.Map; + +/** + * Helper for filtering call method objects in various ways. + */ +public class CallMethodFilters extends ApiHelper { + + /** + * Helper method for subscribed clients to remove any item that is not enabled from the hashmap + * @param input HashMap returned from a broadcast + * @param output HashMap with only enabled items + */ + public static void removeDisabled(HashMap input, + HashMap output) { + for (Map.Entry entry : input.entrySet()) { + ComponentName key = entry.getKey(); + CallMethodInfo value = entry.getValue(); + + if (value.mStatus == PluginStatus.ENABLED) { + output.put(key, value); + } + } + } + + public static HashMap getAllEnabledCallMethods( + AmbientDataSubscription instance) { + HashMap cmi = new HashMap<>(); + removeDisabled(instance.getPluginInfo(), cmi); + return cmi; + } + + public static HashMap getAllEnabledAndHiddenCallMethods( + AmbientDataSubscription instance) { + HashMap cmi = new HashMap<>(); + for (Map.Entry entry : instance.getPluginInfo().entrySet()) { + ComponentName key = entry.getKey(); + CallMethodInfo value = entry.getValue(); + + if (value.mStatus == PluginStatus.ENABLED || value.mStatus == PluginStatus.HIDDEN) { + cmi.put(key, value); + } + } + return cmi; + } + + public static CallMethodInfo getMethodForMimeType(String mimeType, boolean enableOnly, + AmbientDataSubscription instance) { + + CallMethodInfo targetEntry = null; + for (CallMethodInfo entry : instance.getPluginInfo().values()) { + // During development, mimetype sometimes came back null + // find out why mimetype may be null + if (!TextUtils.isEmpty(entry.mMimeType)) { + if (enableOnly && entry.mStatus != PluginStatus.ENABLED) { + continue; + } + if (entry.mMimeType.equals(mimeType)) { + targetEntry = entry; + break; + } + } + } + return targetEntry; + } + +} diff --git a/src-ambient/com/android/phone/common/incall/utils/CallMethodUtils.java b/src-ambient/com/android/phone/common/incall/utils/CallMethodUtils.java new file mode 100644 index 0000000..d29f805 --- /dev/null +++ b/src-ambient/com/android/phone/common/incall/utils/CallMethodUtils.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.phone.common.incall.utils; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.telecom.PhoneAccount; +import android.telecom.PhoneAccountHandle; +import android.telecom.TelecomManager; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.Log; + +import android.widget.Toast; +import com.android.contacts.common.model.AccountTypeManager; +import com.android.contacts.common.model.account.AccountType; +import com.android.contacts.common.model.account.AccountWithDataSet; +import com.android.phone.common.R; +import com.android.phone.common.incall.CallMethodInfo; +import com.android.phone.common.incall.StartInCallCallReceiver; +import com.cyanogen.ambient.incall.extension.StatusCodes; + +import java.util.ArrayList; +import java.util.List; + +import static com.cyanogen.ambient.incall.util.InCallHelper.NO_COLOR; + +/** + * Basic Utils for call method modifications + */ +public class CallMethodUtils { + + private final static String TAG = CallMethodUtils.class.getSimpleName(); + private final static boolean DEBUG = false; + + public final static String PREF_SPINNER_COACHMARK_SHOW = "pref_spinner_coachmark_shown"; + public final static String PREF_LAST_ENABLED_PROVIDER = "pref_last_enabled_provider"; + public final static String PREF_INTERNATIONAL_CALLS = "pref_international_calls"; + public final static String PREF_WIFI_CALL = "pref_wifi_call"; + + public static CallMethodInfo getDefaultSimInfo(Context context) { + final TelecomManager telecomMgr = + (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); + PhoneAccountHandle handle = + telecomMgr.getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL); + if (handle == null) { + return null; + } + return phoneToCallMethod(context, handle); + } + + public static List getSimInfoList(Context context) { + final TelecomManager telecomMgr = + (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); + final List accountHandles = telecomMgr.getCallCapablePhoneAccounts(); + ArrayList callMethodInfoList = new ArrayList<>(); + for (PhoneAccountHandle accountHandle : accountHandles) { + CallMethodInfo info = phoneToCallMethod(context, accountHandle); + if (info != null) { + callMethodInfoList.add(info); + } + } + return callMethodInfoList; + } + + private static String getPhoneAccountName(Context context, PhoneAccount phoneAccount, + int slotId) { + if (phoneAccount == null) { + // Slot IDs are zero based + return context.getString(R.string.call_method_spinner_item_unknown_sim, slotId + 1); + } else if (phoneAccount.getLabel() != null) { + return phoneAccount.getLabel().toString(); + } + return null; + } + + private static int getPhoneAccountColor(SubscriptionInfo info) { + if (info != null) { + return info.getIconTint(); + } else { + return NO_COLOR; + } + } + + private static CallMethodInfo phoneToCallMethod(Context context, + PhoneAccountHandle phoneAccountHandle) { + final SubscriptionManager subMgr = (SubscriptionManager) context + .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); + final TelecomManager telecomMgr = + (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); + final TelephonyManager telephonyMgr = + (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); + PhoneAccount phoneAccount = telecomMgr.getPhoneAccount(phoneAccountHandle); + + if (phoneAccount == null) { + return null; + } + + CallMethodInfo callMethodInfo = new CallMethodInfo(); + + callMethodInfo.mComponent = phoneAccountHandle.getComponentName(); + callMethodInfo.mId = phoneAccountHandle.getId(); + callMethodInfo.mUserHandle = phoneAccountHandle.getUserHandle(); + callMethodInfo.mSubId = telephonyMgr.getSubIdForPhoneAccount(phoneAccount); + callMethodInfo.mSlotId = SubscriptionManager.getSlotId(callMethodInfo.mSubId); + callMethodInfo.mName = getPhoneAccountName(context, phoneAccount, callMethodInfo.mSlotId); + callMethodInfo.mColor = + getPhoneAccountColor(subMgr.getActiveSubscriptionInfo(callMethodInfo.mSubId)); + callMethodInfo.mIsInCallProvider = false; + + final int simState = telephonyMgr.getSimState(callMethodInfo.mSlotId); + if ((simState == TelephonyManager.SIM_STATE_ABSENT) || + (simState == TelephonyManager.SIM_STATE_UNKNOWN)) { + return null; + } + + return callMethodInfo; + } + + /* + * Look up the currently logged in plugin account in case plugin fails to return a valid + * account handle + */ + public static String lookupAccountHandle(Context context, String targetAccountType) { + // Gather account handles logged into the device as a backup, in case + // plugins fail to return the account handle even when it reports its + // state as authenticated + AccountTypeManager accountTypes = AccountTypeManager.getInstance(context); + List accounts = accountTypes.getAccounts(false); + ArrayMap accountMap = new ArrayMap<>(); + + for (AccountWithDataSet account : accounts) { + AccountType accountType = + accountTypes.getAccountType(account.type, account.dataSet); + if (accountType.isExtension() && !account.hasData(context)) { + // Hide extensions with no raw_contacts. + continue; + } + if (DEBUG) { + Log.d(TAG, "account.type: " + account.type + "account.name: " + account.name); + } + // currently only handle one account per account type use case + accountMap.put(account.type, account.name); + } + return accountMap.containsKey(targetAccountType) ? accountMap.get(targetAccountType) : ""; + } + + /* + * Return if the plugin is a soft logged-out state (authenticated is false and there's still + * an account saved in account manager) + */ + public static boolean isSoftLoggedOut(Context context, CallMethodInfo cmi) { + return (!cmi.mIsAuthenticated && !TextUtils.isEmpty(lookupAccountHandle(context, + cmi.mAccountType))); + } + + + public static StartInCallCallReceiver getVoIPResultReceiver(Context context, CallMethodInfo cmi, + String originCode) { + return getVoIPResultReceiver(context, cmi, originCode, null); + } + + public static StartInCallCallReceiver getVoIPResultReceiver(final Context context, + final CallMethodInfo cmi, final String originCode, + final StartInCallCallReceiver.InCallCallListener listener) { + + StartInCallCallReceiver svcrr = + new StartInCallCallReceiver(new Handler(Looper.getMainLooper())); + + svcrr.setReceiver(new StartInCallCallReceiver.Receiver() { + + @Override + public void onReceiveResult(int resultCode, Bundle resultData) { + if (DEBUG) Log.i(TAG, "Got Start VoIP Call result callback code = " + resultCode); + + switch (resultCode) { + case StatusCodes.StartCall.CALL_FAILURE_INSUFFICIENT_CREDITS: + case StatusCodes.StartCall.CALL_FAILURE_INVALID_NUMBER: + case StatusCodes.StartCall.CALL_FAILURE_TIMEOUT: + case StatusCodes.StartCall.CALL_FAILURE_UNAUTHENTICATED: + case StatusCodes.StartCall.CALL_FAILURE: + String text = context.getResources() + .getString(R.string.invalid_number_text); + text = String.format(text, cmi.mName); + Toast.makeText(context, text, Toast.LENGTH_LONG).show(); + break; + default: + Log.i(TAG, "Nothing to do for this Start VoIP Call resultcode = " + + resultCode); + break; + } + if (listener != null) { + listener.onResult(resultCode); + } + } + + }); + + return svcrr; + } +} diff --git a/src-ambient/com/android/phone/common/incall/utils/MimeTypeUtils.java b/src-ambient/com/android/phone/common/incall/utils/MimeTypeUtils.java new file mode 100644 index 0000000..8f0c260 --- /dev/null +++ b/src-ambient/com/android/phone/common/incall/utils/MimeTypeUtils.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.phone.common.incall.utils; + +import android.content.Context; + +import com.android.phone.common.ambient.AmbientDataSubscription; +import com.android.phone.common.incall.CallMethodInfo; +import com.android.phone.common.incall.api.ApiHelper; +import com.cyanogen.ambient.plugin.PluginStatus; +import com.google.common.base.Joiner; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static com.android.phone.common.ambient.TypedPendingResult.GENERAL_MIME_TYPE; +import static com.android.phone.common.ambient.TypedPendingResult.IM_MIME_TYPE; +import static com.android.phone.common.ambient.TypedPendingResult.VIDEO_MIME_TYPE; + + +/** + * Class with logic to get mimes and mime types. + */ +public class MimeTypeUtils extends ApiHelper { + + private static String getMimeTypes(AmbientDataSubscription instance, + boolean enabledOnly, int mimeID) { + + String mimeTypes = ""; + List mimeTypesList = new ArrayList<>(); + for (CallMethodInfo cmi : instance.getPluginInfo().values()) { + String mimeType = null; + switch(mimeID) { + case GENERAL_MIME_TYPE: + mimeType = cmi.mMimeType; + break; + case IM_MIME_TYPE: + mimeType = cmi.mVideoCallableMimeType; + break; + case VIDEO_MIME_TYPE: + mimeType = cmi.mImMimeType; + } + + if (!enabledOnly) { + mimeTypesList.add(mimeType); + continue; + } + + if (cmi.mStatus == PluginStatus.ENABLED) { + mimeTypesList.add(mimeType); + } + } + + if (!mimeTypesList.isEmpty()) { + mimeTypes = Joiner.on(",").skipNulls().join(mimeTypesList); + } + return mimeTypes; + } + + + /** + * A few items need a list of mime types in a comma delimited list. Since we are already + * querying all the plugins. We can easily build this list ahead of time. + * + * Items that require this should subscribe and grab this updated list when needed. + * @return string of all (not limited to enabled) mime types + */ + public static String getAllMimeTypes(AmbientDataSubscription instance) { + return getMimeTypes(instance, false, GENERAL_MIME_TYPE); + } + + /** + * A few items need a list of mime types in a comma delimited list. Since we are already + * querying all the plugins. We can easily build this list ahead of time. + * + * Items that require this should subscribe and grab this updated list when needed. + * @return string of enabled mime types + */ + public static String getAllEnabledMimeTypes(AmbientDataSubscription instance) { + return getMimeTypes(instance, true, GENERAL_MIME_TYPE); + } + + /** + * A few items need a list of video callable mime types in a comma delimited list. + * Since we are already querying all the plugins. We can easily build this list ahead of time. + * + * Items that require this should subscribe and grab this updated list when needed. + * @return string of enabled video callable mime types + */ + public static String getAllEnabledVideoCallableMimeTypes( + AmbientDataSubscription instance) { + return getMimeTypes(instance, true, VIDEO_MIME_TYPE); + } + + public static String getAllEnabledImMimeTypes( + AmbientDataSubscription instance) { + return getMimeTypes(instance, true, IM_MIME_TYPE); + } + + public static Set getAllEnabledVideoImMimeSet( + AmbientDataSubscription instance) { + String[] videoMimes = getAllEnabledVideoCallableMimeTypes(instance).split(","); + String[] imMimes = getAllEnabledImMimeTypes(instance).split(","); + HashSet mimeSet = new HashSet<>(); + + if (videoMimes != null) { + mimeSet.addAll(Arrays.asList(videoMimes)); + } + if (imMimes != null) { + mimeSet.addAll(Arrays.asList(imMimes)); + } + return mimeSet; + } + + public static Set getAllEnabledVoiceMimeSet( + AmbientDataSubscription instance) { + String[] mimes = getAllEnabledMimeTypes(instance).split(","); + HashSet mimeSet = new HashSet<>(); + if (mimes != null) { + mimeSet.addAll(Arrays.asList(mimes)); + } + return mimeSet; + } + + +} diff --git a/src-ambient/com/android/phone/common/nudge/api/ApiHelper.java b/src-ambient/com/android/phone/common/nudge/api/ApiHelper.java new file mode 100644 index 0000000..d2ca30f --- /dev/null +++ b/src-ambient/com/android/phone/common/nudge/api/ApiHelper.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.phone.common.nudge.api; + +import com.cyanogen.ambient.discovery.NudgeApi; +import com.cyanogen.ambient.discovery.NudgeServices; + +/** + * Helper for getting Nudge Api + */ +public class ApiHelper { + + static NudgeApi thisApi() { + return NudgeServices.NudgeApi; + } + +} diff --git a/src-ambient/com/android/phone/common/nudge/api/NudgeQueries.java b/src-ambient/com/android/phone/common/nudge/api/NudgeQueries.java new file mode 100644 index 0000000..a48e079 --- /dev/null +++ b/src-ambient/com/android/phone/common/nudge/api/NudgeQueries.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.phone.common.nudge.api; + +import android.content.ComponentName; +import android.content.Context; +import android.util.Log; + +import com.android.phone.common.ambient.TypedPendingResult; +import com.android.phone.common.nudge.utils.NudgeUtils; +import com.cyanogen.ambient.common.api.AmbientApiClient; +import com.cyanogen.ambient.discovery.NudgeServices; + +/** + * Queries for Nudges + */ +public class NudgeQueries extends ApiHelper { + + private static TypedPendingResult getNudgeForKey(AmbientApiClient client, + ComponentName componentName, String key) { + int type = NudgeTypedResult.getNudgeTypeForKey(key); + return new TypedPendingResult(thisApi().getConfigurationForKey( + client, componentName, key), type); + } + + /** + * Get a nudge configuration for the mod + * @param cn of mod + * + * @return TypedPendingResult to identify which field to modify in our CallMethodInfo object + */ + public static TypedPendingResult getNudgeConfig(AmbientApiClient client, + Context c, ComponentName cn, String key) { + + ComponentName nudgeComponent = NudgeUtils.getNudgeComponent(c, cn); + if (nudgeComponent != null) { + return NudgeQueries.getNudgeForKey(client, nudgeComponent, key); + } + return null; + } +} diff --git a/src-ambient/com/android/phone/common/nudge/api/NudgeTypedResult.java b/src-ambient/com/android/phone/common/nudge/api/NudgeTypedResult.java new file mode 100644 index 0000000..39a9d19 --- /dev/null +++ b/src-ambient/com/android/phone/common/nudge/api/NudgeTypedResult.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.phone.common.nudge.api; + +import com.android.phone.common.ambient.TypedPendingResult; +import com.cyanogen.ambient.common.api.PendingResult; +import com.cyanogen.ambient.discovery.util.NudgeKey; + +import android.util.Log; + +/** + * InCall Pending result types. + */ +public class NudgeTypedResult extends TypedPendingResult { + + private static final String TAG = NudgeTypedResult.class.getSimpleName(); + + public NudgeTypedResult(PendingResult pendingResult, int type) { + super(pendingResult, type); + } + + public static int getNudgeTypeForKey(String key) { + switch (key) { + case NudgeKey.INCALL_CREDIT_NUDGE: + return INCALL_CREDIT_NUDGE; + case NudgeKey.INCALL_CONTACT_CARD_LOGIN: + return INCALL_CONTACT_CARD_LOGIN; + case NudgeKey.INCALL_CONTACT_CARD_DOWNLOAD: + return INCALL_CONTACT_CARD_DOWNLOAD; + case NudgeKey.INCALL_CONTACT_FRAGMENT_LOGIN: + return INCALL_CONTACT_FRAGMENT_LOGIN; + default: + Log.e(TAG, "No nudge type!"); + return NONE; + } + } +} diff --git a/src-ambient/com/android/phone/common/nudge/utils/NudgeUtils.java b/src-ambient/com/android/phone/common/nudge/utils/NudgeUtils.java new file mode 100644 index 0000000..921c97f --- /dev/null +++ b/src-ambient/com/android/phone/common/nudge/utils/NudgeUtils.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.phone.common.nudge.utils; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ResolveInfo; + +import java.util.List; + +/** + * Utilities class to help with nudge events. + */ +public class NudgeUtils { + + public static final String NUDGE_PROVIDER_INTENT = "cyanogen.service.NUDGE_PROVIDER"; + + public static ComponentName getNudgeComponent(Context context, final ComponentName cn) { + // find a nudge component if it exists for this package + Intent nudgeIntent = new Intent(NUDGE_PROVIDER_INTENT); + nudgeIntent.setPackage(cn.getPackageName()); + List resolved = context.getPackageManager() + .queryIntentServices(nudgeIntent, 0); + if (resolved != null && !resolved.isEmpty()) { + ResolveInfo result = resolved.get(0); + return new ComponentName(result.serviceInfo.applicationInfo.packageName, + result.serviceInfo.name); + } + // if a nudge component doesn't exist, just finish here + return null; + } +} diff --git a/src-ambient/incall/AuthenticationListenerImpl.java b/src-ambient/incall/AuthenticationListenerImpl.java deleted file mode 100644 index aec72c8..0000000 --- a/src-ambient/incall/AuthenticationListenerImpl.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.android.phone.common.incall; - -import android.content.ComponentName; -import android.os.RemoteException; -import android.util.Log; - -import com.cyanogen.ambient.incall.extension.IAuthenticationListener; - -public class AuthenticationListenerImpl extends IAuthenticationListener.Stub { - private static final String TAG = "AuthenticationListener"; - private ComponentName mComponentName; - - public static AuthenticationListenerImpl getInstance(ComponentName componentName) { - return new AuthenticationListenerImpl(componentName); - } - - private AuthenticationListenerImpl(ComponentName cn) { - mComponentName = cn; - } - - @Override - public void authenticationStateUpdated(int state) throws RemoteException { - Log.d(TAG, "Getting authenticationStateUpdated for: " + mComponentName + " state: " + - state); - CallMethodHelper.updateAuthenticationState(mComponentName, state); - } -} \ No newline at end of file diff --git a/src-ambient/incall/CallCreditListenerImpl.java b/src-ambient/incall/CallCreditListenerImpl.java deleted file mode 100644 index 13fcb30..0000000 --- a/src-ambient/incall/CallCreditListenerImpl.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.android.phone.common.incall; - -import android.content.ComponentName; -import android.os.RemoteException; -import android.util.Log; - -import com.cyanogen.ambient.incall.extension.GetCreditInfoResult; -import com.cyanogen.ambient.incall.extension.ICallCreditListener; - -public class CallCreditListenerImpl extends ICallCreditListener.Stub { - private static final String TAG = "CallCreditListener"; - private ComponentName mComponentName; - - public static CallCreditListenerImpl getInstance(ComponentName componentName) { - return new CallCreditListenerImpl(componentName); - } - - private CallCreditListenerImpl(ComponentName cn) { - mComponentName = cn; - } - - @Override - public void creditInfoUpdated(GetCreditInfoResult gcir) - throws RemoteException { - Log.d(TAG, "getting creditInfoUpdated for: " + mComponentName + " gcir: " + gcir); - CallMethodHelper.updateCreditInfo(mComponentName, gcir); - } -} \ No newline at end of file diff --git a/src-ambient/incall/CallMethodHelper.java b/src-ambient/incall/CallMethodHelper.java deleted file mode 100644 index 5352c28..0000000 --- a/src-ambient/incall/CallMethodHelper.java +++ /dev/null @@ -1,1250 +0,0 @@ -/* - * Copyright (C) 2015 The CyanogenMod 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.phone.common.incall; - -import android.app.PendingIntent; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.res.Resources; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.preference.PreferenceManager; -import android.text.TextUtils; -import android.util.Log; -import android.widget.Toast; - -import com.android.phone.common.R; -import com.android.phone.common.ambient.AmbientConnection; -import com.android.phone.common.util.StartInCallCallReceiver; -import com.cyanogen.ambient.analytics.Event; -import com.cyanogen.ambient.common.api.AmbientApiClient; -import com.cyanogen.ambient.common.api.CommonStatusCodes; -import com.cyanogen.ambient.common.api.PendingResult; -import com.cyanogen.ambient.common.api.Result; -import com.cyanogen.ambient.common.api.ResultCallback; -import com.cyanogen.ambient.discovery.NudgeServices; -import com.cyanogen.ambient.discovery.results.BundleResult; -import com.cyanogen.ambient.discovery.util.NudgeKey; -import com.cyanogen.ambient.incall.InCallApi; -import com.cyanogen.ambient.incall.InCallServices; -import com.cyanogen.ambient.incall.extension.CreditBalance; -import com.cyanogen.ambient.incall.extension.CreditInfo; -import com.cyanogen.ambient.incall.extension.GetCreditInfoResult; -import com.cyanogen.ambient.incall.extension.HintTextResult; -import com.cyanogen.ambient.incall.extension.IAuthenticationListener; -import com.cyanogen.ambient.incall.extension.ICallCreditListener; -import com.cyanogen.ambient.incall.extension.InCallContactInfo; -import com.cyanogen.ambient.incall.extension.StatusCodes; -import com.cyanogen.ambient.incall.results.AccountHandleResult; -import com.cyanogen.ambient.incall.results.AuthenticationStateResult; -import com.cyanogen.ambient.incall.results.GetCreditInfoResultResult; -import com.cyanogen.ambient.incall.results.HintTextResultResult; -import com.cyanogen.ambient.incall.results.InCallProviderInfoResult; -import com.cyanogen.ambient.incall.results.InstalledPluginsResult; -import com.cyanogen.ambient.incall.results.MimeTypeResult; -import com.cyanogen.ambient.incall.results.PendingIntentResult; -import com.cyanogen.ambient.incall.results.PluginStatusResult; -import com.cyanogen.ambient.incall.util.InCallProviderInfo; -import com.cyanogen.ambient.plugin.PluginStatus; -import com.google.common.base.Joiner; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static com.cyanogen.ambient.incall.util.InCallHelper.NO_COLOR; - -/** - * Call Method Helper - In charge of keeping a running and updated hashmap of all InCallProviders - * currently installed. - * - * Fragments and Activities can subscribe to changes with subscribe. - * - */ -public class CallMethodHelper { - - protected static final String TAG = CallMethodHelper.class.getSimpleName(); - protected static final boolean DEBUG = false; - - protected static CallMethodHelper sInstance; - protected AmbientApiClient mClient; - protected Context mContext; - protected InCallApi mInCallApi; - protected Handler mMainHandler; - protected static List mInstalledPlugins; - protected static HashMap mCallMethodInfos = new HashMap<>(); - protected static HashMap mCallCreditListeners = new - HashMap<>(); - protected static HashMap mAuthenticationListeners = new - HashMap<>(); - protected static HashMap mRegisteredClients = new HashMap<>(); - protected static boolean dataHasBeenBroadcastPreviously = false; - // determine which info types to load - - - public interface CallMethodReceiver { - void onChanged(HashMap callMethodInfos); - } - - /** - * Broadcasts mCallMethodInfos to all registered clients on the Main thread. - */ - protected static void broadcast() { - getInstance().mMainHandler.post(new Runnable() { - @Override - public void run() { - if (DEBUG) Log.d(TAG, "broadcast"); - for (CallMethodReceiver client : mRegisteredClients.values()) { - client.onChanged(mCallMethodInfos); - } - enableListeners(); - if (DEBUG) { - for (CallMethodInfo cmi : mCallMethodInfos.values()) { - Log.v(TAG, "Broadcast: " + cmi.mName); - } - } - dataHasBeenBroadcastPreviously = true; - } - }); - } - - private static void enableListeners() { - if (DEBUG) Log.d(TAG, "Enabling Listeners"); - for (ComponentName callProviders : mCallMethodInfos.keySet()) { - if (!mCallCreditListeners.containsKey(callProviders)) { - CallCreditListenerImpl listener = - CallCreditListenerImpl.getInstance(callProviders); - getInstance().mInCallApi.addCreditListener(getInstance().mClient, callProviders, - listener); - mCallCreditListeners.put(callProviders, listener); - } - if (!mAuthenticationListeners.containsKey(callProviders)) { - AuthenticationListenerImpl listener = - AuthenticationListenerImpl.getInstance(callProviders); - getInstance().mInCallApi.addAuthenticationListener(getInstance().mClient, - callProviders, listener); - mAuthenticationListeners.put(callProviders, listener); - } - } - } - - private static void disableListeners() { - if (DEBUG) Log.d(TAG, "Disabling Listeners"); - for(ComponentName callCreditProvider : mCallCreditListeners.keySet()) { - if (mCallCreditListeners.get(callCreditProvider) != null) { - getInstance().mInCallApi.removeCreditListener(getInstance().mClient, - callCreditProvider, mCallCreditListeners.get(callCreditProvider)); - } - } - for (ComponentName plugin : mAuthenticationListeners.keySet()) { - if (mAuthenticationListeners.get(plugin) != null) { - getInstance().mInCallApi.removeAuthenticationListener(getInstance().mClient, - plugin, mAuthenticationListeners.get(plugin)); - } - } - mCallCreditListeners.clear(); - mAuthenticationListeners.clear(); - } - - /** - * Helper method for subscribed clients to remove any item that is not enabled from the hashmap - * @param input HashMap returned from a broadcast - * @param output HashMap with only enabled items - */ - public static void removeDisabled(HashMap input, - HashMap output) { - for (Map.Entry entry : input.entrySet()) { - ComponentName key = entry.getKey(); - CallMethodInfo value = entry.getValue(); - - if (value.mStatus == PluginStatus.ENABLED) { - output.put(key, value); - } - } - } - - public static HashMap getAllEnabledCallMethods() { - HashMap cmi = new HashMap(); - for (Map.Entry entry : mCallMethodInfos.entrySet()) { - ComponentName key = entry.getKey(); - CallMethodInfo value = entry.getValue(); - - if (value.mStatus == PluginStatus.ENABLED) { - cmi.put(key, value); - } - } - return cmi; - } - - public static HashMap getAllEnabledAndHiddenCallMethods() { - HashMap cmi = new HashMap(); - synchronized (mCallMethodInfos) { - for (Map.Entry entry : mCallMethodInfos.entrySet()) { - ComponentName key = entry.getKey(); - CallMethodInfo value = entry.getValue(); - - if (value.mStatus == PluginStatus.ENABLED || value.mStatus == PluginStatus.HIDDEN) { - cmi.put(key, value); - } - } - } - return cmi; - } - - public static CallMethodInfo getMethodForMimeType(String mimeType, boolean enableOnly) { - CallMethodInfo targetEntry = null; - synchronized (mCallMethodInfos) { - for (CallMethodInfo entry : mCallMethodInfos.values()) { - // TODO: find out why mimetype may be null - if (!TextUtils.isEmpty(entry.mMimeType)) { - if (enableOnly && entry.mStatus != PluginStatus.ENABLED) { - continue; - } - if (entry.mMimeType.equals(mimeType)) { - targetEntry = entry; - break; - } - } - } - } - return targetEntry; - } - - /*** - * Registers the client, on register returns boolean if - * callMethodInfo data is already collected and the initial broadcast has been sent. - * @param id unique string for the client - * @param cmr client receiver - * @return boolean isempty - */ - public static synchronized boolean subscribe(String id, CallMethodReceiver cmr) { - mRegisteredClients.put(id, cmr); - - return dataHasBeenBroadcastPreviously; - } - - /** - * Unsubscribes the client. All clients should unsubscribe when they are removed. - * @param id of the client to remove - */ - public static synchronized void unsubscribe(String id) { - mRegisteredClients.remove(id); - disableListeners(); - } - - /** - * Get a single instance of our call method helper. There should only be ever one instance. - * @return - */ - protected static synchronized CallMethodHelper getInstance() { - if (sInstance == null) { - sInstance = new CallMethodHelper(); - } - return sInstance; - } - - public interface InCallCallListener { - void onResult(int resultCode); - } - - /** - * Generic CallResultReceiver with basic error handling - * @param cmi - * @return - */ - public static StartInCallCallReceiver getVoIPResultReceiver(final CallMethodInfo cmi, - final String originCode) { - return getVoIPResultReceiver(cmi, originCode, null); - } - - public static StartInCallCallReceiver getVoIPResultReceiver(final CallMethodInfo cmi, - final String originCode, final InCallCallListener listener) { - StartInCallCallReceiver svcrr = - new StartInCallCallReceiver(new Handler(Looper.getMainLooper())); - - svcrr.setReceiver(new StartInCallCallReceiver.Receiver() { - - @Override - public void onReceiveResult(int resultCode, Bundle resultData) { - if (DEBUG) Log.i(TAG, "Got Start VoIP Call result callback code = " + resultCode); - - switch (resultCode) { - case StatusCodes.StartCall.CALL_FAILURE_INSUFFICIENT_CREDITS: - case StatusCodes.StartCall.CALL_FAILURE_INVALID_NUMBER: - case StatusCodes.StartCall.CALL_FAILURE_TIMEOUT: - case StatusCodes.StartCall.CALL_FAILURE_UNAUTHENTICATED: - case StatusCodes.StartCall.CALL_FAILURE: - String text = getInstance().mContext.getResources() - .getString(R.string.invalid_number_text); - text = String.format(text, cmi.mName); - Toast.makeText(getInstance().mContext, text, Toast.LENGTH_LONG).show(); - break; - default: - Log.i(TAG, "Nothing to do for this Start VoIP Call resultcode = " - + resultCode); - break; - } - if (listener != null) { - listener.onResult(resultCode); - } - } - - }); - - return svcrr; - } - - /** - * Start our Helper and kick off our first ModCore queries. - * @param context - */ - public static void init(Context context) { - CallMethodHelper helper = getInstance(); - helper.mContext = context; - helper.mClient = AmbientConnection.CLIENT.get(context); - helper.mInCallApi = InCallServices.getInstance(); - helper.mMainHandler = new Handler(context.getMainLooper()); - refresh(); - } - - /** - * *sip* ahhhh so refreshing - */ - public static void refresh() { - updateCallPlugins(); - } - - /** - * This is helpful for items that don't want to subscribe to updates or for things that - * need a quick CMI and have a component name. - * @param cn Component name wanted. - * @return specific call method when given a component name. - */ - public static CallMethodInfo getCallMethod(ComponentName cn) { - CallMethodInfo cmi = null; - synchronized (mCallMethodInfos) { - if (mCallMethodInfos.containsKey(cn)) { - cmi = mCallMethodInfos.get(cn); - } - } - return cmi; - } - - /** - * This is useful for items that subscribe after the initial broadcast has been sent out and - * need to go get some data right away - * @return the current HashMap of CMIs. - */ - public static HashMap getAllCallMethods() { - // after the initial broadcast on resume we need to go and get some new data - // this data will broadcast as soon as it becomes available - return mCallMethodInfos; - } - - public static void refreshDynamicItems() { - enableListeners(); - for (ComponentName cn : mCallMethodInfos.keySet()) { - HashMap apiCallbacks = new HashMap(); - getCallMethodAuthenticated(cn, apiCallbacks); - getCreditInfo(cn, apiCallbacks); - executeAll(apiCallbacks); - } - } - - /** - * A few items need a list of mime types in a comma delimited list. Since we are already - * querying all the plugins. We can easily build this list ahead of time. - * - * Items that require this should subscribe and grab this updated list when needed. - * @return string of all (not limited to enabled) mime types - */ - public static String getAllMimeTypes() { - String mimeTypes = ""; - - List mimeTypesList = new ArrayList<>(); - synchronized (mCallMethodInfos) { - for (CallMethodInfo cmi : mCallMethodInfos.values()) { - mimeTypesList.add(cmi.mMimeType); - } - } - - if (!mimeTypesList.isEmpty()) { - mimeTypes = Joiner.on(",").skipNulls().join(mimeTypesList); - } - return mimeTypes; - } - - /** - * A few items need a list of mime types in a comma delimited list. Since we are already - * querying all the plugins. We can easily build this list ahead of time. - * - * Items that require this should subscribe and grab this updated list when needed. - * @return string of enabled mime types - */ - public static String getAllEnabledMimeTypes() { - String mimeTypes = ""; - - List enabledMimeTypes = new ArrayList<>(); - synchronized (mCallMethodInfos) { - for (CallMethodInfo cmi : mCallMethodInfos.values()) { - if (cmi.mStatus == PluginStatus.ENABLED) { - enabledMimeTypes.add(cmi.mMimeType); - } - } - } - - if (!enabledMimeTypes.isEmpty()) { - mimeTypes = Joiner.on(",").skipNulls().join(enabledMimeTypes); - } - return mimeTypes; - } - - /** - * A few items need a list of video callable mime types in a comma delimited list. - * Since we are already querying all the plugins. We can easily build this list ahead of time. - * - * Items that require this should subscribe and grab this updated list when needed. - * @return string of enabled video callable mime types - */ - public static String getAllEnabledVideoCallableMimeTypes() { - String mimeTypes = ""; - - List enabledMimeTypes = new ArrayList<>(); - synchronized (mCallMethodInfos) { - for (CallMethodInfo cmi : mCallMethodInfos.values()) { - if (cmi.mStatus == PluginStatus.ENABLED) { - enabledMimeTypes.add(cmi.mVideoCallableMimeType); - } - } - } - - if (!enabledMimeTypes.isEmpty()) { - mimeTypes = Joiner.on(",").skipNulls().join(enabledMimeTypes); - } - return mimeTypes; - } - - public static String getAllEnabledImMimeTypes() { - String mimeTypes = ""; - - List enabledMimeTypes = new ArrayList<>(); - synchronized (mCallMethodInfos) { - for (CallMethodInfo cmi : mCallMethodInfos.values()) { - if (cmi.mStatus == PluginStatus.ENABLED) { - enabledMimeTypes.add(cmi.mImMimeType); - } - } - } - if (!enabledMimeTypes.isEmpty()) { - mimeTypes = Joiner.on(",").skipNulls().join(enabledMimeTypes); - } - return mimeTypes; - } - - public static void updateCreditInfo(ComponentName name, GetCreditInfoResult gcir) { - CallMethodInfo cmi = getCallMethodIfExists(name); - if (cmi != null) { - if (gcir == null || gcir.creditInfo == null) { - // Build zero credit dummy if no result found. - cmi.mProviderCreditInfo = - new CreditInfo(new CreditBalance(0, null), null); - } else { - cmi.mProviderCreditInfo = gcir.creditInfo; - } - - // Since a CallMethodInfo object was updated here, we should let the subscribers know - broadcast(); - } - } - - public static void updateAuthenticationState(ComponentName name, int state) { - CallMethodInfo cmi = getCallMethodIfExists(name); - if (cmi != null) { - cmi.mIsAuthenticated = state == StatusCodes.AuthenticationState - .LOGGED_IN; - mCallMethodInfos.put(name, cmi); - - // Since a CallMethodInfo object was updated here, we should let the subscribers know - broadcast(); - } - } - - /** - * Broadcast to subscribers once we know we've gathered all our data. Do not do this until we - * have everything we need for sure. - * - * This method is called after every callback from AmbientCore. We will keep track of all of - * the callbacks, once we have accounted for all callbacks from all plugins, we can go ahead - * and update subscribers. - */ - protected static void maybeBroadcastToSubscribers(ResultCallback callback, - HashMap apiCallbacks) { - if (callback != null) { - apiCallbacks.remove(callback); - } - if (DEBUG) Log.d(TAG, "maybeBroadcastToSubscribers: mInstalledPugins:" + mInstalledPlugins - .size()); - if (apiCallbacks.isEmpty()) { - // we are on the last item. broadcast updated hashmap - broadcast(); - } - } - - /** - * In order to speed up the process we make calls for providers that may be invalid - * To prevent this, make sure every resultcallback uses this before filling in the hashmap. - * @param cn componentname - * @return callmethodinfo if valid, otherwise null - */ - public static CallMethodInfo getCallMethodIfExists(ComponentName cn) { - if (mCallMethodInfos.containsKey(cn)) { - return mCallMethodInfos.get(cn); - } else { - return null; - } - } - - /** - * Prepare to query and fire off ModCore calls in all directions - */ - protected static void updateCallPlugins() { - getInstance().mInCallApi.getInstalledPlugins(getInstance().mClient) - .setResultCallback(new ResultCallback() { - @Override - public void onResult(InstalledPluginsResult installedPluginsResult) { - if (DEBUG) Log.d(TAG, "+++updateCallPlugins"); - // got installed components - mInstalledPlugins = installedPluginsResult.components; - - mCallMethodInfos.clear(); - - if (mInstalledPlugins == null || mInstalledPlugins.size() == 0) { - broadcast(); - return; - } - - for (ComponentName cn : mInstalledPlugins) { - HashMap apiCallbacks = - new HashMap(); - mCallMethodInfos.put(cn, new CallMethodInfo()); - getCallMethodInfo(cn, apiCallbacks); - getCallMethodStatus(cn, apiCallbacks); - getCallMethodMimeType(cn, apiCallbacks); - getCallMethodVideoCallableMimeType(cn, apiCallbacks); - getCallMethodAuthenticated(cn, apiCallbacks); - getLoginIntent(cn, apiCallbacks); - getSettingsIntent(cn, apiCallbacks); - getHintText(cn, apiCallbacks); - getCreditInfo(cn, apiCallbacks); - getManageCreditsIntent(cn, apiCallbacks); - checkLowCreditConfig(cn, apiCallbacks); - executeAll(apiCallbacks); - } - } - }); - } - - protected static void executeAll(HashMap apiCallbacks) { - for (ResultCallback resultCallback : apiCallbacks.keySet()) { - PendingResult pendingResult = apiCallbacks.get(resultCallback); - pendingResult.setResultCallback(resultCallback); - } - } - - /** - * Get the plugin info - * @param cn - * @param apiCallbacks keeps track of the target callbacks before broadcast - */ - protected static void getCallMethodInfo(final ComponentName cn, final - HashMap apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getProviderInfo(getInstance() - .mClient, cn); - ResultCallback callback = new ResultCallback() { - @Override - public void onResult(InCallProviderInfoResult inCallProviderInfoResult) { - InCallProviderInfo icpi = inCallProviderInfoResult.inCallProviderInfo; - if (icpi == null) { - mCallMethodInfos.remove(cn); - return; - } - - PackageManager packageManager = getInstance().mContext.getPackageManager(); - Resources pluginResources = null; - try { - pluginResources = packageManager.getResourcesForApplication( - cn.getPackageName()); - } catch (PackageManager.NameNotFoundException e) { - Log.e(TAG, "Plugin isn't installed: " + cn); - mCallMethodInfos.remove(cn); - return; - } - - synchronized (mCallMethodInfos) { - CallMethodInfo cmi = getCallMethodIfExists(cn); - - if (cmi == null) { - return; - } - - try { - cmi.mSingleColorBrandIcon = - pluginResources.getDrawable(icpi.getSingleColorBrandIcon(), - null); - cmi.mActionOneIcon = - pluginResources.getDrawable(icpi.getActionOneIcon(), null); - cmi.mActionTwoIcon = - pluginResources.getDrawable(icpi.getActionTwoIcon(), null); - cmi.mBrandIcon = - pluginResources.getDrawable(icpi.getBrandIcon(), null); - cmi.mLoginIcon = - pluginResources.getDrawable(icpi.getLoginIcon(), null); - cmi.mVoiceIcon = pluginResources.getDrawable(icpi - .getVoiceMimeIcon(), null); - cmi.mVideoIcon = pluginResources.getDrawable(icpi - .getVideoMimeIcon(), null); - cmi.mImIcon = pluginResources.getDrawable(icpi.getImMimeIcon(), - null); - cmi.mBadgeIcon = pluginResources.getDrawable(icpi.getBadgeIcon(), - null); - - } catch (Resources.NotFoundException e) { - Log.e(TAG, "Resource Not found: " + cn); - mCallMethodInfos.remove(cn); - return; - } - - cmi.mSlotId = -1; - cmi.mSubId = -1; - cmi.mColor = NO_COLOR; - cmi.mSubscriptionButtonText = icpi.getSubscriptionButtonText(); - cmi.mCreditButtonText = icpi.getCreditsButtonText(); - // If present, use the deprecated attribute defined hint text. - // These values may be overwritten by getHintText. - cmi.mT9HintDescriptionNoCreditOrSub = icpi.getT9HintDescription(); - cmi.mT9HintDescriptionHasCreditOrSub = icpi.getT9HintDescription(); - cmi.mActionOneText = icpi.getActionOneTitle(); - cmi.mActionTwoText = icpi.getActionTwoTitle(); - cmi.mIsInCallProvider = true; - - cmi.mComponent = cn; - cmi.mNudgeComponent = icpi.getNudgeComponent() == null ? null : - ComponentName.unflattenFromString(icpi.getNudgeComponent()); - cmi.mDependentPackage = icpi.getDependentPackage(); - cmi.mName = icpi.getTitle(); - cmi.mSummary = icpi.getSummary(); - cmi.mAccountType = icpi.getAccountType(); - cmi.mAccountHandle = icpi.getAccountHandle(); - if (TextUtils.isEmpty(cmi.mAccountHandle)) { - cmi.mAccountHandle = CallMethodUtils.lookupAccountHandle( - getInstance().mContext, cmi.mAccountType); - } - cmi.mBrandIconId = icpi.getBrandIcon(); - cmi.mLoginIconId = icpi.getLoginIcon(); - - cmi.mAccountType = icpi.getAccountType(); - mCallMethodInfos.put(cn, cmi); - maybeBroadcastToSubscribers(this, apiCallbacks); - } - } - }; - apiCallbacks.put(callback, result); - } - - /** - * Get our plugin enabled status - * @param cn - * @param apiCallbacks keeps track of the target callbacks before broadcast - */ - protected static void getCallMethodStatus(final ComponentName cn, final - HashMap apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getPluginStatus(getInstance().mClient, cn); - ResultCallback callback = new ResultCallback() { - @Override - public void onResult(PluginStatusResult pluginStatusResult) { - synchronized (mCallMethodInfos) { - CallMethodInfo cmi = getCallMethodIfExists(cn); - if (cmi != null) { - cmi.mStatus = pluginStatusResult.status; - mCallMethodInfos.put(cn, cmi); - maybeBroadcastToSubscribers(this, apiCallbacks); - } - } - } - }; - apiCallbacks.put(callback, result); - } - - /** - * Send an event to the component - * @param cn componentName to send the data to. - */ - public static void shipAnalyticsToPlugin(final ComponentName cn, Event e) { - if (cn == null) { - return; - } - if (DEBUG) { - Log.d(TAG, "componentName: " + cn.toShortString()); - Log.d(TAG, "Event: " + e.toString()); - } - if (getInstance() == null || - getInstance().mInCallApi == null || - getInstance().mClient == null) { - // For testing purposes, this might be null - return; - } - getInstance().mInCallApi.sendAnalyticsEventToPlugin(getInstance().mClient, cn, e) - .setResultCallback(new ResultCallback() { - @Override - public void onResult(Result result) { - if (DEBUG) { - Log.v(TAG, "Event sent with result: " + result.getStatus() - .getStatusMessage()); - } - } - }); - } - - /** - * Get the call method mime type - * @param cn - * @param apiCallbacks keeps track of the target callback before broadcast - */ - protected static void getCallMethodMimeType(final ComponentName cn, final - HashMap apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getCallableMimeType(getInstance().mClient, - cn); - ResultCallback callback = new ResultCallback() { - @Override - public void onResult(MimeTypeResult mimeTypeResult) { - synchronized (mCallMethodInfos) { - CallMethodInfo cmi = getCallMethodIfExists(cn); - if (cmi != null) { - cmi.mMimeType = mimeTypeResult.mimeType; - mCallMethodInfos.put(cn, cmi); - maybeBroadcastToSubscribers(this, apiCallbacks); - } - } - } - }; - apiCallbacks.put(callback, result); - } - - /** - * Get the call method mime type - * @param cn - * @param apiCallbacks keeps track of the target callback before broadcast - */ - protected static void getCallMethodVideoCallableMimeType(final ComponentName cn, final - HashMap apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getVideoCallableMimeType(getInstance() - .mClient, cn); - ResultCallback callback = new ResultCallback() { - @Override - public void onResult(MimeTypeResult mimeTypeResult) { - synchronized (mCallMethodInfos) { - CallMethodInfo cmi = getCallMethodIfExists(cn); - if (cmi != null) { - cmi.mVideoCallableMimeType = mimeTypeResult.mimeType; - mCallMethodInfos.put(cn, cmi); - maybeBroadcastToSubscribers(this, apiCallbacks); - } - } - } - }; - apiCallbacks.put(callback, result); - } - - /** - * Get the IM mime type - * @param cn - * @param apiCallbacks keeps track of the target callbacks before broadcast - */ - protected static void getCallMethodImMimeType(final ComponentName cn, final - HashMap apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getImMimeType(getInstance().mClient, cn); - ResultCallback callback = new ResultCallback() { - @Override - public void onResult(MimeTypeResult mimeTypeResult) { - synchronized (mCallMethodInfos) { - CallMethodInfo cmi = getCallMethodIfExists(cn); - if (cmi != null) { - cmi.mImMimeType = mimeTypeResult.mimeType; - mCallMethodInfos.put(cn, cmi); - maybeBroadcastToSubscribers(this, apiCallbacks); - } - } - } - }; - apiCallbacks.put(callback, result); - } - - /** - * Get the Authentication state of the callmethod - * @param cn - * @param apiCallbacks keeps track of the target callbacks before broadcast - */ - protected static void getCallMethodAuthenticated(final ComponentName cn, final - HashMap apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getAuthenticationState(getInstance() - .mClient, cn); - ResultCallback callback = new ResultCallback() { - @Override - public void onResult(AuthenticationStateResult result) { - synchronized (mCallMethodInfos) { - CallMethodInfo cmi = getCallMethodIfExists(cn); - if (cmi != null) { - cmi.mIsAuthenticated = result.result == StatusCodes.AuthenticationState - .LOGGED_IN; - mCallMethodInfos.put(cn, cmi); - maybeBroadcastToSubscribers(this, apiCallbacks); - } - } - } - }; - apiCallbacks.put(callback, result); - } - - /** - * Get the logged in account handle of the callmethod - * @param cn - * @param apiCallbacks keeps track of the target callbacks before broadcast - */ - protected static void getCallMethodAccountHandle(final ComponentName cn, final - HashMap apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getAccountHandle(getInstance().mClient, cn); - ResultCallback callback = new ResultCallback() { - @Override - public void onResult(AccountHandleResult result) { - synchronized (mCallMethodInfos) { - CallMethodInfo cmi = getCallMethodIfExists(cn); - if (cmi != null) { - cmi.mAccountHandle = result.accountHandle; - if (TextUtils.isEmpty(cmi.mAccountHandle)) { - cmi.mAccountHandle = - CallMethodUtils.lookupAccountHandle( - getInstance().mContext, cmi.mAccountType); - } - mCallMethodInfos.put(cn, cmi); - maybeBroadcastToSubscribers(this, apiCallbacks); - } - } - } - }; - apiCallbacks.put(callback, result); - } - - /** - * Get the settings intent for the callmethod - * @param cn - * @param apiCallbacks keeps track of the target callback counts before broadcast - */ - private static void getSettingsIntent(final ComponentName cn, final - HashMap apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getSettingsIntent(getInstance().mClient, - cn); - ResultCallback callback = new ResultCallback() { - @Override - public void onResult(PendingIntentResult pendingIntentResult) { - synchronized (mCallMethodInfos) { - CallMethodInfo cmi = getCallMethodIfExists(cn); - if (cmi != null) { - cmi.mSettingsIntent = pendingIntentResult.intent; - mCallMethodInfos.put(cn, cmi); - maybeBroadcastToSubscribers(this, apiCallbacks); - } - } - } - }; - apiCallbacks.put(callback, result); - } - - /** - * Get the hint texts for the callmethod - * @param cn - * @param apiCallbacks keeps track of the target callback counts before broadcast - */ - private static void getHintText(final ComponentName cn, - final HashMap apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getHintText(getInstance().mClient, cn); - ResultCallback callback = new ResultCallback() { - @Override - public void onResult(HintTextResultResult hintTextResultResult) { - if (hintTextResultResult != null) { - HintTextResult result = hintTextResultResult.result; - if (result != null) { - synchronized (mCallMethodInfos) { - CallMethodInfo cmi = getCallMethodIfExists(cn); - if (cmi != null) { - String hintText = result.getNoCreditOrSubscriptionHint(); - if (!TextUtils.isEmpty(hintText)) { - cmi.mT9HintDescriptionNoCreditOrSub = hintText; - } - - hintText = result.getHasCreditOrSubscriptionHint(); - if (!TextUtils.isEmpty(hintText)) { - cmi.mT9HintDescriptionHasCreditOrSub = hintText; - } - mCallMethodInfos.put(cn, cmi); - maybeBroadcastToSubscribers(this, apiCallbacks); - } - } - } - } - } - }; - apiCallbacks.put(callback, result); - } - - private static void getCreditInfo(final ComponentName cn, final - HashMap apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getCreditInfo(getInstance().mClient, cn); - ResultCallback callback = new ResultCallback() { - @Override - public void onResult(GetCreditInfoResultResult getCreditInfoResultResult) { - CallMethodInfo cmi = getCallMethodIfExists(cn); - if (cmi != null) { - GetCreditInfoResult gcir = getCreditInfoResultResult.result; - if (gcir == null || gcir.creditInfo == null) { - // Build zero credit dummy if no result found. - cmi.mProviderCreditInfo = - new CreditInfo(new CreditBalance(0, null), null); - } else { - cmi.mProviderCreditInfo = gcir.creditInfo; - } - mCallMethodInfos.put(cn, cmi); - maybeBroadcastToSubscribers(this, apiCallbacks); - } - } - }; - apiCallbacks.put(callback, result); - } - - private static void getManageCreditsIntent(final ComponentName cn, final - HashMap apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getManageCreditsIntent(getInstance() - .mClient, cn); - ResultCallback callback = new ResultCallback() { - @Override - public void onResult(PendingIntentResult pendingIntentResult) { - CallMethodInfo cmi = getCallMethodIfExists(cn); - if (cmi != null) { - cmi.mManageCreditIntent = pendingIntentResult.intent; - mCallMethodInfos.put(cn, cmi); - maybeBroadcastToSubscribers(this, apiCallbacks); - } - } - }; - apiCallbacks.put(callback, result); - } - - private static void checkLowCreditConfig(final ComponentName cn, final - HashMap apiCallbacks) { - // find a nudge component if it exists for this package - Intent nudgeIntent = new Intent("cyanogen.service.NUDGE_PROVIDER"); - nudgeIntent.setPackage(cn.getPackageName()); - List resolved = getInstance().mContext.getPackageManager() - .queryIntentServices(nudgeIntent, 0); - if (resolved != null && !resolved.isEmpty()) { - ResolveInfo result = resolved.get(0); - ComponentName nudgeComponent = new ComponentName(result.serviceInfo.applicationInfo - .packageName, result.serviceInfo.name); - collectLowCreditConfig(cn, nudgeComponent, apiCallbacks); - return; - } - - // if a nudge component doesn't exist, just finish here - maybeBroadcastToSubscribers(null, apiCallbacks); - } - - private static void collectLowCreditConfig(final ComponentName pluginComponent, final - ComponentName nudgeComponent, final HashMap - apiCallbacks) { - PendingResult result = NudgeServices.NudgeApi.getConfigurationForKey(getInstance().mClient, - nudgeComponent, NudgeKey.INCALL_CREDIT_NUDGE); - ResultCallback callback = new ResultCallback() { - @Override - public void onResult(BundleResult bundleResult) { - CallMethodInfo cmi = getCallMethodIfExists(pluginComponent); - if (cmi != null) { - if (bundleResult != null && bundleResult.bundle != null && - bundleResult.bundle.containsKey(NudgeKey - .INCALL_PARAM_CREDIT_WARN)) { - Object creditWarn = bundleResult.bundle.get(NudgeKey - .INCALL_PARAM_CREDIT_WARN); - if (creditWarn.getClass().equals(Integer.class)) { - cmi.mCreditWarn = (Integer) creditWarn; - } else if (creditWarn.getClass().equals(Float.class)) { - cmi.mCreditWarn = (Float) creditWarn; - } else { - Log.e(TAG, "Invalid value for Credit Warn limit: " + creditWarn); - } - mCallMethodInfos.put(pluginComponent, cmi); - } - maybeBroadcastToSubscribers(this, apiCallbacks); - } - } - }; - apiCallbacks.put(callback, result); - } - - protected static void getLoginIntent(final ComponentName cn, final - HashMap apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getLoginIntent(getInstance().mClient, cn); - ResultCallback callback = new ResultCallback() { - @Override - public void onResult(PendingIntentResult pendingIntentResult) { - synchronized (mCallMethodInfos) { - CallMethodInfo cmi = getCallMethodIfExists(cn); - if (cmi != null) { - cmi.mLoginIntent = pendingIntentResult.intent; - mCallMethodInfos.put(cn, cmi); - maybeBroadcastToSubscribers(this, apiCallbacks); - } - } - } - }; - apiCallbacks.put(callback, result); - } - - /** - * Get the the contact directory search intent with a callback - * @param cn - * @param apiCallbacks keeps track of the target callbacks before broadcast - */ - protected static void getDefaultDirectorySearchIntent(final ComponentName cn, final - HashMap apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getDirectorySearchIntent(getInstance() - .mClient, cn, Uri.parse("")); - ResultCallback callback = new ResultCallback() { - @Override - public void onResult(PendingIntentResult pendingIntentResult) { - synchronized (mCallMethodInfos) { - CallMethodInfo cmi = getCallMethodIfExists(cn); - if (cmi != null) { - cmi.mDefaultDirectorySearchIntent = pendingIntentResult.intent; - maybeBroadcastToSubscribers(this, apiCallbacks); - } - } - } - }; - apiCallbacks.put(callback, result); - } - - /** - * Get the the invite intent with a callback - * @param cn - * @param contactInfo contact info contains display name, phone, and contact Uri for lookup - */ - protected static void getInviteIntent(final ComponentName cn, InCallContactInfo contactInfo) { - getInstance().mInCallApi.getInviteIntent(getInstance().mClient, cn, - contactInfo).setResultCallback(new ResultCallback() { - @Override - public void onResult(PendingIntentResult pendingIntentResult) { - synchronized (mCallMethodInfos) { - CallMethodInfo cmi = getCallMethodIfExists(cn); - if (cmi != null) { - cmi.mInviteIntent = pendingIntentResult.intent; - } - } - } - }); - } - - /** - * Get the the contact directory search intent with a blocking call - * @param cn - * @param contactInfo contact info contains display name, phone and contact Uri for lookup - */ - - public static PendingIntent getInviteIntentSync(final ComponentName cn, InCallContactInfo - contactInfo) { - PendingIntentResult pendingIntentResult = - getInstance().mInCallApi.getInviteIntent(getInstance().mClient, cn, contactInfo) - .await(); - CallMethodInfo cmi = getCallMethodIfExists(cn); - if (cmi != null) { - cmi.mInviteIntent = pendingIntentResult.intent; - } - return cmi.mInviteIntent; - } - - /** - * Get the the contact directory search intent with a callback - * @param cn - * @param contactUri contact lookup Uri - */ - protected static void getDirectorySearchIntent(final ComponentName cn, Uri contactUri) { - getInstance().mInCallApi.getDirectorySearchIntent(getInstance().mClient, cn, contactUri) - .setResultCallback(new ResultCallback() { - @Override - public void onResult(PendingIntentResult pendingIntentResult) { - synchronized (mCallMethodInfos) { - CallMethodInfo cmi = getCallMethodIfExists(cn); - if (cmi != null) { - cmi.mDirectorySearchIntent = pendingIntentResult.intent; - } - } - } - }); - } - - /** - * Get the the contact directory search intent with a blocking call - * @param cn - * @param contactUri contact lookup Uri - */ - - public static PendingIntent getDirectorySearchIntentSync(final ComponentName cn, Uri - contactUri) { - PendingIntentResult pendingIntentResult = - getInstance().mInCallApi.getDirectorySearchIntent(getInstance().mClient, cn, - contactUri).await(); - CallMethodInfo cmi = getCallMethodIfExists(cn); - if (cmi != null) { - cmi.mDirectorySearchIntent = pendingIntentResult.intent; - } - return cmi.mDirectorySearchIntent; - } - - protected static ComponentName getNudgeComponent(final ComponentName cn) { - // find a nudge component if it exists for this package - Intent nudgeIntent = new Intent("cyanogen.service.NUDGE_PROVIDER"); - nudgeIntent.setPackage(cn.getPackageName()); - List resolved = getInstance().mContext.getPackageManager() - .queryIntentServices(nudgeIntent, 0); - if (resolved != null && !resolved.isEmpty()) { - ResolveInfo result = resolved.get(0); - return new ComponentName(result.serviceInfo.applicationInfo - .packageName, result.serviceInfo.name); - } - // if a nudge component doesn't exist, just finish here - return null; - } - - protected static void getNudgeConfiguration(final ComponentName cn, final String key, final - HashMap apiCallbacks) { - final ComponentName nudgeComponent; - - CallMethodInfo cm = null; - synchronized (mCallMethodInfos) { - cm = getCallMethodIfExists(cn); - } - if (cm == null) { - return; - } - - if (cm.mNudgeComponent == null) { - nudgeComponent = getNudgeComponent(cn); - cm.mNudgeComponent = nudgeComponent; - } else { - nudgeComponent = cm.mNudgeComponent; - } - PendingResult result = NudgeServices.NudgeApi.getConfigurationForKey(getInstance() - .mClient, nudgeComponent, key); - ResultCallback callback = new ResultCallback() { - @Override - public void onResult(BundleResult bundleResult) { - synchronized (mCallMethodInfos) { - CallMethodInfo cmi = getCallMethodIfExists(cn); - if (bundleResult != null && bundleResult.bundle != null) { - Bundle nudgeConfig = bundleResult.bundle; - switch (key) { - case NudgeKey.INCALL_CONTACT_FRAGMENT_LOGIN: - cmi.mLoginSubtitle = - nudgeConfig.getString(NudgeKey.NUDGE_PARAM_SUBTITLE, ""); - break; - case NudgeKey.INCALL_CONTACT_CARD_LOGIN: - cmi.mLoginNudgeEnable = - nudgeConfig.getBoolean(NudgeKey.NUDGE_PARAM_ENABLED, true) - && - PreferenceManager.getDefaultSharedPreferences - (getInstance().mContext) - .getBoolean(cn.getClassName() + "." + key, - true); - cmi.mLoginNudgeTitle = - nudgeConfig.getString(NudgeKey.NUDGE_PARAM_TITLE); - cmi.mLoginNudgeSubtitle = - nudgeConfig.getString(NudgeKey.NUDGE_PARAM_SUBTITLE); - cmi.mLoginNudgeActionText = nudgeConfig.getString(NudgeKey. - NUDGE_PARAM_ACTION_TEXT); - break; - case NudgeKey.INCALL_CONTACT_CARD_DOWNLOAD: - cmi.mInstallNudgeEnable = - nudgeConfig.getBoolean(NudgeKey.NUDGE_PARAM_ENABLED, true) - && - PreferenceManager.getDefaultSharedPreferences - (getInstance().mContext) - .getBoolean(cn.getClassName() + "." + key, - true); - cmi.mInstallNudgeTitle = - nudgeConfig.getString(NudgeKey.NUDGE_PARAM_TITLE); - cmi.mInstallNudgeSubtitle = - nudgeConfig.getString(NudgeKey.NUDGE_PARAM_SUBTITLE); - cmi.mInstallNudgeActionText = nudgeConfig.getString(NudgeKey. - NUDGE_PARAM_ACTION_TEXT); - break; - default: - break; - - } - } - - maybeBroadcastToSubscribers(this, apiCallbacks); - } - } - }; - apiCallbacks.put(callback, result); - } - - public static Set getAllEnabledVoiceMimeSet() { - String[] mimes = getAllEnabledMimeTypes().split(","); - HashSet mimeSet = new HashSet(); - if (mimes != null) { - mimeSet.addAll(Arrays.asList(mimes)); - } - return mimeSet; - } - - public static Set getAllEnabledVideoImMimeSet() { - String[] videoMimes = getAllEnabledVideoCallableMimeTypes().split(","); - String[] imMimes = getAllEnabledImMimeTypes().split(","); - HashSet mimeSet = new HashSet(); - - if (videoMimes != null) { - mimeSet.addAll(Arrays.asList(videoMimes)); - } - if (imMimes != null) { - mimeSet.addAll(Arrays.asList(imMimes)); - } - return mimeSet; - } - - public static boolean infoReady() { - return dataHasBeenBroadcastPreviously; - } -} diff --git a/src-ambient/incall/CallMethodInfo.java b/src-ambient/incall/CallMethodInfo.java deleted file mode 100644 index 7c6ec96..0000000 --- a/src-ambient/incall/CallMethodInfo.java +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Copyright (C) 2015 The CyanogenMod 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.phone.common.incall; - -import android.app.PendingIntent; -import android.content.ComponentName; -import android.content.Context; -import android.content.res.Resources; -import android.graphics.drawable.Drawable; -import android.os.UserHandle; -import android.provider.ContactsContract; -import android.telecom.PhoneAccountHandle; -import android.telephony.PhoneNumberUtils; -import android.telephony.SubscriptionManager; -import android.text.TextUtils; -import android.util.Log; -import com.android.phone.common.ambient.AmbientConnection; -import com.android.phone.common.incall.CallMethodHelper.InCallCallListener; -import com.android.phone.common.R; -import com.android.phone.common.util.StartInCallCallReceiver; -import com.cyanogen.ambient.incall.InCallServices; -import com.cyanogen.ambient.incall.extension.CreditBalance; -import com.cyanogen.ambient.incall.extension.CreditInfo; -import com.cyanogen.ambient.incall.extension.StartCallRequest; -import com.cyanogen.ambient.incall.extension.SubscriptionInfo; -import com.google.common.base.Objects; - -import java.math.BigDecimal; -import java.util.Currency; -import java.util.List; - -public class CallMethodInfo { - - public String mId; - public UserHandle mUserHandle; - public ComponentName mComponent; - public String mName; - public String mSummary; - public int mSlotId; - public int mSubId; - public int mColor; - public int mStatus; - public boolean mIsAuthenticated; - public String mMimeType; - public String mVideoCallableMimeType; - public String mImMimeType; - public String mSubscriptionButtonText; - public String mCreditButtonText; - public String mT9HintDescriptionNoCreditOrSub; - public String mT9HintDescriptionHasCreditOrSub; - public PendingIntent mSettingsIntent; - /* Plugin's simple brand icon (24dp x 24dp) - Expected format: Vector Drawable (.xml) - 2 colors allowed. */ - public Drawable mBrandIcon; - /* Plugin's single color simple brand icon (24dp x 24dp) - Same as to pluginBrandIcon, but only a single color. - Please keep image single color, it will be tinted in the client application. - Expected format: Vector Drawable (.xml) - 1 color allowed. */ - public Drawable mSingleColorBrandIcon; - /* Plugin's attribution badge (17dp x 17dp) - Similar to pluginBrandIcon. - To make badge pop against (on top of) images, please add a white outline. - Expected format: Vector Drawable (.xml) - 2 colors allowed. */ - public Drawable mBadgeIcon; - /* Plugin's full brand icon (any width (max 360dp) x 60dp height) - May contain full brand name or icon image. - Please keep image single color, it will be tinted in the client application. - Expected format: Vector Drawable (.xml) - 1 color allowed. */ - public Drawable mLoginIcon; - /* Plugin's first action icon (24dp x 24dp) - This icon is used in conjunction with pluginActionOneTitle. - Expected format: Vector Drawable (.xml) - 2 colors allowed. */ - public Drawable mActionOneIcon; - /* Plugin's second action icon (24dp x 24dp) - This icon is used in conjunction with pluginActionTwoTitle. - Expected format: Vector Drawable (.xml) - 2 colors allowed. */ - public Drawable mActionTwoIcon; - public String mActionOneText; - public String mActionTwoText; - public boolean mIsInCallProvider; - public PendingIntent mManageCreditIntent; - public CreditInfo mProviderCreditInfo; - public float mCreditWarn = 0.0f; - public PendingIntent mLoginIntent; - public PendingIntent mDefaultDirectorySearchIntent; // empty contact Uri - public PendingIntent mDirectorySearchIntent; - public PendingIntent mInviteIntent; - public String mAccountType; - private int mCurrencyAmount; - public String mAccountHandle; - public int mBrandIconId; // resource ID - public int mLoginIconId; - - public ComponentName mNudgeComponent; - public String mLoginSubtitle; - // Contact login nudge - public boolean mLoginNudgeEnable; - public String mLoginNudgeTitle; - public String mLoginNudgeSubtitle; - public String mLoginNudgeActionText; - // Contact install nudge - public boolean mInstallNudgeEnable; - public String mInstallNudgeTitle; - public String mInstallNudgeSubtitle; - public String mInstallNudgeActionText; - public String mDependentPackage; - /* Plugin's IM action icon (24dp x 24dp) - Expected format: Vector Drawable (.xml) - 1 color allowed. */ - public Drawable mImIcon; - /* Plugin's video call action icon (24dp x 24dp) - Expected format: Vector Drawable (.xml) - 1 color allowed. */ - public Drawable mVideoIcon; - /* Plugin's voice call action icon (24dp x 24dp) - Expected format: Vector Drawable (.xml) - 1 color allowed. */ - public Drawable mVoiceIcon; - private static CallMethodInfo sEmergencyCallMethod; - - @Override - public int hashCode() { - return Objects.hashCode(mId, mComponent, mName, mSlotId, mSubId); - } - - public static final String TAG = "CallMethodInfo"; - - @Override - public boolean equals(Object object) { - if (this == object) { - return true; - } - if (object instanceof CallMethodInfo) { - final CallMethodInfo info = (CallMethodInfo) object; - return Objects.equal(this.mId, info.mId) - && Objects.equal(this.mComponent, info.mComponent) - && Objects.equal(this.mName, info.mName) - && Objects.equal(this.mSlotId, info.mSlotId) - && Objects.equal(this.mSubId, info.mSubId); - } - return false; - } - - /** - * return empty mock call method that represents emergency call only mode - */ - public static CallMethodInfo getEmergencyCallMethod(Context ctx) { - if (sEmergencyCallMethod == null) { - sEmergencyCallMethod = new CallMethodInfo(); - sEmergencyCallMethod.mName = - ctx.getString(R.string.call_method_spinner_item_emergency_call); - sEmergencyCallMethod.mColor = - ctx.getResources().getColor(R.color.emergency_call_icon_color); - sEmergencyCallMethod.mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - sEmergencyCallMethod.mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX; - sEmergencyCallMethod.mIsInCallProvider = false; - } - - return sEmergencyCallMethod; - } - - public static PhoneAccountHandle getPhoneAccountHandleFromCallMethodInfo(Context ctx, - CallMethodInfo callMethodInfo, String number) { - CallMethodInfo emergencyCallMethod = getEmergencyCallMethod(ctx); - // If no sim is selected, or emergency callmethod selected, or number is - // an emergency number, phone account handle should be null, and will use the - // default account. - // Else, create PhoneAccountHandle from selected callmethod components and - // initial call using that account. - PhoneAccountHandle handle = null; - if (callMethodInfo != null && !callMethodInfo.mIsInCallProvider && - !callMethodInfo.equals(emergencyCallMethod) && - (number == null || !PhoneNumberUtils.isEmergencyNumber(number))) { - handle = new PhoneAccountHandle(callMethodInfo.mComponent, - callMethodInfo.mId, - callMethodInfo.mUserHandle); - } - return handle; - } - - public void placeCall(String origin, String number, Context c) { - placeCall(origin, number, c, false); - } - - public void placeCall(String origin, String number, Context c, boolean isVideoCall) { - placeCall(origin, number, c, isVideoCall, false, null, null); - } - - public void placeCall(String origin, String number, Context c, boolean isVideoCall, boolean - forcePSTN) { - placeCall(origin, number, c, isVideoCall, forcePSTN, null, null); - } - - public void placeCall(String origin, String number, Context c, boolean isVideoCall, boolean - forcePSTN, InCallCallListener listener) { - placeCall(origin, number, c, isVideoCall, forcePSTN, null, listener); - } - - public void placeCall(String origin, String number, Context c, boolean isVideoCall, - boolean forcePSTN, String numberMimeType) { - placeCall(origin, number, c, isVideoCall, forcePSTN, numberMimeType, null); - } - - public void placeCall(String origin, String number, Context c, boolean isVideoCall, - boolean forcePSTN, String numberMimeType, InCallCallListener listener) { - - StartInCallCallReceiver svcrr - = CallMethodHelper.getVoIPResultReceiver(this, origin, listener); - StartCallRequest request = new StartCallRequest(number, origin, 0, svcrr); - - if (isVideoCall) { - InCallServices.getInstance().startVideoCall( - AmbientConnection.CLIENT.get(c), this.mComponent, request); - } else { - if (forcePSTN || (numberMimeType != null && numberMimeType.equals(ContactsContract - .CommonDataKinds.Phone.CONTENT_ITEM_TYPE))) { - InCallServices.getInstance().startOutCall( - AmbientConnection.CLIENT.get(c), this.mComponent, request); - } else { - InCallServices.getInstance().startVoiceCall( - AmbientConnection.CLIENT.get(c), this.mComponent, request); - } - } - } - - public String getCreditsDescriptionText(Resources r) { - String ret = null; - CreditInfo ci = this.mProviderCreditInfo; - - List subscriptionInfos = ci.subscriptions; - - if (showSubscriptions()) { - int size = subscriptionInfos.size(); - return r.getQuantityString(R.plurals.number_of_incall_subscriptions, size, size); - } else { - CreditBalance balance = ci.balance; - if (balance != null) { - mCurrencyAmount = (int) balance.balance; - try { - if (balance.currencyCode != null) { - Currency currencyCode = Currency.getInstance(balance.currencyCode); - BigDecimal availableCredit = BigDecimal.valueOf(mCurrencyAmount, - currencyCode.getDefaultFractionDigits()); - - - return currencyCode.getSymbol(r.getConfiguration().locale) - + availableCredit.toString(); - } else { - throw new IllegalArgumentException(); - } - } catch (IllegalArgumentException e) { - Log.w(TAG, "Unable to retrieve currency code for plugin: " + - this.mComponent); - - return null; - } - } else { - Log.e(TAG, "Plugin has credit component but the balance and subscriptions are" + - " null. This should never happen. Not showing credit banner due to " + - "failures."); - - return null; - } - } - } - - public boolean hasAvailableCredit() { - boolean hasAvailableCredit = false; - CreditInfo ci = this.mProviderCreditInfo; - if (ci != null) { - CreditBalance balance = ci.balance; - if (balance != null) { - mCurrencyAmount = (int) balance.balance; - hasAvailableCredit = mCurrencyAmount > 0; - } - } - return hasAvailableCredit; - } - - public boolean hasSubscription() { - boolean hasSubscription = false; - CreditInfo ci = this.mProviderCreditInfo; - if (ci != null) { - List subscriptionInfos = ci.subscriptions; - hasSubscription = subscriptionInfos != null && !subscriptionInfos.isEmpty(); - } - return hasSubscription; - } - - public boolean showSubscriptions() { - // If the user has > 0 credits, we don't want to show the subscription. - boolean hasCredits = hasAvailableCredit(); - boolean hasSubscriptions = hasSubscription(); - - return !hasCredits && hasSubscriptions; - } - - public int getCurrencyAmount() { - return mCurrencyAmount; - } - - /** - * Returns hint text based on credit and subscription availability. - * If either is null or empty, use the non-null/non-empty string. - * Null is returned if both are null or empty. - * @return hintText String hint text for the current situation - */ - public String getHintText() { - String hintText = null; - boolean hasAvailableCredit = hasAvailableCredit(); - boolean usesSubscriptions = hasSubscription(); - if (!hasAvailableCredit && !usesSubscriptions) { - // No credits and no subscription - if (!TextUtils.isEmpty(mT9HintDescriptionNoCreditOrSub)) { - // Use no credits or subscription hint - hintText = mT9HintDescriptionNoCreditOrSub; - } else if (!TextUtils.isEmpty(mT9HintDescriptionHasCreditOrSub)) { - // If no credit/sub hint empty, but has credit/sub hint valid, use it - hintText = mT9HintDescriptionHasCreditOrSub; - } - } else { - // Has credits or subscription - if (!TextUtils.isEmpty(mT9HintDescriptionHasCreditOrSub)) { - // Use has credits/sub hint text - hintText = mT9HintDescriptionHasCreditOrSub; - } else if (!TextUtils.isEmpty(mT9HintDescriptionNoCreditOrSub)) { - // If has credit/sub hint empty, but no credit/sub hint valid, use it - hintText = mT9HintDescriptionNoCreditOrSub; - } - } - return hintText; - } -} diff --git a/src-ambient/incall/CallMethodSpinnerAdapter.java b/src-ambient/incall/CallMethodSpinnerAdapter.java deleted file mode 100644 index c3a61d3..0000000 --- a/src-ambient/incall/CallMethodSpinnerAdapter.java +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright (C) 2015 The CyanogenMod 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.phone.common.incall; - -import android.content.ComponentName; -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.LayerDrawable; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.ImageView; -import android.widget.SpinnerAdapter; -import android.widget.TextView; - -import com.android.phone.common.R; -import com.android.phone.common.util.VolteUtils; - -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static com.cyanogen.ambient.incall.util.InCallHelper.NO_COLOR; - -public class CallMethodSpinnerAdapter extends ArrayAdapter - implements SpinnerAdapter { - private static final String TAG = CallMethodSpinnerAdapter.class.getSimpleName(); - public static final int POSITION_UNKNOWN = -1; - - private final Context mContext; - private Map mComponentMap; - private boolean mShowVolte = false; - - public CallMethodSpinnerAdapter(Context context, List objects, boolean - showVolte) { - super(context, 0, objects); - mContext = context.getApplicationContext(); - processCallMethods(objects); - mShowVolte = showVolte; - } - - /** - * Remove all elements from the list. - */ - @Override - public void clear() { - super.clear(); - if (mComponentMap != null) { - mComponentMap.clear(); - } - } - - /** - * Adds the specified call method info at the end of the array. - * - * @param object The call method info entry to add at the end of the array. - */ - public void add(CallMethodInfo object) { - super.add(object); - processCallMethod(object); - } - - /** - * Adds the specified list of call method infos at the end of the array. - * - * @param objects The list of call method info entries to add at the end of the array. - */ - public void addAll(List objects) { - super.addAll(objects); - processCallMethods(objects); - } - - /** - * Adds the specified list of call method infos at the end of the array. - * - * @param objects The list of call method info entries to add at the end of the array. - */ - public void addAll(HashMap objects) { - super.addAll(objects.values()); - processCallMethods(objects.values()); - } - - /** - * Get a View that displays the data at the specified position in the data set. You can either - * create a View manually or inflate it from an XML layout file. When the View is inflated, the - * parent View (GridView, ListView...) will apply default layout parameters unless you use - * {@link LayoutInflater#inflate(int, ViewGroup, boolean)} - * to specify a root view and to prevent attachment to the root. - * - * @param position The position of the item within the adapter's data set of the item whose - * view we want. - * @param convertView The old view to reuse, if possible. Note: You should check that this view - * is non-null and of an appropriate type before using. If it is not possible - * to convert this view to display the correct data, this method can create a - * new view. Heterogeneous lists can specify their number of view types, so - * that this View is always of the right type - * (see {@link #getViewTypeCount()} and {@link #getItemViewType(int)}). - * @param parent The parent that this view will eventually be attached to - * @return A View corresponding to the data at the specified position. - */ - @Override - public View getView(int position, View convertView, ViewGroup parent) { - CallMethodInfo callMethodInfo = getItem(position); - boolean volteInUse = (callMethodInfo.mSubId > 0) && - VolteUtils.isVolteInUse(mContext, callMethodInfo.mSubId); - - SpinnerItemViewHolder holder = null; - // Note: if mVolteInUse changes, it invalidates cached views - if (convertView == null || (holder = (SpinnerItemViewHolder)convertView.getTag()) == null - || holder.volteInUse != volteInUse) - { - convertView = LayoutInflater.from(mContext).inflate(R.layout - .call_method_spinner_item, parent, false); - if (holder == null) holder = new SpinnerItemViewHolder(); - convertView.setTag(holder); - holder.volteInUse = volteInUse; - holder.callTypeIcon = (ImageView) convertView.findViewById(R.id - .call_method_spinner_item_image); - holder.volteIcon = (ImageView) convertView.findViewById(R.id - .call_method_spinner_volte_image); - } - if (mShowVolte && volteInUse) { - holder.volteIcon.setVisibility(View.VISIBLE); - } else { - holder.volteIcon.setVisibility(View.GONE); - } - setIcon(convertView, callMethodInfo); - - return convertView; - } - - /** - *

Get a {@link View} that displays in the drop down popup - * the data at the specified position in the data set.

- * - * @param position index of the item whose view we want. - * @param convertView the old view to reuse, if possible. Note: You should - * check that this view is non-null and of an appropriate type before - * using. If it is not possible to convert this view to display the - * correct data, this method can create a new view. - * @param parent the parent that this view will eventually be attached to - * @return a {@link View} corresponding to the data at the - * specified position. - */ - @Override - public View getDropDownView(int position, View convertView, ViewGroup parent) { - CallMethodInfo callMethodInfo = getItem(position); - boolean volteInUse = (callMethodInfo.mSubId > 0) && - VolteUtils.isVolteInUse(mContext, callMethodInfo.mSubId); - - SpinnerDropDownItemViewHolder holder = null; - int tag = volteInUse ? R.string.call_method_spinner_volte_drop_down_tag : - R.string.call_method_spinner_drop_down_tag; - if (convertView == null || (holder = (SpinnerDropDownItemViewHolder) convertView.getTag - (tag)) == null) { - int resId = R.layout.call_method_spinner_dropdown_item; - convertView = LayoutInflater.from(mContext).inflate(resId, parent, false); - holder = new SpinnerDropDownItemViewHolder(); - holder.volteInUse = volteInUse; - holder.callTypeIcon = (ImageView) convertView.findViewById(R.id - .call_method_spinner_item_image); - holder.volteIcon = (ImageView) convertView.findViewById(R.id - .call_method_spinner_volte_image); - holder.textView = (TextView) convertView.findViewById(R.id - .call_method_spinner_item_text); - convertView.setTag(tag, holder); - } - if (mShowVolte && volteInUse) { - holder.volteIcon.setVisibility(View.VISIBLE); - } else { - holder.volteIcon.setVisibility(View.GONE); - } - setIcon(convertView, callMethodInfo); - holder.textView.setText(callMethodInfo.mName); - - return convertView; - } - - private void setIcon(View convertView, CallMethodInfo callMethodInfo) { - ImageView icon = (ImageView) convertView.findViewById(R.id.call_method_spinner_item_image); - if (callMethodInfo.mBrandIcon != null) { - icon.setImageDrawable(callMethodInfo.mBrandIcon); - icon.getDrawable().setTintList(null); - icon.setBackground(null); - } else { - int drawableId = getIconForSlot(callMethodInfo.mSlotId); - - Drawable forground = mContext.getDrawable(drawableId); - Drawable background = mContext.getDrawable(R.drawable.ic_sim_backing); - - if (callMethodInfo.mColor != NO_COLOR) { - forground.setTint(callMethodInfo.mColor); - } else { - forground.setTint(mContext.getResources().getColor(R.color.sim_icon_color)); - } - - Drawable[] layers = {background, forground}; - LayerDrawable layerDrawable = new LayerDrawable(layers); - icon.setImageDrawable(layerDrawable); - } - } - - public static int getIconForSlot(int slotId) { - switch (slotId) { - case -1: - // SubscriptionManager.INVALID_SIM_SLOT_INDEX - return R.drawable.ic_nosim; - case 1: - return R.drawable.ic_sim_2; - case 2: - return R.drawable.ic_sim_3; - case 3: - return R.drawable.ic_sim_4; - default: - return R.drawable.ic_sim_1; - } - } - - /** - * Map call method component names to spinner positions. - * @param callMethodInfo new call method - */ - private void processCallMethod(CallMethodInfo callMethodInfo) { - if (mComponentMap == null) { - mComponentMap = new HashMap(); - } - if (callMethodInfo != null) { - String key = getCallMethodKey(callMethodInfo); - mComponentMap.put(key, mComponentMap.size()); - } - } - - /** - * Map call method component names to spinner positions. - * @param callMethodInfoList list of current call methods - */ - private void processCallMethods(Collection callMethodInfoList) { - if (mComponentMap == null) { - mComponentMap = new HashMap(); - } - if (callMethodInfoList != null) { - for (CallMethodInfo info : callMethodInfoList) { - processCallMethod(info); - } - } - } - - /** - * Returns the position of the specified component within the spinner - * @param key String key for call method lookup - * @return Position of specified component, POSITION_UNKNOWN if not found. - */ - public int getPosition(String key) { - int position = POSITION_UNKNOWN; - if (!TextUtils.isEmpty(key)) { - position = mComponentMap.containsKey(key) ? - mComponentMap.get(key) : POSITION_UNKNOWN; - } - return position; - } - - /** - * Returns an ID for the specified CallMethodInfo - * @param info CallMethodInfo to create id for - * @return String key for specified CallMethodInfo. - */ - public static String getCallMethodKey(CallMethodInfo info) { - if (info == null) { - return null; - } - return String.valueOf(info.hashCode()); - } - - private static class SpinnerItemViewHolder { - ImageView callTypeIcon; - ImageView volteIcon; - boolean volteInUse; - } - - private static class SpinnerDropDownItemViewHolder { - ImageView callTypeIcon; - ImageView volteIcon; - TextView textView; - boolean volteInUse; - } -} diff --git a/src-ambient/incall/CallMethodUtils.java b/src-ambient/incall/CallMethodUtils.java deleted file mode 100644 index 87a2348..0000000 --- a/src-ambient/incall/CallMethodUtils.java +++ /dev/null @@ -1,151 +0,0 @@ -package com.android.phone.common.incall; - -import android.content.ComponentName; -import android.content.Context; -import android.telecom.PhoneAccount; -import android.telecom.PhoneAccountHandle; -import android.telecom.TelecomManager; -import android.telephony.SubscriptionInfo; -import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; -import android.text.TextUtils; -import android.util.ArrayMap; -import android.util.Log; - -import com.android.contacts.common.model.AccountTypeManager; -import com.android.contacts.common.model.account.AccountType; -import com.android.contacts.common.model.account.AccountWithDataSet; -import com.android.phone.common.R; - -import java.util.ArrayList; -import java.util.List; - -import static com.cyanogen.ambient.incall.util.InCallHelper.NO_COLOR; - -/** - * Basic Utils for call method modifications - */ -public class CallMethodUtils { - private final static String TAG = CallMethodUtils.class.getSimpleName(); - private final static boolean DEBUG = false; - public final static String PREF_SPINNER_COACHMARK_SHOW = "pref_spinner_coachmark_shown"; - public final static String PREF_LAST_ENABLED_PROVIDER = "pref_last_enabled_provider"; - public final static String PREF_INTERNATIONAL_CALLS = "pref_international_calls"; - public final static String PREF_WIFI_CALL = "pref_wifi_call"; - - public static CallMethodInfo getDefaultSimInfo(Context context) { - final TelecomManager telecomMgr = - (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); - PhoneAccountHandle handle = - telecomMgr.getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL); - if (handle == null) { - return null; - } - return phoneToCallMethod(context, handle); - } - - public static List getSimInfoList(Context context) { - final TelecomManager telecomMgr = - (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); - final List accountHandles = telecomMgr.getCallCapablePhoneAccounts(); - ArrayList callMethodInfoList = new ArrayList(); - for (PhoneAccountHandle accountHandle : accountHandles) { - CallMethodInfo info = phoneToCallMethod(context, accountHandle); - if (info != null) { - callMethodInfoList.add(info); - } - } - return callMethodInfoList; - } - - private static String getPhoneAccountName(Context context, PhoneAccount phoneAccount, - int slotId) { - if (phoneAccount == null) { - // Slot IDs are zero based - return context.getString(R.string.call_method_spinner_item_unknown_sim, slotId + 1); - } else if (phoneAccount.getLabel() != null) { - return phoneAccount.getLabel().toString(); - } - return null; - } - - private static int getPhoneAccountColor(SubscriptionInfo info) { - if (info != null) { - return info.getIconTint(); - } else { - return NO_COLOR; - } - } - - private static CallMethodInfo phoneToCallMethod(Context context, - PhoneAccountHandle phoneAccountHandle) { - final SubscriptionManager subMgr = (SubscriptionManager) context - .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); - final TelecomManager telecomMgr = - (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); - final TelephonyManager telephonyMgr = - (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); - PhoneAccount phoneAccount = telecomMgr.getPhoneAccount(phoneAccountHandle); - - if (phoneAccount == null) { - return null; - } - - CallMethodInfo callMethodInfo = new CallMethodInfo(); - - callMethodInfo.mComponent = phoneAccountHandle.getComponentName(); - callMethodInfo.mId = phoneAccountHandle.getId(); - callMethodInfo.mUserHandle = phoneAccountHandle.getUserHandle(); - callMethodInfo.mSubId = telephonyMgr.getSubIdForPhoneAccount(phoneAccount); - callMethodInfo.mSlotId = SubscriptionManager.getSlotId(callMethodInfo.mSubId); - callMethodInfo.mName = getPhoneAccountName(context, phoneAccount, callMethodInfo.mSlotId); - callMethodInfo.mColor = - getPhoneAccountColor(subMgr.getActiveSubscriptionInfo(callMethodInfo.mSubId)); - callMethodInfo.mIsInCallProvider = false; - - final int simState = telephonyMgr.getSimState(callMethodInfo.mSlotId); - if ((simState == TelephonyManager.SIM_STATE_ABSENT) || - (simState == TelephonyManager.SIM_STATE_UNKNOWN)) { - return null; - } - - return callMethodInfo; - } - - /* - * Look up the currently logged in plugin account in case plugin fails to return a valid - * account handle - */ - public static String lookupAccountHandle(Context context, String targetAccountType) { - // Gather account handles logged into the device as a backup, in case - // plugins fail to return the account handle even when it reports its - // state as authenticated - AccountTypeManager accountTypes = AccountTypeManager.getInstance(context); - List accounts = accountTypes.getAccounts(false); - ArrayMap accountMap = new ArrayMap(); - - for (AccountWithDataSet account : accounts) { - AccountType accountType = - accountTypes.getAccountType(account.type, account.dataSet); - if (accountType.isExtension() && !account.hasData(context)) { - // Hide extensions with no raw_contacts. - continue; - } - if (DEBUG) { - Log.d(TAG, "account.type: " + account.type + "account.name: " + account.name); - } - // currently only handle one account per account type use case - accountMap.put(account.type, account.name); - } - return accountMap.containsKey(targetAccountType) ? accountMap.get(targetAccountType) : ""; - } - - /* - * Return if the plugin is a soft logged-out state (authenticated is false and there's still - * an account saved in account manager) - */ - public static boolean isSoftLoggedOut(Context context, CallMethodInfo cmi) { - return (!cmi.mIsAuthenticated && !TextUtils.isEmpty(lookupAccountHandle(context, - cmi.mAccountType))); - } -} diff --git a/src-ambient/incall/CreditBarHelper.java b/src-ambient/incall/CreditBarHelper.java deleted file mode 100644 index 7a8eedc..0000000 --- a/src-ambient/incall/CreditBarHelper.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.android.phone.common.incall; - -import android.app.PendingIntent; -import android.content.res.Resources; -import android.text.TextUtils; -import android.util.Log; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; - -import com.android.phone.common.R; -import com.android.phone.common.incall.CallMethodInfo; - -/** - * Helper method used to handle incall credit bars - * - * The credit bar's origin code comes from google. We've added a second credit bar in dialer that - * uses these same helper method. - */ -public class CreditBarHelper { - - private static final String TAG = CreditBarHelper.class.getSimpleName(); - - public interface CreditBarVisibilityListener { - public void creditsBarVisibilityChanged(int visibility); - } - - public static void clearCallRateInformation(ViewGroup v, CreditBarVisibilityListener cpvl) { - setCallRateInformation(null, v, null, null, null, false, cpvl); - } - - public static void setCallRateInformation(Resources res, ViewGroup creditsBar, - String creditText, String buttonText, final PendingIntent buttonIntent, - boolean warnIfLow, CreditBarVisibilityListener cpvl) { - if (TextUtils.isEmpty(creditText) && TextUtils.isEmpty(buttonText) && - buttonIntent == null) { - if (creditsBar.getVisibility() != View.GONE) { - creditsBar.setVisibility(View.GONE); - cpvl.creditsBarVisibilityChanged(View.GONE); - } - return; - } - if (creditsBar.getVisibility() != View.VISIBLE) { - creditsBar.setVisibility(View.VISIBLE); - cpvl.creditsBarVisibilityChanged(View.VISIBLE); - } - - // These views already exist, we are hijacking them. - TextView credit = (TextView) creditsBar.findViewById(R.id.ild_country); - TextView button = (TextView) creditsBar.findViewById(R.id.ild_rate); - - if (res != null) { - int color = warnIfLow ? - res.getColor(R.color.credit_banner_alert_color) : - res.getColor(R.color.credit_banner_text); - credit.setTextColor(color); - } - - credit.setText(creditText); - button.setText(buttonText); - button.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - try { - if (buttonIntent != null) { - buttonIntent.send(); - } else { - Log.wtf(TAG, "The intent we attempted to fire was null"); - } - } catch (PendingIntent.CanceledException e) { - e.printStackTrace(); - } - } - }); - } - - public static void callMethodCredits(ViewGroup v, CallMethodInfo cmi, Resources r, - CreditBarVisibilityListener cpvl) { - String creditText = cmi.getCreditsDescriptionText(r); - String buttonText = null; - PendingIntent button; - - boolean warnIfLow = false; - if (TextUtils.isEmpty(creditText)) { - clearCallRateInformation(v, cpvl); - return; - } else { - if (cmi.mIsAuthenticated) { - button = cmi.mManageCreditIntent; - if (cmi.showSubscriptions()) { - buttonText = cmi.mSubscriptionButtonText; - } else { - if (cmi.getCurrencyAmount() <= cmi.mCreditWarn) { - warnIfLow = true; - } - buttonText = cmi.mCreditButtonText; - } - } else { - buttonText = r.getString(R.string.sign_in_credit_banner_text); - creditText = ""; - button = cmi.mLoginIntent; - } - } - setCallRateInformation(r, v, creditText, buttonText, button, warnIfLow, cpvl); - } -} diff --git a/src/com/android/phone/common/util/StartInCallCallReceiver.java b/src/com/android/phone/common/util/StartInCallCallReceiver.java deleted file mode 100644 index c2dd3c0..0000000 --- a/src/com/android/phone/common/util/StartInCallCallReceiver.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.android.phone.common.util; - -import android.os.Bundle; -import android.os.Handler; -import android.os.ResultReceiver; -import android.util.Log; - -import java.lang.ref.WeakReference; - -public class StartInCallCallReceiver extends ResultReceiver { - private static final String TAG = StartInCallCallReceiver.class.getSimpleName(); - private static final boolean DEBUG = false; - - private WeakReference mReceiver; - - public StartInCallCallReceiver(Handler handler) { - super(handler); - } - - @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - if (DEBUG) { - Log.d(TAG, "Result received resultCode: " + resultCode + " resultData: " + resultData); - } - - if (mReceiver != null) { - Receiver receiver = mReceiver.get(); - if (receiver != null) { - receiver.onReceiveResult(resultCode, resultData); - } - } - } - - public interface Receiver { - public void onReceiveResult(int resultCode, Bundle resultData); - } - - public void setReceiver(Receiver receiver) { - mReceiver = new WeakReference(receiver); - } - -} \ No newline at end of file -- cgit v1.2.3