diff options
author | Stephen Bird <sbird@cyngn.com> | 2016-03-31 15:03:11 -0700 |
---|---|---|
committer | Stephen Bird <sbird@cyngn.com> | 2016-04-27 16:34:09 -0700 |
commit | b26222887395b00c45324e999bc1bb0850cfc1bf (patch) | |
tree | bd01ca84ac63029b389043929874563238c73266 | |
parent | 2dd63acad0216e87207e33e3e5ca739e14c3f408 (diff) | |
download | packages_apps_PhoneCommon-b26222887395b00c45324e999bc1bb0850cfc1bf.tar.gz packages_apps_PhoneCommon-b26222887395b00c45324e999bc1bb0850cfc1bf.tar.bz2 packages_apps_PhoneCommon-b26222887395b00c45324e999bc1bb0850cfc1bf.zip |
[1/4] Refactor CallMethodHelper
Ticket: CD-589
Change-Id: I3b24eedffddbf0960023dc2f1429038e5353de2f
(cherry picked from commit ecc708d3c1417b60f316a0387d17f2cf4c67c2b1)
27 files changed, 2043 insertions, 1363 deletions
diff --git a/src-ambient/ambient/AmbientConnection.java b/src-ambient/com/android/phone/common/ambient/AmbientConnection.java index ed4c0c0..04fe63a 100644 --- a/src-ambient/ambient/AmbientConnection.java +++ b/src-ambient/com/android/phone/common/ambient/AmbientConnection.java @@ -19,7 +19,7 @@ public class AmbientConnection { public static final SingletonHolder<AmbientApiClient, Context> CLIENT = new SingletonHolder<AmbientApiClient, Context>() { - private static final String TAG = "Dialer.AmbientSingletonHolder"; + private static final String TAG = "PhoneCommon.AmbientSingletonHolder"; @Override protected AmbientApiClient create(Context context) { @@ -35,26 +35,26 @@ public class AmbientConnection { new AmbientApiClient.OnConnectionFailedListener() { @Override public void onConnectionFailed(ConnectionResult result) { - Log.w(TAG, "Ambient connection failed: " + result); + Log.w(TAG, "Connection failed: " + result); } }); client.registerDisconnectionListener( new AmbientApiClient.OnDisconnectionListener() { @Override public void onDisconnection() { - Log.d(TAG, "Ambient connection disconnected"); + Log.d(TAG, "Connection disconnected"); } }); client.registerConnectionCallbacks( new AmbientApiClient.ConnectionCallbacks() { @Override public void onConnected(Bundle connectionHint) { - Log.d(TAG, "Ambient connection established"); + Log.d(TAG, "Connection established"); } @Override public void onConnectionSuspended(int cause) { - Log.d(TAG, "Ambient connection suspended"); + Log.d(TAG, "Connection suspended"); } }); client.connect(); @@ -62,5 +62,4 @@ public class AmbientConnection { } }; - } 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<M> { + + 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<ComponentName, M> mPluginInfo; + + private static boolean mDataHasBeenBroadcastPreviously = false; + + // A list of our registered clients, who all register with the CallMethodReceiver + private static HashMap<String, PluginChanged> 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<ComponentName, Boolean> 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<Result>() { + + @Override + public void onResult(Result result) { + List<ComponentName> installedPlugins = getPluginComponents(result); + for (ComponentName cn : installedPlugins) { + ArrayList<TypedPendingResult> 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<M> { + void onChanged(HashMap<ComponentName, M> 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<ComponentName> 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<TypedPendingResult> 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<TypedPendingResult> 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<TypedPendingResult> apiCallbacks = new ArrayList<>(); + onDynamicRefreshRequested(apiCallbacks, cn); + executeAll(apiCallbacks, cn); + } + } + + private void executeAll(final ArrayList<TypedPendingResult> apiCallbacks, + final ComponentName componentName) { + + final ArrayList<PendingResult> 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<PendingResult> 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<PendingResult> 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<ComponentName, M> getPluginInfo() { + return mPluginInfo; + } +} diff --git a/src-ambient/ambient/SingletonHolder.java b/src-ambient/com/android/phone/common/ambient/SingletonHolder.java index 52890fc..bca6023 100644 --- a/src-ambient/ambient/SingletonHolder.java +++ b/src-ambient/com/android/phone/common/ambient/SingletonHolder.java @@ -46,5 +46,11 @@ public abstract class SingletonHolder<E, I> { 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/incall/CallMethodInfo.java b/src-ambient/com/android/phone/common/incall/CallMethodInfo.java index 7c6ec96..cbc5242 100644 --- a/src-ambient/incall/CallMethodInfo.java +++ b/src-ambient/com/android/phone/common/incall/CallMethodInfo.java @@ -29,9 +29,10 @@ 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.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; @@ -54,6 +55,7 @@ public class CallMethodInfo { public int mSubId; public int mColor; public int mStatus; + public boolean mIsInCallProvider; public boolean mIsAuthenticated; public String mMimeType; public String mVideoCallableMimeType; @@ -62,7 +64,6 @@ public class CallMethodInfo { 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. */ @@ -85,58 +86,44 @@ public class CallMethodInfo { 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. + /* Plugin's video call action icon (24dp x 24dp) 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. + 1 color allowed. */ + public Drawable mVideoIcon; + /* Plugin's IM action icon (24dp x 24dp) Expected format: Vector Drawable (.xml) - 2 colors allowed. */ - public Drawable mActionTwoIcon; - public String mActionOneText; - public String mActionTwoText; - public boolean mIsInCallProvider; - public PendingIntent mManageCreditIntent; + 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; - private int mCurrencyAmount; + public String mDependentPackage; + public String mLoginSubtitle; public String mAccountHandle; + public int mLoginIconId; // resource ID 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; + // Contact login nudge + public boolean mLoginNudgeEnable; + public String mLoginNudgeSubtitle; + public String mLoginNudgeActionText; @Override public int hashCode() { @@ -212,7 +199,7 @@ public class CallMethodInfo { } public void placeCall(String origin, String number, Context c, boolean isVideoCall, boolean - forcePSTN, InCallCallListener listener) { + forcePSTN, StartInCallCallReceiver.InCallCallListener listener) { placeCall(origin, number, c, isVideoCall, forcePSTN, null, listener); } @@ -222,10 +209,11 @@ public class CallMethodInfo { } public void placeCall(String origin, String number, Context c, boolean isVideoCall, - boolean forcePSTN, String numberMimeType, InCallCallListener listener) { + boolean forcePSTN, String numberMimeType, + StartInCallCallReceiver.InCallCallListener listener) { StartInCallCallReceiver svcrr - = CallMethodHelper.getVoIPResultReceiver(this, origin, listener); + = CallMethodUtils.getVoIPResultReceiver(c, this, origin, listener); StartCallRequest request = new StartCallRequest(number, origin, 0, svcrr); if (isVideoCall) { @@ -244,7 +232,6 @@ public class CallMethodInfo { } public String getCreditsDescriptionText(Resources r) { - String ret = null; CreditInfo ci = this.mProviderCreditInfo; List<SubscriptionInfo> subscriptionInfos = ci.subscriptions; @@ -350,4 +337,21 @@ public class CallMethodInfo { } 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/incall/CallMethodSpinnerAdapter.java b/src-ambient/com/android/phone/common/incall/CallMethodSpinnerAdapter.java index c3a61d3..cb3a8fe 100644 --- a/src-ambient/incall/CallMethodSpinnerAdapter.java +++ b/src-ambient/com/android/phone/common/incall/CallMethodSpinnerAdapter.java @@ -41,6 +41,7 @@ import static com.cyanogen.ambient.incall.util.InCallHelper.NO_COLOR; public class CallMethodSpinnerAdapter extends ArrayAdapter<CallMethodInfo> implements SpinnerAdapter { + private static final String TAG = CallMethodSpinnerAdapter.class.getSimpleName(); public static final int POSITION_UNKNOWN = -1; 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<ContactsDataSubscription, Context> sInstance = + new SingletonHolder<ContactsDataSubscription, Context>() { + + @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<TypedPendingResult> 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<TypedPendingResult> 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<String> getAllPluginComponentNames() { + Set<String> names = new HashSet<String>(); + HashMap<ComponentName, CallMethodInfo> plugins = this.getPluginInfo(); + for (ComponentName cn : plugins.keySet()) { + names.add(cn.flattenToString()); + } + return names; + } +} diff --git a/src-ambient/incall/CreditBarHelper.java b/src-ambient/com/android/phone/common/incall/CreditBarHelper.java index 7a8eedc..7ef7584 100644 --- a/src-ambient/incall/CreditBarHelper.java +++ b/src-ambient/com/android/phone/common/incall/CreditBarHelper.java @@ -6,11 +6,9 @@ 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 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<CallMethodInfo> { + + 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<DialerDataSubscription, Context> sInstance = + new SingletonHolder<DialerDataSubscription, Context>() { + + @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<ComponentName> getPluginComponents(Result result) { + return InCallResults.gotInstalledPlugins((InstalledPluginsResult)result); + } + + @Override + protected void requestedModInfo(ArrayList<TypedPendingResult> 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<TypedPendingResult> 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/com/android/phone/common/util/StartInCallCallReceiver.java b/src-ambient/com/android/phone/common/incall/StartInCallCallReceiver.java index c2dd3c0..52bfdd0 100644 --- a/src/com/android/phone/common/util/StartInCallCallReceiver.java +++ b/src-ambient/com/android/phone/common/incall/StartInCallCallReceiver.java @@ -1,4 +1,20 @@ -package com.android.phone.common.util; +/* + * 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; @@ -13,6 +29,10 @@ public class StartInCallCallReceiver extends ResultReceiver { private WeakReference<Receiver> mReceiver; + public interface InCallCallListener { + void onResult(int resultCode); + } + public StartInCallCallReceiver(Handler handler) { super(handler); } @@ -32,7 +52,7 @@ public class StartInCallCallReceiver extends ResultReceiver { } public interface Receiver { - public void onReceiveResult(int resultCode, Bundle resultData); + void onReceiveResult(int resultCode, Bundle resultData); } public void setReceiver(Receiver receiver) { 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<CallMethodInfo> 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<CallMethodInfo> 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<CallMethodInfo> 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<CallMethodInfo> 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<CallMethodInfo> 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<Result>() { + @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<ComponentName> gotInstalledPlugins(InstalledPluginsResult result) { + if (result.components == null) { + return new ArrayList<ComponentName>(); + } + 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<CallMethodInfo> 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<CallMethodInfo> 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<CallMethodInfo> 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<ComponentName, CallMethodInfo> input, + HashMap<ComponentName, CallMethodInfo> output) { + for (Map.Entry<ComponentName, CallMethodInfo> entry : input.entrySet()) { + ComponentName key = entry.getKey(); + CallMethodInfo value = entry.getValue(); + + if (value.mStatus == PluginStatus.ENABLED) { + output.put(key, value); + } + } + } + + public static HashMap<ComponentName, CallMethodInfo> getAllEnabledCallMethods( + AmbientDataSubscription<CallMethodInfo> instance) { + HashMap<ComponentName, CallMethodInfo> cmi = new HashMap<>(); + removeDisabled(instance.getPluginInfo(), cmi); + return cmi; + } + + public static HashMap<ComponentName, CallMethodInfo> getAllEnabledAndHiddenCallMethods( + AmbientDataSubscription<CallMethodInfo> instance) { + HashMap<ComponentName, CallMethodInfo> cmi = new HashMap<>(); + for (Map.Entry<ComponentName, CallMethodInfo> 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<CallMethodInfo> 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/incall/CallMethodUtils.java b/src-ambient/com/android/phone/common/incall/utils/CallMethodUtils.java index 87a2348..d29f805 100644 --- a/src-ambient/incall/CallMethodUtils.java +++ b/src-ambient/com/android/phone/common/incall/utils/CallMethodUtils.java @@ -1,7 +1,25 @@ -package com.android.phone.common.incall; +/* + * 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.os.Bundle; +import android.os.Handler; +import android.os.Looper; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; @@ -12,10 +30,14 @@ 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; @@ -26,8 +48,10 @@ 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"; @@ -48,7 +72,7 @@ public class CallMethodUtils { final TelecomManager telecomMgr = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); final List<PhoneAccountHandle> accountHandles = telecomMgr.getCallCapablePhoneAccounts(); - ArrayList<CallMethodInfo> callMethodInfoList = new ArrayList<CallMethodInfo>(); + ArrayList<CallMethodInfo> callMethodInfoList = new ArrayList<>(); for (PhoneAccountHandle accountHandle : accountHandles) { CallMethodInfo info = phoneToCallMethod(context, accountHandle); if (info != null) { @@ -122,7 +146,7 @@ public class CallMethodUtils { // state as authenticated AccountTypeManager accountTypes = AccountTypeManager.getInstance(context); List<AccountWithDataSet> accounts = accountTypes.getAccounts(false); - ArrayMap<String, String> accountMap = new ArrayMap<String, String>(); + ArrayMap<String, String> accountMap = new ArrayMap<>(); for (AccountWithDataSet account : accounts) { AccountType accountType = @@ -148,4 +172,49 @@ public class CallMethodUtils { 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<CallMethodInfo> instance, + boolean enabledOnly, int mimeID) { + + String mimeTypes = ""; + List<String> 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<CallMethodInfo> 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<CallMethodInfo> 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<CallMethodInfo> instance) { + return getMimeTypes(instance, true, VIDEO_MIME_TYPE); + } + + public static String getAllEnabledImMimeTypes( + AmbientDataSubscription<CallMethodInfo> instance) { + return getMimeTypes(instance, true, IM_MIME_TYPE); + } + + public static Set<String> getAllEnabledVideoImMimeSet( + AmbientDataSubscription<CallMethodInfo> instance) { + String[] videoMimes = getAllEnabledVideoCallableMimeTypes(instance).split(","); + String[] imMimes = getAllEnabledImMimeTypes(instance).split(","); + HashSet<String> mimeSet = new HashSet<>(); + + if (videoMimes != null) { + mimeSet.addAll(Arrays.asList(videoMimes)); + } + if (imMimes != null) { + mimeSet.addAll(Arrays.asList(imMimes)); + } + return mimeSet; + } + + public static Set<String> getAllEnabledVoiceMimeSet( + AmbientDataSubscription<CallMethodInfo> instance) { + String[] mimes = getAllEnabledMimeTypes(instance).split(","); + HashSet<String> 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<ResolveInfo> 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<ComponentName> mInstalledPlugins; - protected static HashMap<ComponentName, CallMethodInfo> mCallMethodInfos = new HashMap<>(); - protected static HashMap<ComponentName, ICallCreditListener> mCallCreditListeners = new - HashMap<>(); - protected static HashMap<ComponentName, IAuthenticationListener> mAuthenticationListeners = new - HashMap<>(); - protected static HashMap<String, CallMethodReceiver> mRegisteredClients = new HashMap<>(); - protected static boolean dataHasBeenBroadcastPreviously = false; - // determine which info types to load - - - public interface CallMethodReceiver { - void onChanged(HashMap<ComponentName, CallMethodInfo> 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<ComponentName, CallMethodInfo> input, - HashMap<ComponentName, CallMethodInfo> output) { - for (Map.Entry<ComponentName, CallMethodInfo> entry : input.entrySet()) { - ComponentName key = entry.getKey(); - CallMethodInfo value = entry.getValue(); - - if (value.mStatus == PluginStatus.ENABLED) { - output.put(key, value); - } - } - } - - public static HashMap<ComponentName, CallMethodInfo> getAllEnabledCallMethods() { - HashMap<ComponentName, CallMethodInfo> cmi = new HashMap<ComponentName, CallMethodInfo>(); - for (Map.Entry<ComponentName, CallMethodInfo> 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<ComponentName, CallMethodInfo> getAllEnabledAndHiddenCallMethods() { - HashMap<ComponentName, CallMethodInfo> cmi = new HashMap<ComponentName, CallMethodInfo>(); - synchronized (mCallMethodInfos) { - for (Map.Entry<ComponentName, CallMethodInfo> 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<ComponentName, CallMethodInfo> 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<ResultCallback, PendingResult> apiCallbacks = new HashMap<ResultCallback, - PendingResult>(); - 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<String> 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<String> 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<String> 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<String> 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<ResultCallback, PendingResult> 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<InstalledPluginsResult>() { - @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<ResultCallback, PendingResult> apiCallbacks = - new HashMap<ResultCallback, PendingResult>(); - 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<ResultCallback, PendingResult> 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<ResultCallback, PendingResult> apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getProviderInfo(getInstance() - .mClient, cn); - ResultCallback callback = new ResultCallback<InCallProviderInfoResult>() { - @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<ResultCallback, PendingResult> apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getPluginStatus(getInstance().mClient, cn); - ResultCallback callback = new ResultCallback<PluginStatusResult>() { - @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<Result>() { - @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<ResultCallback, PendingResult> apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getCallableMimeType(getInstance().mClient, - cn); - ResultCallback callback = new ResultCallback<MimeTypeResult>() { - @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<ResultCallback, PendingResult> apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getVideoCallableMimeType(getInstance() - .mClient, cn); - ResultCallback callback = new ResultCallback<MimeTypeResult>() { - @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<ResultCallback, PendingResult> apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getImMimeType(getInstance().mClient, cn); - ResultCallback callback = new ResultCallback<MimeTypeResult>() { - @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<ResultCallback, PendingResult> apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getAuthenticationState(getInstance() - .mClient, cn); - ResultCallback callback = new ResultCallback<AuthenticationStateResult>() { - @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<ResultCallback, PendingResult> apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getAccountHandle(getInstance().mClient, cn); - ResultCallback callback = new ResultCallback<AccountHandleResult>() { - @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<ResultCallback, PendingResult> apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getSettingsIntent(getInstance().mClient, - cn); - ResultCallback callback = new ResultCallback<PendingIntentResult>() { - @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<ResultCallback, PendingResult> apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getHintText(getInstance().mClient, cn); - ResultCallback callback = new ResultCallback<HintTextResultResult>() { - @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<ResultCallback, PendingResult> apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getCreditInfo(getInstance().mClient, cn); - ResultCallback callback = new ResultCallback<GetCreditInfoResultResult>() { - @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<ResultCallback, PendingResult> apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getManageCreditsIntent(getInstance() - .mClient, cn); - ResultCallback callback = new ResultCallback<PendingIntentResult>() { - @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<ResultCallback, PendingResult> apiCallbacks) { - // find a nudge component if it exists for this package - Intent nudgeIntent = new Intent("cyanogen.service.NUDGE_PROVIDER"); - nudgeIntent.setPackage(cn.getPackageName()); - List<ResolveInfo> 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<ResultCallback, PendingResult> - apiCallbacks) { - PendingResult result = NudgeServices.NudgeApi.getConfigurationForKey(getInstance().mClient, - nudgeComponent, NudgeKey.INCALL_CREDIT_NUDGE); - ResultCallback callback = new ResultCallback<BundleResult>() { - @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<ResultCallback, PendingResult> apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getLoginIntent(getInstance().mClient, cn); - ResultCallback callback = new ResultCallback<PendingIntentResult>() { - @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<ResultCallback, PendingResult> apiCallbacks) { - PendingResult result = getInstance().mInCallApi.getDirectorySearchIntent(getInstance() - .mClient, cn, Uri.parse("")); - ResultCallback callback = new ResultCallback<PendingIntentResult>() { - @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<PendingIntentResult>() { - @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<PendingIntentResult>() { - @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<ResolveInfo> 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<ResultCallback, PendingResult> 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<BundleResult>() { - @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<String> getAllEnabledVoiceMimeSet() { - String[] mimes = getAllEnabledMimeTypes().split(","); - HashSet<String> mimeSet = new HashSet<String>(); - if (mimes != null) { - mimeSet.addAll(Arrays.asList(mimes)); - } - return mimeSet; - } - - public static Set<String> getAllEnabledVideoImMimeSet() { - String[] videoMimes = getAllEnabledVideoCallableMimeTypes().split(","); - String[] imMimes = getAllEnabledImMimeTypes().split(","); - HashSet<String> mimeSet = new HashSet<String>(); - - if (videoMimes != null) { - mimeSet.addAll(Arrays.asList(videoMimes)); - } - if (imMimes != null) { - mimeSet.addAll(Arrays.asList(imMimes)); - } - return mimeSet; - } - - public static boolean infoReady() { - return dataHasBeenBroadcastPreviously; - } -} |