From 033ed5926d2924d35b9c82898ab4e835da56b3af Mon Sep 17 00:00:00 2001 From: Chiao Cheng Date: Fri, 16 Aug 2013 15:45:35 -0700 Subject: Adding interfaces for phone number service. - Fetch image url if present. Bug: 10359919 Change-Id: Idf2ff5405255f1e90a383902762585ae68249dbe --- InCallUI/AndroidManifest.xml | 1 + .../src/com/android/incallui/AnswerFragment.java | 10 +- .../src/com/android/incallui/BaseFragment.java | 25 ++++- .../com/android/incallui/CallButtonFragment.java | 18 ++-- .../src/com/android/incallui/CallCardFragment.java | 34 +++++-- .../com/android/incallui/CallCardPresenter.java | 79 ++++++++++++++- .../src/com/android/incallui/DialpadFragment.java | 8 +- .../incallui/service/PhoneNumberService.java | 40 ++++++++ .../src/com/android/incallui/util/HttpFetcher.java | 111 +++++++++++++++++++++ .../com/android/incalluibind/ServiceFactory.java | 32 ++++++ 10 files changed, 335 insertions(+), 23 deletions(-) create mode 100644 InCallUI/src/com/android/incallui/service/PhoneNumberService.java create mode 100644 InCallUI/src/com/android/incallui/util/HttpFetcher.java create mode 100644 InCallUI/src/com/android/incalluibind/ServiceFactory.java diff --git a/InCallUI/AndroidManifest.xml b/InCallUI/AndroidManifest.xml index 0b603e0b3..3196dba60 100644 --- a/InCallUI/AndroidManifest.xml +++ b/InCallUI/AndroidManifest.xml @@ -22,6 +22,7 @@ + implements - GlowPadWrapper.AnswerListener, AnswerPresenter.AnswerUi { +public class AnswerFragment extends BaseFragment + implements GlowPadWrapper.AnswerListener, AnswerPresenter.AnswerUi { /** * The popup showing the list of canned responses. @@ -56,6 +56,11 @@ public class AnswerFragment extends BaseFragment implements return new AnswerPresenter(); } + @Override + AnswerPresenter.AnswerUi getUi() { + return this; + } + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -63,7 +68,6 @@ public class AnswerFragment extends BaseFragment implements container, false); glowPad.setAnswerListener(this); - getPresenter().onUiReady(this); return glowPad; } diff --git a/InCallUI/src/com/android/incallui/BaseFragment.java b/InCallUI/src/com/android/incallui/BaseFragment.java index a88f8407c..0f3d6b4ae 100644 --- a/InCallUI/src/com/android/incallui/BaseFragment.java +++ b/InCallUI/src/com/android/incallui/BaseFragment.java @@ -17,21 +17,38 @@ package com.android.incallui; import android.app.Fragment; +import android.os.Bundle; +import android.view.View; + +import com.android.internal.util.Preconditions; /** * */ -public abstract class BaseFragment extends Fragment { +public abstract class BaseFragment, U extends Ui> extends Fragment { private T mPresenter; + abstract T createPresenter(); + + abstract U getUi(); + protected BaseFragment() { - this.mPresenter = createPresenter(); + mPresenter = createPresenter(); } - abstract T createPresenter(); - + /** + * Presenter will be available after onActivityCreated(). + * + * @return The presenter associated with this fragment. + */ public T getPresenter() { return mPresenter; } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + mPresenter.onUiReady(getUi()); + } } diff --git a/InCallUI/src/com/android/incallui/CallButtonFragment.java b/InCallUI/src/com/android/incallui/CallButtonFragment.java index 21bfb1cf5..15d044684 100644 --- a/InCallUI/src/com/android/incallui/CallButtonFragment.java +++ b/InCallUI/src/com/android/incallui/CallButtonFragment.java @@ -16,9 +16,7 @@ package com.android.incallui; -import android.content.Context; import android.graphics.drawable.LayerDrawable; -import android.media.AudioManager; import android.os.Bundle; import android.view.LayoutInflater; import android.view.Menu; @@ -37,9 +35,10 @@ import com.android.services.telephony.common.AudioMode; /** * Fragment for call control buttons */ -public class CallButtonFragment extends BaseFragment - implements CallButtonPresenter.CallButtonUi, OnMenuItemClickListener, - OnDismissListener, View.OnClickListener, CompoundButton.OnCheckedChangeListener { +public class CallButtonFragment + extends BaseFragment + implements CallButtonPresenter.CallButtonUi, OnMenuItemClickListener, OnDismissListener, + View.OnClickListener, CompoundButton.OnCheckedChangeListener { private ToggleButton mMuteButton; private ToggleButton mAudioButton; @@ -60,6 +59,11 @@ public class CallButtonFragment extends BaseFragment return new CallButtonPresenter(); } + @Override + CallButtonPresenter.CallButtonUi getUi() { + return this; + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -115,8 +119,8 @@ public class CallButtonFragment extends BaseFragment } @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - getPresenter().onUiReady(this); + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); // set the buttons updateAudioButtons(getPresenter().getSupportedAudio()); diff --git a/InCallUI/src/com/android/incallui/CallCardFragment.java b/InCallUI/src/com/android/incallui/CallCardFragment.java index 18b51d9f8..347743845 100644 --- a/InCallUI/src/com/android/incallui/CallCardFragment.java +++ b/InCallUI/src/com/android/incallui/CallCardFragment.java @@ -31,12 +31,13 @@ import android.view.ViewStub; import android.widget.ImageView; import android.widget.TextView; +import com.android.incalluibind.ServiceFactory; import com.android.services.telephony.common.Call; /** * Fragment for call card. */ -public class CallCardFragment extends BaseFragment +public class CallCardFragment extends BaseFragment implements CallCardPresenter.CallCardUi { // Primary caller info @@ -55,22 +56,37 @@ public class CallCardFragment extends BaseFragment // Cached DisplayMetrics density. private float mDensity; + @Override + CallCardPresenter.CallCardUi getUi() { + return this; + } + @Override CallCardPresenter createPresenter() { return new CallCardPresenter(); } + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getPresenter().init(ServiceFactory.newPhoneNumberService(getActivity())); + } + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + mDensity = getResources().getDisplayMetrics().density; return inflater.inflate(R.layout.call_card, container, false); } - @Override public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + mPhoneNumber = (TextView) view.findViewById(R.id.phoneNumber); mName = (TextView) view.findViewById(R.id.name); mNumberLabel = (TextView) view.findViewById(R.id.label); @@ -78,10 +94,6 @@ public class CallCardFragment extends BaseFragment mPhoto = (ImageView) view.findViewById(R.id.photo); mCallStateLabel = (TextView) view.findViewById(R.id.callStateLabel); mElapsedTime = (TextView) view.findViewById(R.id.elapsedTime); - - // This method call will begin the callbacks on CallCardUi. We need to ensure - // everything needed for the callbacks is set up before this is called. - getPresenter().onUiReady(this); } @Override @@ -99,6 +111,16 @@ public class CallCardFragment extends BaseFragment } } + @Override + public void setName(String name) { + mName.setText(name); + } + + @Override + public void setImage(Bitmap image) { + setDrawableToImageView(mPhoto, new BitmapDrawable(getResources(), image)); + } + @Override public void setPrimary(String number, String name, boolean nameIsNumber, String label, Drawable photo, boolean isConference) { diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java index bf27a1bb7..7eec3fb65 100644 --- a/InCallUI/src/com/android/incallui/CallCardPresenter.java +++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java @@ -17,6 +17,14 @@ package com.android.incallui; import android.graphics.drawable.Drawable; +import android.content.ContentUris; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.AsyncTask; +import android.provider.ContactsContract.Contacts; import android.text.TextUtils; import android.text.format.DateUtils; @@ -25,20 +33,27 @@ import com.android.incallui.ContactInfoCache.ContactCacheEntry; import com.android.incallui.ContactInfoCache.ContactInfoCacheCallback; import com.android.incallui.InCallPresenter.InCallState; import com.android.incallui.InCallPresenter.InCallStateListener; - +import com.android.incallui.service.PhoneNumberService; +import com.android.incallui.util.HttpFetcher; +import com.android.incalluibind.ServiceFactory; import com.android.services.telephony.common.AudioMode; import com.android.services.telephony.common.Call; import com.android.services.telephony.common.Call.DisconnectCause; +import java.io.IOException; + /** * Presenter for the Call Card Fragment. + *

* This class listens for changes to InCallState and passes it along to the fragment. */ public class CallCardPresenter extends Presenter implements InCallStateListener, AudioModeListener, ContactInfoCacheCallback { + private static final String TAG = CallCardPresenter.class.getSimpleName(); private static final long CALL_TIME_UPDATE_INTERVAL = 1000; // in milliseconds + private PhoneNumberService mPhoneNumberService; private AudioModeProvider mAudioModeProvider; private ContactInfoCache mContactInfoCache; private Call mPrimary; @@ -58,6 +73,10 @@ public class CallCardPresenter extends Presenter }); } + public void init(PhoneNumberService phoneNumberService) { + mPhoneNumberService = phoneNumberService; + } + @Override public void onUiReady(CallCardUi ui) { super.onUiReady(ui); @@ -81,6 +100,7 @@ public class CallCardPresenter extends Presenter @Override public void onStateChange(InCallState state, CallList callList) { + Logger.d(TAG, "onStateChange()"); final CallCardUi ui = getUi(); if (ui == null) { return; @@ -233,10 +253,12 @@ public class CallCardPresenter extends Presenter if (mPrimary != null && mPrimary.getCallId() == callId) { mPrimaryContactInfo = entry; updatePrimaryDisplayInfo(); + lookupPhoneNumber(mPrimary.getNumber()); } if (mSecondary != null && mSecondary.getCallId() == callId) { mSecondaryContactInfo = entry; updateSecondaryDisplayInfo(); + // TODO(klp): investigate reverse lookup for secondary call. } } @@ -260,6 +282,59 @@ public class CallCardPresenter extends Presenter } + public void lookupPhoneNumber(String phoneNumber) { + if (mPhoneNumberService != null) { + mPhoneNumberService.getPhoneNumberInfo(phoneNumber, + new PhoneNumberService.PhoneNumberServiceListener() { + @Override + public void onPhoneNumberInfoComplete( + final PhoneNumberService.PhoneNumberInfo info) { + if (info == null) { + return; + } + // TODO(klp): Ui is sometimes null due to something being shutdown. + if (getUi() != null) { + if (info.getName() != null) { + getUi().setName(info.getName()); + } + + if (info.getImageUrl() != null) { + fetchImage(info.getImageUrl()); + } + } + } + }); + } + } + + private void fetchImage(final String url) { + if (url != null) { + new AsyncTask() { + + @Override + protected Bitmap doInBackground(Void... params) { + // Fetch the image + try { + final byte[] image = HttpFetcher.getRequestAsByteArray(url); + return BitmapFactory.decodeByteArray(image, 0, image.length); + } catch (IOException e) { + Logger.e(TAG, "Unable to download/decode photo.", e); + } + return null; + } + + @Override + protected void onPostExecute(Bitmap bitmap) { + // TODO(klp): same as above, figure out why it's null. + if (getUi() != null) { + getUi().setImage(bitmap); + } + } + + }.execute(); + } + } + /** * Gets the name to display for the call. */ @@ -310,5 +385,7 @@ public class CallCardPresenter extends Presenter void setSecondary(boolean show, String name, String label, Drawable photo); void setCallState(int state, Call.DisconnectCause cause, boolean bluetoothOn); void setPrimaryCallElapsedTime(boolean show, String duration); + void setName(String name); + void setImage(Bitmap bitmap); } } diff --git a/InCallUI/src/com/android/incallui/DialpadFragment.java b/InCallUI/src/com/android/incallui/DialpadFragment.java index 6be923cfb..b996e4f15 100644 --- a/InCallUI/src/com/android/incallui/DialpadFragment.java +++ b/InCallUI/src/com/android/incallui/DialpadFragment.java @@ -31,7 +31,7 @@ import java.util.HashMap; /** * Fragment for call control buttons */ -public class DialpadFragment extends BaseFragment +public class DialpadFragment extends BaseFragment implements DialpadPresenter.DialpadUi, View.OnTouchListener, View.OnKeyListener, View.OnHoverListener, View.OnClickListener { @@ -163,6 +163,11 @@ public class DialpadFragment extends BaseFragment return new DialpadPresenter(); } + @Override + DialpadPresenter.DialpadUi getUi() { + return this; + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -181,7 +186,6 @@ public class DialpadFragment extends BaseFragment setupKeypad(parent); } - getPresenter().onUiReady(this); return parent; } diff --git a/InCallUI/src/com/android/incallui/service/PhoneNumberService.java b/InCallUI/src/com/android/incallui/service/PhoneNumberService.java new file mode 100644 index 000000000..1e0cb950a --- /dev/null +++ b/InCallUI/src/com/android/incallui/service/PhoneNumberService.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.incallui.service; + +/** + * + */ +public interface PhoneNumberService { + public void getPhoneNumberInfo(String phoneNumber, PhoneNumberServiceListener listener); + + public interface PhoneNumberServiceListener { + + /** + * Callback when a phone number has been looked up. + * + * @param info The looked up information. Or (@literal null} if there are no results. + */ + public void onPhoneNumberInfoComplete(PhoneNumberInfo info); + } + + public interface PhoneNumberInfo { + public String getName(); + public String getPhoneNumber(); + public String getImageUrl(); + } +} diff --git a/InCallUI/src/com/android/incallui/util/HttpFetcher.java b/InCallUI/src/com/android/incallui/util/HttpFetcher.java new file mode 100644 index 000000000..b54bf718f --- /dev/null +++ b/InCallUI/src/com/android/incallui/util/HttpFetcher.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.incallui.util; + +import android.os.SystemClock; +import android.util.Log; + +import com.google.common.io.Closeables; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; + +/** + * Utility for making http requests. + */ +public class HttpFetcher { + + private static final String TAG = HttpFetcher.class.getSimpleName(); + + /** + * Send a http request to the given url. + * + * @param urlString The url to request. + * @return The response body as a byte array. Or {@literal null} if status code is not 2xx. + * @throws java.io.IOException when an error occurs. + */ + public static byte[] getRequestAsByteArray(String urlString) throws IOException { + Log.d(TAG, "fetching " + urlString); + HttpURLConnection conn = null; + InputStream is = null; + boolean isError = false; + final long start = SystemClock.uptimeMillis(); + try { + final URL url = new URL(urlString); + conn = (HttpURLConnection) url.openConnection(); + Log.d(TAG, "response code: " + conn.getResponseCode()); + // All 2xx codes are successful. + if (conn.getResponseCode() / 100 == 2) { + is = conn.getInputStream(); + } else { + is = conn.getErrorStream(); + isError = true; + } + + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final byte[] buffer = new byte[1024]; + int bytesRead; + + while ((bytesRead = is.read(buffer)) != -1) { + baos.write(buffer, 0, bytesRead); + } + + if (isError) { + handleBadResponse(urlString, baos.toByteArray()); + return null; + } + + final byte[] response = baos.toByteArray(); + Log.d(TAG, "received " + response.length + " bytes"); + final long end = SystemClock.uptimeMillis(); + Log.d(TAG, "fetch took " + (end - start) + " ms"); + return response; + } finally { + Closeables.closeQuietly(is); + if (conn != null) { + conn.disconnect(); + } + } + } + + /** + * Send a http request to the given url. + * + * @param urlString The url to request. + * @return The response body as a String. Or {@literal null} if status code is not 2xx. + * @throws java.io.IOException when an error occurs. + */ + public static String getRequestAsString(String urlString) throws IOException { + final byte[] byteArr = getRequestAsByteArray(urlString); + if (byteArr == null) { + // Encountered error response... just return. + return null; + } + final String response = new String(byteArr); + Log.d(TAG, "response body: "); + Log.d(TAG, response); + return response; + } + + private static void handleBadResponse(String url, byte[] response) { + Log.w(TAG, "Got bad response code from url: " + url); + Log.w(TAG, new String(response)); + } +} diff --git a/InCallUI/src/com/android/incalluibind/ServiceFactory.java b/InCallUI/src/com/android/incalluibind/ServiceFactory.java new file mode 100644 index 000000000..7191f146b --- /dev/null +++ b/InCallUI/src/com/android/incalluibind/ServiceFactory.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.incalluibind; + +import android.content.Context; + +import com.android.incallui.service.PhoneNumberService; + +/** + * Default static binder for services. + */ +public class ServiceFactory { + + public static PhoneNumberService newPhoneNumberService(Context context) { + // no phone number service. + return null; + } +} -- cgit v1.2.3