Remove unused old UI code
Remove unused UI code copied over from apps/Phone that is no longer used.
Bug:10608890
Change-Id: I41a0bd01102df70aa9a28bfa24994eca05e3fc0c
diff --git a/proguard.flags b/proguard.flags
index c5f4c72..c4af490 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -1,6 +1 @@
-# Keep names that are used only by animation framework.
--keepclasseswithmembers class com.android.phone.AnimationUtils$CrossFadeDrawable {
- *** setCrossFadeAlpha(...);
-}
-
--verbose
\ No newline at end of file
+-verbose
diff --git a/src/com/android/phone/AnimationUtils.java b/src/com/android/phone/AnimationUtils.java
deleted file mode 100644
index f7d9e2e..0000000
--- a/src/com/android/phone/AnimationUtils.java
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Copyright (C) 2012 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.phone;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.graphics.Canvas;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewPropertyAnimator;
-import android.widget.ImageView;
-
-/**
- * Utilities for Animation.
- */
-public class AnimationUtils {
- private static final String LOG_TAG = AnimationUtils.class.getSimpleName();
- /**
- * Turn on when you're interested in fading animation. Intentionally untied from other debug
- * settings.
- */
- private static final boolean FADE_DBG = false;
-
- /**
- * Duration for animations in msec, which can be used with
- * {@link ViewPropertyAnimator#setDuration(long)} for example.
- */
- public static final int ANIMATION_DURATION = 250;
-
- private AnimationUtils() {
- }
-
- /**
- * Simple Utility class that runs fading animations on specified views.
- */
- public static class Fade {
-
- // View tag that's set during the fade-out animation; see hide() and
- // isFadingOut().
- private static final int FADE_STATE_KEY = R.id.fadeState;
- private static final String FADING_OUT = "fading_out";
-
- /**
- * Sets the visibility of the specified view to View.VISIBLE and then
- * fades it in. If the view is already visible (and not in the middle
- * of a fade-out animation), this method will return without doing
- * anything.
- *
- * @param view The view to be faded in
- */
- public static void show(final View view) {
- if (FADE_DBG) log("Fade: SHOW view " + view + "...");
- if (FADE_DBG) log("Fade: - visibility = " + view.getVisibility());
- if ((view.getVisibility() != View.VISIBLE) || isFadingOut(view)) {
- view.animate().cancel();
- // ...and clear the FADE_STATE_KEY tag in case we just
- // canceled an in-progress fade-out animation.
- view.setTag(FADE_STATE_KEY, null);
-
- view.setAlpha(0);
- view.setVisibility(View.VISIBLE);
- view.animate().setDuration(ANIMATION_DURATION);
- view.animate().alpha(1);
- if (FADE_DBG) log("Fade: ==> SHOW " + view
- + " DONE. Set visibility = " + View.VISIBLE);
- } else {
- if (FADE_DBG) log("Fade: ==> Ignoring, already visible AND not fading out.");
- }
- }
-
- /**
- * Fades out the specified view and then sets its visibility to the
- * specified value (either View.INVISIBLE or View.GONE). If the view
- * is not currently visibile, the method will return without doing
- * anything.
- *
- * Note that *during* the fade-out the view itself will still have
- * visibility View.VISIBLE, although the isFadingOut() method will
- * return true (in case the UI code needs to detect this state.)
- *
- * @param view The view to be hidden
- * @param visibility The value to which the view's visibility will be
- * set after it fades out.
- * Must be either View.INVISIBLE or View.GONE.
- */
- public static void hide(final View view, final int visibility) {
- if (FADE_DBG) log("Fade: HIDE view " + view + "...");
- if (view.getVisibility() == View.VISIBLE &&
- (visibility == View.INVISIBLE || visibility == View.GONE)) {
-
- // Use a view tag to mark this view as being in the middle
- // of a fade-out animation.
- view.setTag(FADE_STATE_KEY, FADING_OUT);
-
- view.animate().cancel();
- view.animate().setDuration(ANIMATION_DURATION);
- view.animate().alpha(0f).setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- view.setAlpha(1);
- view.setVisibility(visibility);
- view.animate().setListener(null);
- // ...and we're done with the fade-out, so clear the view tag.
- view.setTag(FADE_STATE_KEY, null);
- if (FADE_DBG) log("Fade: HIDE " + view
- + " DONE. Set visibility = " + visibility);
- }
- });
- }
- }
-
- /**
- * @return true if the specified view is currently in the middle
- * of a fade-out animation. (During the fade-out, the view's
- * visibility is still VISIBLE, although in many cases the UI
- * should behave as if it's already invisible or gone. This
- * method allows the UI code to detect that state.)
- *
- * @see #hide(View, int)
- */
- public static boolean isFadingOut(final View view) {
- if (FADE_DBG) {
- log("Fade: isFadingOut view " + view + "...");
- log("Fade: - getTag() returns: " + view.getTag(FADE_STATE_KEY));
- log("Fade: - returning: " + (view.getTag(FADE_STATE_KEY) == FADING_OUT));
- }
- return (view.getTag(FADE_STATE_KEY) == FADING_OUT);
- }
-
- }
-
- /**
- * Drawable achieving cross-fade, just like TransitionDrawable. We can have
- * call-backs via animator object (see also {@link CrossFadeDrawable#getAnimator()}).
- */
- private static class CrossFadeDrawable extends LayerDrawable {
- private final ObjectAnimator mAnimator;
-
- public CrossFadeDrawable(Drawable[] layers) {
- super(layers);
- mAnimator = ObjectAnimator.ofInt(this, "crossFadeAlpha", 0xff, 0);
- }
-
- private int mCrossFadeAlpha;
-
- /**
- * This will be used from ObjectAnimator.
- * Note: this method is protected by proguard.flags so that it won't be removed
- * automatically.
- */
- @SuppressWarnings("unused")
- public void setCrossFadeAlpha(int alpha) {
- mCrossFadeAlpha = alpha;
- invalidateSelf();
- }
-
- public ObjectAnimator getAnimator() {
- return mAnimator;
- }
-
- @Override
- public void draw(Canvas canvas) {
- Drawable first = getDrawable(0);
- Drawable second = getDrawable(1);
-
- if (mCrossFadeAlpha > 0) {
- first.setAlpha(mCrossFadeAlpha);
- first.draw(canvas);
- first.setAlpha(255);
- }
-
- if (mCrossFadeAlpha < 0xff) {
- second.setAlpha(0xff - mCrossFadeAlpha);
- second.draw(canvas);
- second.setAlpha(0xff);
- }
- }
- }
-
- private static CrossFadeDrawable newCrossFadeDrawable(Drawable first, Drawable second) {
- Drawable[] layers = new Drawable[2];
- layers[0] = first;
- layers[1] = second;
- return new CrossFadeDrawable(layers);
- }
-
- /**
- * Starts cross-fade animation using TransitionDrawable. Nothing will happen if "from" and "to"
- * are the same.
- */
- public static void startCrossFade(
- final ImageView imageView, final Drawable from, final Drawable to) {
- // We skip the cross-fade when those two Drawables are equal, or they are BitmapDrawables
- // pointing to the same Bitmap.
- final boolean areSameImage = from.equals(to) ||
- ((from instanceof BitmapDrawable)
- && (to instanceof BitmapDrawable)
- && ((BitmapDrawable) from).getBitmap()
- .equals(((BitmapDrawable) to).getBitmap()));
- if (!areSameImage) {
- if (FADE_DBG) {
- log("Start cross-fade animation for " + imageView
- + "(" + Integer.toHexString(from.hashCode()) + " -> "
- + Integer.toHexString(to.hashCode()) + ")");
- }
-
- CrossFadeDrawable crossFadeDrawable = newCrossFadeDrawable(from, to);
- ObjectAnimator animator = crossFadeDrawable.getAnimator();
- imageView.setImageDrawable(crossFadeDrawable);
- animator.setDuration(ANIMATION_DURATION);
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- if (FADE_DBG) {
- log("cross-fade animation start ("
- + Integer.toHexString(from.hashCode()) + " -> "
- + Integer.toHexString(to.hashCode()) + ")");
- }
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (FADE_DBG) {
- log("cross-fade animation ended ("
- + Integer.toHexString(from.hashCode()) + " -> "
- + Integer.toHexString(to.hashCode()) + ")");
- }
- animation.removeAllListeners();
- // Workaround for issue 6300562; this will force the drawable to the
- // resultant one regardless of animation glitch.
- imageView.setImageDrawable(to);
- }
- });
- animator.start();
-
- /* We could use TransitionDrawable here, but it may cause some weird animation in
- * some corner cases. See issue 6300562
- * TODO: decide which to be used in the long run. TransitionDrawable is old but system
- * one. Ours uses new animation framework and thus have callback (great for testing),
- * while no framework support for the exact class.
-
- Drawable[] layers = new Drawable[2];
- layers[0] = from;
- layers[1] = to;
- TransitionDrawable transitionDrawable = new TransitionDrawable(layers);
- imageView.setImageDrawable(transitionDrawable);
- transitionDrawable.startTransition(ANIMATION_DURATION); */
- imageView.setTag(to);
- } else {
- if (FADE_DBG) {
- log("*Not* start cross-fade. " + imageView);
- }
- }
- }
-
- // Debugging / testing code
-
- private static void log(String msg) {
- Log.d(LOG_TAG, msg);
- }
-}
\ No newline at end of file
diff --git a/src/com/android/phone/BitmapUtils.java b/src/com/android/phone/BitmapUtils.java
deleted file mode 100644
index 94d4bf9..0000000
--- a/src/com/android/phone/BitmapUtils.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2011 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.phone;
-
-import android.graphics.Bitmap;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.util.Log;
-
-
-/**
- * Image effects used by the in-call UI.
- */
-public class BitmapUtils {
- private static final String TAG = "BitmapUtils";
- private static final boolean DBG =
- (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
-
- /** This class is never instantiated. */
- private BitmapUtils() {
- }
-
- //
- // Gaussian blur effect
- //
- // gaussianBlur() and related methods are borrowed from
- // BackgroundUtils.java in the Music2 code (which itself was based on
- // code from the old Cooliris android Gallery app.)
- //
- // TODO: possibly consider caching previously-generated blurred bitmaps;
- // see getAdaptedBitmap() and mAdaptedBitmapCache in the music app code.
- //
-
- private static final int RED_MASK = 0xff0000;
- private static final int RED_MASK_SHIFT = 16;
- private static final int GREEN_MASK = 0x00ff00;
- private static final int GREEN_MASK_SHIFT = 8;
- private static final int BLUE_MASK = 0x0000ff;
-
- /**
- * Creates a blurred version of the given Bitmap.
- *
- * @param bitmap the input bitmap, presumably a 96x96 pixel contact
- * thumbnail.
- */
- public static Bitmap createBlurredBitmap(Bitmap bitmap) {
- if (DBG) log("createBlurredBitmap()...");
- long startTime = SystemClock.uptimeMillis();
- if (bitmap == null) {
- Log.w(TAG, "createBlurredBitmap: null bitmap");
- return null;
- }
-
- if (DBG) log("- input bitmap: " + bitmap.getWidth() + " x " + bitmap.getHeight());
-
- // The bitmap we pass to gaussianBlur() needs to have a width
- // that's a power of 2, so scale up to 128x128.
- final int scaledSize = 128;
- bitmap = Bitmap.createScaledBitmap(bitmap,
- scaledSize, scaledSize,
- true /* filter */);
- if (DBG) log("- after resize: " + bitmap.getWidth() + " x " + bitmap.getHeight());
-
- bitmap = gaussianBlur(bitmap);
- if (DBG) log("- after blur: " + bitmap.getWidth() + " x " + bitmap.getHeight());
-
- long endTime = SystemClock.uptimeMillis();
- if (DBG) log("createBlurredBitmap() done (elapsed = " + (endTime - startTime) + " msec)");
- return bitmap;
- }
-
- /**
- * Apply a gaussian blur filter, and return a new (blurred) bitmap
- * that's the same size as the input bitmap.
- *
- * @param source input bitmap, whose width must be a power of 2
- */
- public static Bitmap gaussianBlur(Bitmap source) {
- int width = source.getWidth();
- int height = source.getHeight();
- if (DBG) log("gaussianBlur(): input: " + width + " x " + height);
-
- // Create a source and destination buffer for the image.
- int numPixels = width * height;
- int[] in = new int[numPixels];
- int[] tmp = new int[numPixels];
-
- // Get the source pixels as 32-bit ARGB.
- source.getPixels(in, 0, width, 0, 0, width, height);
-
- // Gaussian is a separable kernel, so it is decomposed into a horizontal
- // and vertical pass.
- // The filter function applies the kernel across each row and transposes
- // the output.
- // Hence we apply it twice to provide efficient horizontal and vertical
- // convolution.
- // The filter discards the alpha channel.
- gaussianBlurFilter(in, tmp, width, height);
- gaussianBlurFilter(tmp, in, width, height);
-
- // Return a bitmap scaled to the desired size.
- Bitmap filtered = Bitmap.createBitmap(in, width, height, Bitmap.Config.ARGB_8888);
- source.recycle();
- return filtered;
- }
-
- private static void gaussianBlurFilter(int[] in, int[] out, int width, int height) {
- // This function is currently hardcoded to blur with RADIUS = 4.
- // (If you change RADIUS, you'll have to change the weights[] too.)
- final int RADIUS = 4;
- final int[] weights = { 13, 23, 32, 39, 42, 39, 32, 23, 13}; // Adds up to 256
- int inPos = 0;
- int widthMask = width - 1; // width must be a power of two.
- for (int y = 0; y < height; ++y) {
- // Compute the alpha value.
- int alpha = 0xff;
- // Compute output values for the row.
- int outPos = y;
- for (int x = 0; x < width; ++x) {
- int red = 0;
- int green = 0;
- int blue = 0;
- for (int i = -RADIUS; i <= RADIUS; ++i) {
- int argb = in[inPos + (widthMask & (x + i))];
- int weight = weights[i+RADIUS];
- red += weight *((argb & RED_MASK) >> RED_MASK_SHIFT);
- green += weight *((argb & GREEN_MASK) >> GREEN_MASK_SHIFT);
- blue += weight *(argb & BLUE_MASK);
- }
- // Output the current pixel.
- out[outPos] = (alpha << 24) | ((red >> 8) << RED_MASK_SHIFT)
- | ((green >> 8) << GREEN_MASK_SHIFT)
- | (blue >> 8);
- outPos += height;
- }
- inPos += width;
- }
- }
-
- //
- // Debugging
- //
-
- private static void log(String msg) {
- Log.d(TAG, msg);
- }
-}
diff --git a/src/com/android/phone/CallCard.java b/src/com/android/phone/CallCard.java
deleted file mode 100644
index bfb14ac..0000000
--- a/src/com/android/phone/CallCard.java
+++ /dev/null
@@ -1,1746 +0,0 @@
-/*
- * Copyright (C) 2006 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.phone;
-
-import android.animation.LayoutTransition;
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Message;
-import android.provider.ContactsContract.Contacts;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewStub;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.internal.telephony.Call;
-import com.android.internal.telephony.CallManager;
-import com.android.internal.telephony.CallerInfo;
-import com.android.internal.telephony.CallerInfoAsyncQuery;
-import com.android.internal.telephony.Connection;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-
-import java.util.List;
-
-
-/**
- * "Call card" UI element: the in-call screen contains a tiled layout of call
- * cards, each representing the state of a current "call" (ie. an active call,
- * a call on hold, or an incoming call.)
- */
-public class CallCard extends LinearLayout
- implements CallTime.OnTickListener, CallerInfoAsyncQuery.OnQueryCompleteListener,
- ContactsAsyncHelper.OnImageLoadCompleteListener {
- private static final String LOG_TAG = "CallCard";
- private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
-
- private static final int TOKEN_UPDATE_PHOTO_FOR_CALL_STATE = 0;
- private static final int TOKEN_DO_NOTHING = 1;
-
- /**
- * Used with {@link ContactsAsyncHelper#startObtainPhotoAsync(int, Context, Uri,
- * ContactsAsyncHelper.OnImageLoadCompleteListener, Object)}
- */
- private static class AsyncLoadCookie {
- public final ImageView imageView;
- public final CallerInfo callerInfo;
- public final Call call;
- public AsyncLoadCookie(ImageView imageView, CallerInfo callerInfo, Call call) {
- this.imageView = imageView;
- this.callerInfo = callerInfo;
- this.call = call;
- }
- }
-
- /**
- * Reference to the InCallScreen activity that owns us. This may be
- * null if we haven't been initialized yet *or* after the InCallScreen
- * activity has been destroyed.
- */
- private InCallScreen mInCallScreen;
-
- // Phone app instance
- private PhoneGlobals mApplication;
-
- // Top-level subviews of the CallCard
- /** Container for info about the current call(s) */
- private ViewGroup mCallInfoContainer;
- /** Primary "call info" block (the foreground or ringing call) */
- private ViewGroup mPrimaryCallInfo;
- /** "Call banner" for the primary call */
- private ViewGroup mPrimaryCallBanner;
- /** Secondary "call info" block (the background "on hold" call) */
- private ViewStub mSecondaryCallInfo;
-
-
- // "Call state" widgets
- private TextView mCallStateLabel;
- private TextView mElapsedTime;
-
- // Text colors, used for various labels / titles
- private int mTextColorCallTypeSip;
-
- // The main block of info about the "primary" or "active" call,
- // including photo / name / phone number / etc.
- private ImageView mPhoto;
- private View mPhotoDimEffect;
-
- private TextView mName;
- private TextView mPhoneNumber;
- private TextView mLabel;
- private TextView mCallTypeLabel;
- // private TextView mSocialStatus;
-
- /**
- * Uri being used to load contact photo for mPhoto. Will be null when nothing is being loaded,
- * or a photo is already loaded.
- */
- private Uri mLoadingPersonUri;
-
- // Info about the "secondary" call, which is the "call on hold" when
- // two lines are in use.
- private TextView mSecondaryCallName;
- private ImageView mSecondaryCallPhoto;
- private View mSecondaryCallPhotoDimEffect;
-
- // Onscreen hint for the incoming call RotarySelector widget.
- private int mIncomingCallWidgetHintTextResId;
- private int mIncomingCallWidgetHintColorResId;
-
- private CallTime mCallTime;
-
- // Track the state for the photo.
- private ContactsAsyncHelper.ImageTracker mPhotoTracker;
-
- // Cached DisplayMetrics density.
- private float mDensity;
-
- /**
- * Sent when it takes too long (MESSAGE_DELAY msec) to load a contact photo for the given
- * person, at which we just start showing the default avatar picture instead of the person's
- * one. Note that we will *not* cancel the ongoing query and eventually replace the avatar
- * with the person's photo, when it is available anyway.
- */
- private static final int MESSAGE_SHOW_UNKNOWN_PHOTO = 101;
- private static final int MESSAGE_DELAY = 500; // msec
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MESSAGE_SHOW_UNKNOWN_PHOTO:
- showImage(mPhoto, R.drawable.picture_unknown);
- break;
- default:
- Log.wtf(LOG_TAG, "mHandler: unexpected message: " + msg);
- break;
- }
- }
- };
-
- public CallCard(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- if (DBG) log("CallCard constructor...");
- if (DBG) log("- this = " + this);
- if (DBG) log("- context " + context + ", attrs " + attrs);
-
- mApplication = PhoneGlobals.getInstance();
-
- mCallTime = new CallTime(this);
-
- // create a new object to track the state for the photo.
- mPhotoTracker = new ContactsAsyncHelper.ImageTracker();
-
- mDensity = getResources().getDisplayMetrics().density;
- if (DBG) log("- Density: " + mDensity);
- }
-
- /* package */ void setInCallScreenInstance(InCallScreen inCallScreen) {
- mInCallScreen = inCallScreen;
- }
-
- @Override
- public void onTickForCallTimeElapsed(long timeElapsed) {
- // While a call is in progress, update the elapsed time shown
- // onscreen.
- updateElapsedTimeWidget(timeElapsed);
- }
-
- /* package */ void stopTimer() {
- mCallTime.cancelTimer();
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- if (DBG) log("CallCard onFinishInflate(this = " + this + ")...");
-
- mCallInfoContainer = (ViewGroup) findViewById(R.id.call_info_container);
- mPrimaryCallInfo = (ViewGroup) findViewById(R.id.primary_call_info);
- mPrimaryCallBanner = (ViewGroup) findViewById(R.id.primary_call_banner);
-
- mCallStateLabel = (TextView) findViewById(R.id.callStateLabel);
- mElapsedTime = (TextView) findViewById(R.id.elapsedTime);
-
- // Text colors
- mTextColorCallTypeSip = getResources().getColor(R.color.incall_callTypeSip);
-
- // "Caller info" area, including photo / name / phone numbers / etc
- mPhoto = (ImageView) findViewById(R.id.photo);
- mPhotoDimEffect = findViewById(R.id.dim_effect_for_primary_photo);
-
- mName = (TextView) findViewById(R.id.name);
- mPhoneNumber = (TextView) findViewById(R.id.phoneNumber);
- mLabel = (TextView) findViewById(R.id.label);
- mCallTypeLabel = (TextView) findViewById(R.id.callTypeLabel);
- // mSocialStatus = (TextView) findViewById(R.id.socialStatus);
-
- // Secondary info area, for the background ("on hold") call
- mSecondaryCallInfo = (ViewStub) findViewById(R.id.secondary_call_info);
- }
-
- /**
- * Updates the state of all UI elements on the CallCard, based on the
- * current state of the phone.
- */
- /* package */ void updateState(CallManager cm) {
- if (DBG) log("updateState(" + cm + ")...");
-
- // Update the onscreen UI based on the current state of the phone.
-
- PhoneConstants.State state = cm.getState(); // IDLE, RINGING, or OFFHOOK
- Call ringingCall = cm.getFirstActiveRingingCall();
- Call fgCall = cm.getActiveFgCall();
- Call bgCall = cm.getFirstActiveBgCall();
-
- // Update the overall layout of the onscreen elements, if in PORTRAIT.
- // Portrait uses a programatically altered layout, whereas landscape uses layout xml's.
- // Landscape view has the views side by side, so no shifting of the picture is needed
- if (!PhoneUtils.isLandscape(this.getContext())) {
- updateCallInfoLayout(state);
- }
-
- // If the FG call is dialing/alerting, we should display for that call
- // and ignore the ringing call. This case happens when the telephony
- // layer rejects the ringing call while the FG call is dialing/alerting,
- // but the incoming call *does* briefly exist in the DISCONNECTING or
- // DISCONNECTED state.
- if ((ringingCall.getState() != Call.State.IDLE)
- && !fgCall.getState().isDialing()) {
- // A phone call is ringing, call waiting *or* being rejected
- // (ie. another call may also be active as well.)
- updateRingingCall(cm);
- } else if ((fgCall.getState() != Call.State.IDLE)
- || (bgCall.getState() != Call.State.IDLE)) {
- // We are here because either:
- // (1) the phone is off hook. At least one call exists that is
- // dialing, active, or holding, and no calls are ringing or waiting,
- // or:
- // (2) the phone is IDLE but a call just ended and it's still in
- // the DISCONNECTING or DISCONNECTED state. In this case, we want
- // the main CallCard to display "Hanging up" or "Call ended".
- // The normal "foreground call" code path handles both cases.
- updateForegroundCall(cm);
- } else {
- // We don't have any DISCONNECTED calls, which means that the phone
- // is *truly* idle.
- if (mApplication.inCallUiState.showAlreadyDisconnectedState) {
- // showAlreadyDisconnectedState implies the phone call is disconnected
- // and we want to show the disconnected phone call for a moment.
- //
- // This happens when a phone call ends while the screen is off,
- // which means the user had no chance to see the last status of
- // the call. We'll turn off showAlreadyDisconnectedState flag
- // and bail out of the in-call screen soon.
- updateAlreadyDisconnected(cm);
- } else {
- // It's very rare to be on the InCallScreen at all in this
- // state, but it can happen in some cases:
- // - A stray onPhoneStateChanged() event came in to the
- // InCallScreen *after* it was dismissed.
- // - We're allowed to be on the InCallScreen because
- // an MMI or USSD is running, but there's no actual "call"
- // to display.
- // - We're displaying an error dialog to the user
- // (explaining why the call failed), so we need to stay on
- // the InCallScreen so that the dialog will be visible.
- //
- // In these cases, put the callcard into a sane but "blank" state:
- updateNoCall(cm);
- }
- }
- }
-
- /**
- * Updates the overall size and positioning of mCallInfoContainer and
- * the "Call info" blocks, based on the phone state.
- */
- private void updateCallInfoLayout(PhoneConstants.State state) {
- boolean ringing = (state == PhoneConstants.State.RINGING);
- if (DBG) log("updateCallInfoLayout()... ringing = " + ringing);
-
- // Based on the current state, update the overall
- // CallCard layout:
-
- // - Update the bottom margin of mCallInfoContainer to make sure
- // the call info area won't overlap with the touchable
- // controls on the bottom part of the screen.
-
- int reservedVerticalSpace = mInCallScreen.getInCallTouchUi().getTouchUiHeight();
- ViewGroup.MarginLayoutParams callInfoLp =
- (ViewGroup.MarginLayoutParams) mCallInfoContainer.getLayoutParams();
- callInfoLp.bottomMargin = reservedVerticalSpace; // Equivalent to setting
- // android:layout_marginBottom in XML
- if (DBG) log(" ==> callInfoLp.bottomMargin: " + reservedVerticalSpace);
- mCallInfoContainer.setLayoutParams(callInfoLp);
- }
-
- /**
- * Updates the UI for the state where the phone is in use, but not ringing.
- */
- private void updateForegroundCall(CallManager cm) {
- if (DBG) log("updateForegroundCall()...");
- // if (DBG) PhoneUtils.dumpCallManager();
-
- Call fgCall = cm.getActiveFgCall();
- Call bgCall = cm.getFirstActiveBgCall();
-
- if (fgCall.getState() == Call.State.IDLE) {
- if (DBG) log("updateForegroundCall: no active call, show holding call");
- // TODO: make sure this case agrees with the latest UI spec.
-
- // Display the background call in the main info area of the
- // CallCard, since there is no foreground call. Note that
- // displayMainCallStatus() will notice if the call we passed in is on
- // hold, and display the "on hold" indication.
- fgCall = bgCall;
-
- // And be sure to not display anything in the "on hold" box.
- bgCall = null;
- }
-
- displayMainCallStatus(cm, fgCall);
-
- Phone phone = fgCall.getPhone();
-
- int phoneType = phone.getPhoneType();
- if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
- if ((mApplication.cdmaPhoneCallState.getCurrentCallState()
- == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE)
- && mApplication.cdmaPhoneCallState.IsThreeWayCallOrigStateDialing()) {
- displaySecondaryCallStatus(cm, fgCall);
- } else {
- //This is required so that even if a background call is not present
- // we need to clean up the background call area.
- displaySecondaryCallStatus(cm, bgCall);
- }
- } else if ((phoneType == PhoneConstants.PHONE_TYPE_GSM)
- || (phoneType == PhoneConstants.PHONE_TYPE_SIP)) {
- displaySecondaryCallStatus(cm, bgCall);
- }
- }
-
- /**
- * Updates the UI for the state where an incoming call is ringing (or
- * call waiting), regardless of whether the phone's already offhook.
- */
- private void updateRingingCall(CallManager cm) {
- if (DBG) log("updateRingingCall()...");
-
- Call ringingCall = cm.getFirstActiveRingingCall();
-
- // Display caller-id info and photo from the incoming call:
- displayMainCallStatus(cm, ringingCall);
-
- // And even in the Call Waiting case, *don't* show any info about
- // the current ongoing call and/or the current call on hold.
- // (Since the caller-id info for the incoming call totally trumps
- // any info about the current call(s) in progress.)
- displaySecondaryCallStatus(cm, null);
- }
-
- /**
- * Updates the UI for the state where an incoming call is just disconnected while we want to
- * show the screen for a moment.
- *
- * This case happens when the whole in-call screen is in background when phone calls are hanged
- * up, which means there's no way to determine which call was the last call finished. Right now
- * this method simply shows the previous primary call status with a photo, closing the
- * secondary call status. In most cases (including conference call or misc call happening in
- * CDMA) this behaves right.
- *
- * If there were two phone calls both of which were hung up but the primary call was the
- * first, this would behave a bit odd (since the first one still appears as the
- * "last disconnected").
- */
- private void updateAlreadyDisconnected(CallManager cm) {
- // For the foreground call, we manually set up every component based on previous state.
- mPrimaryCallInfo.setVisibility(View.VISIBLE);
- mCallStateLabel.setVisibility(View.VISIBLE);
- mCallStateLabel.setText(mContext.getString(R.string.card_title_call_ended));
- mElapsedTime.setVisibility(View.VISIBLE);
- mCallTime.cancelTimer();
-
- // Just hide it.
- displaySecondaryCallStatus(cm, null);
- }
-
- /**
- * Updates the UI for the state where the phone is not in use.
- * This is analogous to updateForegroundCall() and updateRingingCall(),
- * but for the (uncommon) case where the phone is
- * totally idle. (See comments in updateState() above.)
- *
- * This puts the callcard into a sane but "blank" state.
- */
- private void updateNoCall(CallManager cm) {
- if (DBG) log("updateNoCall()...");
-
- displayMainCallStatus(cm, null);
- displaySecondaryCallStatus(cm, null);
- }
-
- /**
- * Updates the main block of caller info on the CallCard
- * (ie. the stuff in the primaryCallInfo block) based on the specified Call.
- */
- private void displayMainCallStatus(CallManager cm, Call call) {
- if (DBG) log("displayMainCallStatus(call " + call + ")...");
-
- if (call == null) {
- // There's no call to display, presumably because the phone is idle.
- mPrimaryCallInfo.setVisibility(View.GONE);
- return;
- }
- mPrimaryCallInfo.setVisibility(View.VISIBLE);
-
- Call.State state = call.getState();
- if (DBG) log(" - call.state: " + call.getState());
-
- switch (state) {
- case ACTIVE:
- case DISCONNECTING:
- // update timer field
- if (DBG) log("displayMainCallStatus: start periodicUpdateTimer");
- mCallTime.setActiveCallMode(call);
- mCallTime.reset();
- mCallTime.periodicUpdateTimer();
-
- break;
-
- case HOLDING:
- // update timer field
- mCallTime.cancelTimer();
-
- break;
-
- case DISCONNECTED:
- // Stop getting timer ticks from this call
- mCallTime.cancelTimer();
-
- break;
-
- case DIALING:
- case ALERTING:
- // Stop getting timer ticks from a previous call
- mCallTime.cancelTimer();
-
- break;
-
- case INCOMING:
- case WAITING:
- // Stop getting timer ticks from a previous call
- mCallTime.cancelTimer();
-
- break;
-
- case IDLE:
- // The "main CallCard" should never be trying to display
- // an idle call! In updateState(), if the phone is idle,
- // we call updateNoCall(), which means that we shouldn't
- // have passed a call into this method at all.
- Log.w(LOG_TAG, "displayMainCallStatus: IDLE call in the main call card!");
-
- // (It is possible, though, that we had a valid call which
- // became idle *after* the check in updateState() but
- // before we get here... So continue the best we can,
- // with whatever (stale) info we can get from the
- // passed-in Call object.)
-
- break;
-
- default:
- Log.w(LOG_TAG, "displayMainCallStatus: unexpected call state: " + state);
- break;
- }
-
- updateCallStateWidgets(call);
-
- if (PhoneUtils.isConferenceCall(call)) {
- // Update onscreen info for a conference call.
- updateDisplayForConference(call);
- } else {
- // Update onscreen info for a regular call (which presumably
- // has only one connection.)
- Connection conn = null;
- int phoneType = call.getPhone().getPhoneType();
- if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
- conn = call.getLatestConnection();
- } else if ((phoneType == PhoneConstants.PHONE_TYPE_GSM)
- || (phoneType == PhoneConstants.PHONE_TYPE_SIP)) {
- conn = call.getEarliestConnection();
- } else {
- throw new IllegalStateException("Unexpected phone type: " + phoneType);
- }
-
- if (conn == null) {
- if (DBG) log("displayMainCallStatus: connection is null, using default values.");
- // if the connection is null, we run through the behaviour
- // we had in the past, which breaks down into trivial steps
- // with the current implementation of getCallerInfo and
- // updateDisplayForPerson.
- CallerInfo info = PhoneUtils.getCallerInfo(getContext(), null /* conn */);
- updateDisplayForPerson(info, PhoneConstants.PRESENTATION_ALLOWED, false, call,
- conn);
- } else {
- if (DBG) log(" - CONN: " + conn + ", state = " + conn.getState());
- int presentation = conn.getNumberPresentation();
-
- // make sure that we only make a new query when the current
- // callerinfo differs from what we've been requested to display.
- boolean runQuery = true;
- Object o = conn.getUserData();
- if (o instanceof PhoneUtils.CallerInfoToken) {
- runQuery = mPhotoTracker.isDifferentImageRequest(
- ((PhoneUtils.CallerInfoToken) o).currentInfo);
- } else {
- runQuery = mPhotoTracker.isDifferentImageRequest(conn);
- }
-
- // Adding a check to see if the update was caused due to a Phone number update
- // or CNAP update. If so then we need to start a new query
- if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
- Object obj = conn.getUserData();
- String updatedNumber = conn.getAddress();
- String updatedCnapName = conn.getCnapName();
- CallerInfo info = null;
- if (obj instanceof PhoneUtils.CallerInfoToken) {
- info = ((PhoneUtils.CallerInfoToken) o).currentInfo;
- } else if (o instanceof CallerInfo) {
- info = (CallerInfo) o;
- }
-
- if (info != null) {
- if (updatedNumber != null && !updatedNumber.equals(info.phoneNumber)) {
- if (DBG) log("- displayMainCallStatus: updatedNumber = "
- + updatedNumber);
- runQuery = true;
- }
- if (updatedCnapName != null && !updatedCnapName.equals(info.cnapName)) {
- if (DBG) log("- displayMainCallStatus: updatedCnapName = "
- + updatedCnapName);
- runQuery = true;
- }
- }
- }
-
- if (runQuery) {
- if (DBG) log("- displayMainCallStatus: starting CallerInfo query...");
- PhoneUtils.CallerInfoToken info =
- PhoneUtils.startGetCallerInfo(getContext(), conn, this, call);
- updateDisplayForPerson(info.currentInfo, presentation, !info.isFinal,
- call, conn);
- } else {
- // No need to fire off a new query. We do still need
- // to update the display, though (since we might have
- // previously been in the "conference call" state.)
- if (DBG) log("- displayMainCallStatus: using data we already have...");
- if (o instanceof CallerInfo) {
- CallerInfo ci = (CallerInfo) o;
- // Update CNAP information if Phone state change occurred
- ci.cnapName = conn.getCnapName();
- ci.numberPresentation = conn.getNumberPresentation();
- ci.namePresentation = conn.getCnapNamePresentation();
- if (DBG) log("- displayMainCallStatus: CNAP data from Connection: "
- + "CNAP name=" + ci.cnapName
- + ", Number/Name Presentation=" + ci.numberPresentation);
- if (DBG) log(" ==> Got CallerInfo; updating display: ci = " + ci);
- updateDisplayForPerson(ci, presentation, false, call, conn);
- } else if (o instanceof PhoneUtils.CallerInfoToken){
- CallerInfo ci = ((PhoneUtils.CallerInfoToken) o).currentInfo;
- if (DBG) log("- displayMainCallStatus: CNAP data from Connection: "
- + "CNAP name=" + ci.cnapName
- + ", Number/Name Presentation=" + ci.numberPresentation);
- if (DBG) log(" ==> Got CallerInfoToken; updating display: ci = " + ci);
- updateDisplayForPerson(ci, presentation, true, call, conn);
- } else {
- Log.w(LOG_TAG, "displayMainCallStatus: runQuery was false, "
- + "but we didn't have a cached CallerInfo object! o = " + o);
- // TODO: any easy way to recover here (given that
- // the CallCard is probably displaying stale info
- // right now?) Maybe force the CallCard into the
- // "Unknown" state?
- }
- }
- }
- }
-
- // In some states we override the "photo" ImageView to be an
- // indication of the current state, rather than displaying the
- // regular photo as set above.
- updatePhotoForCallState(call);
-
- // One special feature of the "number" text field: For incoming
- // calls, while the user is dragging the RotarySelector widget, we
- // use mPhoneNumber to display a hint like "Rotate to answer".
- if (mIncomingCallWidgetHintTextResId != 0) {
- // Display the hint!
- mPhoneNumber.setText(mIncomingCallWidgetHintTextResId);
- mPhoneNumber.setTextColor(getResources().getColor(mIncomingCallWidgetHintColorResId));
- mPhoneNumber.setVisibility(View.VISIBLE);
- mLabel.setVisibility(View.GONE);
- }
- // If we don't have a hint to display, just don't touch
- // mPhoneNumber and mLabel. (Their text / color / visibility have
- // already been set correctly, by either updateDisplayForPerson()
- // or updateDisplayForConference().)
- }
-
- /**
- * Implemented for CallerInfoAsyncQuery.OnQueryCompleteListener interface.
- * refreshes the CallCard data when it called.
- */
- @Override
- public void onQueryComplete(int token, Object cookie, CallerInfo ci) {
- if (DBG) log("onQueryComplete: token " + token + ", cookie " + cookie + ", ci " + ci);
-
- if (cookie instanceof Call) {
- // grab the call object and update the display for an individual call,
- // as well as the successive call to update image via call state.
- // If the object is a textview instead, we update it as we need to.
- if (DBG) log("callerinfo query complete, updating ui from displayMainCallStatus()");
- Call call = (Call) cookie;
- Connection conn = null;
- int phoneType = call.getPhone().getPhoneType();
- if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
- conn = call.getLatestConnection();
- } else if ((phoneType == PhoneConstants.PHONE_TYPE_GSM)
- || (phoneType == PhoneConstants.PHONE_TYPE_SIP)) {
- conn = call.getEarliestConnection();
- } else {
- throw new IllegalStateException("Unexpected phone type: " + phoneType);
- }
- PhoneUtils.CallerInfoToken cit =
- PhoneUtils.startGetCallerInfo(getContext(), conn, this, null);
-
- int presentation = PhoneConstants.PRESENTATION_ALLOWED;
- if (conn != null) presentation = conn.getNumberPresentation();
- if (DBG) log("- onQueryComplete: presentation=" + presentation
- + ", contactExists=" + ci.contactExists);
-
- // Depending on whether there was a contact match or not, we want to pass in different
- // CallerInfo (for CNAP). Therefore if ci.contactExists then use the ci passed in.
- // Otherwise, regenerate the CIT from the Connection and use the CallerInfo from there.
- if (ci.contactExists) {
- updateDisplayForPerson(ci, PhoneConstants.PRESENTATION_ALLOWED, false, call, conn);
- } else {
- updateDisplayForPerson(cit.currentInfo, presentation, false, call, conn);
- }
- updatePhotoForCallState(call);
-
- } else if (cookie instanceof TextView){
- if (DBG) log("callerinfo query complete, updating ui from ongoing or onhold");
- ((TextView) cookie).setText(PhoneUtils.getCompactNameFromCallerInfo(ci, mContext));
- }
- }
-
- /**
- * Implemented for ContactsAsyncHelper.OnImageLoadCompleteListener interface.
- * make sure that the call state is reflected after the image is loaded.
- */
- @Override
- public void onImageLoadComplete(int token, Drawable photo, Bitmap photoIcon, Object cookie) {
- mHandler.removeMessages(MESSAGE_SHOW_UNKNOWN_PHOTO);
- if (mLoadingPersonUri != null) {
- // Start sending view notification after the current request being done.
- // New image may possibly be available from the next phone calls.
- //
- // TODO: may be nice to update the image view again once the newer one
- // is available on contacts database.
- PhoneUtils.sendViewNotificationAsync(mApplication, mLoadingPersonUri);
- } else {
- // This should not happen while we need some verbose info if it happens..
- Log.w(LOG_TAG, "Person Uri isn't available while Image is successfully loaded.");
- }
- mLoadingPersonUri = null;
-
- AsyncLoadCookie asyncLoadCookie = (AsyncLoadCookie) cookie;
- CallerInfo callerInfo = asyncLoadCookie.callerInfo;
- ImageView imageView = asyncLoadCookie.imageView;
- Call call = asyncLoadCookie.call;
-
- callerInfo.cachedPhoto = photo;
- callerInfo.cachedPhotoIcon = photoIcon;
- callerInfo.isCachedPhotoCurrent = true;
-
- // Note: previously ContactsAsyncHelper has done this job.
- // TODO: We will need fade-in animation. See issue 5236130.
- if (photo != null) {
- showImage(imageView, photo);
- } else if (photoIcon != null) {
- showImage(imageView, photoIcon);
- } else {
- showImage(imageView, R.drawable.picture_unknown);
- }
-
- if (token == TOKEN_UPDATE_PHOTO_FOR_CALL_STATE) {
- updatePhotoForCallState(call);
- }
- }
-
- /**
- * Updates the "call state label" and the elapsed time widget based on the
- * current state of the call.
- */
- private void updateCallStateWidgets(Call call) {
- if (DBG) log("updateCallStateWidgets(call " + call + ")...");
- final Call.State state = call.getState();
- final Context context = getContext();
- final Phone phone = call.getPhone();
- final int phoneType = phone.getPhoneType();
-
- String callStateLabel = null; // Label to display as part of the call banner
- int bluetoothIconId = 0; // Icon to display alongside the call state label
-
- switch (state) {
- case IDLE:
- // "Call state" is meaningless in this state.
- break;
-
- case ACTIVE:
- // We normally don't show a "call state label" at all in
- // this state (but see below for some special cases).
- break;
-
- case HOLDING:
- callStateLabel = context.getString(R.string.card_title_on_hold);
- break;
-
- case DIALING:
- case ALERTING:
- callStateLabel = context.getString(R.string.card_title_dialing);
- break;
-
- case INCOMING:
- case WAITING:
- callStateLabel = context.getString(R.string.card_title_incoming_call);
- break;
-
- case DISCONNECTING:
- // While in the DISCONNECTING state we display a "Hanging up"
- // message in order to make the UI feel more responsive. (In
- // GSM it's normal to see a delay of a couple of seconds while
- // negotiating the disconnect with the network, so the "Hanging
- // up" state at least lets the user know that we're doing
- // something. This state is currently not used with CDMA.)
- callStateLabel = context.getString(R.string.card_title_hanging_up);
- break;
-
- case DISCONNECTED:
- callStateLabel = getCallFailedString(call);
- break;
-
- default:
- Log.wtf(LOG_TAG, "updateCallStateWidgets: unexpected call state: " + state);
- break;
- }
-
- // Check a couple of other special cases (these are all CDMA-specific).
-
- // TODO(klp): This code should go into the CallModeler logic instead of the UI.
- if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
- if ((state == Call.State.ACTIVE)
- && mApplication.cdmaPhoneCallState.IsThreeWayCallOrigStateDialing()) {
- // Display "Dialing" while dialing a 3Way call, even
- // though the foreground call state is actually ACTIVE.
- callStateLabel = context.getString(R.string.card_title_dialing);
- } else if (PhoneGlobals.getInstance().notifier.getIsCdmaRedialCall()) {
- callStateLabel = context.getString(R.string.card_title_redialing);
- }
- }
- if (PhoneUtils.isPhoneInEcm(phone)) {
- // In emergency callback mode (ECM), use a special label
- // that shows your own phone number.
- callStateLabel = getECMCardTitle(context, phone);
- }
-
- final InCallUiState inCallUiState = mApplication.inCallUiState;
- if (DBG) {
- log("==> callStateLabel: '" + callStateLabel
- + "', bluetoothIconId = " + bluetoothIconId);
- }
-
- // Animation will be done by mCallerDetail's LayoutTransition, but in some cases, we don't
- // want that.
- // - DIALING: This is at the beginning of the phone call.
- // - DISCONNECTING, DISCONNECTED: Screen will disappear soon; we have no time for animation.
- final boolean skipAnimation = (state == Call.State.DIALING
- || state == Call.State.DISCONNECTING
- || state == Call.State.DISCONNECTED);
- LayoutTransition layoutTransition = null;
-
- if (!TextUtils.isEmpty(callStateLabel)) {
- mCallStateLabel.setVisibility(View.VISIBLE);
- mCallStateLabel.setText(callStateLabel);
-
- // ...and display the icon too if necessary.
- if (bluetoothIconId != 0) {
- mCallStateLabel.setCompoundDrawablesWithIntrinsicBounds(bluetoothIconId, 0, 0, 0);
- mCallStateLabel.setCompoundDrawablePadding((int) (mDensity * 5));
- } else {
- // Clear out any icons
- mCallStateLabel.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
- }
- } else {
- mCallStateLabel.setVisibility(View.GONE);
- // Gravity is aligned left when receiving an incoming call in landscape.
- // In that rare case, the gravity needs to be reset to the right.
- // Also, setText("") is used since there is a delay in making the view GONE,
- // so the user will otherwise see the text jump to the right side before disappearing.
- if(mCallStateLabel.getGravity() != Gravity.END) {
- mCallStateLabel.setText("");
- mCallStateLabel.setGravity(Gravity.END);
- }
- }
-
- // ...and update the elapsed time widget too.
- switch (state) {
- case ACTIVE:
- case DISCONNECTING:
- // Show the time with fade-in animation.
- AnimationUtils.Fade.show(mElapsedTime);
- updateElapsedTimeWidget(call);
- break;
-
- case DISCONNECTED:
- // In the "Call ended" state, leave the mElapsedTime widget
- // visible, but don't touch it (so we continue to see the
- // elapsed time of the call that just ended.)
- // Check visibility to keep possible fade-in animation.
- if (mElapsedTime.getVisibility() != View.VISIBLE) {
- mElapsedTime.setVisibility(View.VISIBLE);
- }
- break;
-
- default:
- // Call state here is IDLE, ACTIVE, HOLDING, DIALING, ALERTING,
- // INCOMING, or WAITING.
- // In all of these states, the "elapsed time" is meaningless, so
- // don't show it.
- AnimationUtils.Fade.hide(mElapsedTime, View.INVISIBLE);
-
- // Additionally, in call states that can only occur at the start
- // of a call, reset the elapsed time to be sure we won't display
- // stale info later (like if we somehow go straight from DIALING
- // or ALERTING to DISCONNECTED, which can actually happen in
- // some failure cases like "line busy").
- if ((state == Call.State.DIALING) || (state == Call.State.ALERTING)) {
- updateElapsedTimeWidget(0);
- }
-
- break;
- }
- }
-
- /**
- * Updates mElapsedTime based on the given {@link Call} object's information.
- *
- * @see CallTime#getCallDuration(Call)
- * @see Connection#getDurationMillis()
- */
- /* package */ void updateElapsedTimeWidget(Call call) {
- long duration = CallTime.getCallDuration(call); // msec
- updateElapsedTimeWidget(duration / 1000);
- // Also see onTickForCallTimeElapsed(), which updates this
- // widget once per second while the call is active.
- }
-
- /**
- * Updates mElapsedTime based on the specified number of seconds.
- */
- private void updateElapsedTimeWidget(long timeElapsed) {
- // if (DBG) log("updateElapsedTimeWidget: " + timeElapsed);
- mElapsedTime.setText(DateUtils.formatElapsedTime(timeElapsed));
- }
-
- /**
- * Updates the "on hold" box in the "other call" info area
- * (ie. the stuff in the secondaryCallInfo block)
- * based on the specified Call.
- * Or, clear out the "on hold" box if the specified call
- * is null or idle.
- */
- private void displaySecondaryCallStatus(CallManager cm, Call call) {
- if (DBG) log("displayOnHoldCallStatus(call =" + call + ")...");
-
- if ((call == null) || (PhoneGlobals.getInstance().isOtaCallInActiveState())) {
- mSecondaryCallInfo.setVisibility(View.GONE);
- return;
- }
-
- Call.State state = call.getState();
- switch (state) {
- case HOLDING:
- // Ok, there actually is a background call on hold.
- // Display the "on hold" box.
-
- // Note this case occurs only on GSM devices. (On CDMA,
- // the "call on hold" is actually the 2nd connection of
- // that ACTIVE call; see the ACTIVE case below.)
- showSecondaryCallInfo();
-
- if (PhoneUtils.isConferenceCall(call)) {
- if (DBG) log("==> conference call.");
- mSecondaryCallName.setText(getContext().getString(R.string.confCall));
- showImage(mSecondaryCallPhoto, R.drawable.picture_conference);
- } else {
- // perform query and update the name temporarily
- // make sure we hand the textview we want updated to the
- // callback function.
- if (DBG) log("==> NOT a conf call; call startGetCallerInfo...");
- PhoneUtils.CallerInfoToken infoToken = PhoneUtils.startGetCallerInfo(
- getContext(), call, this, mSecondaryCallName);
- mSecondaryCallName.setText(
- PhoneUtils.getCompactNameFromCallerInfo(infoToken.currentInfo,
- getContext()));
-
- // Also pull the photo out of the current CallerInfo.
- // (Note we assume we already have a valid photo at
- // this point, since *presumably* the caller-id query
- // was already run at some point *before* this call
- // got put on hold. If there's no cached photo, just
- // fall back to the default "unknown" image.)
- if (infoToken.isFinal) {
- showCachedImage(mSecondaryCallPhoto, infoToken.currentInfo);
- } else {
- showImage(mSecondaryCallPhoto, R.drawable.picture_unknown);
- }
- }
-
- AnimationUtils.Fade.show(mSecondaryCallPhotoDimEffect);
- break;
-
- case ACTIVE:
- // CDMA: This is because in CDMA when the user originates the second call,
- // although the Foreground call state is still ACTIVE in reality the network
- // put the first call on hold.
- if (mApplication.phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
- showSecondaryCallInfo();
-
- List<Connection> connections = call.getConnections();
- if (connections.size() > 2) {
- // This means that current Mobile Originated call is the not the first 3-Way
- // call the user is making, which in turn tells the PhoneGlobals that we no
- // longer know which previous caller/party had dropped out before the user
- // made this call.
- mSecondaryCallName.setText(
- getContext().getString(R.string.card_title_in_call));
- showImage(mSecondaryCallPhoto, R.drawable.picture_unknown);
- } else {
- // This means that the current Mobile Originated call IS the first 3-Way
- // and hence we display the first callers/party's info here.
- Connection conn = call.getEarliestConnection();
- PhoneUtils.CallerInfoToken infoToken = PhoneUtils.startGetCallerInfo(
- getContext(), conn, this, mSecondaryCallName);
-
- // Get the compactName to be displayed, but then check that against
- // the number presentation value for the call. If it's not an allowed
- // presentation, then display the appropriate presentation string instead.
- CallerInfo info = infoToken.currentInfo;
-
- String name = PhoneUtils.getCompactNameFromCallerInfo(info, getContext());
- boolean forceGenericPhoto = false;
- if (info != null && info.numberPresentation !=
- PhoneConstants.PRESENTATION_ALLOWED) {
- name = PhoneUtils.getPresentationString(
- getContext(), info.numberPresentation);
- forceGenericPhoto = true;
- }
- mSecondaryCallName.setText(name);
-
- // Also pull the photo out of the current CallerInfo.
- // (Note we assume we already have a valid photo at
- // this point, since *presumably* the caller-id query
- // was already run at some point *before* this call
- // got put on hold. If there's no cached photo, just
- // fall back to the default "unknown" image.)
- if (!forceGenericPhoto && infoToken.isFinal) {
- showCachedImage(mSecondaryCallPhoto, info);
- } else {
- showImage(mSecondaryCallPhoto, R.drawable.picture_unknown);
- }
- }
- } else {
- // We shouldn't ever get here at all for non-CDMA devices.
- Log.w(LOG_TAG, "displayOnHoldCallStatus: ACTIVE state on non-CDMA device");
- mSecondaryCallInfo.setVisibility(View.GONE);
- }
-
- AnimationUtils.Fade.hide(mSecondaryCallPhotoDimEffect, View.GONE);
- break;
-
- default:
- // There's actually no call on hold. (Presumably this call's
- // state is IDLE, since any other state is meaningless for the
- // background call.)
- mSecondaryCallInfo.setVisibility(View.GONE);
- break;
- }
- }
-
- private void showSecondaryCallInfo() {
- // This will call ViewStub#inflate() when needed.
- mSecondaryCallInfo.setVisibility(View.VISIBLE);
- if (mSecondaryCallName == null) {
- mSecondaryCallName = (TextView) findViewById(R.id.secondaryCallName);
- }
- if (mSecondaryCallPhoto == null) {
- mSecondaryCallPhoto = (ImageView) findViewById(R.id.secondaryCallPhoto);
- }
- if (mSecondaryCallPhotoDimEffect == null) {
- mSecondaryCallPhotoDimEffect = findViewById(R.id.dim_effect_for_secondary_photo);
- mSecondaryCallPhotoDimEffect.setOnClickListener(mInCallScreen);
- // Add a custom OnTouchListener to manually shrink the "hit target".
- mSecondaryCallPhotoDimEffect.setOnTouchListener(new SmallerHitTargetTouchListener());
- }
- mInCallScreen.updateButtonStateOutsideInCallTouchUi();
- }
-
- /**
- * Method which is expected to be called from
- * {@link InCallScreen#updateButtonStateOutsideInCallTouchUi()}.
- */
- /* package */ void setSecondaryCallClickable(boolean clickable) {
- if (mSecondaryCallPhotoDimEffect != null) {
- mSecondaryCallPhotoDimEffect.setEnabled(clickable);
- }
- }
-
- private String getCallFailedString(Call call) {
- Connection c = call.getEarliestConnection();
- int resID;
-
- if (c == null) {
- if (DBG) log("getCallFailedString: connection is null, using default values.");
- // if this connection is null, just assume that the
- // default case occurs.
- resID = R.string.card_title_call_ended;
- } else {
-
- Connection.DisconnectCause cause = c.getDisconnectCause();
-
- // TODO: The card *title* should probably be "Call ended" in all
- // cases, but if the DisconnectCause was an error condition we should
- // probably also display the specific failure reason somewhere...
-
- switch (cause) {
- case BUSY:
- resID = R.string.callFailed_userBusy;
- break;
-
- case CONGESTION:
- resID = R.string.callFailed_congestion;
- break;
-
- case TIMED_OUT:
- resID = R.string.callFailed_timedOut;
- break;
-
- case SERVER_UNREACHABLE:
- resID = R.string.callFailed_server_unreachable;
- break;
-
- case NUMBER_UNREACHABLE:
- resID = R.string.callFailed_number_unreachable;
- break;
-
- case INVALID_CREDENTIALS:
- resID = R.string.callFailed_invalid_credentials;
- break;
-
- case SERVER_ERROR:
- resID = R.string.callFailed_server_error;
- break;
-
- case OUT_OF_NETWORK:
- resID = R.string.callFailed_out_of_network;
- break;
-
- case LOST_SIGNAL:
- case CDMA_DROP:
- resID = R.string.callFailed_noSignal;
- break;
-
- case LIMIT_EXCEEDED:
- resID = R.string.callFailed_limitExceeded;
- break;
-
- case POWER_OFF:
- resID = R.string.callFailed_powerOff;
- break;
-
- case ICC_ERROR:
- resID = R.string.callFailed_simError;
- break;
-
- case OUT_OF_SERVICE:
- resID = R.string.callFailed_outOfService;
- break;
-
- case INVALID_NUMBER:
- case UNOBTAINABLE_NUMBER:
- resID = R.string.callFailed_unobtainable_number;
- break;
-
- default:
- resID = R.string.card_title_call_ended;
- break;
- }
- }
- return getContext().getString(resID);
- }
-
- /**
- * Updates the name / photo / number / label fields on the CallCard
- * based on the specified CallerInfo.
- *
- * If the current call is a conference call, use
- * updateDisplayForConference() instead.
- */
- private void updateDisplayForPerson(CallerInfo info,
- int presentation,
- boolean isTemporary,
- Call call,
- Connection conn) {
- if (DBG) log("updateDisplayForPerson(" + info + ")\npresentation:" +
- presentation + " isTemporary:" + isTemporary);
-
- // inform the state machine that we are displaying a photo.
- mPhotoTracker.setPhotoRequest(info);
- mPhotoTracker.setPhotoState(ContactsAsyncHelper.ImageTracker.DISPLAY_IMAGE);
-
- // The actual strings we're going to display onscreen:
- String displayName;
- String displayNumber = null;
- String label = null;
- Uri personUri = null;
- // String socialStatusText = null;
- // Drawable socialStatusBadge = null;
-
- // Gather missing info unless the call is generic, in which case we wouldn't use
- // the gathered information anyway.
- if (info != null && !call.isGeneric()) {
-
- // It appears that there is a small change in behaviour with the
- // PhoneUtils' startGetCallerInfo whereby if we query with an
- // empty number, we will get a valid CallerInfo object, but with
- // fields that are all null, and the isTemporary boolean input
- // parameter as true.
-
- // In the past, we would see a NULL callerinfo object, but this
- // ends up causing null pointer exceptions elsewhere down the
- // line in other cases, so we need to make this fix instead. It
- // appears that this was the ONLY call to PhoneUtils
- // .getCallerInfo() that relied on a NULL CallerInfo to indicate
- // an unknown contact.
-
- // Currently, infi.phoneNumber may actually be a SIP address, and
- // if so, it might sometimes include the "sip:" prefix. That
- // prefix isn't really useful to the user, though, so strip it off
- // if present. (For any other URI scheme, though, leave the
- // prefix alone.)
- // TODO: It would be cleaner for CallerInfo to explicitly support
- // SIP addresses instead of overloading the "phoneNumber" field.
- // Then we could remove this hack, and instead ask the CallerInfo
- // for a "user visible" form of the SIP address.
- String number = info.phoneNumber;
- if ((number != null) && number.startsWith("sip:")) {
- number = number.substring(4);
- }
-
- if (TextUtils.isEmpty(info.name)) {
- // No valid "name" in the CallerInfo, so fall back to
- // something else.
- // (Typically, we promote the phone number up to the "name" slot
- // onscreen, and possibly display a descriptive string in the
- // "number" slot.)
- if (TextUtils.isEmpty(number)) {
- // No name *or* number! Display a generic "unknown" string
- // (or potentially some other default based on the presentation.)
- displayName = PhoneUtils.getPresentationString(getContext(), presentation);
- if (DBG) log(" ==> no name *or* number! displayName = " + displayName);
- } else if (presentation != PhoneConstants.PRESENTATION_ALLOWED) {
- // This case should never happen since the network should never send a phone #
- // AND a restricted presentation. However we leave it here in case of weird
- // network behavior
- displayName = PhoneUtils.getPresentationString(getContext(), presentation);
- if (DBG) log(" ==> presentation not allowed! displayName = " + displayName);
- } else if (!TextUtils.isEmpty(info.cnapName)) {
- // No name, but we do have a valid CNAP name, so use that.
- displayName = info.cnapName;
- info.name = info.cnapName;
- displayNumber = number;
- if (DBG) log(" ==> cnapName available: displayName '"
- + displayName + "', displayNumber '" + displayNumber + "'");
- } else {
- // No name; all we have is a number. This is the typical
- // case when an incoming call doesn't match any contact,
- // or if you manually dial an outgoing number using the
- // dialpad.
-
- // Promote the phone number up to the "name" slot:
- displayName = number;
-
- // ...and use the "number" slot for a geographical description
- // string if available (but only for incoming calls.)
- if ((conn != null) && (conn.isIncoming())) {
- // TODO (CallerInfoAsyncQuery cleanup): Fix the CallerInfo
- // query to only do the geoDescription lookup in the first
- // place for incoming calls.
- displayNumber = info.geoDescription; // may be null
- }
-
- if (DBG) log(" ==> no name; falling back to number: displayName '"
- + displayName + "', displayNumber '" + displayNumber + "'");
- }
- } else {
- // We do have a valid "name" in the CallerInfo. Display that
- // in the "name" slot, and the phone number in the "number" slot.
- if (presentation != PhoneConstants.PRESENTATION_ALLOWED) {
- // This case should never happen since the network should never send a name
- // AND a restricted presentation. However we leave it here in case of weird
- // network behavior
- displayName = PhoneUtils.getPresentationString(getContext(), presentation);
- if (DBG) log(" ==> valid name, but presentation not allowed!"
- + " displayName = " + displayName);
- } else {
- displayName = info.name;
- displayNumber = number;
- label = info.phoneLabel;
- if (DBG) log(" ==> name is present in CallerInfo: displayName '"
- + displayName + "', displayNumber '" + displayNumber + "'");
- }
- }
- personUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, info.person_id);
- if (DBG) log("- got personUri: '" + personUri
- + "', based on info.person_id: " + info.person_id);
- } else {
- displayName = PhoneUtils.getPresentationString(getContext(), presentation);
- }
-
- if (call.isGeneric()) {
- updateGenericInfoUi();
- } else {
- updateInfoUi(displayName, displayNumber, label);
- }
-
- // Update mPhoto
- // if the temporary flag is set, we know we'll be getting another call after
- // the CallerInfo has been correctly updated. So, we can skip the image
- // loading until then.
-
- // If the photoResource is filled in for the CallerInfo, (like with the
- // Emergency Number case), then we can just set the photo image without
- // requesting for an image load. Please refer to CallerInfoAsyncQuery.java
- // for cases where CallerInfo.photoResource may be set. We can also avoid
- // the image load step if the image data is cached.
- if (isTemporary && (info == null || !info.isCachedPhotoCurrent)) {
- mPhoto.setTag(null);
- mPhoto.setVisibility(View.INVISIBLE);
- } else if (info != null && info.photoResource != 0){
- showImage(mPhoto, info.photoResource);
- } else if (!showCachedImage(mPhoto, info)) {
- if (personUri == null) {
- Log.w(LOG_TAG, "personPri is null. Just use Unknown picture.");
- showImage(mPhoto, R.drawable.picture_unknown);
- } else if (personUri.equals(mLoadingPersonUri)) {
- if (DBG) {
- log("The requested Uri (" + personUri + ") is being loaded already."
- + " Ignoret the duplicate load request.");
- }
- } else {
- // Remember which person's photo is being loaded right now so that we won't issue
- // unnecessary load request multiple times, which will mess up animation around
- // the contact photo.
- mLoadingPersonUri = personUri;
-
- // Forget the drawable previously used.
- mPhoto.setTag(null);
- // Show empty screen for a moment.
- mPhoto.setVisibility(View.INVISIBLE);
- // Load the image with a callback to update the image state.
- // When the load is finished, onImageLoadComplete() will be called.
- ContactsAsyncHelper.startObtainPhotoAsync(TOKEN_UPDATE_PHOTO_FOR_CALL_STATE,
- getContext(), personUri, this, new AsyncLoadCookie(mPhoto, info, call));
-
- // If the image load is too slow, we show a default avatar icon afterward.
- // If it is fast enough, this message will be canceled on onImageLoadComplete().
- mHandler.removeMessages(MESSAGE_SHOW_UNKNOWN_PHOTO);
- mHandler.sendEmptyMessageDelayed(MESSAGE_SHOW_UNKNOWN_PHOTO, MESSAGE_DELAY);
- }
- }
-
- // If the phone call is on hold, show it with darker status.
- // Right now we achieve it by overlaying opaque View.
- // Note: See also layout file about why so and what is the other possibilities.
- if (call.getState() == Call.State.HOLDING) {
- AnimationUtils.Fade.show(mPhotoDimEffect);
- } else {
- AnimationUtils.Fade.hide(mPhotoDimEffect, View.GONE);
- }
-
- // Other text fields:
- updateCallTypeLabel(call);
- // updateSocialStatus(socialStatusText, socialStatusBadge, call); // Currently unused
- }
-
- /**
- * Updates the info portion of the UI to be generic. Used for CDMA 3-way calls.
- */
- private void updateGenericInfoUi() {
- mName.setText(R.string.card_title_in_call);
- mPhoneNumber.setVisibility(View.GONE);
- mLabel.setVisibility(View.GONE);
- }
-
- /**
- * Updates the info portion of the call card with passed in values.
- */
- private void updateInfoUi(String displayName, String displayNumber, String label) {
- mName.setText(displayName);
- mName.setVisibility(View.VISIBLE);
-
- if (TextUtils.isEmpty(displayNumber)) {
- mPhoneNumber.setVisibility(View.GONE);
- // We have a real phone number as "mName" so make it always LTR
- mName.setTextDirection(View.TEXT_DIRECTION_LTR);
- } else {
- mPhoneNumber.setText(displayNumber);
- mPhoneNumber.setVisibility(View.VISIBLE);
- // We have a real phone number as "mPhoneNumber" so make it always LTR
- mPhoneNumber.setTextDirection(View.TEXT_DIRECTION_LTR);
- }
-
- if (TextUtils.isEmpty(label)) {
- mLabel.setVisibility(View.GONE);
- } else {
- mLabel.setText(label);
- mLabel.setVisibility(View.VISIBLE);
- }
- }
-
- /**
- * Updates the name / photo / number / label fields
- * for the special "conference call" state.
- *
- * If the current call has only a single connection, use
- * updateDisplayForPerson() instead.
- */
- private void updateDisplayForConference(Call call) {
- if (DBG) log("updateDisplayForConference()...");
-
- int phoneType = call.getPhone().getPhoneType();
- if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
- // This state corresponds to both 3-Way merged call and
- // Call Waiting accepted call.
- // In this case we display the UI in a "generic" state, with
- // the generic "dialing" icon and no caller information,
- // because in this state in CDMA the user does not really know
- // which caller party he is talking to.
- showImage(mPhoto, R.drawable.picture_dialing);
- mName.setText(R.string.card_title_in_call);
- } else if ((phoneType == PhoneConstants.PHONE_TYPE_GSM)
- || (phoneType == PhoneConstants.PHONE_TYPE_SIP)) {
- // Normal GSM (or possibly SIP?) conference call.
- // Display the "conference call" image as the contact photo.
- // TODO: Better visual treatment for contact photos in a
- // conference call (see bug 1313252).
- showImage(mPhoto, R.drawable.picture_conference);
- mName.setText(R.string.card_title_conf_call);
- } else {
- throw new IllegalStateException("Unexpected phone type: " + phoneType);
- }
-
- mName.setVisibility(View.VISIBLE);
-
- // TODO: For a conference call, the "phone number" slot is specced
- // to contain a summary of who's on the call, like "Bill Foldes
- // and Hazel Nutt" or "Bill Foldes and 2 others".
- // But for now, just hide it:
- mPhoneNumber.setVisibility(View.GONE);
- mLabel.setVisibility(View.GONE);
-
- // Other text fields:
- updateCallTypeLabel(call);
- // updateSocialStatus(null, null, null); // socialStatus is never visible in this state
-
- // TODO: for a GSM conference call, since we do actually know who
- // you're talking to, consider also showing names / numbers /
- // photos of some of the people on the conference here, so you can
- // see that info without having to click "Manage conference". We
- // probably have enough space to show info for 2 people, at least.
- //
- // To do this, our caller would pass us the activeConnections
- // list, and we'd call PhoneUtils.getCallerInfo() separately for
- // each connection.
- }
-
- /**
- * Updates the CallCard "photo" IFF the specified Call is in a state
- * that needs a special photo (like "busy" or "dialing".)
- *
- * If the current call does not require a special image in the "photo"
- * slot onscreen, don't do anything, since presumably the photo image
- * has already been set (to the photo of the person we're talking, or
- * the generic "picture_unknown" image, or the "conference call"
- * image.)
- */
- private void updatePhotoForCallState(Call call) {
- if (DBG) log("updatePhotoForCallState(" + call + ")...");
- int photoImageResource = 0;
-
- // Check for the (relatively few) telephony states that need a
- // special image in the "photo" slot.
- Call.State state = call.getState();
- switch (state) {
- case DISCONNECTED:
- // Display the special "busy" photo for BUSY or CONGESTION.
- // Otherwise (presumably the normal "call ended" state)
- // leave the photo alone.
- Connection c = call.getEarliestConnection();
- // if the connection is null, we assume the default case,
- // otherwise update the image resource normally.
- if (c != null) {
- Connection.DisconnectCause cause = c.getDisconnectCause();
- if ((cause == Connection.DisconnectCause.BUSY)
- || (cause == Connection.DisconnectCause.CONGESTION)) {
- photoImageResource = R.drawable.picture_busy;
- }
- } else if (DBG) {
- log("updatePhotoForCallState: connection is null, ignoring.");
- }
-
- // TODO: add special images for any other DisconnectCauses?
- break;
-
- case ALERTING:
- case DIALING:
- default:
- // Leave the photo alone in all other states.
- // If this call is an individual call, and the image is currently
- // displaying a state, (rather than a photo), we'll need to update
- // the image.
- // This is for the case where we've been displaying the state and
- // now we need to restore the photo. This can happen because we
- // only query the CallerInfo once, and limit the number of times
- // the image is loaded. (So a state image may overwrite the photo
- // and we would otherwise have no way of displaying the photo when
- // the state goes away.)
-
- // if the photoResource field is filled-in in the Connection's
- // caller info, then we can just use that instead of requesting
- // for a photo load.
-
- // look for the photoResource if it is available.
- CallerInfo ci = null;
- {
- Connection conn = null;
- int phoneType = call.getPhone().getPhoneType();
- if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
- conn = call.getLatestConnection();
- } else if ((phoneType == PhoneConstants.PHONE_TYPE_GSM)
- || (phoneType == PhoneConstants.PHONE_TYPE_SIP)) {
- conn = call.getEarliestConnection();
- } else {
- throw new IllegalStateException("Unexpected phone type: " + phoneType);
- }
-
- if (conn != null) {
- Object o = conn.getUserData();
- if (o instanceof CallerInfo) {
- ci = (CallerInfo) o;
- } else if (o instanceof PhoneUtils.CallerInfoToken) {
- ci = ((PhoneUtils.CallerInfoToken) o).currentInfo;
- }
- }
- }
-
- if (ci != null) {
- photoImageResource = ci.photoResource;
- }
-
- // If no photoResource found, check to see if this is a conference call. If
- // it is not a conference call:
- // 1. Try to show the cached image
- // 2. If the image is not cached, check to see if a load request has been
- // made already.
- // 3. If the load request has not been made [DISPLAY_DEFAULT], start the
- // request and note that it has started by updating photo state with
- // [DISPLAY_IMAGE].
- if (photoImageResource == 0) {
- if (!PhoneUtils.isConferenceCall(call)) {
- if (!showCachedImage(mPhoto, ci) && (mPhotoTracker.getPhotoState() ==
- ContactsAsyncHelper.ImageTracker.DISPLAY_DEFAULT)) {
- Uri photoUri = mPhotoTracker.getPhotoUri();
- if (photoUri == null) {
- Log.w(LOG_TAG, "photoUri became null. Show default avatar icon");
- showImage(mPhoto, R.drawable.picture_unknown);
- } else {
- if (DBG) {
- log("start asynchronous load inside updatePhotoForCallState()");
- }
- mPhoto.setTag(null);
- // Make it invisible for a moment
- mPhoto.setVisibility(View.INVISIBLE);
- ContactsAsyncHelper.startObtainPhotoAsync(TOKEN_DO_NOTHING,
- getContext(), photoUri, this,
- new AsyncLoadCookie(mPhoto, ci, null));
- }
- mPhotoTracker.setPhotoState(
- ContactsAsyncHelper.ImageTracker.DISPLAY_IMAGE);
- }
- }
- } else {
- showImage(mPhoto, photoImageResource);
- mPhotoTracker.setPhotoState(ContactsAsyncHelper.ImageTracker.DISPLAY_IMAGE);
- return;
- }
- break;
- }
-
- if (photoImageResource != 0) {
- if (DBG) log("- overrriding photo image: " + photoImageResource);
- showImage(mPhoto, photoImageResource);
- // Track the image state.
- mPhotoTracker.setPhotoState(ContactsAsyncHelper.ImageTracker.DISPLAY_DEFAULT);
- }
- }
-
- /**
- * Try to display the cached image from the callerinfo object.
- *
- * @return true if we were able to find the image in the cache, false otherwise.
- */
- private static final boolean showCachedImage(ImageView view, CallerInfo ci) {
- if ((ci != null) && ci.isCachedPhotoCurrent) {
- if (ci.cachedPhoto != null) {
- showImage(view, ci.cachedPhoto);
- } else {
- showImage(view, R.drawable.picture_unknown);
- }
- return true;
- }
- return false;
- }
-
- /** Helper function to display the resource in the imageview AND ensure its visibility.*/
- private static final void showImage(ImageView view, int resource) {
- showImage(view, view.getContext().getResources().getDrawable(resource));
- }
-
- private static final void showImage(ImageView view, Bitmap bitmap) {
- showImage(view, new BitmapDrawable(view.getContext().getResources(), bitmap));
- }
-
- /** Helper function to display the drawable in the imageview AND ensure its visibility.*/
- private static final void showImage(ImageView view, Drawable drawable) {
- Resources res = view.getContext().getResources();
- Drawable current = (Drawable) view.getTag();
-
- if (current == null) {
- if (DBG) log("Start fade-in animation for " + view);
- view.setImageDrawable(drawable);
- AnimationUtils.Fade.show(view);
- view.setTag(drawable);
- } else {
- AnimationUtils.startCrossFade(view, current, drawable);
- view.setVisibility(View.VISIBLE);
- }
- }
-
- /**
- * Returns the special card title used in emergency callback mode (ECM),
- * which shows your own phone number.
- */
- private String getECMCardTitle(Context context, Phone phone) {
- String rawNumber = phone.getLine1Number(); // may be null or empty
- String formattedNumber;
- if (!TextUtils.isEmpty(rawNumber)) {
- formattedNumber = PhoneNumberUtils.formatNumber(rawNumber);
- } else {
- formattedNumber = context.getString(R.string.unknown);
- }
- String titleFormat = context.getString(R.string.card_title_my_phone_number);
- return String.format(titleFormat, formattedNumber);
- }
-
- /**
- * Updates the "Call type" label, based on the current foreground call.
- * This is a special label and/or branding we display for certain
- * kinds of calls.
- *
- * (So far, this is used only for SIP calls, which get an
- * "Internet call" label. TODO: But eventually, the telephony
- * layer might allow each pluggable "provider" to specify a string
- * and/or icon to be displayed here.)
- */
- private void updateCallTypeLabel(Call call) {
- int phoneType = (call != null) ? call.getPhone().getPhoneType() :
- PhoneConstants.PHONE_TYPE_NONE;
- if (phoneType == PhoneConstants.PHONE_TYPE_SIP) {
- mCallTypeLabel.setVisibility(View.VISIBLE);
- mCallTypeLabel.setText(R.string.incall_call_type_label_sip);
- mCallTypeLabel.setTextColor(mTextColorCallTypeSip);
- // If desired, we could also display a "badge" next to the label, as follows:
- // mCallTypeLabel.setCompoundDrawablesWithIntrinsicBounds(
- // callTypeSpecificBadge, null, null, null);
- // mCallTypeLabel.setCompoundDrawablePadding((int) (mDensity * 6));
- } else {
- mCallTypeLabel.setVisibility(View.GONE);
- }
- }
-
- /**
- * Updates the "social status" label with the specified text and
- * (optional) badge.
- */
- /*private void updateSocialStatus(String socialStatusText,
- Drawable socialStatusBadge,
- Call call) {
- // The socialStatus field is *only* visible while an incoming call
- // is ringing, never in any other call state.
- if ((socialStatusText != null)
- && (call != null)
- && call.isRinging()
- && !call.isGeneric()) {
- mSocialStatus.setVisibility(View.VISIBLE);
- mSocialStatus.setText(socialStatusText);
- mSocialStatus.setCompoundDrawablesWithIntrinsicBounds(
- socialStatusBadge, null, null, null);
- mSocialStatus.setCompoundDrawablePadding((int) (mDensity * 6));
- } else {
- mSocialStatus.setVisibility(View.GONE);
- }
- }*/
-
- /**
- * Hides the top-level UI elements of the call card: The "main
- * call card" element representing the current active or ringing call,
- * and also the info areas for "ongoing" or "on hold" calls in some
- * states.
- *
- * This is intended to be used in special states where the normal
- * in-call UI is totally replaced by some other UI, like OTA mode on a
- * CDMA device.
- *
- * To bring back the regular CallCard UI, just re-run the normal
- * updateState() call sequence.
- */
- public void hideCallCardElements() {
- mPrimaryCallInfo.setVisibility(View.GONE);
- mSecondaryCallInfo.setVisibility(View.GONE);
- }
-
- /*
- * Updates the hint (like "Rotate to answer") that we display while
- * the user is dragging the incoming call RotarySelector widget.
- */
- /* package */ void setIncomingCallWidgetHint(int hintTextResId, int hintColorResId) {
- mIncomingCallWidgetHintTextResId = hintTextResId;
- mIncomingCallWidgetHintColorResId = hintColorResId;
- }
-
- // Accessibility event support.
- // Since none of the CallCard elements are focusable, we need to manually
- // fill in the AccessibilityEvent here (so that the name / number / etc will
- // get pronounced by a screen reader, for example.)
- @Override
- public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
- if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
- dispatchPopulateAccessibilityEvent(event, mName);
- dispatchPopulateAccessibilityEvent(event, mPhoneNumber);
- return true;
- }
-
- dispatchPopulateAccessibilityEvent(event, mCallStateLabel);
- dispatchPopulateAccessibilityEvent(event, mPhoto);
- dispatchPopulateAccessibilityEvent(event, mName);
- dispatchPopulateAccessibilityEvent(event, mPhoneNumber);
- dispatchPopulateAccessibilityEvent(event, mLabel);
- // dispatchPopulateAccessibilityEvent(event, mSocialStatus);
- if (mSecondaryCallName != null) {
- dispatchPopulateAccessibilityEvent(event, mSecondaryCallName);
- }
- if (mSecondaryCallPhoto != null) {
- dispatchPopulateAccessibilityEvent(event, mSecondaryCallPhoto);
- }
- return true;
- }
-
- private void dispatchPopulateAccessibilityEvent(AccessibilityEvent event, View view) {
- List<CharSequence> eventText = event.getText();
- int size = eventText.size();
- view.dispatchPopulateAccessibilityEvent(event);
- // if no text added write null to keep relative position
- if (size == eventText.size()) {
- eventText.add(null);
- }
- }
-
- public void clear() {
- // The existing phone design is to keep an instance of call card forever. Until that
- // design changes, this method is needed to clear (reset) the call card for the next call
- // so old data is not shown.
-
- // Other elements can also be cleared here. Starting with elapsed time to fix a bug.
- mElapsedTime.setVisibility(View.GONE);
- mElapsedTime.setText(null);
- }
-
-
- // Debugging / testing code
-
- private static void log(String msg) {
- Log.d(LOG_TAG, msg);
- }
-}
diff --git a/src/com/android/phone/CallController.java b/src/com/android/phone/CallController.java
index 2cceed0..52dbf76 100644
--- a/src/com/android/phone/CallController.java
+++ b/src/com/android/phone/CallController.java
@@ -23,7 +23,6 @@
import com.android.phone.CallGatewayManager.RawGatewayInfo;
import com.android.phone.Constants.CallStatusCode;
import com.android.phone.ErrorDialogActivity;
-import com.android.phone.InCallUiState.InCallScreenMode;
import com.android.phone.OtaUtils.CdmaOtaScreenState;
import android.app.AlertDialog;
@@ -194,8 +193,6 @@
log("placeCall()... intent = " + intent);
if (VDBG) log(" extras = " + intent.getExtras());
- final InCallUiState inCallUiState = mApp.inCallUiState;
-
// TODO: Do we need to hold a wake lock while this method runs?
// Or did we already acquire one somewhere earlier
// in this sequence (like when we first received the CALL intent?)
@@ -256,18 +253,6 @@
case SUCCESS:
case EXITED_ECM:
if (DBG) log("==> placeCall(): success from placeCallInternal(): " + status);
-
- if (status == CallStatusCode.EXITED_ECM) {
- // Call succeeded, but we also need to tell the
- // InCallScreen to show the "Exiting ECM" warning.
- inCallUiState.setPendingCallStatusCode(CallStatusCode.EXITED_ECM);
- } else {
- // Call succeeded. There's no "error condition" that
- // needs to be displayed to the user, so clear out the
- // InCallUiState's "pending call status code".
- inCallUiState.clearPendingCallStatusCode();
- }
-
break;
default:
@@ -289,20 +274,6 @@
// in-call UI. Or if there was an error, the InCallScreen will
// notice the InCallUiState pending call status code flag and display an
// error indication instead.)
-
- // TODO: double-check the behavior of mApp.displayCallScreen()
- // if the InCallScreen is already visible:
- // - make sure it forces the UI to refresh
- // - make sure it does NOT launch a new InCallScreen on top
- // of the current one (i.e. the Back button should not take
- // you back to the previous InCallScreen)
- // - it's probably OK to go thru a fresh pause/resume sequence
- // though (since that should be fast now)
- // - if necessary, though, maybe PhoneApp.displayCallScreen()
- // could notice that the InCallScreen is already in the foreground,
- // and if so simply call updateInCallScreen() instead.
-
- mApp.displayCallScreen();
}
/**
@@ -324,7 +295,6 @@
// TODO: This method is too long. Break it down into more
// manageable chunks.
- final InCallUiState inCallUiState = mApp.inCallUiState;
final Uri uri = intent.getData();
final String scheme = (uri != null) ? uri.getScheme() : null;
String number;
@@ -461,13 +431,6 @@
}
}
- // Ok, we can proceed with this outgoing call.
-
- // Reset some InCallUiState flags, just in case they're still set
- // from a prior call.
- inCallUiState.needToShowCallLostDialog = false;
- inCallUiState.clearProgressIndication();
-
// We have a valid number, so try to actually place a call:
// make sure we pass along the intent's URI which is a
// reference to the contact. We may have a provider gateway
@@ -501,40 +464,7 @@
// app.cdmaOtaInCallScreenUiState.state are redundant.
// Combine them.
- if (VDBG) log ("- inCallUiState.inCallScreenMode = "
- + inCallUiState.inCallScreenMode);
- if (inCallUiState.inCallScreenMode == InCallScreenMode.OTA_NORMAL) {
- if (VDBG) log ("==> OTA_NORMAL note: switching to OTA_STATUS_LISTENING.");
- mApp.cdmaOtaScreenState.otaScreenState =
- CdmaOtaScreenState.OtaScreenState.OTA_STATUS_LISTENING;
- }
-
boolean voicemailUriSpecified = scheme != null && scheme.equals("voicemail");
- // When voicemail is requested most likely the user wants to open
- // dialpad immediately, so we show it in the first place.
- // Otherwise we want to make sure the user can see the regular
- // in-call UI while the new call is dialing, and when it
- // first gets connected.)
- inCallUiState.showDialpad = voicemailUriSpecified;
-
- // For voicemails, we add context text to let the user know they
- // are dialing their voicemail.
- // TODO: This is only set here and becomes problematic when swapping calls
- inCallUiState.dialpadContextText = voicemailUriSpecified ?
- phone.getVoiceMailAlphaTag() : "";
-
- // Also, in case a previous call was already active (i.e. if
- // we just did "Add call"), clear out the "history" of DTMF
- // digits you typed, to make sure it doesn't persist from the
- // previous call to the new call.
- // TODO: it would be more precise to do this when the actual
- // phone state change happens (i.e. when a new foreground
- // call appears and the previous call moves to the
- // background), but the InCallScreen doesn't keep enough
- // state right now to notice that specific transition in
- // onPhoneStateChanged().
- inCallUiState.dialpadDigits = null;
-
// Check for an obscure ECM-related scenario: If the phone
// is currently in ECM (Emergency callback mode) and we
// dial a non-emergency number, that automatically
diff --git a/src/com/android/phone/CallNotifier.java b/src/com/android/phone/CallNotifier.java
index bbffbd2..a5b467d 100644
--- a/src/com/android/phone/CallNotifier.java
+++ b/src/com/android/phone/CallNotifier.java
@@ -304,7 +304,6 @@
if (DBG) log("Received CALLWAITING_ADDCALL_DISABLE_TIMEOUT event ...");
// Set the mAddCallMenuStateAfterCW state to true
mApplication.cdmaPhoneCallState.setAddCallMenuStateAfterCallWaiting(true);
- mApplication.updateInCallScreen();
break;
case CallStateMonitor.PHONE_STATE_DISPLAYINFO:
@@ -526,7 +525,6 @@
// we should instead provide a higher-level API via OtaUtils.
if (dialogState) mApplication.dismissOtaDialogs();
mApplication.clearOtaState();
- mApplication.clearInCallScreenMode();
return false;
}
}
@@ -738,12 +736,6 @@
// make sure audio is in in-call mode now
PhoneUtils.setAudioMode(mCM);
- // if the call screen is showing, let it handle the event,
- // otherwise handle it here.
- if (!mApplication.isShowingCallScreen()) {
- mApplication.requestWakeState(PhoneGlobals.WakeState.SLEEP);
- }
-
// Since we're now in-call, the Ringer should definitely *not*
// be ringing any more. (This is just a sanity-check; we
// already stopped the ringer explicitly back in
diff --git a/src/com/android/phone/DTMFTwelveKeyDialer.java b/src/com/android/phone/DTMFTwelveKeyDialer.java
deleted file mode 100644
index 4dd4a88..0000000
--- a/src/com/android/phone/DTMFTwelveKeyDialer.java
+++ /dev/null
@@ -1,1116 +0,0 @@
-/*
- * Copyright (C) 2008 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.phone;
-
-import android.content.Context;
-import android.media.AudioManager;
-import android.media.ToneGenerator;
-import android.os.Handler;
-import android.os.Message;
-import android.provider.Settings;
-import android.telephony.PhoneNumberUtils;
-import android.text.Editable;
-import android.text.SpannableString;
-import android.text.method.DialerKeyListener;
-import android.text.style.RelativeSizeSpan;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.accessibility.AccessibilityManager;
-import android.view.ViewStub;
-import android.widget.EditText;
-
-import com.android.internal.telephony.CallManager;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.Queue;
-
-
-/**
- * Dialer class that encapsulates the DTMF twelve key behaviour.
- * This model backs up the UI behaviour in DTMFTwelveKeyDialerView.java.
- */
-public class DTMFTwelveKeyDialer implements View.OnTouchListener, View.OnKeyListener,
- View.OnHoverListener, View.OnClickListener {
- private static final String LOG_TAG = "DTMFTwelveKeyDialer";
- private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
-
- // events
- private static final int PHONE_DISCONNECT = 100;
- private static final int DTMF_SEND_CNF = 101;
- private static final int DTMF_STOP = 102;
-
- /** Accessibility manager instance used to check touch exploration state. */
- private final AccessibilityManager mAccessibilityManager;
-
- private CallManager mCM;
- private ToneGenerator mToneGenerator;
- private final Object mToneGeneratorLock = new Object();
-
- // indicate if we want to enable the local tone playback.
- private boolean mLocalToneEnabled;
-
- // indicates that we are using automatically shortened DTMF tones
- boolean mShortTone;
-
- // indicate if the confirmation from TelephonyFW is pending.
- private boolean mDTMFBurstCnfPending = false;
-
- // Queue to queue the short dtmf characters.
- private Queue<Character> mDTMFQueue = new LinkedList<Character>();
-
- // Short Dtmf tone duration
- private static final int DTMF_DURATION_MS = 120;
-
-
- /** Hash Map to map a character to a tone*/
- private static final HashMap<Character, Integer> mToneMap =
- new HashMap<Character, Integer>();
- /** Hash Map to map a view id to a character*/
- private static final HashMap<Integer, Character> mDisplayMap =
- new HashMap<Integer, Character>();
- /** Set up the static maps*/
- static {
- // Map the key characters to tones
- mToneMap.put('1', ToneGenerator.TONE_DTMF_1);
- mToneMap.put('2', ToneGenerator.TONE_DTMF_2);
- mToneMap.put('3', ToneGenerator.TONE_DTMF_3);
- mToneMap.put('4', ToneGenerator.TONE_DTMF_4);
- mToneMap.put('5', ToneGenerator.TONE_DTMF_5);
- mToneMap.put('6', ToneGenerator.TONE_DTMF_6);
- mToneMap.put('7', ToneGenerator.TONE_DTMF_7);
- mToneMap.put('8', ToneGenerator.TONE_DTMF_8);
- mToneMap.put('9', ToneGenerator.TONE_DTMF_9);
- mToneMap.put('0', ToneGenerator.TONE_DTMF_0);
- mToneMap.put('#', ToneGenerator.TONE_DTMF_P);
- mToneMap.put('*', ToneGenerator.TONE_DTMF_S);
-
- // Map the buttons to the display characters
- mDisplayMap.put(R.id.one, '1');
- mDisplayMap.put(R.id.two, '2');
- mDisplayMap.put(R.id.three, '3');
- mDisplayMap.put(R.id.four, '4');
- mDisplayMap.put(R.id.five, '5');
- mDisplayMap.put(R.id.six, '6');
- mDisplayMap.put(R.id.seven, '7');
- mDisplayMap.put(R.id.eight, '8');
- mDisplayMap.put(R.id.nine, '9');
- mDisplayMap.put(R.id.zero, '0');
- mDisplayMap.put(R.id.pound, '#');
- mDisplayMap.put(R.id.star, '*');
- }
-
- /** EditText field used to display the DTMF digits sent so far.
- Note this is null in some modes (like during the CDMA OTA call,
- where there's no onscreen "digits" display.) */
- private EditText mDialpadDigits;
-
- // InCallScreen reference.
- private InCallScreen mInCallScreen;
-
- /**
- * The DTMFTwelveKeyDialerView we use to display the dialpad.
- *
- * Only one of mDialerView or mDialerStub will have a legitimate object; the other one will be
- * null at that moment. Either of following scenarios will occur:
- *
- * - If the constructor with {@link DTMFTwelveKeyDialerView} is called, mDialerView will
- * obtain that object, and mDialerStub will be null. mDialerStub won't be used in this case.
- *
- * - If the constructor with {@link ViewStub} is called, mDialerView will be null at that
- * moment, and mDialerStub will obtain the ViewStub object.
- * When the dialer is required by the user (i.e. until {@link #openDialer(boolean)} being
- * called), mDialerStub will inflate the dialer, and make mDialerStub itself null.
- * mDialerStub won't be used afterward.
- */
- private DTMFTwelveKeyDialerView mDialerView;
-
- /**
- * {@link ViewStub} holding {@link DTMFTwelveKeyDialerView}. See the comments for mDialerView.
- */
- private ViewStub mDialerStub;
-
- // KeyListener used with the "dialpad digits" EditText widget.
- private DTMFKeyListener mDialerKeyListener;
-
- /**
- * Our own key listener, specialized for dealing with DTMF codes.
- * 1. Ignore the backspace since it is irrelevant.
- * 2. Allow ONLY valid DTMF characters to generate a tone and be
- * sent as a DTMF code.
- * 3. All other remaining characters are handled by the superclass.
- *
- * This code is purely here to handle events from the hardware keyboard
- * while the DTMF dialpad is up.
- */
- private class DTMFKeyListener extends DialerKeyListener {
-
- private DTMFKeyListener() {
- super();
- }
-
- /**
- * Overriden to return correct DTMF-dialable characters.
- */
- @Override
- protected char[] getAcceptedChars(){
- return DTMF_CHARACTERS;
- }
-
- /** special key listener ignores backspace. */
- @Override
- public boolean backspace(View view, Editable content, int keyCode,
- KeyEvent event) {
- return false;
- }
-
- /**
- * Return true if the keyCode is an accepted modifier key for the
- * dialer (ALT or SHIFT).
- */
- private boolean isAcceptableModifierKey(int keyCode) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_ALT_LEFT:
- case KeyEvent.KEYCODE_ALT_RIGHT:
- case KeyEvent.KEYCODE_SHIFT_LEFT:
- case KeyEvent.KEYCODE_SHIFT_RIGHT:
- return true;
- default:
- return false;
- }
- }
-
- /**
- * Overriden so that with each valid button press, we start sending
- * a dtmf code and play a local dtmf tone.
- */
- @Override
- public boolean onKeyDown(View view, Editable content,
- int keyCode, KeyEvent event) {
- // if (DBG) log("DTMFKeyListener.onKeyDown, keyCode " + keyCode + ", view " + view);
-
- // find the character
- char c = (char) lookup(event, content);
-
- // if not a long press, and parent onKeyDown accepts the input
- if (event.getRepeatCount() == 0 && super.onKeyDown(view, content, keyCode, event)) {
-
- boolean keyOK = ok(getAcceptedChars(), c);
-
- // if the character is a valid dtmf code, start playing the tone and send the
- // code.
- if (keyOK) {
- if (DBG) log("DTMFKeyListener reading '" + c + "' from input.");
- processDtmf(c);
- } else if (DBG) {
- log("DTMFKeyListener rejecting '" + c + "' from input.");
- }
- return true;
- }
- return false;
- }
-
- /**
- * Overriden so that with each valid button up, we stop sending
- * a dtmf code and the dtmf tone.
- */
- @Override
- public boolean onKeyUp(View view, Editable content,
- int keyCode, KeyEvent event) {
- // if (DBG) log("DTMFKeyListener.onKeyUp, keyCode " + keyCode + ", view " + view);
-
- super.onKeyUp(view, content, keyCode, event);
-
- // find the character
- char c = (char) lookup(event, content);
-
- boolean keyOK = ok(getAcceptedChars(), c);
-
- if (keyOK) {
- if (DBG) log("Stopping the tone for '" + c + "'");
- stopTone();
- return true;
- }
-
- return false;
- }
-
- /**
- * Handle individual keydown events when we DO NOT have an Editable handy.
- */
- public boolean onKeyDown(KeyEvent event) {
- char c = lookup(event);
- if (DBG) log("DTMFKeyListener.onKeyDown: event '" + c + "'");
-
- // if not a long press, and parent onKeyDown accepts the input
- if (event.getRepeatCount() == 0 && c != 0) {
- // if the character is a valid dtmf code, start playing the tone and send the
- // code.
- if (ok(getAcceptedChars(), c)) {
- if (DBG) log("DTMFKeyListener reading '" + c + "' from input.");
- processDtmf(c);
- return true;
- } else if (DBG) {
- log("DTMFKeyListener rejecting '" + c + "' from input.");
- }
- }
- return false;
- }
-
- /**
- * Handle individual keyup events.
- *
- * @param event is the event we are trying to stop. If this is null,
- * then we just force-stop the last tone without checking if the event
- * is an acceptable dialer event.
- */
- public boolean onKeyUp(KeyEvent event) {
- if (event == null) {
- //the below piece of code sends stopDTMF event unnecessarily even when a null event
- //is received, hence commenting it.
- /*if (DBG) log("Stopping the last played tone.");
- stopTone();*/
- return true;
- }
-
- char c = lookup(event);
- if (DBG) log("DTMFKeyListener.onKeyUp: event '" + c + "'");
-
- // TODO: stopTone does not take in character input, we may want to
- // consider checking for this ourselves.
- if (ok(getAcceptedChars(), c)) {
- if (DBG) log("Stopping the tone for '" + c + "'");
- stopTone();
- return true;
- }
-
- return false;
- }
-
- /**
- * Find the Dialer Key mapped to this event.
- *
- * @return The char value of the input event, otherwise
- * 0 if no matching character was found.
- */
- private char lookup(KeyEvent event) {
- // This code is similar to {@link DialerKeyListener#lookup(KeyEvent, Spannable) lookup}
- int meta = event.getMetaState();
- int number = event.getNumber();
-
- if (!((meta & (KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON)) == 0) || (number == 0)) {
- int match = event.getMatch(getAcceptedChars(), meta);
- number = (match != 0) ? match : number;
- }
-
- return (char) number;
- }
-
- /**
- * Check to see if the keyEvent is dialable.
- */
- boolean isKeyEventAcceptable (KeyEvent event) {
- return (ok(getAcceptedChars(), lookup(event)));
- }
-
- /**
- * Overrides the characters used in {@link DialerKeyListener#CHARACTERS}
- * These are the valid dtmf characters.
- */
- public final char[] DTMF_CHARACTERS = new char[] {
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '#', '*'
- };
- }
-
- /**
- * Our own handler to take care of the messages from the phone state changes
- */
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- // disconnect action
- // make sure to close the dialer on ALL disconnect actions.
- case PHONE_DISCONNECT:
- if (DBG) log("disconnect message recieved, shutting down.");
- // unregister since we are closing.
- mCM.unregisterForDisconnect(this);
- closeDialer(false);
- break;
- case DTMF_SEND_CNF:
- if (DBG) log("dtmf confirmation received from FW.");
- // handle burst dtmf confirmation
- handleBurstDtmfConfirmation();
- break;
- case DTMF_STOP:
- if (DBG) log("dtmf stop received");
- stopTone();
- break;
- }
- }
- };
-
-
- /**
- * DTMFTwelveKeyDialer constructor with {@link DTMFTwelveKeyDialerView}
- *
- * @param parent the InCallScreen instance that owns us.
- * @param dialerView the DTMFTwelveKeyDialerView we should use to display the dialpad.
- */
- public DTMFTwelveKeyDialer(InCallScreen parent,
- DTMFTwelveKeyDialerView dialerView) {
- this(parent);
-
- // The passed-in DTMFTwelveKeyDialerView *should* always be
- // non-null, now that the in-call UI uses only portrait mode.
- if (dialerView == null) {
- Log.e(LOG_TAG, "DTMFTwelveKeyDialer: null dialerView!", new IllegalStateException());
- // ...continue as best we can, although things will
- // be pretty broken without the mDialerView UI elements!
- }
- mDialerView = dialerView;
- if (DBG) log("- Got passed-in mDialerView: " + mDialerView);
-
- if (mDialerView != null) {
- setupDialerView();
- }
- }
-
- /**
- * DTMFTwelveKeyDialer constructor with {@link ViewStub}.
- *
- * When the dialer is required for the first time (e.g. when {@link #openDialer(boolean)} is
- * called), the object will inflate the ViewStub by itself, assuming the ViewStub will return
- * {@link DTMFTwelveKeyDialerView} on {@link ViewStub#inflate()}.
- *
- * @param parent the InCallScreen instance that owns us.
- * @param dialerStub ViewStub which will return {@link DTMFTwelveKeyDialerView} on
- * {@link ViewStub#inflate()}.
- */
- public DTMFTwelveKeyDialer(InCallScreen parent, ViewStub dialerStub) {
- this(parent);
-
- mDialerStub = dialerStub;
- if (DBG) log("- Got passed-in mDialerStub: " + mDialerStub);
-
- // At this moment mDialerView is still null. We delay calling setupDialerView().
- }
-
- /**
- * Private constructor used for initialization calls common to all public
- * constructors.
- *
- * @param parent the InCallScreen instance that owns us.
- */
- private DTMFTwelveKeyDialer(InCallScreen parent) {
- if (DBG) log("DTMFTwelveKeyDialer constructor... this = " + this);
-
- mInCallScreen = parent;
- mCM = PhoneGlobals.getInstance().mCM;
- mAccessibilityManager = (AccessibilityManager) parent.getSystemService(
- Context.ACCESSIBILITY_SERVICE);
- }
-
- /**
- * Prepare the dialer view and relevant variables.
- */
- private void setupDialerView() {
- if (DBG) log("setupDialerView()");
- mDialerView.setDialer(this);
-
- // In the normal in-call DTMF dialpad, mDialpadDigits is an
- // EditText used to display the digits the user has typed so
- // far. But some other modes (like the OTA call) have no
- // "digits" display at all, in which case mDialpadDigits will
- // be null.
- mDialpadDigits = (EditText) mDialerView.findViewById(R.id.dtmfDialerField);
- if (mDialpadDigits != null) {
- mDialerKeyListener = new DTMFKeyListener();
- mDialpadDigits.setKeyListener(mDialerKeyListener);
-
- // remove the long-press context menus that support
- // the edit (copy / paste / select) functions.
- mDialpadDigits.setLongClickable(false);
- }
-
- // Hook up touch / key listeners for the buttons in the onscreen
- // keypad.
- setupKeypad(mDialerView);
- }
-
- /**
- * Null out our reference to the InCallScreen activity.
- * This indicates that the InCallScreen activity has been destroyed.
- * At the same time, get rid of listeners since we're not going to
- * be valid anymore.
- */
- /* package */ void clearInCallScreenReference() {
- if (DBG) log("clearInCallScreenReference()...");
- mInCallScreen = null;
- mDialerKeyListener = null;
- mHandler.removeMessages(DTMF_SEND_CNF);
- synchronized (mDTMFQueue) {
- mDTMFBurstCnfPending = false;
- mDTMFQueue.clear();
- }
- closeDialer(false);
- }
-
- /**
- * Dialer code that runs when the dialer is brought up.
- * This includes layout changes, etc, and just prepares the dialer model for use.
- */
- private void onDialerOpen(boolean animate) {
- if (DBG) log("onDialerOpen()...");
-
- // Any time the dialer is open, listen for "disconnect" events (so
- // we can close ourself.)
- mCM.registerForDisconnect(mHandler, PHONE_DISCONNECT, null);
-
- // On some devices the screen timeout is set to a special value
- // while the dialpad is up.
- PhoneGlobals.getInstance().updateWakeState();
-
- // Give the InCallScreen a chance to do any necessary UI updates.
- if (mInCallScreen != null) {
- mInCallScreen.onDialerOpen(animate);
- } else {
- Log.e(LOG_TAG, "InCallScreen object was null during onDialerOpen()");
- }
- }
-
- /**
- * Allocates some resources we keep around during a "dialer session".
- *
- * (Currently, a "dialer session" just means any situation where we
- * might need to play local DTMF tones, which means that we need to
- * keep a ToneGenerator instance around. A ToneGenerator instance
- * keeps an AudioTrack resource busy in AudioFlinger, so we don't want
- * to keep it around forever.)
- *
- * Call {@link stopDialerSession} to release the dialer session
- * resources.
- */
- public void startDialerSession() {
- if (DBG) log("startDialerSession()... this = " + this);
-
- // see if we need to play local tones.
- if (PhoneGlobals.getInstance().getResources().getBoolean(R.bool.allow_local_dtmf_tones)) {
- mLocalToneEnabled = Settings.System.getInt(mInCallScreen.getContentResolver(),
- Settings.System.DTMF_TONE_WHEN_DIALING, 1) == 1;
- } else {
- mLocalToneEnabled = false;
- }
- if (DBG) log("- startDialerSession: mLocalToneEnabled = " + mLocalToneEnabled);
-
- // create the tone generator
- // if the mToneGenerator creation fails, just continue without it. It is
- // a local audio signal, and is not as important as the dtmf tone itself.
- if (mLocalToneEnabled) {
- synchronized (mToneGeneratorLock) {
- if (mToneGenerator == null) {
- try {
- mToneGenerator = new ToneGenerator(AudioManager.STREAM_DTMF, 80);
- } catch (RuntimeException e) {
- if (DBG) log("Exception caught while creating local tone generator: " + e);
- mToneGenerator = null;
- }
- }
- }
- }
- }
-
- /**
- * Dialer code that runs when the dialer is closed.
- * This releases resources acquired when we start the dialer.
- */
- private void onDialerClose(boolean animate) {
- if (DBG) log("onDialerClose()...");
-
- // reset back to a short delay for the poke lock.
- PhoneGlobals app = PhoneGlobals.getInstance();
- app.updateWakeState();
-
- mCM.unregisterForDisconnect(mHandler);
-
- // Give the InCallScreen a chance to do any necessary UI updates.
- if (mInCallScreen != null) {
- mInCallScreen.onDialerClose(animate);
- } else {
- Log.e(LOG_TAG, "InCallScreen object was null during onDialerClose()");
- }
- }
-
- /**
- * Releases resources we keep around during a "dialer session"
- * (see {@link startDialerSession}).
- *
- * It's safe to call this even without a corresponding
- * startDialerSession call.
- */
- public void stopDialerSession() {
- // release the tone generator.
- synchronized (mToneGeneratorLock) {
- if (mToneGenerator != null) {
- mToneGenerator.release();
- mToneGenerator = null;
- }
- }
- }
-
- /**
- * Called externally (from InCallScreen) to play a DTMF Tone.
- */
- public boolean onDialerKeyDown(KeyEvent event) {
- if (DBG) log("Notifying dtmf key down.");
- if (mDialerKeyListener != null) {
- return mDialerKeyListener.onKeyDown(event);
- } else {
- return false;
- }
- }
-
- /**
- * Called externally (from InCallScreen) to cancel the last DTMF Tone played.
- */
- public boolean onDialerKeyUp(KeyEvent event) {
- if (DBG) log("Notifying dtmf key up.");
- if (mDialerKeyListener != null) {
- return mDialerKeyListener.onKeyUp(event);
- } else {
- return false;
- }
- }
-
- /**
- * setup the keys on the dialer activity, using the keymaps.
- */
- private void setupKeypad(DTMFTwelveKeyDialerView dialerView) {
- // for each view id listed in the displaymap
- View button;
- for (int viewId : mDisplayMap.keySet()) {
- // locate the view
- button = dialerView.findViewById(viewId);
- // Setup the listeners for the buttons
- button.setOnTouchListener(this);
- button.setClickable(true);
- button.setOnKeyListener(this);
- button.setOnHoverListener(this);
- button.setOnClickListener(this);
- }
- }
-
- /**
- * catch the back and call buttons to return to the in call activity.
- */
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- // if (DBG) log("onKeyDown: keyCode " + keyCode);
- switch (keyCode) {
- // finish for these events
- case KeyEvent.KEYCODE_BACK:
- case KeyEvent.KEYCODE_CALL:
- if (DBG) log("exit requested");
- closeDialer(true); // do the "closing" animation
- return true;
- }
- return mInCallScreen.onKeyDown(keyCode, event);
- }
-
- /**
- * catch the back and call buttons to return to the in call activity.
- */
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- // if (DBG) log("onKeyUp: keyCode " + keyCode);
- return mInCallScreen.onKeyUp(keyCode, event);
- }
-
- /**
- * Implemented for {@link android.view.View.OnHoverListener}. Handles touch
- * events for accessibility when touch exploration is enabled.
- */
- @Override
- public boolean onHover(View v, MotionEvent event) {
- // When touch exploration is turned on, lifting a finger while inside
- // the button's hover target bounds should perform a click action.
- if (mAccessibilityManager.isEnabled()
- && mAccessibilityManager.isTouchExplorationEnabled()) {
- final int left = v.getPaddingLeft();
- final int right = (v.getWidth() - v.getPaddingRight());
- final int top = v.getPaddingTop();
- final int bottom = (v.getHeight() - v.getPaddingBottom());
-
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_HOVER_ENTER:
- // Lift-to-type temporarily disables double-tap activation.
- v.setClickable(false);
- break;
- case MotionEvent.ACTION_HOVER_EXIT:
- final int x = (int) event.getX();
- final int y = (int) event.getY();
- if ((x > left) && (x < right) && (y > top) && (y < bottom)) {
- v.performClick();
- }
- v.setClickable(true);
- break;
- }
- }
-
- return false;
- }
-
- @Override
- public void onClick(View v) {
- // When accessibility is on, simulate press and release to preserve the
- // semantic meaning of performClick(). Required for Braille support.
- if (mAccessibilityManager.isEnabled()) {
- final int id = v.getId();
- // Checking the press state prevents double activation.
- if (!v.isPressed() && mDisplayMap.containsKey(id)) {
- processDtmf(mDisplayMap.get(id), true /* timedShortTone */);
- }
- }
- }
-
- /**
- * Implemented for the TouchListener, process the touch events.
- */
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- int viewId = v.getId();
-
- // if the button is recognized
- if (mDisplayMap.containsKey(viewId)) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- // Append the character mapped to this button, to the display.
- // start the tone
- processDtmf(mDisplayMap.get(viewId));
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- // stop the tone on ANY other event, except for MOVE.
- stopTone();
- break;
- }
- // do not return true [handled] here, since we want the
- // press / click animation to be handled by the framework.
- }
- return false;
- }
-
- /**
- * Implements View.OnKeyListener for the DTMF buttons. Enables dialing with trackball/dpad.
- */
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- // if (DBG) log("onKey: keyCode " + keyCode + ", view " + v);
-
- if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
- int viewId = v.getId();
- if (mDisplayMap.containsKey(viewId)) {
- switch (event.getAction()) {
- case KeyEvent.ACTION_DOWN:
- if (event.getRepeatCount() == 0) {
- processDtmf(mDisplayMap.get(viewId));
- }
- break;
- case KeyEvent.ACTION_UP:
- stopTone();
- break;
- }
- // do not return true [handled] here, since we want the
- // press / click animation to be handled by the framework.
- }
- }
- return false;
- }
-
- /**
- * Returns true if the dialer is in "open" state, meaning it is already visible *and* it
- * isn't fading out. Note that during fade-out animation the View will return VISIBLE but
- * will become GONE soon later, so you would want to use this method instead of
- * {@link View#getVisibility()}.
- *
- * Fade-in animation, on the other hand, will set the View's visibility VISIBLE soon after
- * the request, so we don't need to take care much of it. In other words,
- * {@link #openDialer(boolean)} soon makes the visibility VISIBLE and thus this method will
- * return true just after the method call.
- *
- * Note: during the very early stage of "open" state, users may not see the dialpad yet because
- * of its fading-in animation, while they will see it shortly anyway. Similarly, during the
- * early stage of "closed" state (opposite of "open" state), users may still see the dialpad
- * due to fading-out animation, but it will vanish shortly and thus we can treat it as "closed",
- * or "not open". To make the transition clearer, we call the state "open", not "shown" nor
- * "visible".
- */
- public boolean isOpened() {
- // Return whether or not the dialer view is visible.
- // (Note that if we're in the middle of a fade-out animation, that
- // also counts as "not visible" even though mDialerView itself is
- // technically still VISIBLE.)
- return (mDialerView != null
- &&(mDialerView.getVisibility() == View.VISIBLE)
- && !AnimationUtils.Fade.isFadingOut(mDialerView));
- }
-
- /**
- * Forces the dialer into the "open" state.
- * Does nothing if the dialer is already open.
- *
- * The "open" state includes the state the dialer is fading in.
- * {@link InCallScreen#onDialerOpen(boolean)} will change visibility state and do
- * actual animation.
- *
- * @param animate if true, open the dialer with an animation.
- *
- * @see #isOpened
- */
- public void openDialer(boolean animate) {
- if (DBG) log("openDialer()...");
-
- if (mDialerView == null && mDialerStub != null) {
- if (DBG) log("Dialer isn't ready. Inflate it from ViewStub.");
- mDialerView = (DTMFTwelveKeyDialerView) mDialerStub.inflate();
- setupDialerView();
- mDialerStub = null;
- }
-
- if (!isOpened()) {
- // Make the dialer view visible.
- if (animate) {
- AnimationUtils.Fade.show(mDialerView);
- } else {
- mDialerView.setVisibility(View.VISIBLE);
- }
- onDialerOpen(animate);
- }
- }
-
- /**
- * Forces the dialer into the "closed" state.
- * Does nothing if the dialer is already closed.
- *
- * {@link InCallScreen#onDialerOpen(boolean)} will change visibility state and do
- * actual animation.
- *
- * @param animate if true, close the dialer with an animation.
- *
- * @see #isOpened
- */
- public void closeDialer(boolean animate) {
- if (DBG) log("closeDialer()...");
-
- if (isOpened()) {
- // Hide the dialer view.
- if (animate) {
- AnimationUtils.Fade.hide(mDialerView, View.GONE);
- } else {
- mDialerView.setVisibility(View.GONE);
- }
- onDialerClose(animate);
- }
- }
-
- /**
- * Processes the specified digit as a DTMF key, by playing the
- * appropriate DTMF tone, and appending the digit to the EditText
- * field that displays the DTMF digits sent so far.
- *
- * @see #processDtmf(char, boolean)
- */
- private final void processDtmf(char c) {
- processDtmf(c, false);
- }
-
- /**
- * Processes the specified digit as a DTMF key, by playing the appropriate
- * DTMF tone (or short tone if requested), and appending the digit to the
- * EditText field that displays the DTMF digits sent so far.
- */
- private final void processDtmf(char c, boolean timedShortTone) {
- // if it is a valid key, then update the display and send the dtmf tone.
- if (PhoneNumberUtils.is12Key(c)) {
- if (DBG) log("updating display and sending dtmf tone for '" + c + "'");
-
- // Append this key to the "digits" widget.
- if (mDialpadDigits != null) {
- // TODO: maybe *don't* manually append this digit if
- // mDialpadDigits is focused and this key came from the HW
- // keyboard, since in that case the EditText field will
- // get the key event directly and automatically appends
- // whetever the user types.
- // (Or, a cleaner fix would be to just make mDialpadDigits
- // *not* handle HW key presses. That seems to be more
- // complicated than just setting focusable="false" on it,
- // though.)
- mDialpadDigits.getText().append(c);
- }
-
- // Play the tone if it exists.
- if (mToneMap.containsKey(c)) {
- // begin tone playback.
- startTone(c, timedShortTone);
- }
- } else if (DBG) {
- log("ignoring dtmf request for '" + c + "'");
- }
-
- // Any DTMF keypress counts as explicit "user activity".
- PhoneGlobals.getInstance().pokeUserActivity();
- }
-
- /**
- * Clears out the display of "DTMF digits typed so far" that's kept in
- * mDialpadDigits.
- *
- * The InCallScreen is responsible for calling this method any time a
- * new call becomes active (or, more simply, any time a call ends).
- * This is how we make sure that the "history" of DTMF digits you type
- * doesn't persist from one call to the next.
- *
- * TODO: it might be more elegent if the dialpad itself could remember
- * the call that we're associated with, and clear the digits if the
- * "current call" has changed since last time. (This would require
- * some unique identifier that's different for each call. We can't
- * just use the foreground Call object, since that's a singleton that
- * lasts the whole life of the phone process. Instead, maybe look at
- * the Connection object that comes back from getEarliestConnection()?
- * Or getEarliestConnectTime()?)
- *
- * Or to be even fancier, we could keep a mapping of *multiple*
- * "active calls" to DTMF strings. That way you could have two lines
- * in use and swap calls multiple times, and we'd still remember the
- * digits for each call. (But that's such an obscure use case that
- * it's probably not worth the extra complexity.)
- */
- public void clearDigits() {
- if (DBG) log("clearDigits()...");
-
- if (mDialpadDigits != null) {
- mDialpadDigits.setText("");
- }
-
- setDialpadContext("");
- }
-
- /**
- * Set the context text (hint) to show in the dialpad Digits EditText.
- *
- * This is currently only used for displaying a value for "Voice Mail"
- * calls since they default to the dialpad and we want to give users better
- * context when they dial voicemail.
- *
- * TODO: Is there value in extending this functionality for all contacts
- * and not just Voice Mail calls?
- * TODO: This should include setting the digits as well as the context
- * once we start saving the digits properly...and properly in this case
- * ideally means moving some of processDtmf() out of this class.
- */
- public void setDialpadContext(String contextValue) {
- if (mDialpadDigits != null) {
- if (contextValue == null) {
- contextValue = "";
- }
- final SpannableString hint = new SpannableString(contextValue);
- hint.setSpan(new RelativeSizeSpan(0.8f), 0, hint.length(), 0);
- mDialpadDigits.setHint(hint);
- }
- }
-
- /**
- * Plays the local tone based the phone type.
- */
- public void startTone(char c, boolean timedShortTone) {
- // Only play the tone if it exists.
- if (!mToneMap.containsKey(c)) {
- return;
- }
-
- if (!mInCallScreen.okToDialDTMFTones()) {
- return;
- }
-
- // Read the settings as it may be changed by the user during the call
- Phone phone = mCM.getFgPhone();
- mShortTone = useShortDtmfTones(phone, phone.getContext());
-
- // Before we go ahead and start a tone, we need to make sure that any pending
- // stop-tone message is processed.
- if (mHandler.hasMessages(DTMF_STOP)) {
- mHandler.removeMessages(DTMF_STOP);
- stopTone();
- }
-
- if (DBG) log("startDtmfTone()...");
-
- // For Short DTMF we need to play the local tone for fixed duration
- if (mShortTone) {
- sendShortDtmfToNetwork(c);
- } else {
- // Pass as a char to be sent to network
- if (DBG) log("send long dtmf for " + c);
- mCM.startDtmf(c);
-
- // If it is a timed tone, queue up the stop command in DTMF_DURATION_MS.
- if (timedShortTone) {
- mHandler.sendMessageDelayed(mHandler.obtainMessage(DTMF_STOP), DTMF_DURATION_MS);
- }
- }
- startLocalToneIfNeeded(c);
- }
-
-
- /**
- * Plays the local tone based the phone type, optionally forcing a short
- * tone.
- */
- public void startLocalToneIfNeeded(char c) {
- // if local tone playback is enabled, start it.
- // Only play the tone if it exists.
- if (!mToneMap.containsKey(c)) {
- return;
- }
- if (mLocalToneEnabled) {
- synchronized (mToneGeneratorLock) {
- if (mToneGenerator == null) {
- if (DBG) log("startDtmfTone: mToneGenerator == null, tone: " + c);
- } else {
- if (DBG) log("starting local tone " + c);
- int toneDuration = -1;
- if (mShortTone) {
- toneDuration = DTMF_DURATION_MS;
- }
- mToneGenerator.startTone(mToneMap.get(c), toneDuration);
- }
- }
- }
- }
-
- /**
- * Check to see if the keyEvent is dialable.
- */
- boolean isKeyEventAcceptable (KeyEvent event) {
- return (mDialerKeyListener != null && mDialerKeyListener.isKeyEventAcceptable(event));
- }
-
- /**
- * static logging method
- */
- private static void log(String msg) {
- Log.d(LOG_TAG, msg);
- }
-
- /**
- * Stops the local tone based on the phone type.
- */
- public void stopTone() {
- // We do not rely on InCallScreen#okToDialDTMFTones() here since it is ok to stop tones
- // without starting them.
-
- if (!mShortTone) {
- if (DBG) log("stopping remote tone.");
- mCM.stopDtmf();
- stopLocalToneIfNeeded();
- }
- }
-
- /**
- * Stops the local tone based on the phone type.
- */
- public void stopLocalToneIfNeeded() {
- if (!mShortTone) {
- // if local tone playback is enabled, stop it.
- if (DBG) log("trying to stop local tone...");
- if (mLocalToneEnabled) {
- synchronized (mToneGeneratorLock) {
- if (mToneGenerator == null) {
- if (DBG) log("stopLocalTone: mToneGenerator == null");
- } else {
- if (DBG) log("stopping local tone.");
- mToneGenerator.stopTone();
- }
- }
- }
- }
- }
-
- /**
- * Sends the dtmf character over the network for short DTMF settings
- * When the characters are entered in quick succession,
- * the characters are queued before sending over the network.
- */
- private void sendShortDtmfToNetwork(char dtmfDigit) {
- synchronized (mDTMFQueue) {
- if (mDTMFBurstCnfPending == true) {
- // Insert the dtmf char to the queue
- mDTMFQueue.add(new Character(dtmfDigit));
- } else {
- String dtmfStr = Character.toString(dtmfDigit);
- mCM.sendBurstDtmf(dtmfStr, 0, 0, mHandler.obtainMessage(DTMF_SEND_CNF));
- // Set flag to indicate wait for Telephony confirmation.
- mDTMFBurstCnfPending = true;
- }
- }
- }
-
- /**
- * Handles Burst Dtmf Confirmation from the Framework.
- */
- void handleBurstDtmfConfirmation() {
- Character dtmfChar = null;
- synchronized (mDTMFQueue) {
- mDTMFBurstCnfPending = false;
- if (!mDTMFQueue.isEmpty()) {
- dtmfChar = mDTMFQueue.remove();
- Log.i(LOG_TAG, "The dtmf character removed from queue" + dtmfChar);
- }
- }
- if (dtmfChar != null) {
- sendShortDtmfToNetwork(dtmfChar);
- }
- }
-
- /**
- * On GSM devices, we never use short tones.
- * On CDMA devices, it depends upon the settings.
- */
- private static boolean useShortDtmfTones(Phone phone, Context context) {
- int phoneType = phone.getPhoneType();
- if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
- return false;
- } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
- int toneType = android.provider.Settings.System.getInt(
- context.getContentResolver(),
- Settings.System.DTMF_TONE_TYPE_WHEN_DIALING,
- Constants.DTMF_TONE_TYPE_NORMAL);
- if (toneType == Constants.DTMF_TONE_TYPE_NORMAL) {
- return true;
- } else {
- return false;
- }
- } else if (phoneType == PhoneConstants.PHONE_TYPE_SIP) {
- return false;
- } else {
- throw new IllegalStateException("Unexpected phone type: " + phoneType);
- }
- }
-
-}
diff --git a/src/com/android/phone/DTMFTwelveKeyDialerView.java b/src/com/android/phone/DTMFTwelveKeyDialerView.java
deleted file mode 100644
index e0502b7..0000000
--- a/src/com/android/phone/DTMFTwelveKeyDialerView.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2008 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.phone;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.FocusFinder;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-
-import java.util.ArrayList;
-
-/**
- * DTMFTwelveKeyDialerView is the view logic that the DTMFDialer uses.
- * This is really a thin wrapper around Linear Layout that intercepts
- * some user interactions to provide the correct UI behaviour for the
- * dialer.
- *
- * See dtmf_twelve_key_dialer_view.xml.
- */
-class DTMFTwelveKeyDialerView extends LinearLayout {
-
- private static final String LOG_TAG = "PHONE/DTMFTwelveKeyDialerView";
- private static final boolean DBG = false;
-
- private DTMFTwelveKeyDialer mDialer;
-
-
- public DTMFTwelveKeyDialerView (Context context) {
- super(context);
- }
-
- public DTMFTwelveKeyDialerView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- void setDialer (DTMFTwelveKeyDialer dialer) {
- mDialer = dialer;
- }
-
- /**
- * Normally we ignore everything except for the BACK and CALL keys.
- * For those, we pass them to the model (and then the InCallScreen).
- */
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- if (DBG) log("dispatchKeyEvent(" + event + ")...");
-
- int keyCode = event.getKeyCode();
- if (mDialer != null) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_BACK:
- case KeyEvent.KEYCODE_CALL:
- return event.isDown() ? mDialer.onKeyDown(keyCode, event) :
- mDialer.onKeyUp(keyCode, event);
- }
- }
-
- if (DBG) log("==> dispatchKeyEvent: forwarding event to the DTMFDialer");
- return super.dispatchKeyEvent(event);
- }
-
- private void log(String msg) {
- Log.d(LOG_TAG, msg);
- }
-}
diff --git a/src/com/android/phone/EmergencyCallHelper.java b/src/com/android/phone/EmergencyCallHelper.java
index a23e3e0..866f2be 100644
--- a/src/com/android/phone/EmergencyCallHelper.java
+++ b/src/com/android/phone/EmergencyCallHelper.java
@@ -21,7 +21,6 @@
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import com.android.phone.Constants.CallStatusCode;
-import com.android.phone.InCallUiState.ProgressIndicationType;
import android.content.Context;
import android.content.Intent;
@@ -183,10 +182,6 @@
// for some reason.
startRetryTimer();
- // And finally, let the in-call UI know that we need to
- // display the "Turning on radio..." progress indication.
- mApp.inCallUiState.setProgressIndication(ProgressIndicationType.TURNING_ON_RADIO);
-
// (Our caller is responsible for calling mApp.displayCallScreen().)
}
@@ -220,14 +215,7 @@
// Deregister for the service state change events.
unregisterForServiceStateChanged();
- // Take down the "Turning on radio..." indication.
- mApp.inCallUiState.clearProgressIndication();
-
placeEmergencyCall();
-
- // The in-call UI is probably still up at this point,
- // but make sure of that:
- mApp.displayCallScreen();
} else {
// The service state changed, but we're still not ready to call yet.
// (This probably was the transition from STATE_POWER_OFF to
@@ -307,9 +295,6 @@
// these any more now that the radio is powered-on.
unregisterForServiceStateChanged();
- // Take down the "Turning on radio..." indication.
- mApp.inCallUiState.clearProgressIndication();
-
placeEmergencyCall(); // If the call fails, placeEmergencyCall()
// will schedule a retry.
} else {
@@ -324,10 +309,6 @@
// totally if we've had too many failures.)
scheduleRetryOrBailOut();
}
-
- // Finally, the in-call UI is probably still up at this point,
- // but make sure of that:
- mApp.displayCallScreen();
}
/**
@@ -441,13 +422,9 @@
if (mNumRetriesSoFar > MAX_NUM_RETRIES) {
Log.w(TAG, "scheduleRetryOrBailOut: hit MAX_NUM_RETRIES; giving up...");
cleanup();
- // ...and have the InCallScreen display a generic failure
- // message.
- mApp.inCallUiState.setPendingCallStatusCode(CallStatusCode.CALL_FAILED);
} else {
if (DBG) log("- Scheduling another retry...");
startRetryTimer();
- mApp.inCallUiState.setProgressIndication(ProgressIndicationType.RETRYING);
}
}
@@ -475,9 +452,6 @@
private void cleanup() {
if (DBG) log("cleanup()...");
- // Take down the "Turning on radio..." indication.
- mApp.inCallUiState.clearProgressIndication();
-
unregisterForServiceStateChanged();
unregisterForDisconnect();
cancelRetryTimer();
@@ -490,10 +464,6 @@
}
mPartialWakeLock = null;
}
-
- // And finally, ask the in-call UI to refresh itself (to clean up the
- // progress indication if necessary), if it's currently visible.
- mApp.updateInCallScreen();
}
private void startRetryTimer() {
diff --git a/src/com/android/phone/InCallControlState.java b/src/com/android/phone/InCallControlState.java
deleted file mode 100644
index 38eaf5c..0000000
--- a/src/com/android/phone/InCallControlState.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright (C) 2009 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.phone;
-
-import android.telephony.PhoneNumberUtils;
-import android.util.Log;
-
-import com.android.internal.telephony.Call;
-import com.android.internal.telephony.Connection;
-import com.android.internal.telephony.CallManager;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.TelephonyCapabilities;
-
-/**
- * Helper class to keep track of enabledness, visibility, and "on/off"
- * or "checked" state of the various controls available in the in-call
- * UI, based on the current telephony state.
- *
- * This class is independent of the exact UI controls used on any given
- * device. To avoid cluttering up the "view" code (i.e. InCallTouchUi)
- * with logic about which functions are available right now, we instead
- * have that logic here, and provide simple boolean flags to indicate the
- * state and/or enabledness of all possible in-call user operations.
- *
- * (In other words, this is the "model" that corresponds to the "view"
- * implemented by InCallTouchUi.)
- */
-public class InCallControlState {
- private static final String LOG_TAG = "InCallControlState";
- private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
-
- private final BluetoothManager mBluetoothManager;
- private InCallScreen mInCallScreen;
- private CallManager mCM;
-
- //
- // Our "public API": Boolean flags to indicate the state and/or
- // enabledness of all possible in-call user operations:
- //
-
- public boolean manageConferenceVisible;
- public boolean manageConferenceEnabled;
- //
- public boolean canAddCall;
- //
- public boolean canEndCall;
- //
- public boolean canSwap;
- public boolean canMerge;
- //
- public boolean bluetoothEnabled;
- public boolean bluetoothIndicatorOn;
- //
- public boolean speakerEnabled;
- public boolean speakerOn;
- //
- public boolean canMute;
- public boolean muteIndicatorOn;
- //
- public boolean dialpadEnabled;
- public boolean dialpadVisible;
- //
- /** True if the "Hold" function is *ever* available on this device */
- public boolean supportsHold;
- /** True if the call is currently on hold */
- public boolean onHold;
- /** True if the "Hold" or "Unhold" function should be available right now */
- // TODO: this name is misleading. Let's break this apart into
- // separate canHold and canUnhold flags, and have the caller look at
- // "canHold || canUnhold" to decide whether the hold/unhold UI element
- // should be visible.
- public boolean canHold;
-
-
- public InCallControlState(InCallScreen inCallScreen, CallManager cm,
- BluetoothManager bluetoothManager) {
- if (DBG) log("InCallControlState constructor...");
- mInCallScreen = inCallScreen;
- mCM = cm;
- mBluetoothManager = bluetoothManager;
- }
-
- /**
- * Updates all our public boolean flags based on the current state of
- * the Phone.
- */
- public void update() {
- final PhoneConstants.State state = mCM.getState(); // coarse-grained voice call state
- final Call fgCall = mCM.getActiveFgCall();
- final Call.State fgCallState = fgCall.getState();
- final boolean hasActiveForegroundCall = (fgCallState == Call.State.ACTIVE);
- final boolean hasHoldingCall = mCM.hasActiveBgCall();
-
- // Manage conference:
- if (TelephonyCapabilities.supportsConferenceCallManagement(fgCall.getPhone())) {
- // This item is visible only if the foreground call is a
- // conference call, and it's enabled unless the "Manage
- // conference" UI is already up.
- manageConferenceVisible = PhoneUtils.isConferenceCall(fgCall);
- manageConferenceEnabled =
- manageConferenceVisible && !mInCallScreen.isManageConferenceMode();
- } else {
- // This device has no concept of managing a conference call.
- manageConferenceVisible = false;
- manageConferenceEnabled = false;
- }
-
- // "Add call":
- canAddCall = PhoneUtils.okToAddCall(mCM);
-
- // "End call": always enabled unless the phone is totally idle.
- // Note that while the phone is ringing, the InCallTouchUi widget isn't
- // visible at all, so the state of the End button doesn't matter. However
- // we *do* still set canEndCall to true in this case, purely to prevent a
- // UI glitch when the InCallTouchUi widget first appears, immediately after
- // answering an incoming call.
- canEndCall = (mCM.hasActiveFgCall() || mCM.hasActiveRingingCall() || mCM.hasActiveBgCall());
-
- // Swap / merge calls
- canSwap = PhoneUtils.okToSwapCalls(mCM);
- canMerge = PhoneUtils.okToMergeCalls(mCM);
-
- // "Bluetooth":
- if (mBluetoothManager.isBluetoothAvailable()) {
- bluetoothEnabled = true;
- bluetoothIndicatorOn = mBluetoothManager.isBluetoothAudioConnectedOrPending();
- } else {
- bluetoothEnabled = false;
- bluetoothIndicatorOn = false;
- }
-
- // "Speaker": always enabled unless the phone is totally idle.
- // The current speaker state comes from the AudioManager.
- speakerEnabled = (state != PhoneConstants.State.IDLE);
- speakerOn = PhoneUtils.isSpeakerOn(mInCallScreen);
-
- // "Mute": only enabled when the foreground call is ACTIVE.
- // (It's meaningless while on hold, or while DIALING/ALERTING.)
- // It's also explicitly disabled during emergency calls or if
- // emergency callback mode (ECM) is active.
- Connection c = fgCall.getLatestConnection();
- boolean isEmergencyCall = false;
- if (c != null) isEmergencyCall =
- PhoneNumberUtils.isLocalEmergencyNumber(c.getAddress(),
- fgCall.getPhone().getContext());
- boolean isECM = PhoneUtils.isPhoneInEcm(fgCall.getPhone());
- if (isEmergencyCall || isECM) { // disable "Mute" item
- canMute = false;
- muteIndicatorOn = false;
- } else {
- canMute = hasActiveForegroundCall;
- muteIndicatorOn = PhoneUtils.getMute();
- }
-
- // "Dialpad": Enabled only when it's OK to use the dialpad in the
- // first place.
- dialpadEnabled = mInCallScreen.okToShowDialpad();
-
- // Also keep track of whether the dialpad is currently "opened"
- // (i.e. visible).
- dialpadVisible = mInCallScreen.isDialerOpened();
-
- // "Hold:
- if (TelephonyCapabilities.supportsHoldAndUnhold(fgCall.getPhone())) {
- // This phone has the concept of explicit "Hold" and "Unhold" actions.
- supportsHold = true;
- // "On hold" means that there's a holding call and
- // *no* foreground call. (If there *is* a foreground call,
- // that's "two lines in use".)
- onHold = hasHoldingCall && (fgCallState == Call.State.IDLE);
- // The "Hold" control is disabled entirely if there's
- // no way to either hold or unhold in the current state.
- boolean okToHold = hasActiveForegroundCall && !hasHoldingCall;
- boolean okToUnhold = onHold;
- canHold = okToHold || okToUnhold;
- } else if (hasHoldingCall && (fgCallState == Call.State.IDLE)) {
- // Even when foreground phone device doesn't support hold/unhold, phone devices
- // for background holding calls may do.
- //
- // If the foreground call is ACTIVE, we should turn on "swap" button instead.
- final Call bgCall = mCM.getFirstActiveBgCall();
- if (bgCall != null &&
- TelephonyCapabilities.supportsHoldAndUnhold(bgCall.getPhone())) {
- supportsHold = true;
- onHold = true;
- canHold = true;
- }
- } else {
- // This device has no concept of "putting a call on hold."
- supportsHold = false;
- onHold = false;
- canHold = false;
- }
-
- if (DBG) dumpState();
- }
-
- public void dumpState() {
- log("InCallControlState:");
- log(" manageConferenceVisible: " + manageConferenceVisible);
- log(" manageConferenceEnabled: " + manageConferenceEnabled);
- log(" canAddCall: " + canAddCall);
- log(" canEndCall: " + canEndCall);
- log(" canSwap: " + canSwap);
- log(" canMerge: " + canMerge);
- log(" bluetoothEnabled: " + bluetoothEnabled);
- log(" bluetoothIndicatorOn: " + bluetoothIndicatorOn);
- log(" speakerEnabled: " + speakerEnabled);
- log(" speakerOn: " + speakerOn);
- log(" canMute: " + canMute);
- log(" muteIndicatorOn: " + muteIndicatorOn);
- log(" dialpadEnabled: " + dialpadEnabled);
- log(" dialpadVisible: " + dialpadVisible);
- log(" onHold: " + onHold);
- log(" canHold: " + canHold);
- }
-
- private void log(String msg) {
- Log.d(LOG_TAG, msg);
- }
-}
diff --git a/src/com/android/phone/InCallScreen.java b/src/com/android/phone/InCallScreen.java
index 53cd40d..b18cc43 100644
--- a/src/com/android/phone/InCallScreen.java
+++ b/src/com/android/phone/InCallScreen.java
@@ -16,101 +16,7 @@
package com.android.phone;
-import android.app.Activity;
-import android.app.ActivityOptions;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.ProgressDialog;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHeadset;
-import android.bluetooth.BluetoothProfile;
-import android.bluetooth.IBluetoothHeadsetPhone;
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnCancelListener;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Typeface;
-import android.media.AudioManager;
-import android.os.AsyncResult;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.telephony.ServiceState;
-import android.text.TextUtils;
-import android.text.method.DialerKeyListener;
-import android.util.EventLog;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewStub;
-import android.view.Window;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.EditText;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.android.internal.telephony.Call;
-import com.android.internal.telephony.CallManager;
-import com.android.internal.telephony.Connection;
-import com.android.internal.telephony.MmiCode;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.TelephonyCapabilities;
-import com.android.phone.Constants.CallStatusCode;
-import com.android.phone.InCallUiState.InCallScreenMode;
-import com.android.phone.OtaUtils.CdmaOtaScreenState;
-
-import java.util.List;
-
-
-/**
- * Phone app "in call" screen.
- */
-public class InCallScreen extends Activity
- implements View.OnClickListener {
- private static final String LOG_TAG = "InCallScreen";
-
- private static final boolean DBG =
- (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
- private static final boolean VDBG = (PhoneGlobals.DBG_LEVEL >= 2);
-
- /**
- * Intent extra used to specify whether the DTMF dialpad should be
- * initially visible when bringing up the InCallScreen. (If this
- * extra is present, the dialpad will be initially shown if the extra
- * has the boolean value true, and initially hidden otherwise.)
- */
- // TODO: Should be EXTRA_SHOW_DIALPAD for consistency.
- static final String SHOW_DIALPAD_EXTRA = "com.android.phone.ShowDialpad";
-
- // Amount of time (in msec) that we display the "Call ended" state.
- // The "short" value is for calls ended by the local user, and the
- // "long" value is for calls ended by the remote caller.
- private static final int CALL_ENDED_SHORT_DELAY = 200; // msec
- private static final int CALL_ENDED_LONG_DELAY = 2000; // msec
- private static final int CALL_ENDED_EXTRA_LONG_DELAY = 5000; // msec
-
- // Amount of time that we display the PAUSE alert Dialog showing the
- // post dial string yet to be send out to the n/w
- private static final int PAUSE_PROMPT_DIALOG_TIMEOUT = 2000; //msec
-
- // Amount of time that we display the provider info if applicable.
- private static final int PROVIDER_INFO_TIMEOUT = 5000; // msec
-
+public class InCallScreen {
// These are values for the settings of the auto retry mode:
// 0 = disabled
// 1 = enabled
@@ -118,4071 +24,4 @@
// they should be moved to Settings where the value is being looked up in the first place
static final int AUTO_RETRY_OFF = 0;
static final int AUTO_RETRY_ON = 1;
-
- // Message codes; see mHandler below.
- // Note message codes < 100 are reserved for the PhoneApp.
- private static final int PHONE_STATE_CHANGED = 101;
- private static final int PHONE_DISCONNECT = 102;
- private static final int EVENT_HEADSET_PLUG_STATE_CHANGED = 103;
- private static final int POST_ON_DIAL_CHARS = 104;
- private static final int WILD_PROMPT_CHAR_ENTERED = 105;
- private static final int ADD_VOICEMAIL_NUMBER = 106;
- private static final int DONT_ADD_VOICEMAIL_NUMBER = 107;
- private static final int DELAYED_CLEANUP_AFTER_DISCONNECT = 108;
- private static final int SUPP_SERVICE_FAILED = 110;
- private static final int PHONE_CDMA_CALL_WAITING = 115;
- private static final int REQUEST_CLOSE_SPC_ERROR_NOTICE = 118;
- private static final int REQUEST_CLOSE_OTA_FAILURE_NOTICE = 119;
- private static final int EVENT_PAUSE_DIALOG_COMPLETE = 120;
- private static final int EVENT_HIDE_PROVIDER_INFO = 121; // Time to remove the info.
- private static final int REQUEST_UPDATE_SCREEN = 122;
- private static final int PHONE_INCOMING_RING = 123;
- private static final int PHONE_NEW_RINGING_CONNECTION = 124;
-
- // When InCallScreenMode is UNDEFINED set the default action
- // to ACTION_UNDEFINED so if we are resumed the activity will
- // know its undefined. In particular checkIsOtaCall will return
- // false.
- public static final String ACTION_UNDEFINED = "com.android.phone.InCallScreen.UNDEFINED";
-
- /** Status codes returned from syncWithPhoneState(). */
- private enum SyncWithPhoneStateStatus {
- /**
- * Successfully updated our internal state based on the telephony state.
- */
- SUCCESS,
-
- /**
- * There was no phone state to sync with (i.e. the phone was
- * completely idle). In most cases this means that the
- * in-call UI shouldn't be visible in the first place, unless
- * we need to remain in the foreground while displaying an
- * error message.
- */
- PHONE_NOT_IN_USE
- }
-
- private boolean mRegisteredForPhoneStates;
-
- private PhoneGlobals mApp;
- private CallManager mCM;
-
- // TODO: need to clean up all remaining uses of mPhone.
- // (There may be more than one Phone instance on the device, so it's wrong
- // to just keep a single mPhone field. Instead, any time we need a Phone
- // reference we should get it dynamically from the CallManager, probably
- // based on the current foreground Call.)
- private Phone mPhone;
-
- /** Main in-call UI elements. */
- private CallCard mCallCard;
-
- // UI controls:
- private InCallControlState mInCallControlState;
- private InCallTouchUi mInCallTouchUi;
- private RespondViaSmsManager mRespondViaSmsManager; // see internalRespondViaSms()
- private ManageConferenceUtils mManageConferenceUtils;
-
- // DTMF Dialer controller and its view:
- private DTMFTwelveKeyDialer mDialer;
-
- private EditText mWildPromptText;
-
- // Various dialogs we bring up (see dismissAllDialogs()).
- // TODO: convert these all to use the "managed dialogs" framework.
- //
- // The MMI started dialog can actually be one of 2 items:
- // 1. An alert dialog if the MMI code is a normal MMI
- // 2. A progress dialog if the user requested a USSD
- private Dialog mMmiStartedDialog;
- private AlertDialog mMissingVoicemailDialog;
- private AlertDialog mGenericErrorDialog;
- private AlertDialog mSuppServiceFailureDialog;
- private AlertDialog mWaitPromptDialog;
- private AlertDialog mWildPromptDialog;
- private AlertDialog mCallLostDialog;
- private AlertDialog mPausePromptDialog;
- private AlertDialog mExitingECMDialog;
- // NOTE: if you add a new dialog here, be sure to add it to dismissAllDialogs() also.
-
- // ProgressDialog created by showProgressIndication()
- private ProgressDialog mProgressDialog;
-
- // TODO: If the Activity class ever provides an easy way to get the
- // current "activity lifecycle" state, we can remove these flags.
- private boolean mIsDestroyed = false;
- private boolean mIsForegroundActivity = false;
- private PowerManager mPowerManager;
-
- // For use with Pause/Wait dialogs
- private String mPostDialStrAfterPause;
- private boolean mPauseInProgress = false;
-
- // Info about the most-recently-disconnected Connection, which is used
- // to determine what should happen when exiting the InCallScreen after a
- // call. (This info is set by onDisconnect(), and used by
- // delayedCleanupAfterDisconnect().)
- private Connection.DisconnectCause mLastDisconnectCause;
-
- /** In-call audio routing options; see switchInCallAudio(). */
- public enum InCallAudioMode {
- SPEAKER, // Speakerphone
- BLUETOOTH, // Bluetooth headset (if available)
- EARPIECE, // Handset earpiece (or wired headset, if connected)
- }
-
-
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- if (mIsDestroyed) {
- if (DBG) log("Handler: ignoring message " + msg + "; we're destroyed!");
- return;
- }
- if (!mIsForegroundActivity) {
- if (DBG) log("Handler: handling message " + msg + " while not in foreground");
- // Continue anyway; some of the messages below *want* to
- // be handled even if we're not the foreground activity
- // (like DELAYED_CLEANUP_AFTER_DISCONNECT), and they all
- // should at least be safe to handle if we're not in the
- // foreground...
- }
-
- switch (msg.what) {
- case SUPP_SERVICE_FAILED:
- onSuppServiceFailed((AsyncResult) msg.obj);
- break;
-
- case PHONE_STATE_CHANGED:
- onPhoneStateChanged((AsyncResult) msg.obj);
- break;
-
- case PHONE_DISCONNECT:
- onDisconnect((AsyncResult) msg.obj);
- break;
-
- case EVENT_HEADSET_PLUG_STATE_CHANGED:
- // Update the in-call UI, since some UI elements (such
- // as the "Speaker" button) may change state depending on
- // whether a headset is plugged in.
- // TODO: A full updateScreen() is overkill here, since
- // the value of PhoneApp.isHeadsetPlugged() only affects a
- // single onscreen UI element. (But even a full updateScreen()
- // is still pretty cheap, so let's keep this simple
- // for now.)
- updateScreen();
-
- // Also, force the "audio mode" popup to refresh itself if
- // it's visible, since one of its items is either "Wired
- // headset" or "Handset earpiece" depending on whether the
- // headset is plugged in or not.
- mInCallTouchUi.refreshAudioModePopup(); // safe even if the popup's not active
-
- break;
-
- // TODO: sort out MMI code (probably we should remove this method entirely).
- // See also MMI handling code in onResume()
- // case PhoneApp.MMI_INITIATE:
- // onMMIInitiate((AsyncResult) msg.obj);
- // break;
-
- case PhoneGlobals.MMI_CANCEL:
- onMMICancel();
- break;
-
- // handle the mmi complete message.
- // since the message display class has been replaced with
- // a system dialog in PhoneUtils.displayMMIComplete(), we
- // should finish the activity here to close the window.
- case PhoneGlobals.MMI_COMPLETE:
- onMMIComplete((MmiCode) ((AsyncResult) msg.obj).result);
- break;
-
- case POST_ON_DIAL_CHARS:
- handlePostOnDialChars((AsyncResult) msg.obj, (char) msg.arg1);
- break;
-
- case ADD_VOICEMAIL_NUMBER:
- addVoiceMailNumberPanel();
- break;
-
- case DONT_ADD_VOICEMAIL_NUMBER:
- dontAddVoiceMailNumber();
- break;
-
- case DELAYED_CLEANUP_AFTER_DISCONNECT:
- delayedCleanupAfterDisconnect();
- break;
-
- case PHONE_CDMA_CALL_WAITING:
- if (DBG) log("Received PHONE_CDMA_CALL_WAITING event ...");
- Connection cn = mCM.getFirstActiveRingingCall().getLatestConnection();
-
- // Only proceed if we get a valid connection object
- if (cn != null) {
- // Finally update screen with Call waiting info and request
- // screen to wake up
- updateScreen();
- mApp.updateWakeState();
- }
- break;
-
- case REQUEST_CLOSE_SPC_ERROR_NOTICE:
- if (mApp.otaUtils != null) {
- mApp.otaUtils.onOtaCloseSpcNotice();
- }
- break;
-
- case REQUEST_CLOSE_OTA_FAILURE_NOTICE:
- if (mApp.otaUtils != null) {
- mApp.otaUtils.onOtaCloseFailureNotice();
- }
- break;
-
- case EVENT_PAUSE_DIALOG_COMPLETE:
- if (mPausePromptDialog != null) {
- if (DBG) log("- DISMISSING mPausePromptDialog.");
- mPausePromptDialog.dismiss(); // safe even if already dismissed
- mPausePromptDialog = null;
- }
- break;
-
- case EVENT_HIDE_PROVIDER_INFO:
- if (mCallCard != null) {
- mCallCard.updateState(mCM);
- }
- break;
- case REQUEST_UPDATE_SCREEN:
- updateScreen();
- break;
-
- case PHONE_INCOMING_RING:
- onIncomingRing();
- break;
-
- case PHONE_NEW_RINGING_CONNECTION:
- onNewRingingConnection();
- break;
-
- default:
- Log.wtf(LOG_TAG, "mHandler: unexpected message: " + msg);
- break;
- }
- }
- };
-
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
- // Listen for ACTION_HEADSET_PLUG broadcasts so that we
- // can update the onscreen UI when the headset state changes.
- // if (DBG) log("mReceiver: ACTION_HEADSET_PLUG");
- // if (DBG) log("==> intent: " + intent);
- // if (DBG) log(" state: " + intent.getIntExtra("state", 0));
- // if (DBG) log(" name: " + intent.getStringExtra("name"));
- // send the event and add the state as an argument.
- Message message = Message.obtain(mHandler, EVENT_HEADSET_PLUG_STATE_CHANGED,
- intent.getIntExtra("state", 0), 0);
- mHandler.sendMessage(message);
- }
- }
- };
-
-
- @Override
- protected void onCreate(Bundle icicle) {
- Log.i(LOG_TAG, "onCreate()... this = " + this);
- Profiler.callScreenOnCreate();
- super.onCreate(icicle);
-
- // Make sure this is a voice-capable device.
- if (!PhoneGlobals.sVoiceCapable) {
- // There should be no way to ever reach the InCallScreen on a
- // non-voice-capable device, since this activity is not exported by
- // our manifest, and we explicitly disable any other external APIs
- // like the CALL intent and ITelephony.showCallScreen().
- // So the fact that we got here indicates a phone app bug.
- Log.wtf(LOG_TAG, "onCreate() reached on non-voice-capable device");
- finish();
- return;
- }
-
- mApp = PhoneGlobals.getInstance();
- mApp.setInCallScreenInstance(this);
-
- // set this flag so this activity will stay in front of the keyguard
- int flags = WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
- if (mApp.getPhoneState() == PhoneConstants.State.OFFHOOK) {
- // While we are in call, the in-call screen should dismiss the keyguard.
- // This allows the user to press Home to go directly home without going through
- // an insecure lock screen.
- // But we do not want to do this if there is no active call so we do not
- // bypass the keyguard if the call is not answered or declined.
- flags |= WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
- }
-
- WindowManager.LayoutParams lp = getWindow().getAttributes();
- lp.flags |= flags;
-
- setPhone(mApp.phone); // Sets mPhone
-
- mCM = mApp.mCM;
- log("- onCreate: phone state = " + mCM.getState());
-
- requestWindowFeature(Window.FEATURE_NO_TITLE);
-
- // Inflate everything in incall_screen.xml and add it to the screen.
- setContentView(R.layout.incall_screen);
-
- // If in landscape, then one of the ViewStubs (instead of <include>) is used for the
- // incall_touch_ui, because CDMA and GSM button layouts are noticeably different.
- final ViewStub touchUiStub = (ViewStub) findViewById(
- mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA
- ? R.id.inCallTouchUiCdmaStub : R.id.inCallTouchUiStub);
- if (touchUiStub != null) touchUiStub.inflate();
-
- initInCallScreen();
-
- registerForPhoneStates();
-
- // No need to change wake state here; that happens in onResume() when we
- // are actually displayed.
-
- // Handle the Intent we were launched with, but only if this is the
- // the very first time we're being launched (ie. NOT if we're being
- // re-initialized after previously being shut down.)
- // Once we're up and running, any future Intents we need
- // to handle will come in via the onNewIntent() method.
- if (icicle == null) {
- if (DBG) log("onCreate(): this is our very first launch, checking intent...");
- internalResolveIntent(getIntent());
- }
-
- Profiler.callScreenCreated();
- if (DBG) log("onCreate(): exit");
- }
-
- /**
- * Sets the Phone object used internally by the InCallScreen.
- *
- * In normal operation this is called from onCreate(), and the
- * passed-in Phone object comes from the PhoneApp.
- * For testing, test classes can use this method to
- * inject a test Phone instance.
- */
- /* package */ void setPhone(Phone phone) {
- mPhone = phone;
- }
-
- @Override
- protected void onResume() {
- if (DBG) log("onResume()...");
- super.onResume();
-
- mIsForegroundActivity = true;
-
- // The flag shouldn't be turned on when there are actual phone calls.
- if (mCM.hasActiveFgCall() || mCM.hasActiveBgCall() || mCM.hasActiveRingingCall()) {
- mApp.inCallUiState.showAlreadyDisconnectedState = false;
- }
-
- final InCallUiState inCallUiState = mApp.inCallUiState;
- if (VDBG) inCallUiState.dumpState();
-
- updateExpandedViewState();
-
- // Listen for broadcast intents that might affect the onscreen UI.
- registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG));
-
- // Keep a "dialer session" active when we're in the foreground.
- // (This is needed to play DTMF tones.)
- mDialer.startDialerSession();
-
- // Restore various other state from the InCallUiState object:
-
- // Update the onscreen dialpad state to match the InCallUiState.
- if (inCallUiState.showDialpad) {
- openDialpadInternal(false); // no "opening" animation
- } else {
- closeDialpadInternal(false); // no "closing" animation
- }
-
- // Reset the dialpad context
- // TODO: Dialpad digits should be set here as well (once they are saved)
- mDialer.setDialpadContext(inCallUiState.dialpadContextText);
-
- // If there's a "Respond via SMS" popup still around since the
- // last time we were the foreground activity, make sure it's not
- // still active!
- // (The popup should *never* be visible initially when we first
- // come to the foreground; it only ever comes up in response to
- // the user selecting the "SMS" option from the incoming call
- // widget.)
- mRespondViaSmsManager.dismissPopup(); // safe even if already dismissed
-
- // Display an error / diagnostic indication if necessary.
- //
- // When the InCallScreen comes to the foreground, we normally we
- // display the in-call UI in whatever state is appropriate based on
- // the state of the telephony framework (e.g. an outgoing call in
- // DIALING state, an incoming call, etc.)
- //
- // But if the InCallUiState has a "pending call status code" set,
- // that means we need to display some kind of status or error
- // indication to the user instead of the regular in-call UI. (The
- // most common example of this is when there's some kind of
- // failure while initiating an outgoing call; see
- // CallController.placeCall().)
- boolean handledStartupError = false;
- if (inCallUiState.hasPendingCallStatusCode()) {
- if (DBG) log("- onResume: need to show status indication!");
- showStatusIndication(inCallUiState.getPendingCallStatusCode());
-
- // Set handledStartupError to ensure that we won't bail out below.
- // (We need to stay here in the InCallScreen so that the user
- // is able to see the error dialog!)
- handledStartupError = true;
- }
-
- // Set the volume control handler while we are in the foreground.
- final boolean bluetoothConnected = false; //isBluetoothAudioConnected();
-
- // TODO(klp): Move this volume button control code to the UI
- if (bluetoothConnected) {
- setVolumeControlStream(AudioManager.STREAM_BLUETOOTH_SCO);
- } else {
- setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
- }
-
- takeKeyEvents(true);
-
- // If an OTASP call is in progress, use the special OTASP-specific UI.
- boolean inOtaCall = false;
- if (TelephonyCapabilities.supportsOtasp(mPhone)) {
- inOtaCall = checkOtaspStateOnResume();
- }
- if (!inOtaCall) {
- // Always start off in NORMAL mode
- setInCallScreenMode(InCallScreenMode.NORMAL);
- }
-
- // Before checking the state of the CallManager, clean up any
- // connections in the DISCONNECTED state.
- // (The DISCONNECTED state is used only to drive the "call ended"
- // UI; it's totally useless when *entering* the InCallScreen.)
- mCM.clearDisconnected();
-
- // Update the onscreen UI to reflect the current telephony state.
- SyncWithPhoneStateStatus status = syncWithPhoneState();
-
- // Note there's no need to call updateScreen() here;
- // syncWithPhoneState() already did that if necessary.
-
- if (status != SyncWithPhoneStateStatus.SUCCESS) {
- if (DBG) log("- onResume: syncWithPhoneState failed! status = " + status);
- // Couldn't update the UI, presumably because the phone is totally
- // idle.
-
- // Even though the phone is idle, though, we do still need to
- // stay here on the InCallScreen if we're displaying an
- // error dialog (see "showStatusIndication()" above).
-
- if (handledStartupError) {
- // Stay here for now. We'll eventually leave the
- // InCallScreen when the user presses the dialog's OK
- // button (see bailOutAfterErrorDialog()), or when the
- // progress indicator goes away.
- Log.i(LOG_TAG, " ==> syncWithPhoneState failed, but staying here anyway.");
- } else {
- // The phone is idle, and we did NOT handle a
- // startup error during this pass thru onResume.
- //
- // This basically means that we're being resumed because of
- // some action *other* than a new intent. (For example,
- // the user pressing POWER to wake up the device, causing
- // the InCallScreen to come back to the foreground.)
- //
- // In this scenario we do NOT want to stay here on the
- // InCallScreen: we're not showing any useful info to the
- // user (like a dialog), and the in-call UI itself is
- // useless if there's no active call. So bail out.
-
- Log.i(LOG_TAG, " ==> syncWithPhoneState failed; bailing out!");
- dismissAllDialogs();
-
- // Force the InCallScreen to truly finish(), rather than just
- // moving it to the back of the activity stack (which is what
- // our finish() method usually does.)
- // This is necessary to avoid an obscure scenario where the
- // InCallScreen can get stuck in an inconsistent state, somehow
- // causing a *subsequent* outgoing call to fail (bug 4172599).
- endInCallScreenSession(true /* force a real finish() call */);
- return;
- }
- } else if (TelephonyCapabilities.supportsOtasp(mPhone)) {
- if (inCallUiState.inCallScreenMode == InCallScreenMode.OTA_NORMAL ||
- inCallUiState.inCallScreenMode == InCallScreenMode.OTA_ENDED) {
- if (mCallCard != null) mCallCard.setVisibility(View.GONE);
- updateScreen();
- return;
- }
- }
-
- // InCallScreen is now active.
- EventLog.writeEvent(EventLogTags.PHONE_UI_ENTER);
-
- // Update the poke lock and wake lock when we move to the foreground.
- // This will be no-op when prox sensor is effective.
- mApp.updateWakeState();
-
- // Restore the mute state if the last mute state change was NOT
- // done by the user.
- if (mApp.getRestoreMuteOnInCallResume()) {
- // Mute state is based on the foreground call
- PhoneUtils.restoreMuteState();
- mApp.setRestoreMuteOnInCallResume(false);
- }
-
- Profiler.profileViewCreate(getWindow(), InCallScreen.class.getName());
-
- // If there's a pending MMI code, we'll show a dialog here.
- //
- // Note: previously we had shown the dialog when MMI_INITIATE event's coming
- // from telephony layer, while right now we don't because the event comes
- // too early (before in-call screen is prepared).
- // Now we instead check pending MMI code and show the dialog here.
- //
- // This *may* cause some problem, e.g. when the user really quickly starts
- // MMI sequence and calls an actual phone number before the MMI request
- // being completed, which is rather rare.
- //
- // TODO: streamline this logic and have a UX in a better manner.
- // Right now syncWithPhoneState() above will return SUCCESS based on
- // mPhone.getPendingMmiCodes().isEmpty(), while we check it again here.
- // Also we show pre-populated in-call UI under the dialog, which looks
- // not great. (issue 5210375, 5545506)
- // After cleaning them, remove commented-out MMI handling code elsewhere.
- if (!mPhone.getPendingMmiCodes().isEmpty()) {
- if (mMmiStartedDialog == null) {
- MmiCode mmiCode = mPhone.getPendingMmiCodes().get(0);
- Message message = Message.obtain(mHandler, PhoneGlobals.MMI_CANCEL);
- mMmiStartedDialog = PhoneUtils.displayMMIInitiate(this, mmiCode,
- message, mMmiStartedDialog);
- // mInCallScreen needs to receive MMI_COMPLETE/MMI_CANCEL event from telephony,
- // which will dismiss the entire screen.
- }
- }
-
- // This means the screen is shown even though there's no connection, which only happens
- // when the phone call has hung up while the screen is turned off at that moment.
- // We want to show "disconnected" state with photos with appropriate elapsed time for
- // the finished phone call.
- if (mApp.inCallUiState.showAlreadyDisconnectedState) {
- // if (DBG) {
- log("onResume(): detected \"show already disconnected state\" situation."
- + " set up DELAYED_CLEANUP_AFTER_DISCONNECT message with "
- + CALL_ENDED_LONG_DELAY + " msec delay.");
- //}
- mHandler.removeMessages(DELAYED_CLEANUP_AFTER_DISCONNECT);
- mHandler.sendEmptyMessageDelayed(DELAYED_CLEANUP_AFTER_DISCONNECT,
- CALL_ENDED_LONG_DELAY);
- }
-
- if (VDBG) log("onResume() done.");
- }
-
- // onPause is guaranteed to be called when the InCallScreen goes
- // in the background.
- @Override
- protected void onPause() {
- if (DBG) log("onPause()...");
- super.onPause();
-
- mIsForegroundActivity = false;
-
- // "show-already-disconnected-state" should be effective just during the first wake-up.
- // We should never allow it to stay true after that.
- mApp.inCallUiState.showAlreadyDisconnectedState = false;
-
- // Make sure the "Manage conference" chronometer is stopped when
- // we move away from the foreground.
- mManageConferenceUtils.stopConferenceTime();
-
- // as a catch-all, make sure that any dtmf tones are stopped
- // when the UI is no longer in the foreground.
- mDialer.onDialerKeyUp(null);
-
- // Release any "dialer session" resources, now that we're no
- // longer in the foreground.
- mDialer.stopDialerSession();
-
- // If the device is put to sleep as the phone call is ending,
- // we may see cases where the DELAYED_CLEANUP_AFTER_DISCONNECT
- // event gets handled AFTER the device goes to sleep and wakes
- // up again.
-
- // This is because it is possible for a sleep command
- // (executed with the End Call key) to come during the 2
- // seconds that the "Call Ended" screen is up. Sleep then
- // pauses the device (including the cleanup event) and
- // resumes the event when it wakes up.
-
- // To fix this, we introduce a bit of code that pushes the UI
- // to the background if we pause and see a request to
- // DELAYED_CLEANUP_AFTER_DISCONNECT.
-
- // Note: We can try to finish directly, by:
- // 1. Removing the DELAYED_CLEANUP_AFTER_DISCONNECT messages
- // 2. Calling delayedCleanupAfterDisconnect directly
-
- // However, doing so can cause problems between the phone
- // app and the keyguard - the keyguard is trying to sleep at
- // the same time that the phone state is changing. This can
- // end up causing the sleep request to be ignored.
- if (mHandler.hasMessages(DELAYED_CLEANUP_AFTER_DISCONNECT)
- && mCM.getState() != PhoneConstants.State.RINGING) {
- if (DBG) log("DELAYED_CLEANUP_AFTER_DISCONNECT detected, moving UI to background.");
- endInCallScreenSession();
- }
-
- EventLog.writeEvent(EventLogTags.PHONE_UI_EXIT);
-
- // Dismiss any dialogs we may have brought up, just to be 100%
- // sure they won't still be around when we get back here.
- dismissAllDialogs();
-
- updateExpandedViewState();
-
- // ...and *always* reset the system bar back to its normal state
- // when leaving the in-call UI.
- // (While we're the foreground activity, we disable navigation in
- // some call states; see InCallTouchUi.updateState().)
- mApp.notificationMgr.statusBarHelper.enableSystemBarNavigation(true);
-
- // Unregister for broadcast intents. (These affect the visible UI
- // of the InCallScreen, so we only care about them while we're in the
- // foreground.)
- unregisterReceiver(mReceiver);
-
- // Make sure we revert the poke lock and wake lock when we move to
- // the background.
- mApp.updateWakeState();
-
- // clear the dismiss keyguard flag so we are back to the default state
- // when we next resume
- updateKeyguardPolicy(false);
-
- // See also PhoneApp#updatePhoneState(), which takes care of all the other release() calls.
- if (mApp.getUpdateLock().isHeld() && mApp.getPhoneState() == PhoneConstants.State.IDLE) {
- if (DBG) {
- log("Release UpdateLock on onPause() because there's no active phone call.");
- }
- mApp.getUpdateLock().release();
- }
- }
-
- @Override
- protected void onStop() {
- if (DBG) log("onStop()...");
- super.onStop();
-
- stopTimer();
-
- PhoneConstants.State state = mCM.getState();
- if (DBG) log("onStop: state = " + state);
-
- if (state == PhoneConstants.State.IDLE) {
- if (mRespondViaSmsManager.isShowingPopup()) {
- // This means that the user has been opening the "Respond via SMS" dialog even
- // after the incoming call hanging up, and the screen finally went background.
- // In that case we just close the dialog and exit the whole in-call screen.
- mRespondViaSmsManager.dismissPopup();
- }
-
- // when OTA Activation, OTA Success/Failure dialog or OTA SPC
- // failure dialog is running, do not destroy inCallScreen. Because call
- // is already ended and dialog will not get redrawn on slider event.
- if ((mApp.cdmaOtaProvisionData != null) && (mApp.cdmaOtaScreenState != null)
- && ((mApp.cdmaOtaScreenState.otaScreenState !=
- CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION)
- && (mApp.cdmaOtaScreenState.otaScreenState !=
- CdmaOtaScreenState.OtaScreenState.OTA_STATUS_SUCCESS_FAILURE_DLG)
- && (!mApp.cdmaOtaProvisionData.inOtaSpcState))) {
- // we don't want the call screen to remain in the activity history
- // if there are not active or ringing calls.
- if (DBG) log("- onStop: calling finish() to clear activity history...");
- moveTaskToBack(true);
- if (mApp.otaUtils != null) {
- mApp.otaUtils.cleanOtaScreen(true);
- }
- }
- }
- }
-
- @Override
- protected void onDestroy() {
- Log.i(LOG_TAG, "onDestroy()... this = " + this);
- super.onDestroy();
-
- // Set the magic flag that tells us NOT to handle any handler
- // messages that come in asynchronously after we get destroyed.
- mIsDestroyed = true;
-
- mApp.setInCallScreenInstance(null);
-
- // Clear out the InCallScreen references in various helper objects
- // (to let them know we've been destroyed).
- if (mCallCard != null) {
- mCallCard.setInCallScreenInstance(null);
- }
- if (mInCallTouchUi != null) {
- mInCallTouchUi.setInCallScreenInstance(null);
- }
- mRespondViaSmsManager.setInCallScreenInstance(null);
-
- mDialer.clearInCallScreenReference();
- mDialer = null;
-
- unregisterForPhoneStates();
- // No need to change wake state here; that happens in onPause() when we
- // are moving out of the foreground.
-
- // Dismiss all dialogs, to be absolutely sure we won't leak any of
- // them while changing orientation.
- dismissAllDialogs();
-
- // If there's an OtaUtils instance around, clear out its
- // references to our internal widgets.
- if (mApp.otaUtils != null) {
- mApp.otaUtils.clearUiWidgets();
- }
- }
-
- /**
- * Dismisses the in-call screen.
- *
- * We never *really* finish() the InCallScreen, since we don't want to
- * get destroyed and then have to be re-created from scratch for the
- * next call. Instead, we just move ourselves to the back of the
- * activity stack.
- *
- * This also means that we'll no longer be reachable via the BACK
- * button (since moveTaskToBack() puts us behind the Home app, but the
- * home app doesn't allow the BACK key to move you any farther down in
- * the history stack.)
- *
- * (Since the Phone app itself is never killed, this basically means
- * that we'll keep a single InCallScreen instance around for the
- * entire uptime of the device. This noticeably improves the UI
- * responsiveness for incoming calls.)
- */
- @Override
- public void finish() {
- if (DBG) log("finish()...");
- moveTaskToBack(true);
- }
-
- /**
- * End the current in call screen session.
- *
- * This must be called when an InCallScreen session has
- * complete so that the next invocation via an onResume will
- * not be in an old state.
- */
- public void endInCallScreenSession() {
- if (DBG) log("endInCallScreenSession()... phone state = " + mCM.getState());
- endInCallScreenSession(false);
- }
-
- /**
- * Internal version of endInCallScreenSession().
- *
- * @param forceFinish If true, force the InCallScreen to
- * truly finish() rather than just calling moveTaskToBack().
- * @see finish()
- */
- private void endInCallScreenSession(boolean forceFinish) {
- if (DBG) {
- log("endInCallScreenSession(" + forceFinish + ")... phone state = " + mCM.getState());
- }
- if (forceFinish) {
- Log.i(LOG_TAG, "endInCallScreenSession(): FORCING a call to super.finish()!");
- super.finish(); // Call super.finish() rather than our own finish() method,
- // which actually just calls moveTaskToBack().
- } else {
- moveTaskToBack(true);
- }
- setInCallScreenMode(InCallScreenMode.UNDEFINED);
-
- // Call update screen so that the in-call screen goes back to a normal state.
- // This avoids bugs where a previous state will filcker the next time phone is
- // opened.
- updateScreen();
-
- if (mCallCard != null) {
- mCallCard.clear();
- }
- }
-
- /**
- * True when this Activity is in foreground (between onResume() and onPause()).
- */
- /* package */ boolean isForegroundActivity() {
- return mIsForegroundActivity;
- }
-
- /* package */ void updateKeyguardPolicy(boolean dismissKeyguard) {
- if (dismissKeyguard) {
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
- } else {
- getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
- }
- }
-
- private void registerForPhoneStates() {
- if (!mRegisteredForPhoneStates) {
- mCM.registerForPreciseCallStateChanged(mHandler, PHONE_STATE_CHANGED, null);
- mCM.registerForDisconnect(mHandler, PHONE_DISCONNECT, null);
- // TODO: sort out MMI code (probably we should remove this method entirely).
- // See also MMI handling code in onResume()
- // mCM.registerForMmiInitiate(mHandler, PhoneApp.MMI_INITIATE, null);
-
- // register for the MMI complete message. Upon completion,
- // PhoneUtils will bring up a system dialog instead of the
- // message display class in PhoneUtils.displayMMIComplete().
- // We'll listen for that message too, so that we can finish
- // the activity at the same time.
- mCM.registerForMmiComplete(mHandler, PhoneGlobals.MMI_COMPLETE, null);
- mCM.registerForCallWaiting(mHandler, PHONE_CDMA_CALL_WAITING, null);
- mCM.registerForPostDialCharacter(mHandler, POST_ON_DIAL_CHARS, null);
- mCM.registerForSuppServiceFailed(mHandler, SUPP_SERVICE_FAILED, null);
- mCM.registerForIncomingRing(mHandler, PHONE_INCOMING_RING, null);
- mCM.registerForNewRingingConnection(mHandler, PHONE_NEW_RINGING_CONNECTION, null);
- mRegisteredForPhoneStates = true;
- }
- }
-
- private void unregisterForPhoneStates() {
- mCM.unregisterForPreciseCallStateChanged(mHandler);
- mCM.unregisterForDisconnect(mHandler);
- mCM.unregisterForMmiInitiate(mHandler);
- mCM.unregisterForMmiComplete(mHandler);
- mCM.unregisterForCallWaiting(mHandler);
- mCM.unregisterForPostDialCharacter(mHandler);
- mCM.unregisterForSuppServiceFailed(mHandler);
- mCM.unregisterForIncomingRing(mHandler);
- mCM.unregisterForNewRingingConnection(mHandler);
- mRegisteredForPhoneStates = false;
- }
-
- /* package */ void updateAfterRadioTechnologyChange() {
- if (DBG) Log.d(LOG_TAG, "updateAfterRadioTechnologyChange()...");
-
- // Reset the call screen since the calls cannot be transferred
- // across radio technologies.
- resetInCallScreenMode();
-
- // Unregister for all events from the old obsolete phone
- unregisterForPhoneStates();
-
- // (Re)register for all events relevant to the new active phone
- registerForPhoneStates();
-
- // And finally, refresh the onscreen UI. (Note that it's safe
- // to call requestUpdateScreen() even if the radio change ended up
- // causing us to exit the InCallScreen.)
- requestUpdateScreen();
- }
-
- @Override
- protected void onNewIntent(Intent intent) {
- log("onNewIntent: intent = " + intent + ", phone state = " + mCM.getState());
-
- // We're being re-launched with a new Intent. Since it's possible for a
- // single InCallScreen instance to persist indefinitely (even if we
- // finish() ourselves), this sequence can potentially happen any time
- // the InCallScreen needs to be displayed.
-
- // Stash away the new intent so that we can get it in the future
- // by calling getIntent(). (Otherwise getIntent() will return the
- // original Intent from when we first got created!)
- setIntent(intent);
-
- // Activities are always paused before receiving a new intent, so
- // we can count on our onResume() method being called next.
-
- // Just like in onCreate(), handle the intent.
- internalResolveIntent(intent);
- }
-
- private void internalResolveIntent(Intent intent) {
- if (intent == null || intent.getAction() == null) {
- return;
- }
- String action = intent.getAction();
- if (DBG) log("internalResolveIntent: action=" + action);
-
- // In gingerbread and earlier releases, the InCallScreen used to
- // directly handle certain intent actions that could initiate phone
- // calls, namely ACTION_CALL and ACTION_CALL_EMERGENCY, and also
- // OtaUtils.ACTION_PERFORM_CDMA_PROVISIONING.
- //
- // But it doesn't make sense to tie those actions to the InCallScreen
- // (or especially to the *activity lifecycle* of the InCallScreen).
- // Instead, the InCallScreen should only be concerned with running the
- // onscreen UI while in a call. So we've now offloaded the call-control
- // functionality to a new module called CallController, and OTASP calls
- // are now launched from the OtaUtils startInteractiveOtasp() or
- // startNonInteractiveOtasp() methods.
- //
- // So now, the InCallScreen is only ever launched using the ACTION_MAIN
- // action, and (upon launch) performs no functionality other than
- // displaying the UI in a state that matches the current telephony
- // state.
-
- if (action.equals(intent.ACTION_MAIN)) {
- // This action is the normal way to bring up the in-call UI.
- //
- // Most of the interesting work of updating the onscreen UI (to
- // match the current telephony state) happens in the
- // syncWithPhoneState() => updateScreen() sequence that happens in
- // onResume().
- //
- // But we do check here for one extra that can come along with the
- // ACTION_MAIN intent:
-
- if (intent.hasExtra(SHOW_DIALPAD_EXTRA)) {
- // SHOW_DIALPAD_EXTRA can be used here to specify whether the DTMF
- // dialpad should be initially visible. If the extra isn't
- // present at all, we just leave the dialpad in its previous state.
-
- boolean showDialpad = intent.getBooleanExtra(SHOW_DIALPAD_EXTRA, false);
- if (VDBG) log("- internalResolveIntent: SHOW_DIALPAD_EXTRA: " + showDialpad);
-
- // If SHOW_DIALPAD_EXTRA is specified, that overrides whatever
- // the previous state of inCallUiState.showDialpad was.
- mApp.inCallUiState.showDialpad = showDialpad;
-
- final boolean hasActiveCall = mCM.hasActiveFgCall();
- final boolean hasHoldingCall = mCM.hasActiveBgCall();
-
- // There's only one line in use, AND it's on hold, at which we're sure the user
- // wants to use the dialpad toward the exact line, so un-hold the holding line.
- if (showDialpad && !hasActiveCall && hasHoldingCall) {
- PhoneUtils.switchHoldingAndActive(mCM.getFirstActiveBgCall());
- }
- }
- // ...and in onResume() we'll update the onscreen dialpad state to
- // match the InCallUiState.
-
- return;
- }
-
- if (action.equals(OtaUtils.ACTION_DISPLAY_ACTIVATION_SCREEN)) {
- // Bring up the in-call UI in the OTASP-specific "activate" state;
- // see OtaUtils.startInteractiveOtasp(). Note that at this point
- // the OTASP call has not been started yet; we won't actually make
- // the call until the user presses the "Activate" button.
-
- if (!TelephonyCapabilities.supportsOtasp(mPhone)) {
- throw new IllegalStateException(
- "Received ACTION_DISPLAY_ACTIVATION_SCREEN intent on non-OTASP-capable device: "
- + intent);
- }
-
- setInCallScreenMode(InCallScreenMode.OTA_NORMAL);
- if ((mApp.cdmaOtaProvisionData != null)
- && (!mApp.cdmaOtaProvisionData.isOtaCallIntentProcessed)) {
- mApp.cdmaOtaProvisionData.isOtaCallIntentProcessed = true;
- mApp.cdmaOtaScreenState.otaScreenState =
- CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION;
- }
- return;
- }
-
- // Various intent actions that should no longer come here directly:
- if (action.equals(OtaUtils.ACTION_PERFORM_CDMA_PROVISIONING)) {
- // This intent is now handled by the InCallScreenShowActivation
- // activity, which translates it into a call to
- // OtaUtils.startInteractiveOtasp().
- throw new IllegalStateException(
- "Unexpected ACTION_PERFORM_CDMA_PROVISIONING received by InCallScreen: "
- + intent);
- } else if (action.equals(Intent.ACTION_CALL)
- || action.equals(Intent.ACTION_CALL_EMERGENCY)) {
- // ACTION_CALL* intents go to the OutgoingCallBroadcaster, which now
- // translates them into CallController.placeCall() calls rather than
- // launching the InCallScreen directly.
- throw new IllegalStateException("Unexpected CALL action received by InCallScreen: "
- + intent);
- } else if (action.equals(ACTION_UNDEFINED)) {
- // This action is only used for internal bookkeeping; we should
- // never actually get launched with it.
- Log.wtf(LOG_TAG, "internalResolveIntent: got launched with ACTION_UNDEFINED");
- return;
- } else {
- Log.wtf(LOG_TAG, "internalResolveIntent: unexpected intent action: " + action);
- // But continue the best we can (basically treating this case
- // like ACTION_MAIN...)
- return;
- }
- }
-
- private void stopTimer() {
- if (mCallCard != null) mCallCard.stopTimer();
- }
-
- private void initInCallScreen() {
- if (VDBG) log("initInCallScreen()...");
-
- // Have the WindowManager filter out touch events that are "too fat".
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES);
-
- // Initialize the CallCard.
- mCallCard = (CallCard) findViewById(R.id.callCard);
- if (VDBG) log(" - mCallCard = " + mCallCard);
- mCallCard.setInCallScreenInstance(this);
-
- // Initialize the onscreen UI elements.
- initInCallTouchUi();
-
- // Helper class to keep track of enabledness/state of UI controls
- mInCallControlState = new InCallControlState(this, mCM, mApp.getBluetoothManager());
-
- // Helper class to run the "Manage conference" UI
- mManageConferenceUtils = new ManageConferenceUtils(this, mCM);
-
- // The DTMF Dialpad.
- ViewStub stub = (ViewStub) findViewById(R.id.dtmf_twelve_key_dialer_stub);
- mDialer = new DTMFTwelveKeyDialer(this, stub);
- mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
- }
-
- /**
- * Returns true if the phone is "in use", meaning that at least one line
- * is active (ie. off hook or ringing or dialing). Conversely, a return
- * value of false means there's currently no phone activity at all.
- */
- private boolean phoneIsInUse() {
- return mCM.getState() != PhoneConstants.State.IDLE;
- }
-
- private boolean handleDialerKeyDown(int keyCode, KeyEvent event) {
- if (VDBG) log("handleDialerKeyDown: keyCode " + keyCode + ", event " + event + "...");
-
- // As soon as the user starts typing valid dialable keys on the
- // keyboard (presumably to type DTMF tones) we start passing the
- // key events to the DTMFDialer's onDialerKeyDown. We do so
- // only if the okToDialDTMFTones() conditions pass.
- if (okToDialDTMFTones()) {
- return mDialer.onDialerKeyDown(event);
-
- // TODO: If the dialpad isn't currently visible, maybe
- // consider automatically bringing it up right now?
- // (Just to make sure the user sees the digits widget...)
- // But this probably isn't too critical since it's awkward to
- // use the hard keyboard while in-call in the first place,
- // especially now that the in-call UI is portrait-only...
- }
-
- return false;
- }
-
- @Override
- public void onBackPressed() {
- if (DBG) log("onBackPressed()...");
-
- // To consume this BACK press, the code here should just do
- // something and return. Otherwise, call super.onBackPressed() to
- // get the default implementation (which simply finishes the
- // current activity.)
-
- if (mCM.hasActiveRingingCall()) {
- // The Back key, just like the Home key, is always disabled
- // while an incoming call is ringing. (The user *must* either
- // answer or reject the call before leaving the incoming-call
- // screen.)
- if (DBG) log("BACK key while ringing: ignored");
-
- // And consume this event; *don't* call super.onBackPressed().
- return;
- }
-
- // BACK is also used to exit out of any "special modes" of the
- // in-call UI:
-
- if (mDialer.isOpened()) {
- closeDialpadInternal(true); // do the "closing" animation
- return;
- }
-
- if (mApp.inCallUiState.inCallScreenMode == InCallScreenMode.MANAGE_CONFERENCE) {
- // Hide the Manage Conference panel, return to NORMAL mode.
- setInCallScreenMode(InCallScreenMode.NORMAL);
- requestUpdateScreen();
- return;
- }
-
- // Nothing special to do. Fall back to the default behavior.
- super.onBackPressed();
- }
-
- /**
- * Handles the green CALL key while in-call.
- * @return true if we consumed the event.
- */
- private boolean handleCallKey() {
- // The green CALL button means either "Answer", "Unhold", or
- // "Swap calls", or can be a no-op, depending on the current state
- // of the Phone.
-
- final boolean hasRingingCall = mCM.hasActiveRingingCall();
- final boolean hasActiveCall = mCM.hasActiveFgCall();
- final boolean hasHoldingCall = mCM.hasActiveBgCall();
-
- int phoneType = mPhone.getPhoneType();
- if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
- // The green CALL button means either "Answer", "Swap calls/On Hold", or
- // "Add to 3WC", depending on the current state of the Phone.
-
- CdmaPhoneCallState.PhoneCallState currCallState =
- mApp.cdmaPhoneCallState.getCurrentCallState();
- if (hasRingingCall) {
- //Scenario 1: Accepting the First Incoming and Call Waiting call
- if (DBG) log("answerCall: First Incoming and Call Waiting scenario");
- internalAnswerCall(); // Automatically holds the current active call,
- // if there is one
- } else if ((currCallState == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE)
- && (hasActiveCall)) {
- //Scenario 2: Merging 3Way calls
- if (DBG) log("answerCall: Merge 3-way call scenario");
- // Merge calls
- PhoneUtils.mergeCalls(mCM);
- } else if (currCallState == CdmaPhoneCallState.PhoneCallState.CONF_CALL) {
- //Scenario 3: Switching between two Call waiting calls or drop the latest
- // connection if in a 3Way merge scenario
- if (DBG) log("answerCall: Switch btwn 2 calls scenario");
- internalSwapCalls();
- }
- } else if ((phoneType == PhoneConstants.PHONE_TYPE_GSM)
- || (phoneType == PhoneConstants.PHONE_TYPE_SIP)) {
- if (hasRingingCall) {
- // If an incoming call is ringing, the CALL button is actually
- // handled by the PhoneWindowManager. (We do this to make
- // sure that we'll respond to the key even if the InCallScreen
- // hasn't come to the foreground yet.)
- //
- // We'd only ever get here in the extremely rare case that the
- // incoming call started ringing *after*
- // PhoneWindowManager.interceptKeyTq() but before the event
- // got here, or else if the PhoneWindowManager had some
- // problem connecting to the ITelephony service.
- Log.w(LOG_TAG, "handleCallKey: incoming call is ringing!"
- + " (PhoneWindowManager should have handled this key.)");
- // But go ahead and handle the key as normal, since the
- // PhoneWindowManager presumably did NOT handle it:
-
- // There's an incoming ringing call: CALL means "Answer".
- internalAnswerCall();
- } else if (hasActiveCall && hasHoldingCall) {
- // Two lines are in use: CALL means "Swap calls".
- if (DBG) log("handleCallKey: both lines in use ==> swap calls.");
- internalSwapCalls();
- } else if (hasHoldingCall) {
- // There's only one line in use, AND it's on hold.
- // In this case CALL is a shortcut for "unhold".
- if (DBG) log("handleCallKey: call on hold ==> unhold.");
- PhoneUtils.switchHoldingAndActive(
- mCM.getFirstActiveBgCall()); // Really means "unhold" in this state
- } else {
- // The most common case: there's only one line in use, and
- // it's an active call (i.e. it's not on hold.)
- // In this case CALL is a no-op.
- // (This used to be a shortcut for "add call", but that was a
- // bad idea because "Add call" is so infrequently-used, and
- // because the user experience is pretty confusing if you
- // inadvertently trigger it.)
- if (VDBG) log("handleCallKey: call in foregound ==> ignoring.");
- // But note we still consume this key event; see below.
- }
- } else {
- throw new IllegalStateException("Unexpected phone type: " + phoneType);
- }
-
- // We *always* consume the CALL key, since the system-wide default
- // action ("go to the in-call screen") is useless here.
- return true;
- }
-
- boolean isKeyEventAcceptableDTMF (KeyEvent event) {
- return (mDialer != null && mDialer.isKeyEventAcceptable(event));
- }
-
- /**
- * Overriden to track relevant focus changes.
- *
- * If a key is down and some time later the focus changes, we may
- * NOT recieve the keyup event; logically the keyup event has not
- * occured in this window. This issue is fixed by treating a focus
- * changed event as an interruption to the keydown, making sure
- * that any code that needs to be run in onKeyUp is ALSO run here.
- */
- @Override
- public void onWindowFocusChanged(boolean hasFocus) {
- // the dtmf tones should no longer be played
- if (VDBG) log("onWindowFocusChanged(" + hasFocus + ")...");
- if (!hasFocus && mDialer != null) {
- if (VDBG) log("- onWindowFocusChanged: faking onDialerKeyUp()...");
- mDialer.onDialerKeyUp(null);
- }
- }
-
- @Override
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- // if (DBG) log("onKeyUp(keycode " + keyCode + ")...");
-
- // push input to the dialer.
- if ((mDialer != null) && (mDialer.onDialerKeyUp(event))){
- return true;
- } else if (keyCode == KeyEvent.KEYCODE_CALL) {
- // Always consume CALL to be sure the PhoneWindow won't do anything with it
- return true;
- }
- return super.onKeyUp(keyCode, event);
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- // if (DBG) log("onKeyDown(keycode " + keyCode + ")...");
-
- switch (keyCode) {
- case KeyEvent.KEYCODE_CALL:
- boolean handled = handleCallKey();
- if (!handled) {
- Log.w(LOG_TAG, "InCallScreen should always handle KEYCODE_CALL in onKeyDown");
- }
- // Always consume CALL to be sure the PhoneWindow won't do anything with it
- return true;
-
- // Note there's no KeyEvent.KEYCODE_ENDCALL case here.
- // The standard system-wide handling of the ENDCALL key
- // (see PhoneWindowManager's handling of KEYCODE_ENDCALL)
- // already implements exactly what the UI spec wants,
- // namely (1) "hang up" if there's a current active call,
- // or (2) "don't answer" if there's a current ringing call.
-
- case KeyEvent.KEYCODE_CAMERA:
- // Disable the CAMERA button while in-call since it's too
- // easy to press accidentally.
- return true;
-
- case KeyEvent.KEYCODE_VOLUME_UP:
- case KeyEvent.KEYCODE_VOLUME_DOWN:
- case KeyEvent.KEYCODE_VOLUME_MUTE:
- if (mCM.getState() == PhoneConstants.State.RINGING) {
- // If an incoming call is ringing, the VOLUME buttons are
- // actually handled by the PhoneWindowManager. (We do
- // this to make sure that we'll respond to them even if
- // the InCallScreen hasn't come to the foreground yet.)
- //
- // We'd only ever get here in the extremely rare case that the
- // incoming call started ringing *after*
- // PhoneWindowManager.interceptKeyTq() but before the event
- // got here, or else if the PhoneWindowManager had some
- // problem connecting to the ITelephony service.
- Log.w(LOG_TAG, "VOLUME key: incoming call is ringing!"
- + " (PhoneWindowManager should have handled this key.)");
- // But go ahead and handle the key as normal, since the
- // PhoneWindowManager presumably did NOT handle it:
- internalSilenceRinger();
-
- // As long as an incoming call is ringing, we always
- // consume the VOLUME keys.
- return true;
- }
- break;
-
- case KeyEvent.KEYCODE_MUTE:
- onMuteClick();
- return true;
-
- // Various testing/debugging features, enabled ONLY when VDBG == true.
- case KeyEvent.KEYCODE_SLASH:
- if (VDBG) {
- log("----------- InCallScreen View dump --------------");
- // Dump starting from the top-level view of the entire activity:
- Window w = this.getWindow();
- View decorView = w.getDecorView();
- decorView.debug();
- return true;
- }
- break;
- case KeyEvent.KEYCODE_EQUALS:
- if (VDBG) {
- log("----------- InCallScreen call state dump --------------");
- PhoneUtils.dumpCallState(mPhone);
- PhoneUtils.dumpCallManager();
- return true;
- }
- break;
- case KeyEvent.KEYCODE_GRAVE:
- if (VDBG) {
- // Placeholder for other misc temp testing
- log("------------ Temp testing -----------------");
- return true;
- }
- break;
- }
-
- if (event.getRepeatCount() == 0 && handleDialerKeyDown(keyCode, event)) {
- return true;
- }
-
- return super.onKeyDown(keyCode, event);
- }
-
- /**
- * Handle a failure notification for a supplementary service
- * (i.e. conference, switch, separate, transfer, etc.).
- */
- void onSuppServiceFailed(AsyncResult r) {
- Phone.SuppService service = (Phone.SuppService) r.result;
- if (DBG) log("onSuppServiceFailed: " + service);
-
- int errorMessageResId;
- switch (service) {
- case SWITCH:
- // Attempt to switch foreground and background/incoming calls failed
- // ("Failed to switch calls")
- errorMessageResId = R.string.incall_error_supp_service_switch;
- break;
-
- case SEPARATE:
- // Attempt to separate a call from a conference call
- // failed ("Failed to separate out call")
- errorMessageResId = R.string.incall_error_supp_service_separate;
- break;
-
- case TRANSFER:
- // Attempt to connect foreground and background calls to
- // each other (and hanging up user's line) failed ("Call
- // transfer failed")
- errorMessageResId = R.string.incall_error_supp_service_transfer;
- break;
-
- case CONFERENCE:
- // Attempt to add a call to conference call failed
- // ("Conference call failed")
- errorMessageResId = R.string.incall_error_supp_service_conference;
- break;
-
- case REJECT:
- // Attempt to reject an incoming call failed
- // ("Call rejection failed")
- errorMessageResId = R.string.incall_error_supp_service_reject;
- break;
-
- case HANGUP:
- // Attempt to release a call failed ("Failed to release call(s)")
- errorMessageResId = R.string.incall_error_supp_service_hangup;
- break;
-
- case UNKNOWN:
- default:
- // Attempt to use a service we don't recognize or support
- // ("Unsupported service" or "Selected service failed")
- errorMessageResId = R.string.incall_error_supp_service_unknown;
- break;
- }
-
- // mSuppServiceFailureDialog is a generic dialog used for any
- // supp service failure, and there's only ever have one
- // instance at a time. So just in case a previous dialog is
- // still around, dismiss it.
- if (mSuppServiceFailureDialog != null) {
- if (DBG) log("- DISMISSING mSuppServiceFailureDialog.");
- mSuppServiceFailureDialog.dismiss(); // It's safe to dismiss() a dialog
- // that's already dismissed.
- mSuppServiceFailureDialog = null;
- }
-
- mSuppServiceFailureDialog = new AlertDialog.Builder(this)
- .setMessage(errorMessageResId)
- .setPositiveButton(R.string.ok, null)
- .create();
- mSuppServiceFailureDialog.getWindow().addFlags(
- WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
- mSuppServiceFailureDialog.show();
- }
-
- /**
- * Something has changed in the phone's state. Update the UI.
- */
- private void onPhoneStateChanged(AsyncResult r) {
- PhoneConstants.State state = mCM.getState();
- if (DBG) log("onPhoneStateChanged: current state = " + state);
-
- // There's nothing to do here if we're not the foreground activity.
- // (When we *do* eventually come to the foreground, we'll do a
- // full update then.)
- if (!mIsForegroundActivity) {
- if (DBG) log("onPhoneStateChanged: Activity not in foreground! Bailing out...");
- return;
- }
-
- updateExpandedViewState();
-
- // Update the onscreen UI.
- // We use requestUpdateScreen() here (which posts a handler message)
- // instead of calling updateScreen() directly, which allows us to avoid
- // unnecessary work if multiple onPhoneStateChanged() events come in all
- // at the same time.
-
- requestUpdateScreen();
-
- // Make sure we update the poke lock and wake lock when certain
- // phone state changes occur.
- mApp.updateWakeState();
- }
-
- /**
- * Updates the UI after a phone connection is disconnected, as follows:
- *
- * - If this was a missed or rejected incoming call, and no other
- * calls are active, dismiss the in-call UI immediately. (The
- * CallNotifier will still create a "missed call" notification if
- * necessary.)
- *
- * - With any other disconnect cause, if the phone is now totally
- * idle, display the "Call ended" state for a couple of seconds.
- *
- * - Or, if the phone is still in use, stay on the in-call screen
- * (and update the UI to reflect the current state of the Phone.)
- *
- * @param r r.result contains the connection that just ended
- */
- private void onDisconnect(AsyncResult r) {
- Connection c = (Connection) r.result;
- Connection.DisconnectCause cause = c.getDisconnectCause();
- if (DBG) log("onDisconnect: connection '" + c + "', cause = " + cause
- + ", showing screen: " + mApp.isShowingCallScreen());
-
- boolean currentlyIdle = !phoneIsInUse();
- int autoretrySetting = AUTO_RETRY_OFF;
- boolean phoneIsCdma = (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA);
- if (phoneIsCdma) {
- // Get the Auto-retry setting only if Phone State is IDLE,
- // else let it stay as AUTO_RETRY_OFF
- if (currentlyIdle) {
- autoretrySetting = android.provider.Settings.Global.getInt(mPhone.getContext().
- getContentResolver(), android.provider.Settings.Global.CALL_AUTO_RETRY, 0);
- }
- }
-
- // for OTA Call, only if in OTA NORMAL mode, handle OTA END scenario
- if ((mApp.inCallUiState.inCallScreenMode == InCallScreenMode.OTA_NORMAL)
- && ((mApp.cdmaOtaProvisionData != null)
- && (!mApp.cdmaOtaProvisionData.inOtaSpcState))) {
- setInCallScreenMode(InCallScreenMode.OTA_ENDED);
- updateScreen();
- return;
- } else if ((mApp.inCallUiState.inCallScreenMode == InCallScreenMode.OTA_ENDED)
- || ((mApp.cdmaOtaProvisionData != null)
- && mApp.cdmaOtaProvisionData.inOtaSpcState)) {
- if (DBG) log("onDisconnect: OTA Call end already handled");
- return;
- }
-
- // Any time a call disconnects, clear out the "history" of DTMF
- // digits you typed (to make sure it doesn't persist from one call
- // to the next.)
- mDialer.clearDigits();
-
- // Under certain call disconnected states, we want to alert the user
- // with a dialog instead of going through the normal disconnect
- // routine.
- if (cause == Connection.DisconnectCause.CALL_BARRED) {
- showGenericErrorDialog(R.string.callFailed_cb_enabled, false);
- return;
- } else if (cause == Connection.DisconnectCause.FDN_BLOCKED) {
- showGenericErrorDialog(R.string.callFailed_fdn_only, false);
- return;
- } else if (cause == Connection.DisconnectCause.CS_RESTRICTED) {
- showGenericErrorDialog(R.string.callFailed_dsac_restricted, false);
- return;
- } else if (cause == Connection.DisconnectCause.CS_RESTRICTED_EMERGENCY) {
- showGenericErrorDialog(R.string.callFailed_dsac_restricted_emergency, false);
- return;
- } else if (cause == Connection.DisconnectCause.CS_RESTRICTED_NORMAL) {
- showGenericErrorDialog(R.string.callFailed_dsac_restricted_normal, false);
- return;
- }
-
- if (phoneIsCdma) {
- Call.State callState = mApp.notifier.getPreviousCdmaCallState();
- if ((callState == Call.State.ACTIVE)
- && (cause != Connection.DisconnectCause.INCOMING_MISSED)
- && (cause != Connection.DisconnectCause.NORMAL)
- && (cause != Connection.DisconnectCause.LOCAL)
- && (cause != Connection.DisconnectCause.INCOMING_REJECTED)) {
- showCallLostDialog();
- } else if ((callState == Call.State.DIALING || callState == Call.State.ALERTING)
- && (cause != Connection.DisconnectCause.INCOMING_MISSED)
- && (cause != Connection.DisconnectCause.NORMAL)
- && (cause != Connection.DisconnectCause.LOCAL)
- && (cause != Connection.DisconnectCause.INCOMING_REJECTED)) {
-
- if (mApp.inCallUiState.needToShowCallLostDialog) {
- // Show the dialog now since the call that just failed was a retry.
- showCallLostDialog();
- mApp.inCallUiState.needToShowCallLostDialog = false;
- } else {
- if (autoretrySetting == AUTO_RETRY_OFF) {
- // Show the dialog for failed call if Auto Retry is OFF in Settings.
- showCallLostDialog();
- mApp.inCallUiState.needToShowCallLostDialog = false;
- } else {
- // Set the needToShowCallLostDialog flag now, so we'll know to show
- // the dialog if *this* call fails.
- mApp.inCallUiState.needToShowCallLostDialog = true;
- }
- }
- }
- }
-
- // Explicitly clean up up any DISCONNECTED connections
- // in a conference call.
- // [Background: Even after a connection gets disconnected, its
- // Connection object still stays around for a few seconds, in the
- // DISCONNECTED state. With regular calls, this state drives the
- // "call ended" UI. But when a single person disconnects from a
- // conference call there's no "call ended" state at all; in that
- // case we blow away any DISCONNECTED connections right now to make sure
- // the UI updates instantly to reflect the current state.]
- final Call call = c.getCall();
- if (call != null) {
- // We only care about situation of a single caller
- // disconnecting from a conference call. In that case, the
- // call will have more than one Connection (including the one
- // that just disconnected, which will be in the DISCONNECTED
- // state) *and* at least one ACTIVE connection. (If the Call
- // has *no* ACTIVE connections, that means that the entire
- // conference call just ended, so we *do* want to show the
- // "Call ended" state.)
- List<Connection> connections = call.getConnections();
- if (connections != null && connections.size() > 1) {
- for (Connection conn : connections) {
- if (conn.getState() == Call.State.ACTIVE) {
- // This call still has at least one ACTIVE connection!
- // So blow away any DISCONNECTED connections
- // (including, presumably, the one that just
- // disconnected from this conference call.)
-
- // We also force the wake state to refresh, just in
- // case the disconnected connections are removed
- // before the phone state change.
- if (VDBG) log("- Still-active conf call; clearing DISCONNECTED...");
- mApp.updateWakeState();
- mCM.clearDisconnected(); // This happens synchronously.
- break;
- }
- }
- }
- }
-
- // Note: see CallNotifier.onDisconnect() for some other behavior
- // that might be triggered by a disconnect event, like playing the
- // busy/congestion tone.
-
- // Stash away some info about the call that just disconnected.
- // (This might affect what happens after we exit the InCallScreen; see
- // delayedCleanupAfterDisconnect().)
- // TODO: rather than stashing this away now and then reading it in
- // delayedCleanupAfterDisconnect(), it would be cleaner to just pass
- // this as an argument to delayedCleanupAfterDisconnect() (if we call
- // it directly) or else pass it as a Message argument when we post the
- // DELAYED_CLEANUP_AFTER_DISCONNECT message.
- mLastDisconnectCause = cause;
-
- // We bail out immediately (and *don't* display the "call ended"
- // state at all) if this was an incoming call.
- boolean bailOutImmediately =
- ((cause == Connection.DisconnectCause.INCOMING_MISSED)
- || (cause == Connection.DisconnectCause.INCOMING_REJECTED))
- && currentlyIdle;
-
- boolean showingQuickResponseDialog =
- mRespondViaSmsManager != null && mRespondViaSmsManager.isShowingPopup();
-
- // Note: we also do some special handling for the case when a call
- // disconnects with cause==OUT_OF_SERVICE while making an
- // emergency call from airplane mode. That's handled by
- // EmergencyCallHelper.onDisconnect().
-
- if (bailOutImmediately && showingQuickResponseDialog) {
- if (DBG) log("- onDisconnect: Respond-via-SMS dialog is still being displayed...");
-
- // Do *not* exit the in-call UI yet!
- // If the call was an incoming call that was missed *and* the user is using
- // quick response screen, we keep showing the screen for a moment, assuming the
- // user wants to reply the call anyway.
- //
- // For this case, we will exit the screen when:
- // - the message is sent (RespondViaSmsManager)
- // - the message is canceled (RespondViaSmsManager), or
- // - when the whole in-call UI becomes background (onPause())
- } else if (bailOutImmediately) {
- if (DBG) log("- onDisconnect: bailOutImmediately...");
-
- // Exit the in-call UI!
- // (This is basically the same "delayed cleanup" we do below,
- // just with zero delay. Since the Phone is currently idle,
- // this call is guaranteed to immediately finish this activity.)
- delayedCleanupAfterDisconnect();
- } else {
- if (DBG) log("- onDisconnect: delayed bailout...");
- // Stay on the in-call screen for now. (Either the phone is
- // still in use, or the phone is idle but we want to display
- // the "call ended" state for a couple of seconds.)
-
- // Switch to the special "Call ended" state when the phone is idle
- // but there's still a call in the DISCONNECTED state:
- if (currentlyIdle
- && (mCM.hasDisconnectedFgCall() || mCM.hasDisconnectedBgCall())) {
- if (DBG) log("- onDisconnect: switching to 'Call ended' state...");
- setInCallScreenMode(InCallScreenMode.CALL_ENDED);
- }
-
- // Force a UI update in case we need to display anything
- // special based on this connection's DisconnectCause
- // (see CallCard.getCallFailedString()).
- updateScreen();
-
- // Some other misc cleanup that we do if the call that just
- // disconnected was the foreground call.
- final boolean hasActiveCall = mCM.hasActiveFgCall();
- if (!hasActiveCall) {
- if (DBG) log("- onDisconnect: cleaning up after FG call disconnect...");
-
- // Dismiss any dialogs which are only meaningful for an
- // active call *and* which become moot if the call ends.
- if (mWaitPromptDialog != null) {
- if (VDBG) log("- DISMISSING mWaitPromptDialog.");
- mWaitPromptDialog.dismiss(); // safe even if already dismissed
- mWaitPromptDialog = null;
- }
- if (mWildPromptDialog != null) {
- if (VDBG) log("- DISMISSING mWildPromptDialog.");
- mWildPromptDialog.dismiss(); // safe even if already dismissed
- mWildPromptDialog = null;
- }
- if (mPausePromptDialog != null) {
- if (DBG) log("- DISMISSING mPausePromptDialog.");
- mPausePromptDialog.dismiss(); // safe even if already dismissed
- mPausePromptDialog = null;
- }
- }
-
- // Updating the screen wake state is done in onPhoneStateChanged().
-
-
- // CDMA: We only clean up if the Phone state is IDLE as we might receive an
- // onDisconnect for a Call Collision case (rare but possible).
- // For Call collision cases i.e. when the user makes an out going call
- // and at the same time receives an Incoming Call, the Incoming Call is given
- // higher preference. At this time framework sends a disconnect for the Out going
- // call connection hence we should *not* bring down the InCallScreen as the Phone
- // State would be RINGING
- if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
- if (!currentlyIdle) {
- // Clean up any connections in the DISCONNECTED state.
- // This is necessary cause in CallCollision the foreground call might have
- // connections in DISCONNECTED state which needs to be cleared.
- mCM.clearDisconnected();
-
- // The phone is still in use. Stay here in this activity.
- // But we don't need to keep the screen on.
- if (DBG) log("onDisconnect: Call Collision case - staying on InCallScreen.");
- if (DBG) PhoneUtils.dumpCallState(mPhone);
- return;
- }
- }
-
- // This is onDisconnect() request from the last phone call; no available call anymore.
- //
- // When the in-call UI is in background *because* the screen is turned off (unlike the
- // other case where the other activity is being shown), we wake up the screen and
- // show "DISCONNECTED" state once, with appropriate elapsed time. After showing that
- // we *must* bail out of the screen again, showing screen lock if needed.
- //
- //
- // TODO: Consider moving this to CallNotifier. This code assumes the InCallScreen
- // never gets destroyed. For this exact case, it works (since InCallScreen won't be
- // destroyed), while technically this isn't right; Activity may be destroyed when
- // in background.
- if (currentlyIdle && !isForegroundActivity()) {
- log("Force waking up the screen to let users see \"disconnected\" state");
- if (call != null) {
- mCallCard.updateElapsedTimeWidget(call);
- }
- // This variable will be kept true until the next InCallScreen#onPause(), which
- // forcibly turns it off regardless of the situation (for avoiding unnecessary
- // confusion around this special case).
- mApp.inCallUiState.showAlreadyDisconnectedState = true;
-
- // Finally request wake-up..
- mApp.wakeUpScreen();
-
- // InCallScreen#onResume() will set DELAYED_CLEANUP_AFTER_DISCONNECT message,
- // so skip the following section.
- return;
- }
-
- // Finally, arrange for delayedCleanupAfterDisconnect() to get
- // called after a short interval (during which we display the
- // "call ended" state.) At that point, if the
- // Phone is idle, we'll finish out of this activity.
- final int callEndedDisplayDelay;
- switch (cause) {
- // When the local user hanged up the ongoing call, it is ok to dismiss the screen
- // soon. In other cases, we show the "hung up" screen longer.
- //
- // - For expected reasons we will use CALL_ENDED_LONG_DELAY.
- // -- when the peer hanged up the call
- // -- when the local user rejects the incoming call during the other ongoing call
- // (TODO: there may be other cases which should be in this category)
- //
- // - For other unexpected reasons, we will use CALL_ENDED_EXTRA_LONG_DELAY,
- // assuming the local user wants to confirm the disconnect reason.
- case LOCAL:
- callEndedDisplayDelay = CALL_ENDED_SHORT_DELAY;
- break;
- case NORMAL:
- case INCOMING_REJECTED:
- callEndedDisplayDelay = CALL_ENDED_LONG_DELAY;
- break;
- default:
- callEndedDisplayDelay = CALL_ENDED_EXTRA_LONG_DELAY;
- break;
- }
- mHandler.removeMessages(DELAYED_CLEANUP_AFTER_DISCONNECT);
- mHandler.sendEmptyMessageDelayed(DELAYED_CLEANUP_AFTER_DISCONNECT,
- callEndedDisplayDelay);
- }
-
- // Remove 3way timer (only meaningful for CDMA)
- // TODO: this call needs to happen in the CallController, not here.
- // (It should probably be triggered by the CallNotifier's onDisconnect method.)
- // mHandler.removeMessages(THREEWAY_CALLERINFO_DISPLAY_DONE);
- }
-
- /**
- * Brings up the "MMI Started" dialog.
- */
- /* TODO: sort out MMI code (probably we should remove this method entirely). See also
- MMI handling code in onResume()
- private void onMMIInitiate(AsyncResult r) {
- if (VDBG) log("onMMIInitiate()... AsyncResult r = " + r);
-
- // Watch out: don't do this if we're not the foreground activity,
- // mainly since in the Dialog.show() might fail if we don't have a
- // valid window token any more...
- // (Note that this exact sequence can happen if you try to start
- // an MMI code while the radio is off or out of service.)
- if (!mIsForegroundActivity) {
- if (VDBG) log("Activity not in foreground! Bailing out...");
- return;
- }
-
- // Also, if any other dialog is up right now (presumably the
- // generic error dialog displaying the "Starting MMI..." message)
- // take it down before bringing up the real "MMI Started" dialog
- // in its place.
- dismissAllDialogs();
-
- MmiCode mmiCode = (MmiCode) r.result;
- if (VDBG) log(" - MmiCode: " + mmiCode);
-
- Message message = Message.obtain(mHandler, PhoneApp.MMI_CANCEL);
- mMmiStartedDialog = PhoneUtils.displayMMIInitiate(this, mmiCode,
- message, mMmiStartedDialog);
- }*/
-
- /**
- * Handles an MMI_CANCEL event, which is triggered by the button
- * (labeled either "OK" or "Cancel") on the "MMI Started" dialog.
- * @see PhoneUtils#cancelMmiCode(Phone)
- */
- private void onMMICancel() {
- if (VDBG) log("onMMICancel()...");
-
- // First of all, cancel the outstanding MMI code (if possible.)
- PhoneUtils.cancelMmiCode(mPhone);
-
- // Regardless of whether the current MMI code was cancelable, the
- // PhoneApp will get an MMI_COMPLETE event very soon, which will
- // take us to the MMI Complete dialog (see
- // PhoneUtils.displayMMIComplete().)
- //
- // But until that event comes in, we *don't* want to stay here on
- // the in-call screen, since we'll be visible in a
- // partially-constructed state as soon as the "MMI Started" dialog
- // gets dismissed. So let's forcibly bail out right now.
- if (DBG) log("onMMICancel: finishing InCallScreen...");
- dismissAllDialogs();
- endInCallScreenSession();
- }
-
- /**
- * Handles an MMI_COMPLETE event, which is triggered by telephony,
- * implying MMI
- */
- private void onMMIComplete(MmiCode mmiCode) {
- // Check the code to see if the request is ready to
- // finish, this includes any MMI state that is not
- // PENDING.
-
- // if phone is a CDMA phone display feature code completed message
- int phoneType = mPhone.getPhoneType();
- if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
- PhoneUtils.displayMMIComplete(mPhone, mApp, mmiCode, null, null);
- } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
- if (mmiCode.getState() != MmiCode.State.PENDING) {
- if (DBG) log("Got MMI_COMPLETE, finishing InCallScreen...");
- dismissAllDialogs();
- endInCallScreenSession();
- }
- }
- }
-
- /**
- * Handles the POST_ON_DIAL_CHARS message from the Phone
- * (see our call to mPhone.setOnPostDialCharacter() above.)
- *
- * TODO: NEED TO TEST THIS SEQUENCE now that we no longer handle
- * "dialable" key events here in the InCallScreen: we do directly to the
- * Dialer UI instead. Similarly, we may now need to go directly to the
- * Dialer to handle POST_ON_DIAL_CHARS too.
- */
- private void handlePostOnDialChars(AsyncResult r, char ch) {
- Connection c = (Connection) r.result;
-
- if (c != null) {
- Connection.PostDialState state =
- (Connection.PostDialState) r.userObj;
-
- if (VDBG) log("handlePostOnDialChar: state = " +
- state + ", ch = " + ch);
-
- switch (state) {
- case STARTED:
- mDialer.stopLocalToneIfNeeded();
- if (mPauseInProgress) {
- /**
- * Note that on some devices, this will never happen,
- * because we will not ever enter the PAUSE state.
- */
- showPausePromptDialog(c, mPostDialStrAfterPause);
- }
- mPauseInProgress = false;
- mDialer.startLocalToneIfNeeded(ch);
-
- // TODO: is this needed, now that you can't actually
- // type DTMF chars or dial directly from here?
- // If so, we'd need to yank you out of the in-call screen
- // here too (and take you to the 12-key dialer in "in-call" mode.)
- // displayPostDialedChar(ch);
- break;
-
- case WAIT:
- // wait shows a prompt.
- if (DBG) log("handlePostOnDialChars: show WAIT prompt...");
- mDialer.stopLocalToneIfNeeded();
- String postDialStr = c.getRemainingPostDialString();
- showWaitPromptDialog(c, postDialStr);
- break;
-
- case WILD:
- if (DBG) log("handlePostOnDialChars: show WILD prompt");
- mDialer.stopLocalToneIfNeeded();
- showWildPromptDialog(c);
- break;
-
- case COMPLETE:
- mDialer.stopLocalToneIfNeeded();
- break;
-
- case PAUSE:
- // pauses for a brief period of time then continue dialing.
- mDialer.stopLocalToneIfNeeded();
- mPostDialStrAfterPause = c.getRemainingPostDialString();
- mPauseInProgress = true;
- break;
-
- default:
- break;
- }
- }
- }
-
- /**
- * Pop up an alert dialog with OK and Cancel buttons to allow user to
- * Accept or Reject the WAIT inserted as part of the Dial string.
- */
- private void showWaitPromptDialog(final Connection c, String postDialStr) {
- if (DBG) log("showWaitPromptDialogChoice: '" + postDialStr + "'...");
-
- Resources r = getResources();
- StringBuilder buf = new StringBuilder();
- buf.append(r.getText(R.string.wait_prompt_str));
- buf.append(postDialStr);
-
- // if (DBG) log("- mWaitPromptDialog = " + mWaitPromptDialog);
- if (mWaitPromptDialog != null) {
- if (DBG) log("- DISMISSING mWaitPromptDialog.");
- mWaitPromptDialog.dismiss(); // safe even if already dismissed
- mWaitPromptDialog = null;
- }
-
- mWaitPromptDialog = new AlertDialog.Builder(this)
- .setMessage(buf.toString())
- .setPositiveButton(R.string.pause_prompt_yes,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int whichButton) {
- if (DBG) log("handle WAIT_PROMPT_CONFIRMED, proceed...");
- c.proceedAfterWaitChar();
- }
- })
- .setNegativeButton(R.string.pause_prompt_no,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int whichButton) {
- if (DBG) log("handle POST_DIAL_CANCELED!");
- c.cancelPostDial();
- }
- })
- .create();
- mWaitPromptDialog.getWindow().addFlags(
- WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
-
- mWaitPromptDialog.show();
- }
-
- /**
- * Pop up an alert dialog which waits for 2 seconds for each P (Pause) Character entered
- * as part of the Dial String.
- */
- private void showPausePromptDialog(final Connection c, String postDialStrAfterPause) {
- Resources r = getResources();
- StringBuilder buf = new StringBuilder();
- buf.append(r.getText(R.string.pause_prompt_str));
- buf.append(postDialStrAfterPause);
-
- if (mPausePromptDialog != null) {
- if (DBG) log("- DISMISSING mPausePromptDialog.");
- mPausePromptDialog.dismiss(); // safe even if already dismissed
- mPausePromptDialog = null;
- }
-
- mPausePromptDialog = new AlertDialog.Builder(this)
- .setMessage(buf.toString())
- .create();
- mPausePromptDialog.show();
- // 2 second timer
- Message msg = Message.obtain(mHandler, EVENT_PAUSE_DIALOG_COMPLETE);
- mHandler.sendMessageDelayed(msg, PAUSE_PROMPT_DIALOG_TIMEOUT);
- }
-
- private View createWildPromptView() {
- LinearLayout result = new LinearLayout(this);
- result.setOrientation(LinearLayout.VERTICAL);
- result.setPadding(5, 5, 5, 5);
-
- LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT);
-
- TextView promptMsg = new TextView(this);
- promptMsg.setTextSize(14);
- promptMsg.setTypeface(Typeface.DEFAULT_BOLD);
- promptMsg.setText(getResources().getText(R.string.wild_prompt_str));
-
- result.addView(promptMsg, lp);
-
- mWildPromptText = new EditText(this);
- mWildPromptText.setKeyListener(DialerKeyListener.getInstance());
- mWildPromptText.setMovementMethod(null);
- mWildPromptText.setTextSize(14);
- mWildPromptText.setMaxLines(1);
- mWildPromptText.setHorizontallyScrolling(true);
- mWildPromptText.setBackgroundResource(android.R.drawable.editbox_background);
-
- LinearLayout.LayoutParams lp2 = new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT);
- lp2.setMargins(0, 3, 0, 0);
-
- result.addView(mWildPromptText, lp2);
-
- return result;
- }
-
- private void showWildPromptDialog(final Connection c) {
- View v = createWildPromptView();
-
- if (mWildPromptDialog != null) {
- if (VDBG) log("- DISMISSING mWildPromptDialog.");
- mWildPromptDialog.dismiss(); // safe even if already dismissed
- mWildPromptDialog = null;
- }
-
- mWildPromptDialog = new AlertDialog.Builder(this)
- .setView(v)
- .setPositiveButton(
- R.string.send_button,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int whichButton) {
- if (VDBG) log("handle WILD_PROMPT_CHAR_ENTERED, proceed...");
- String replacement = null;
- if (mWildPromptText != null) {
- replacement = mWildPromptText.getText().toString();
- mWildPromptText = null;
- }
- c.proceedAfterWildChar(replacement);
- mApp.pokeUserActivity();
- }
- })
- .setOnCancelListener(
- new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog) {
- if (VDBG) log("handle POST_DIAL_CANCELED!");
- c.cancelPostDial();
- mApp.pokeUserActivity();
- }
- })
- .create();
- mWildPromptDialog.getWindow().addFlags(
- WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
- mWildPromptDialog.show();
-
- mWildPromptText.requestFocus();
- }
-
- /**
- * Updates the state of the in-call UI based on the current state of
- * the Phone. This call has no effect if we're not currently the
- * foreground activity.
- *
- * This method is only allowed to be called from the UI thread (since it
- * manipulates our View hierarchy). If you need to update the screen from
- * some other thread, or if you just want to "post a request" for the screen
- * to be updated (rather than doing it synchronously), call
- * requestUpdateScreen() instead.
- *
- * Right now this method will update UI visibility immediately, with no animation.
- * TODO: have animate flag here and use it anywhere possible.
- */
- private void updateScreen() {
- if (DBG) log("updateScreen()...");
- final InCallScreenMode inCallScreenMode = mApp.inCallUiState.inCallScreenMode;
- if (VDBG) {
- PhoneConstants.State state = mCM.getState();
- log(" - phone state = " + state);
- log(" - inCallScreenMode = " + inCallScreenMode);
- }
-
- // Don't update anything if we're not in the foreground (there's
- // no point updating our UI widgets since we're not visible!)
- // Also note this check also ensures we won't update while we're
- // in the middle of pausing, which could cause a visible glitch in
- // the "activity ending" transition.
- if (!mIsForegroundActivity) {
- if (DBG) log("- updateScreen: not the foreground Activity! Bailing out...");
- return;
- }
-
- if (inCallScreenMode == InCallScreenMode.OTA_NORMAL) {
- if (DBG) log("- updateScreen: OTA call state NORMAL (NOT updating in-call UI)...");
- mCallCard.setVisibility(View.GONE);
- if (mApp.otaUtils != null) {
- mApp.otaUtils.otaShowProperScreen();
- } else {
- Log.w(LOG_TAG, "OtaUtils object is null, not showing any screen for that.");
- }
- return; // Return without updating in-call UI.
- } else if (inCallScreenMode == InCallScreenMode.OTA_ENDED) {
- if (DBG) log("- updateScreen: OTA call ended state (NOT updating in-call UI)...");
- mCallCard.setVisibility(View.GONE);
- // Wake up the screen when we get notification, good or bad.
- mApp.wakeUpScreen();
- if (mApp.cdmaOtaScreenState.otaScreenState
- == CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION) {
- if (DBG) log("- updateScreen: OTA_STATUS_ACTIVATION");
- if (mApp.otaUtils != null) {
- if (DBG) log("- updateScreen: mApp.otaUtils is not null, "
- + "call otaShowActivationScreen");
- mApp.otaUtils.otaShowActivateScreen();
- }
- } else {
- if (DBG) log("- updateScreen: OTA Call end state for Dialogs");
- if (mApp.otaUtils != null) {
- if (DBG) log("- updateScreen: Show OTA Success Failure dialog");
- mApp.otaUtils.otaShowSuccessFailure();
- }
- }
- return; // Return without updating in-call UI.
- } else if (inCallScreenMode == InCallScreenMode.MANAGE_CONFERENCE) {
- if (DBG) log("- updateScreen: manage conference mode (NOT updating in-call UI)...");
- mCallCard.setVisibility(View.GONE);
- updateManageConferencePanelIfNecessary();
- return; // Return without updating in-call UI.
- } else if (inCallScreenMode == InCallScreenMode.CALL_ENDED) {
- if (DBG) log("- updateScreen: call ended state...");
- // Continue with the rest of updateScreen() as usual, since we do
- // need to update the background (to the special "call ended" color)
- // and the CallCard (to show the "Call ended" label.)
- }
-
- if (DBG) log("- updateScreen: updating the in-call UI...");
- // Note we update the InCallTouchUi widget before the CallCard,
- // since the CallCard adjusts its size based on how much vertical
- // space the InCallTouchUi widget needs.
- updateInCallTouchUi();
- mCallCard.updateState(mCM);
-
- // If an incoming call is ringing, make sure the dialpad is
- // closed. (We do this to make sure we're not covering up the
- // "incoming call" UI.)
- if (mCM.getState() == PhoneConstants.State.RINGING) {
- if (mDialer.isOpened()) {
- Log.i(LOG_TAG, "During RINGING state we force hiding dialpad.");
- closeDialpadInternal(false); // don't do the "closing" animation
- }
-
- // At this point, we are guranteed that the dialer is closed.
- // This means that it is safe to clear out the "history" of DTMF digits
- // you may have typed into the previous call (so you don't see the
- // previous call's digits if you answer this call and then bring up the
- // dialpad.)
- //
- // TODO: it would be more precise to do this when you *answer* the
- // incoming call, rather than as soon as it starts ringing, but
- // the InCallScreen doesn't keep enough state right now to notice
- // that specific transition in onPhoneStateChanged().
- // TODO: This clears out the dialpad context as well so when a second
- // call comes in while a voicemail call is happening, the voicemail
- // dialpad will no longer have the "Voice Mail" context. It's a small
- // case so not terribly bad, but we need to maintain a better
- // call-to-callstate mapping before we can fix this.
- mDialer.clearDigits();
- }
-
-
- // Now that we're sure DTMF dialpad is in an appropriate state, reflect
- // the dialpad state into CallCard
- updateCallCardVisibilityPerDialerState(false);
-
- updateProgressIndication();
-
- // Forcibly take down all dialog if an incoming call is ringing.
- if (mCM.hasActiveRingingCall()) {
- dismissAllDialogs();
- } else {
- // Wait prompt dialog is not currently up. But it *should* be
- // up if the FG call has a connection in the WAIT state and
- // the phone isn't ringing.
- String postDialStr = null;
- List<Connection> fgConnections = mCM.getFgCallConnections();
- int phoneType = mCM.getFgPhone().getPhoneType();
- if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
- Connection fgLatestConnection = mCM.getFgCallLatestConnection();
- if (mApp.cdmaPhoneCallState.getCurrentCallState() ==
- CdmaPhoneCallState.PhoneCallState.CONF_CALL) {
- for (Connection cn : fgConnections) {
- if ((cn != null) && (cn.getPostDialState() ==
- Connection.PostDialState.WAIT)) {
- cn.cancelPostDial();
- }
- }
- } else if ((fgLatestConnection != null)
- && (fgLatestConnection.getPostDialState() == Connection.PostDialState.WAIT)) {
- if(DBG) log("show the Wait dialog for CDMA");
- postDialStr = fgLatestConnection.getRemainingPostDialString();
- showWaitPromptDialog(fgLatestConnection, postDialStr);
- }
- } else if ((phoneType == PhoneConstants.PHONE_TYPE_GSM)
- || (phoneType == PhoneConstants.PHONE_TYPE_SIP)) {
- for (Connection cn : fgConnections) {
- if ((cn != null) && (cn.getPostDialState() == Connection.PostDialState.WAIT)) {
- postDialStr = cn.getRemainingPostDialString();
- showWaitPromptDialog(cn, postDialStr);
- }
- }
- } else {
- throw new IllegalStateException("Unexpected phone type: " + phoneType);
- }
- }
- }
-
- /**
- * (Re)synchronizes the onscreen UI with the current state of the
- * telephony framework.
- *
- * @return SyncWithPhoneStateStatus.SUCCESS if we successfully updated the UI, or
- * SyncWithPhoneStateStatus.PHONE_NOT_IN_USE if there was no phone state to sync
- * with (ie. the phone was completely idle). In the latter case, we
- * shouldn't even be in the in-call UI in the first place, and it's
- * the caller's responsibility to bail out of this activity by
- * calling endInCallScreenSession if appropriate.
- *
- * This method directly calls updateScreen() in the normal "phone is
- * in use" case, so there's no need for the caller to do so.
- */
- private SyncWithPhoneStateStatus syncWithPhoneState() {
- boolean updateSuccessful = false;
- if (DBG) log("syncWithPhoneState()...");
- if (DBG) PhoneUtils.dumpCallState(mPhone);
-
- // Make sure the Phone is "in use". (If not, we shouldn't be on
- // this screen in the first place.)
-
- // An active or just-ended OTA call counts as "in use".
- if (TelephonyCapabilities.supportsOtasp(mCM.getFgPhone())
- && ((mApp.inCallUiState.inCallScreenMode == InCallScreenMode.OTA_NORMAL)
- || (mApp.inCallUiState.inCallScreenMode == InCallScreenMode.OTA_ENDED))) {
- // Even when OTA Call ends, need to show OTA End UI,
- // so return Success to allow UI update.
- return SyncWithPhoneStateStatus.SUCCESS;
- }
-
- // If an MMI code is running that also counts as "in use".
- //
- // TODO: We currently only call getPendingMmiCodes() for GSM
- // phones. (The code's been that way all along.) But CDMAPhone
- // does in fact implement getPendingMmiCodes(), so should we
- // check that here regardless of the phone type?
- boolean hasPendingMmiCodes =
- (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM)
- && !mPhone.getPendingMmiCodes().isEmpty();
-
- // Finally, it's also OK to stay here on the InCallScreen if we
- // need to display a progress indicator while something's
- // happening in the background.
- boolean showProgressIndication = mApp.inCallUiState.isProgressIndicationActive();
-
- boolean showScreenEvenAfterDisconnect = mApp.inCallUiState.showAlreadyDisconnectedState;
-
- if (mCM.hasActiveFgCall() || mCM.hasActiveBgCall() || mCM.hasActiveRingingCall()
- || hasPendingMmiCodes || showProgressIndication || showScreenEvenAfterDisconnect) {
- if (VDBG) log("syncWithPhoneState: it's ok to be here; update the screen...");
- updateScreen();
- return SyncWithPhoneStateStatus.SUCCESS;
- }
-
- Log.i(LOG_TAG, "syncWithPhoneState: phone is idle (shouldn't be here)");
- return SyncWithPhoneStateStatus.PHONE_NOT_IN_USE;
- }
-
-
-
- private void handleMissingVoiceMailNumber() {
- if (DBG) log("handleMissingVoiceMailNumber");
-
- final Message msg = Message.obtain(mHandler);
- msg.what = DONT_ADD_VOICEMAIL_NUMBER;
-
- final Message msg2 = Message.obtain(mHandler);
- msg2.what = ADD_VOICEMAIL_NUMBER;
-
- mMissingVoicemailDialog = new AlertDialog.Builder(this)
- .setTitle(R.string.no_vm_number)
- .setMessage(R.string.no_vm_number_msg)
- .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- if (VDBG) log("Missing voicemail AlertDialog: POSITIVE click...");
- msg.sendToTarget(); // see dontAddVoiceMailNumber()
- mApp.pokeUserActivity();
- }})
- .setNegativeButton(R.string.add_vm_number_str,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- if (VDBG) log("Missing voicemail AlertDialog: NEGATIVE click...");
- msg2.sendToTarget(); // see addVoiceMailNumber()
- mApp.pokeUserActivity();
- }})
- .setOnCancelListener(new OnCancelListener() {
- public void onCancel(DialogInterface dialog) {
- if (VDBG) log("Missing voicemail AlertDialog: CANCEL handler...");
- msg.sendToTarget(); // see dontAddVoiceMailNumber()
- mApp.pokeUserActivity();
- }})
- .create();
-
- // When the dialog is up, completely hide the in-call UI
- // underneath (which is in a partially-constructed state).
- mMissingVoicemailDialog.getWindow().addFlags(
- WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-
- mMissingVoicemailDialog.show();
- }
-
- private void addVoiceMailNumberPanel() {
- if (mMissingVoicemailDialog != null) {
- mMissingVoicemailDialog.dismiss();
- mMissingVoicemailDialog = null;
- }
- if (DBG) log("addVoiceMailNumberPanel: finishing InCallScreen...");
- endInCallScreenSession();
-
- if (DBG) log("show vm setting");
-
- // navigate to the Voicemail setting in the Call Settings activity.
- Intent intent = new Intent(CallFeaturesSetting.ACTION_ADD_VOICEMAIL);
- intent.setClass(this, CallFeaturesSetting.class);
- startActivity(intent);
- }
-
- private void dontAddVoiceMailNumber() {
- if (mMissingVoicemailDialog != null) {
- mMissingVoicemailDialog.dismiss();
- mMissingVoicemailDialog = null;
- }
- if (DBG) log("dontAddVoiceMailNumber: finishing InCallScreen...");
- endInCallScreenSession();
- }
-
- /**
- * Do some delayed cleanup after a Phone call gets disconnected.
- *
- * This method gets called a couple of seconds after any DISCONNECT
- * event from the Phone; it's triggered by the
- * DELAYED_CLEANUP_AFTER_DISCONNECT message we send in onDisconnect().
- *
- * If the Phone is totally idle right now, that means we've already
- * shown the "call ended" state for a couple of seconds, and it's now
- * time to endInCallScreenSession this activity.
- *
- * If the Phone is *not* idle right now, that probably means that one
- * call ended but the other line is still in use. In that case, do
- * nothing, and instead stay here on the InCallScreen.
- */
- private void delayedCleanupAfterDisconnect() {
- if (VDBG) log("delayedCleanupAfterDisconnect()... Phone state = " + mCM.getState());
-
- // Clean up any connections in the DISCONNECTED state.
- //
- // [Background: Even after a connection gets disconnected, its
- // Connection object still stays around, in the special
- // DISCONNECTED state. This is necessary because we we need the
- // caller-id information from that Connection to properly draw the
- // "Call ended" state of the CallCard.
- // But at this point we truly don't need that connection any
- // more, so tell the Phone that it's now OK to to clean up any
- // connections still in that state.]
- mCM.clearDisconnected();
-
- // There are two cases where we should *not* exit the InCallScreen:
- // (1) Phone is still in use
- // or
- // (2) There's an active progress indication (i.e. the "Retrying..."
- // progress dialog) that we need to continue to display.
-
- boolean stayHere = phoneIsInUse() || mApp.inCallUiState.isProgressIndicationActive();
-
- if (stayHere) {
- if (DBG) log("- delayedCleanupAfterDisconnect: staying on the InCallScreen...");
- } else {
- // Phone is idle! We should exit the in-call UI now.
- if (DBG) log("- delayedCleanupAfterDisconnect: phone is idle...");
-
- // And (finally!) exit from the in-call screen
- // (but not if we're already in the process of pausing...)
- if (mIsForegroundActivity) {
- if (DBG) log("- delayedCleanupAfterDisconnect: finishing InCallScreen...");
-
- // In some cases we finish the call by taking the user to the
- // Call Log. Otherwise, we simply call endInCallScreenSession,
- // which will take us back to wherever we came from.
- //
- // UI note: In eclair and earlier, we went to the Call Log
- // after outgoing calls initiated on the device, but never for
- // incoming calls. Now we do it for incoming calls too, as
- // long as the call was answered by the user. (We always go
- // back where you came from after a rejected or missed incoming
- // call.)
- //
- // And in any case, *never* go to the call log if we're in
- // emergency mode (i.e. if the screen is locked and a lock
- // pattern or PIN/password is set), or if we somehow got here
- // on a non-voice-capable device.
-
- if (VDBG) log("- Post-call behavior:");
- if (VDBG) log(" - mLastDisconnectCause = " + mLastDisconnectCause);
- if (VDBG) log(" - isPhoneStateRestricted() = " + isPhoneStateRestricted());
-
- // DisconnectCause values in the most common scenarios:
- // - INCOMING_MISSED: incoming ringing call times out, or the
- // other end hangs up while still ringing
- // - INCOMING_REJECTED: user rejects the call while ringing
- // - LOCAL: user hung up while a call was active (after
- // answering an incoming call, or after making an
- // outgoing call)
- // - NORMAL: the other end hung up (after answering an incoming
- // call, or after making an outgoing call)
-
- if ((mLastDisconnectCause != Connection.DisconnectCause.INCOMING_MISSED)
- && (mLastDisconnectCause != Connection.DisconnectCause.INCOMING_REJECTED)
- && !isPhoneStateRestricted()
- && PhoneGlobals.sVoiceCapable) {
- final Intent intent = mApp.createPhoneEndIntentUsingCallOrigin();
- ActivityOptions opts = ActivityOptions.makeCustomAnimation(this,
- R.anim.activity_close_enter, R.anim.activity_close_exit);
- if (VDBG) {
- log("- Show Call Log (or Dialtacts) after disconnect. Current intent: "
- + intent);
- }
- try {
- startActivity(intent, opts.toBundle());
- } catch (ActivityNotFoundException e) {
- // Don't crash if there's somehow no "Call log" at
- // all on this device.
- // (This should never happen, though, since we already
- // checked PhoneApp.sVoiceCapable above, and any
- // voice-capable device surely *should* have a call
- // log activity....)
- Log.w(LOG_TAG, "delayedCleanupAfterDisconnect: "
- + "transition to call log failed; intent = " + intent);
- // ...so just return back where we came from....
- }
- // Even if we did go to the call log, note that we still
- // call endInCallScreenSession (below) to make sure we don't
- // stay in the activity history.
- }
-
- }
- endInCallScreenSession();
-
- // Reset the call origin when the session ends and this in-call UI is being finished.
- mApp.setLatestActiveCallOrigin(null);
- }
- }
-
-
- /**
- * View.OnClickListener implementation.
- *
- * This method handles clicks from UI elements that use the
- * InCallScreen itself as their OnClickListener.
- *
- * Note: Currently this method is used only for a few special buttons:
- * - the mButtonManageConferenceDone "Back to call" button
- * - the "dim" effect for the secondary call photo in CallCard as the second "swap" button
- * - other OTASP-specific buttons managed by OtaUtils.java.
- *
- * *Most* in-call controls are handled by the handleOnscreenButtonClick() method, via the
- * InCallTouchUi widget.
- */
- @Override
- public void onClick(View view) {
- int id = view.getId();
- if (VDBG) log("onClick(View " + view + ", id " + id + ")...");
-
- switch (id) {
- case R.id.manage_done: // mButtonManageConferenceDone
- if (VDBG) log("onClick: mButtonManageConferenceDone...");
- // Hide the Manage Conference panel, return to NORMAL mode.
- setInCallScreenMode(InCallScreenMode.NORMAL);
- requestUpdateScreen();
- break;
-
- case R.id.dim_effect_for_secondary_photo:
- if (mInCallControlState.canSwap) {
- internalSwapCalls();
- }
- break;
-
- default:
- // Presumably one of the OTASP-specific buttons managed by
- // OtaUtils.java.
- // (TODO: It would be cleaner for the OtaUtils instance itself to
- // be the OnClickListener for its own buttons.)
-
- if ((mApp.inCallUiState.inCallScreenMode == InCallScreenMode.OTA_NORMAL
- || mApp.inCallUiState.inCallScreenMode == InCallScreenMode.OTA_ENDED)
- && mApp.otaUtils != null) {
- mApp.otaUtils.onClickHandler(id);
- } else {
- // Uh oh: we *should* only receive clicks here from the
- // buttons managed by OtaUtils.java, but if we're not in one
- // of the special OTASP modes, those buttons shouldn't have
- // been visible in the first place.
- Log.w(LOG_TAG,
- "onClick: unexpected click from ID " + id + " (View = " + view + ")");
- }
- break;
- }
-
- EventLog.writeEvent(EventLogTags.PHONE_UI_BUTTON_CLICK,
- (view instanceof TextView) ? ((TextView) view).getText() : "");
-
- // Clicking any onscreen UI element counts as explicit "user activity".
- mApp.pokeUserActivity();
- }
-
- private void onHoldClick() {
- final boolean hasActiveCall = mCM.hasActiveFgCall();
- final boolean hasHoldingCall = mCM.hasActiveBgCall();
- log("onHoldClick: hasActiveCall = " + hasActiveCall
- + ", hasHoldingCall = " + hasHoldingCall);
- boolean newHoldState;
- boolean holdButtonEnabled;
- if (hasActiveCall && !hasHoldingCall) {
- // There's only one line in use, and that line is active.
- PhoneUtils.switchHoldingAndActive(
- mCM.getFirstActiveBgCall()); // Really means "hold" in this state
- newHoldState = true;
- holdButtonEnabled = true;
- } else if (!hasActiveCall && hasHoldingCall) {
- // There's only one line in use, and that line is on hold.
- PhoneUtils.switchHoldingAndActive(
- mCM.getFirstActiveBgCall()); // Really means "unhold" in this state
- newHoldState = false;
- holdButtonEnabled = true;
- } else {
- // Either zero or 2 lines are in use; "hold/unhold" is meaningless.
- newHoldState = false;
- holdButtonEnabled = false;
- }
- // No need to forcibly update the onscreen UI; just wait for the
- // onPhoneStateChanged() callback. (This seems to be responsive
- // enough.)
-
- // Also, any time we hold or unhold, force the DTMF dialpad to close.
- closeDialpadInternal(true); // do the "closing" animation
- }
-
- /**
- * Toggles in-call audio between speaker and the built-in earpiece (or
- * wired headset.)
- */
- public void toggleSpeaker() {
- // TODO: Turning on the speaker seems to enable the mic
- // whether or not the "mute" feature is active!
- // Not sure if this is an feature of the telephony API
- // that I need to handle specially, or just a bug.
- boolean newSpeakerState = !PhoneUtils.isSpeakerOn(this);
- log("toggleSpeaker(): newSpeakerState = " + newSpeakerState);
-
- PhoneUtils.turnOnSpeaker(this, newSpeakerState, true);
-
- // And update the InCallTouchUi widget (since the "audio mode"
- // button might need to change its appearance based on the new
- // audio state.)
- updateInCallTouchUi();
- }
-
- /*
- * onMuteClick is called only when there is a foreground call
- */
- private void onMuteClick() {
- boolean newMuteState = !PhoneUtils.getMute();
- log("onMuteClick(): newMuteState = " + newMuteState);
- PhoneUtils.setMute(newMuteState);
- }
-
- /**
- * Handle a click on the "Open/Close dialpad" button.
- *
- * @see DTMFTwelveKeyDialer#openDialer(boolean)
- * @see DTMFTwelveKeyDialer#closeDialer(boolean)
- */
- private void onOpenCloseDialpad() {
- if (VDBG) log("onOpenCloseDialpad()...");
- if (mDialer.isOpened()) {
- closeDialpadInternal(true); // do the "closing" animation
- } else {
- openDialpadInternal(true); // do the "opening" animation
- }
- }
-
- /** Internal wrapper around {@link DTMFTwelveKeyDialer#openDialer(boolean)} */
- private void openDialpadInternal(boolean animate) {
- mDialer.openDialer(animate);
- // And update the InCallUiState (so that we'll restore the dialpad
- // to the correct state if we get paused/resumed).
- mApp.inCallUiState.showDialpad = true;
- }
-
- // Internal wrapper around DTMFTwelveKeyDialer.closeDialer()
- private void closeDialpadInternal(boolean animate) {
- mDialer.closeDialer(animate);
- // And update the InCallUiState (so that we'll restore the dialpad
- // to the correct state if we get paused/resumed).
- mApp.inCallUiState.showDialpad = false;
- }
-
- /**
- * Handles button clicks from the InCallTouchUi widget.
- */
- /* package */ void handleOnscreenButtonClick(int id) {
- if (DBG) log("handleOnscreenButtonClick(id " + id + ")...");
-
- switch (id) {
- // Actions while an incoming call is ringing:
- case R.id.incomingCallAnswer:
- internalAnswerCall();
- break;
- case R.id.incomingCallReject:
- hangupRingingCall();
- break;
- case R.id.incomingCallRespondViaSms:
- internalRespondViaSms();
- break;
-
- // The other regular (single-tap) buttons used while in-call:
- case R.id.holdButton:
- onHoldClick();
- break;
- case R.id.swapButton:
- internalSwapCalls();
- break;
- case R.id.endButton:
- internalHangup();
- break;
- case R.id.dialpadButton:
- onOpenCloseDialpad();
- break;
- case R.id.muteButton:
- onMuteClick();
- break;
- case R.id.addButton:
- PhoneUtils.startNewCall(mCM); // Fires off an ACTION_DIAL intent
- break;
- case R.id.mergeButton:
- case R.id.cdmaMergeButton:
- PhoneUtils.mergeCalls(mCM);
- break;
- case R.id.manageConferenceButton:
- // Show the Manage Conference panel.
- setInCallScreenMode(InCallScreenMode.MANAGE_CONFERENCE);
- requestUpdateScreen();
- break;
-
- default:
- Log.w(LOG_TAG, "handleOnscreenButtonClick: unexpected ID " + id);
- break;
- }
-
- // Clicking any onscreen UI element counts as explicit "user activity".
- mApp.pokeUserActivity();
-
- // Just in case the user clicked a "stateful" UI element (like one
- // of the toggle buttons), we force the in-call buttons to update,
- // to make sure the user sees the *new* current state.
- //
- // Note that some in-call buttons will *not* immediately change the
- // state of the UI, namely those that send a request to the telephony
- // layer (like "Hold" or "End call".) For those buttons, the
- // updateInCallTouchUi() call here won't have any visible effect.
- // Instead, the UI will be updated eventually when the next
- // onPhoneStateChanged() event comes in and triggers an updateScreen()
- // call.
- //
- // TODO: updateInCallTouchUi() is overkill here; it would be
- // more efficient to update *only* the affected button(s).
- // (But this isn't a big deal since updateInCallTouchUi() is pretty
- // cheap anyway...)
- updateInCallTouchUi();
- }
-
- /**
- * Display a status or error indication to the user according to the
- * specified InCallUiState.CallStatusCode value.
- */
- private void showStatusIndication(CallStatusCode status) {
- switch (status) {
- case SUCCESS:
- // The InCallScreen does not need to display any kind of error indication,
- // so we shouldn't have gotten here in the first place.
- Log.wtf(LOG_TAG, "showStatusIndication: nothing to display");
- break;
-
- case POWER_OFF:
- // Radio is explictly powered off, presumably because the
- // device is in airplane mode.
- //
- // TODO: For now this UI is ultra-simple: we simply display
- // a message telling the user to turn off airplane mode.
- // But it might be nicer for the dialog to offer the option
- // to turn the radio on right there (and automatically retry
- // the call once network registration is complete.)
- showGenericErrorDialog(R.string.incall_error_power_off,
- true /* isStartupError */);
- break;
-
- case EMERGENCY_ONLY:
- // Only emergency numbers are allowed, but we tried to dial
- // a non-emergency number.
- // (This state is currently unused; see comments above.)
- showGenericErrorDialog(R.string.incall_error_emergency_only,
- true /* isStartupError */);
- break;
-
- case OUT_OF_SERVICE:
- // No network connection.
- showGenericErrorDialog(R.string.incall_error_out_of_service,
- true /* isStartupError */);
- break;
-
- case NO_PHONE_NUMBER_SUPPLIED:
- // The supplied Intent didn't contain a valid phone number.
- // (This is rare and should only ever happen with broken
- // 3rd-party apps.) For now just show a generic error.
- showGenericErrorDialog(R.string.incall_error_no_phone_number_supplied,
- true /* isStartupError */);
- break;
-
- case DIALED_MMI:
- // Our initial phone number was actually an MMI sequence.
- // There's no real "error" here, but we do bring up the
- // a Toast (as requested of the New UI paradigm).
- //
- // In-call MMIs do not trigger the normal MMI Initiate
- // Notifications, so we should notify the user here.
- // Otherwise, the code in PhoneUtils.java should handle
- // user notifications in the form of Toasts or Dialogs.
- if (mCM.getState() == PhoneConstants.State.OFFHOOK) {
- Toast.makeText(mApp, R.string.incall_status_dialed_mmi, Toast.LENGTH_SHORT)
- .show();
- }
- break;
-
- case CALL_FAILED:
- // We couldn't successfully place the call; there was some
- // failure in the telephony layer.
- // TODO: Need UI spec for this failure case; for now just
- // show a generic error.
- showGenericErrorDialog(R.string.incall_error_call_failed,
- true /* isStartupError */);
- break;
-
- case VOICEMAIL_NUMBER_MISSING:
- // We tried to call a voicemail: URI but the device has no
- // voicemail number configured.
- handleMissingVoiceMailNumber();
- break;
-
- case CDMA_CALL_LOST:
- // This status indicates that InCallScreen should display the
- // CDMA-specific "call lost" dialog. (If an outgoing call fails,
- // and the CDMA "auto-retry" feature is enabled, *and* the retried
- // call fails too, we display this specific dialog.)
- //
- // TODO: currently unused; see InCallUiState.needToShowCallLostDialog
- break;
-
- case EXITED_ECM:
- // This status indicates that InCallScreen needs to display a
- // warning that we're exiting ECM (emergency callback mode).
- showExitingECMDialog();
- break;
-
- default:
- throw new IllegalStateException(
- "showStatusIndication: unexpected status code: " + status);
- }
-
- // TODO: still need to make sure that pressing OK or BACK from
- // *any* of the dialogs we launch here ends up calling
- // inCallUiState.clearPendingCallStatusCode()
- // *and*
- // make sure the Dialog handles both OK *and* cancel by calling
- // endInCallScreenSession. (See showGenericErrorDialog() for an
- // example.)
- //
- // (showGenericErrorDialog() currently does this correctly,
- // but handleMissingVoiceMailNumber() probably needs to be fixed too.)
- //
- // Also need to make sure that bailing out of any of these dialogs by
- // pressing Home clears out the pending status code too. (If you do
- // that, neither the dialog's clickListener *or* cancelListener seems
- // to run...)
- }
-
- /**
- * Utility function to bring up a generic "error" dialog, and then bail
- * out of the in-call UI when the user hits OK (or the BACK button.)
- */
- private void showGenericErrorDialog(int resid, boolean isStartupError) {
- CharSequence msg = getResources().getText(resid);
- if (DBG) log("showGenericErrorDialog('" + msg + "')...");
-
- // create the clicklistener and cancel listener as needed.
- DialogInterface.OnClickListener clickListener;
- OnCancelListener cancelListener;
- if (isStartupError) {
- clickListener = new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- bailOutAfterErrorDialog();
- }};
- cancelListener = new OnCancelListener() {
- public void onCancel(DialogInterface dialog) {
- bailOutAfterErrorDialog();
- }};
- } else {
- clickListener = new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- delayedCleanupAfterDisconnect();
- }};
- cancelListener = new OnCancelListener() {
- public void onCancel(DialogInterface dialog) {
- delayedCleanupAfterDisconnect();
- }};
- }
-
- // TODO: Consider adding a setTitle() call here (with some generic
- // "failure" title?)
- mGenericErrorDialog = new AlertDialog.Builder(this)
- .setMessage(msg)
- .setPositiveButton(R.string.ok, clickListener)
- .setOnCancelListener(cancelListener)
- .create();
-
- // When the dialog is up, completely hide the in-call UI
- // underneath (which is in a partially-constructed state).
- mGenericErrorDialog.getWindow().addFlags(
- WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-
- mGenericErrorDialog.show();
- }
-
- private void showCallLostDialog() {
- if (DBG) log("showCallLostDialog()...");
-
- // Don't need to show the dialog if InCallScreen isn't in the forgeround
- if (!mIsForegroundActivity) {
- if (DBG) log("showCallLostDialog: not the foreground Activity! Bailing out...");
- return;
- }
-
- // Don't need to show the dialog again, if there is one already.
- if (mCallLostDialog != null) {
- if (DBG) log("showCallLostDialog: There is a mCallLostDialog already.");
- return;
- }
-
- mCallLostDialog = new AlertDialog.Builder(this)
- .setMessage(R.string.call_lost)
- .setIconAttribute(android.R.attr.alertDialogIcon)
- .create();
- mCallLostDialog.show();
- }
-
- /**
- * Displays the "Exiting ECM" warning dialog.
- *
- * Background: If the phone is currently in ECM (Emergency callback
- * mode) and we dial a non-emergency number, that automatically
- * *cancels* ECM. (That behavior comes from CdmaCallTracker.dial().)
- * When that happens, we need to warn the user that they're no longer
- * in ECM (bug 4207607.)
- *
- * So bring up a dialog explaining what's happening. There's nothing
- * for the user to do, by the way; we're simply providing an
- * indication that they're exiting ECM. We *could* use a Toast for
- * this, but toasts are pretty easy to miss, so instead use a dialog
- * with a single "OK" button.
- *
- * TODO: it's ugly that the code here has to make assumptions about
- * the behavior of the telephony layer (namely that dialing a
- * non-emergency number while in ECM causes us to exit ECM.)
- *
- * Instead, this warning dialog should really be triggered by our
- * handler for the
- * TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED intent in
- * PhoneApp.java. But that won't work until that intent also
- * includes a *reason* why we're exiting ECM, since we need to
- * display this dialog when exiting ECM because of an outgoing call,
- * but NOT if we're exiting ECM because the user manually turned it
- * off via the EmergencyCallbackModeExitDialog.
- *
- * Or, it might be simpler to just have outgoing non-emergency calls
- * *not* cancel ECM. That way the UI wouldn't have to do anything
- * special here.
- */
- private void showExitingECMDialog() {
- Log.i(LOG_TAG, "showExitingECMDialog()...");
-
- if (mExitingECMDialog != null) {
- if (DBG) log("- DISMISSING mExitingECMDialog.");
- mExitingECMDialog.dismiss(); // safe even if already dismissed
- mExitingECMDialog = null;
- }
-
- // When the user dismisses the "Exiting ECM" dialog, we clear out
- // the pending call status code field (since we're done with this
- // dialog), but do *not* bail out of the InCallScreen.
-
- final InCallUiState inCallUiState = mApp.inCallUiState;
- DialogInterface.OnClickListener clickListener = new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- inCallUiState.clearPendingCallStatusCode();
- }};
- OnCancelListener cancelListener = new OnCancelListener() {
- public void onCancel(DialogInterface dialog) {
- inCallUiState.clearPendingCallStatusCode();
- }};
-
- // Ultra-simple AlertDialog with only an OK button:
- mExitingECMDialog = new AlertDialog.Builder(this)
- .setMessage(R.string.progress_dialog_exiting_ecm)
- .setPositiveButton(R.string.ok, clickListener)
- .setOnCancelListener(cancelListener)
- .create();
- mExitingECMDialog.getWindow().addFlags(
- WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
- mExitingECMDialog.show();
- }
-
- private void bailOutAfterErrorDialog() {
- if (mGenericErrorDialog != null) {
- if (DBG) log("bailOutAfterErrorDialog: DISMISSING mGenericErrorDialog.");
- mGenericErrorDialog.dismiss();
- mGenericErrorDialog = null;
- }
- if (DBG) log("bailOutAfterErrorDialog(): end InCallScreen session...");
-
- // Now that the user has dismissed the error dialog (presumably by
- // either hitting the OK button or pressing Back, we can now reset
- // the pending call status code field.
- //
- // (Note that the pending call status is NOT cleared simply
- // by the InCallScreen being paused or finished, since the resulting
- // dialog is supposed to persist across orientation changes or if the
- // screen turns off.)
- //
- // See the "Error / diagnostic indications" section of
- // InCallUiState.java for more detailed info about the
- // pending call status code field.
- final InCallUiState inCallUiState = mApp.inCallUiState;
- inCallUiState.clearPendingCallStatusCode();
-
- // Force the InCallScreen to truly finish(), rather than just
- // moving it to the back of the activity stack (which is what
- // our finish() method usually does.)
- // This is necessary to avoid an obscure scenario where the
- // InCallScreen can get stuck in an inconsistent state, somehow
- // causing a *subsequent* outgoing call to fail (bug 4172599).
- endInCallScreenSession(true /* force a real finish() call */);
- }
-
- /**
- * Dismisses (and nulls out) all persistent Dialogs managed
- * by the InCallScreen. Useful if (a) we're about to bring up
- * a dialog and want to pre-empt any currently visible dialogs,
- * or (b) as a cleanup step when the Activity is going away.
- */
- private void dismissAllDialogs() {
- if (DBG) log("dismissAllDialogs()...");
-
- // Note it's safe to dismiss() a dialog that's already dismissed.
- // (Even if the AlertDialog object(s) below are still around, it's
- // possible that the actual dialog(s) may have already been
- // dismissed by the user.)
-
- if (mMissingVoicemailDialog != null) {
- if (VDBG) log("- DISMISSING mMissingVoicemailDialog.");
- mMissingVoicemailDialog.dismiss();
- mMissingVoicemailDialog = null;
- }
- if (mMmiStartedDialog != null) {
- if (VDBG) log("- DISMISSING mMmiStartedDialog.");
- mMmiStartedDialog.dismiss();
- mMmiStartedDialog = null;
- }
- if (mGenericErrorDialog != null) {
- if (VDBG) log("- DISMISSING mGenericErrorDialog.");
- mGenericErrorDialog.dismiss();
- mGenericErrorDialog = null;
- }
- if (mSuppServiceFailureDialog != null) {
- if (VDBG) log("- DISMISSING mSuppServiceFailureDialog.");
- mSuppServiceFailureDialog.dismiss();
- mSuppServiceFailureDialog = null;
- }
- if (mWaitPromptDialog != null) {
- if (VDBG) log("- DISMISSING mWaitPromptDialog.");
- mWaitPromptDialog.dismiss();
- mWaitPromptDialog = null;
- }
- if (mWildPromptDialog != null) {
- if (VDBG) log("- DISMISSING mWildPromptDialog.");
- mWildPromptDialog.dismiss();
- mWildPromptDialog = null;
- }
- if (mCallLostDialog != null) {
- if (VDBG) log("- DISMISSING mCallLostDialog.");
- mCallLostDialog.dismiss();
- mCallLostDialog = null;
- }
- if ((mApp.inCallUiState.inCallScreenMode == InCallScreenMode.OTA_NORMAL
- || mApp.inCallUiState.inCallScreenMode == InCallScreenMode.OTA_ENDED)
- && mApp.otaUtils != null) {
- mApp.otaUtils.dismissAllOtaDialogs();
- }
- if (mPausePromptDialog != null) {
- if (DBG) log("- DISMISSING mPausePromptDialog.");
- mPausePromptDialog.dismiss();
- mPausePromptDialog = null;
- }
- if (mExitingECMDialog != null) {
- if (DBG) log("- DISMISSING mExitingECMDialog.");
- mExitingECMDialog.dismiss();
- mExitingECMDialog = null;
- }
- }
-
- /**
- * Updates the state of the onscreen "progress indication" used in
- * some (relatively rare) scenarios where we need to wait for
- * something to happen before enabling the in-call UI.
- *
- * If necessary, this method will cause a ProgressDialog (i.e. a
- * spinning wait cursor) to be drawn *on top of* whatever the current
- * state of the in-call UI is.
- *
- * @see InCallUiState.ProgressIndicationType
- */
- private void updateProgressIndication() {
- // If an incoming call is ringing, that takes priority over any
- // possible value of inCallUiState.progressIndication.
- if (mCM.hasActiveRingingCall()) {
- dismissProgressIndication();
- return;
- }
-
- // Otherwise, put up a progress indication if indicated by the
- // inCallUiState.progressIndication field.
- final InCallUiState inCallUiState = mApp.inCallUiState;
- switch (inCallUiState.getProgressIndication()) {
- case NONE:
- // No progress indication necessary, so make sure it's dismissed.
- dismissProgressIndication();
- break;
-
- case TURNING_ON_RADIO:
- showProgressIndication(
- R.string.emergency_enable_radio_dialog_title,
- R.string.emergency_enable_radio_dialog_message);
- break;
-
- case RETRYING:
- showProgressIndication(
- R.string.emergency_enable_radio_dialog_title,
- R.string.emergency_enable_radio_dialog_retry);
- break;
-
- default:
- Log.wtf(LOG_TAG, "updateProgressIndication: unexpected value: "
- + inCallUiState.getProgressIndication());
- dismissProgressIndication();
- break;
- }
- }
-
- /**
- * Show an onscreen "progress indication" with the specified title and message.
- */
- private void showProgressIndication(int titleResId, int messageResId) {
- if (DBG) log("showProgressIndication(message " + messageResId + ")...");
-
- // TODO: make this be a no-op if the progress indication is
- // already visible with the exact same title and message.
-
- dismissProgressIndication(); // Clean up any prior progress indication
- mProgressDialog = new ProgressDialog(this);
- mProgressDialog.setTitle(getText(titleResId));
- mProgressDialog.setMessage(getText(messageResId));
- mProgressDialog.setIndeterminate(true);
- mProgressDialog.setCancelable(false);
- mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
- mProgressDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
- mProgressDialog.show();
- }
-
- /**
- * Dismiss the onscreen "progress indication" (if present).
- */
- private void dismissProgressIndication() {
- if (DBG) log("dismissProgressIndication()...");
- if (mProgressDialog != null) {
- mProgressDialog.dismiss(); // safe even if already dismissed
- mProgressDialog = null;
- }
- }
-
-
- //
- // Helper functions for answering incoming calls.
- //
-
- /**
- * Answer a ringing call. This method does nothing if there's no
- * ringing or waiting call.
- */
- private void internalAnswerCall() {
- if (DBG) log("internalAnswerCall()...");
- // if (DBG) PhoneUtils.dumpCallState(mPhone);
-
- final boolean hasRingingCall = mCM.hasActiveRingingCall();
-
- if (hasRingingCall) {
- Phone phone = mCM.getRingingPhone();
- Call ringing = mCM.getFirstActiveRingingCall();
- int phoneType = phone.getPhoneType();
- if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
- if (DBG) log("internalAnswerCall: answering (CDMA)...");
- if (mCM.hasActiveFgCall()
- && mCM.getFgPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_SIP) {
- // The incoming call is CDMA call and the ongoing
- // call is a SIP call. The CDMA network does not
- // support holding an active call, so there's no
- // way to swap between a CDMA call and a SIP call.
- // So for now, we just don't allow a CDMA call and
- // a SIP call to be active at the same time.We'll
- // "answer incoming, end ongoing" in this case.
- if (DBG) log("internalAnswerCall: answer "
- + "CDMA incoming and end SIP ongoing");
- PhoneUtils.answerAndEndActive(mCM, ringing);
- } else {
- PhoneUtils.answerCall(ringing);
- }
- } else if (phoneType == PhoneConstants.PHONE_TYPE_SIP) {
- if (DBG) log("internalAnswerCall: answering (SIP)...");
- if (mCM.hasActiveFgCall()
- && mCM.getFgPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
- // Similar to the PHONE_TYPE_CDMA handling.
- // The incoming call is SIP call and the ongoing
- // call is a CDMA call. The CDMA network does not
- // support holding an active call, so there's no
- // way to swap between a CDMA call and a SIP call.
- // So for now, we just don't allow a CDMA call and
- // a SIP call to be active at the same time.We'll
- // "answer incoming, end ongoing" in this case.
- if (DBG) log("internalAnswerCall: answer "
- + "SIP incoming and end CDMA ongoing");
- PhoneUtils.answerAndEndActive(mCM, ringing);
- } else {
- PhoneUtils.answerCall(ringing);
- }
- } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
- if (DBG) log("internalAnswerCall: answering (GSM)...");
- // GSM: this is usually just a wrapper around
- // PhoneUtils.answerCall(), *but* we also need to do
- // something special for the "both lines in use" case.
-
- final boolean hasActiveCall = mCM.hasActiveFgCall();
- final boolean hasHoldingCall = mCM.hasActiveBgCall();
-
- if (hasActiveCall && hasHoldingCall) {
- if (DBG) log("internalAnswerCall: answering (both lines in use!)...");
- // The relatively rare case where both lines are
- // already in use. We "answer incoming, end ongoing"
- // in this case, according to the current UI spec.
- PhoneUtils.answerAndEndActive(mCM, ringing);
-
- // Alternatively, we could use
- // PhoneUtils.answerAndEndHolding(mPhone);
- // here to end the on-hold call instead.
- } else {
- if (DBG) log("internalAnswerCall: answering...");
- PhoneUtils.answerCall(ringing); // Automatically holds the current active call,
- // if there is one
- }
- } else {
- throw new IllegalStateException("Unexpected phone type: " + phoneType);
- }
-
- // Call origin is valid only with outgoing calls. Disable it on incoming calls.
- mApp.setLatestActiveCallOrigin(null);
- }
- }
-
- /**
- * Hang up the ringing call (aka "Don't answer").
- */
- /* package */ void hangupRingingCall() {
- if (DBG) log("hangupRingingCall()...");
- if (VDBG) PhoneUtils.dumpCallManager();
- // In the rare case when multiple calls are ringing, the UI policy
- // it to always act on the first ringing call.
- PhoneUtils.hangupRingingCall(mCM.getFirstActiveRingingCall());
- }
-
- /**
- * Silence the ringer (if an incoming call is ringing.)
- */
- private void internalSilenceRinger() {
- if (DBG) log("internalSilenceRinger()...");
- final CallNotifier notifier = mApp.notifier;
- if (notifier.isRinging()) {
- // ringer is actually playing, so silence it.
- notifier.silenceRinger();
- }
- }
-
- /**
- * Respond via SMS to the ringing call.
- * @see RespondViaSmsManager
- */
- private void internalRespondViaSms() {
- log("internalRespondViaSms()...");
- if (VDBG) PhoneUtils.dumpCallManager();
-
- // In the rare case when multiple calls are ringing, the UI policy
- // it to always act on the first ringing call.
- Call ringingCall = mCM.getFirstActiveRingingCall();
-
- // Silence the ringer, since it would be distracting while you're trying
- // to pick a response. (Note that we'll restart the ringer if you bail
- // out of the popup, though; see RespondViaSmsCancelListener.)
- internalSilenceRinger();
- }
-
- /**
- * Hang up the current active call.
- */
- private void internalHangup() {
- PhoneConstants.State state = mCM.getState();
- log("internalHangup()... phone state = " + state);
-
- // Regardless of the phone state, issue a hangup request.
- // (If the phone is already idle, this call will presumably have no
- // effect (but also see the note below.))
- PhoneUtils.hangup(mCM);
-
- // If the user just hung up the only active call, we'll eventually exit
- // the in-call UI after the following sequence:
- // - When the hangup() succeeds, we'll get a DISCONNECT event from
- // the telephony layer (see onDisconnect()).
- // - We immediately switch to the "Call ended" state (see the "delayed
- // bailout" code path in onDisconnect()) and also post a delayed
- // DELAYED_CLEANUP_AFTER_DISCONNECT message.
- // - When the DELAYED_CLEANUP_AFTER_DISCONNECT message comes in (see
- // delayedCleanupAfterDisconnect()) we do some final cleanup, and exit
- // this activity unless the phone is still in use (i.e. if there's
- // another call, or something else going on like an active MMI
- // sequence.)
-
- if (state == PhoneConstants.State.IDLE) {
- // The user asked us to hang up, but the phone was (already) idle!
- Log.w(LOG_TAG, "internalHangup(): phone is already IDLE!");
-
- // This is rare, but can happen in a few cases:
- // (a) If the user quickly double-taps the "End" button. In this case
- // we'll see that 2nd press event during the brief "Call ended"
- // state (where the phone is IDLE), or possibly even before the
- // radio has been able to respond to the initial hangup request.
- // (b) More rarely, this can happen if the user presses "End" at the
- // exact moment that the call ends on its own (like because of the
- // other person hanging up.)
- // (c) Finally, this could also happen if we somehow get stuck here on
- // the InCallScreen with the phone truly idle, perhaps due to a
- // bug where we somehow *didn't* exit when the phone became idle
- // in the first place.
-
- // TODO: as a "safety valve" for case (c), consider immediately
- // bailing out of the in-call UI right here. (The user can always
- // bail out by pressing Home, of course, but they'll probably try
- // pressing End first.)
- //
- // Log.i(LOG_TAG, "internalHangup(): phone is already IDLE! Bailing out...");
- // endInCallScreenSession();
- }
- }
-
- /**
- * InCallScreen-specific wrapper around PhoneUtils.switchHoldingAndActive().
- */
- private void internalSwapCalls() {
- if (DBG) log("internalSwapCalls()...");
-
- // Any time we swap calls, force the DTMF dialpad to close.
- // (We want the regular in-call UI to be visible right now, so the
- // user can clearly see which call is now in the foreground.)
- closeDialpadInternal(true); // do the "closing" animation
-
- // Also, clear out the "history" of DTMF digits you typed, to make
- // sure you don't see digits from call #1 while call #2 is active.
- // (Yes, this does mean that swapping calls twice will cause you
- // to lose any previous digits from the current call; see the TODO
- // comment on DTMFTwelvKeyDialer.clearDigits() for more info.)
- mDialer.clearDigits();
- }
-
- /**
- * Sets the current high-level "mode" of the in-call UI.
- *
- * NOTE: if newMode is CALL_ENDED, the caller is responsible for
- * posting a delayed DELAYED_CLEANUP_AFTER_DISCONNECT message, to make
- * sure the "call ended" state goes away after a couple of seconds.
- *
- * Note this method does NOT refresh of the onscreen UI; the caller is
- * responsible for calling updateScreen() or requestUpdateScreen() if
- * necessary.
- */
- private void setInCallScreenMode(InCallScreenMode newMode) {
- if (DBG) log("setInCallScreenMode: " + newMode);
- mApp.inCallUiState.inCallScreenMode = newMode;
-
- switch (newMode) {
- case MANAGE_CONFERENCE:
- if (!PhoneUtils.isConferenceCall(mCM.getActiveFgCall())) {
- Log.w(LOG_TAG, "MANAGE_CONFERENCE: no active conference call!");
- // Hide the Manage Conference panel, return to NORMAL mode.
- setInCallScreenMode(InCallScreenMode.NORMAL);
- return;
- }
- List<Connection> connections = mCM.getFgCallConnections();
- // There almost certainly will be > 1 connection,
- // since isConferenceCall() just returned true.
- if ((connections == null) || (connections.size() <= 1)) {
- Log.w(LOG_TAG,
- "MANAGE_CONFERENCE: Bogus TRUE from isConferenceCall(); connections = "
- + connections);
- // Hide the Manage Conference panel, return to NORMAL mode.
- setInCallScreenMode(InCallScreenMode.NORMAL);
- return;
- }
-
- // TODO: Don't do this here. The call to
- // initManageConferencePanel() should instead happen
- // automagically in ManageConferenceUtils the very first
- // time you call updateManageConferencePanel() or
- // setPanelVisible(true).
- mManageConferenceUtils.initManageConferencePanel(); // if necessary
-
- mManageConferenceUtils.updateManageConferencePanel(connections);
-
- // The "Manage conference" UI takes up the full main frame,
- // replacing the CallCard PopupWindow.
- mManageConferenceUtils.setPanelVisible(true);
-
- // Start the chronometer.
- // TODO: Similarly, we shouldn't expose startConferenceTime()
- // and stopConferenceTime(); the ManageConferenceUtils
- // class ought to manage the conferenceTime widget itself
- // based on setPanelVisible() calls.
-
- // Note: there is active Fg call since we are in conference call
- long callDuration =
- mCM.getActiveFgCall().getEarliestConnection().getDurationMillis();
- mManageConferenceUtils.startConferenceTime(
- SystemClock.elapsedRealtime() - callDuration);
-
- // No need to close the dialer here, since the Manage
- // Conference UI will just cover it up anyway.
-
- break;
-
- case CALL_ENDED:
- case NORMAL:
- mManageConferenceUtils.setPanelVisible(false);
- mManageConferenceUtils.stopConferenceTime();
- break;
-
- case OTA_NORMAL:
- mApp.otaUtils.setCdmaOtaInCallScreenUiState(
- OtaUtils.CdmaOtaInCallScreenUiState.State.NORMAL);
- break;
-
- case OTA_ENDED:
- mApp.otaUtils.setCdmaOtaInCallScreenUiState(
- OtaUtils.CdmaOtaInCallScreenUiState.State.ENDED);
- break;
-
- case UNDEFINED:
- // Set our Activities intent to ACTION_UNDEFINED so
- // that if we get resumed after we've completed a call
- // the next call will not cause checkIsOtaCall to
- // return true.
- //
- // TODO(OTASP): update these comments
- //
- // With the framework as of October 2009 the sequence below
- // causes the framework to call onResume, onPause, onNewIntent,
- // onResume. If we don't call setIntent below then when the
- // first onResume calls checkIsOtaCall via checkOtaspStateOnResume it will
- // return true and the Activity will be confused.
- //
- // 1) Power up Phone A
- // 2) Place *22899 call and activate Phone A
- // 3) Press the power key on Phone A to turn off the display
- // 4) Call Phone A from Phone B answering Phone A
- // 5) The screen will be blank (Should be normal InCallScreen)
- // 6) Hang up the Phone B
- // 7) Phone A displays the activation screen.
- //
- // Step 3 is the critical step to cause the onResume, onPause
- // onNewIntent, onResume sequence. If step 3 is skipped the
- // sequence will be onNewIntent, onResume and all will be well.
- setIntent(new Intent(ACTION_UNDEFINED));
-
- // Cleanup Ota Screen if necessary and set the panel
- // to VISIBLE.
- if (mCM.getState() != PhoneConstants.State.OFFHOOK) {
- if (mApp.otaUtils != null) {
- mApp.otaUtils.cleanOtaScreen(true);
- }
- } else {
- log("WARNING: Setting mode to UNDEFINED but phone is OFFHOOK,"
- + " skip cleanOtaScreen.");
- }
- break;
- }
- }
-
- /**
- * @return true if the "Manage conference" UI is currently visible.
- */
- /* package */ boolean isManageConferenceMode() {
- return (mApp.inCallUiState.inCallScreenMode == InCallScreenMode.MANAGE_CONFERENCE);
- }
-
- /**
- * Checks if the "Manage conference" UI needs to be updated.
- * If the state of the current conference call has changed
- * since our previous call to updateManageConferencePanel()),
- * do a fresh update. Also, if the current call is no longer a
- * conference call at all, bail out of the "Manage conference" UI and
- * return to InCallScreenMode.NORMAL mode.
- */
- private void updateManageConferencePanelIfNecessary() {
- if (VDBG) log("updateManageConferencePanelIfNecessary: " + mCM.getActiveFgCall() + "...");
-
- List<Connection> connections = mCM.getFgCallConnections();
- if (connections == null) {
- if (VDBG) log("==> no connections on foreground call!");
- // Hide the Manage Conference panel, return to NORMAL mode.
- setInCallScreenMode(InCallScreenMode.NORMAL);
- SyncWithPhoneStateStatus status = syncWithPhoneState();
- if (status != SyncWithPhoneStateStatus.SUCCESS) {
- Log.w(LOG_TAG, "- syncWithPhoneState failed! status = " + status);
- // We shouldn't even be in the in-call UI in the first
- // place, so bail out:
- if (DBG) log("updateManageConferencePanelIfNecessary: endInCallScreenSession... 1");
- endInCallScreenSession();
- return;
- }
- return;
- }
-
- int numConnections = connections.size();
- if (numConnections <= 1) {
- if (VDBG) log("==> foreground call no longer a conference!");
- // Hide the Manage Conference panel, return to NORMAL mode.
- setInCallScreenMode(InCallScreenMode.NORMAL);
- SyncWithPhoneStateStatus status = syncWithPhoneState();
- if (status != SyncWithPhoneStateStatus.SUCCESS) {
- Log.w(LOG_TAG, "- syncWithPhoneState failed! status = " + status);
- // We shouldn't even be in the in-call UI in the first
- // place, so bail out:
- if (DBG) log("updateManageConferencePanelIfNecessary: endInCallScreenSession... 2");
- endInCallScreenSession();
- return;
- }
- return;
- }
-
- // TODO: the test to see if numConnections has changed can go in
- // updateManageConferencePanel(), rather than here.
- if (numConnections != mManageConferenceUtils.getNumCallersInConference()) {
- if (VDBG) log("==> Conference size has changed; need to rebuild UI!");
- mManageConferenceUtils.updateManageConferencePanel(connections);
- }
- }
-
- /**
- * Updates {@link #mCallCard}'s visibility state per DTMF dialpad visibility. They
- * cannot be shown simultaneously and thus we should reflect DTMF dialpad visibility into
- * another.
- *
- * Note: During OTA calls or users' managing conference calls, we should *not* call this method
- * but manually manage both visibility.
- *
- * @see #updateScreen()
- */
- private void updateCallCardVisibilityPerDialerState(boolean animate) {
- // We need to hide the CallCard while the dialpad is visible.
- if (isDialerOpened()) {
- if (VDBG) {
- log("- updateCallCardVisibilityPerDialerState(animate="
- + animate + "): dialpad open, hide mCallCard...");
- }
- if (animate) {
- AnimationUtils.Fade.hide(mCallCard, View.GONE);
- } else {
- mCallCard.setVisibility(View.GONE);
- }
- } else {
- // Dialpad is dismissed; bring back the CallCard if it's supposed to be visible.
- if ((mApp.inCallUiState.inCallScreenMode == InCallScreenMode.NORMAL)
- || (mApp.inCallUiState.inCallScreenMode == InCallScreenMode.CALL_ENDED)) {
- if (VDBG) {
- log("- updateCallCardVisibilityPerDialerState(animate="
- + animate + "): dialpad dismissed, show mCallCard...");
- }
- if (animate) {
- AnimationUtils.Fade.show(mCallCard);
- } else {
- mCallCard.setVisibility(View.VISIBLE);
- }
- }
- }
- }
-
- /**
- * @see DTMFTwelveKeyDialer#isOpened()
- */
- /* package */ boolean isDialerOpened() {
- return (mDialer != null && mDialer.isOpened());
- }
-
- /**
- * Called any time the DTMF dialpad is opened.
- * @see DTMFTwelveKeyDialer#openDialer(boolean)
- */
- /* package */ void onDialerOpen(boolean animate) {
- if (DBG) log("onDialerOpen()...");
-
- // Update the in-call touch UI.
- updateInCallTouchUi();
-
- // Update CallCard UI, which depends on the dialpad.
- updateCallCardVisibilityPerDialerState(animate);
-
- // This counts as explicit "user activity".
- mApp.pokeUserActivity();
-
- //If on OTA Call, hide OTA Screen
- // TODO: This may not be necessary, now that the dialpad is
- // always visible in OTA mode.
- if ((mApp.inCallUiState.inCallScreenMode == InCallScreenMode.OTA_NORMAL
- || mApp.inCallUiState.inCallScreenMode == InCallScreenMode.OTA_ENDED)
- && mApp.otaUtils != null) {
- mApp.otaUtils.hideOtaScreen();
- }
- }
-
- /**
- * Called any time the DTMF dialpad is closed.
- * @see DTMFTwelveKeyDialer#closeDialer(boolean)
- */
- /* package */ void onDialerClose(boolean animate) {
- if (DBG) log("onDialerClose()...");
-
- // OTA-specific cleanup upon closing the dialpad.
- if ((mApp.inCallUiState.inCallScreenMode == InCallScreenMode.OTA_NORMAL)
- || (mApp.inCallUiState.inCallScreenMode == InCallScreenMode.OTA_ENDED)
- || ((mApp.cdmaOtaScreenState != null)
- && (mApp.cdmaOtaScreenState.otaScreenState ==
- CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION))) {
- if (mApp.otaUtils != null) {
- mApp.otaUtils.otaShowProperScreen();
- }
- }
-
- // Update the in-call touch UI.
- updateInCallTouchUi();
-
- // Update CallCard UI, which depends on the dialpad.
- updateCallCardVisibilityPerDialerState(animate);
-
- // This counts as explicit "user activity".
- mApp.pokeUserActivity();
- }
-
- /**
- * Determines when we can dial DTMF tones.
- */
- /* package */ boolean okToDialDTMFTones() {
- final boolean hasRingingCall = mCM.hasActiveRingingCall();
- final Call.State fgCallState = mCM.getActiveFgCallState();
-
- // We're allowed to send DTMF tones when there's an ACTIVE
- // foreground call, and not when an incoming call is ringing
- // (since DTMF tones are useless in that state), or if the
- // Manage Conference UI is visible (since the tab interferes
- // with the "Back to call" button.)
-
- // We can also dial while in ALERTING state because there are
- // some connections that never update to an ACTIVE state (no
- // indication from the network).
- boolean canDial =
- (fgCallState == Call.State.ACTIVE || fgCallState == Call.State.ALERTING)
- && !hasRingingCall
- && (mApp.inCallUiState.inCallScreenMode != InCallScreenMode.MANAGE_CONFERENCE);
-
- if (VDBG) log ("[okToDialDTMFTones] foreground state: " + fgCallState +
- ", ringing state: " + hasRingingCall +
- ", call screen mode: " + mApp.inCallUiState.inCallScreenMode +
- ", result: " + canDial);
-
- return canDial;
- }
-
- /**
- * @return true if the in-call DTMF dialpad should be available to the
- * user, given the current state of the phone and the in-call UI.
- * (This is used to control the enabledness of the "Show
- * dialpad" onscreen button; see InCallControlState.dialpadEnabled.)
- */
- /* package */ boolean okToShowDialpad() {
- // Very similar to okToDialDTMFTones(), but allow DIALING here.
- final Call.State fgCallState = mCM.getActiveFgCallState();
- return okToDialDTMFTones() || (fgCallState == Call.State.DIALING);
- }
-
- /**
- * Initializes the in-call touch UI on devices that need it.
- */
- private void initInCallTouchUi() {
- if (DBG) log("initInCallTouchUi()...");
- // TODO: we currently use the InCallTouchUi widget in at least
- // some states on ALL platforms. But if some devices ultimately
- // end up not using *any* onscreen touch UI, we should make sure
- // to not even inflate the InCallTouchUi widget on those devices.
- mInCallTouchUi = (InCallTouchUi) findViewById(R.id.inCallTouchUi);
- mInCallTouchUi.setInCallScreenInstance(this);
-
- // RespondViaSmsManager implements the "Respond via SMS"
- // feature that's triggered from the incoming call widget.
- mRespondViaSmsManager = new RespondViaSmsManager();
- mRespondViaSmsManager.setInCallScreenInstance(this);
- }
-
- /**
- * Updates the state of the in-call touch UI.
- */
- private void updateInCallTouchUi() {
- if (mInCallTouchUi != null) {
- mInCallTouchUi.updateState(mCM);
- }
- }
-
- /**
- * @return the InCallTouchUi widget
- */
- /* package */ InCallTouchUi getInCallTouchUi() {
- return mInCallTouchUi;
- }
-
- /**
- * Posts a handler message telling the InCallScreen to refresh the
- * onscreen in-call UI.
- *
- * This is just a wrapper around updateScreen(), for use by the
- * rest of the phone app or from a thread other than the UI thread.
- *
- * updateScreen() is a no-op if the InCallScreen is not the foreground
- * activity, so it's safe to call this whether or not the InCallScreen
- * is currently visible.
- */
- /* package */ void requestUpdateScreen() {
- if (DBG) log("requestUpdateScreen()...");
- mHandler.removeMessages(REQUEST_UPDATE_SCREEN);
- mHandler.sendEmptyMessage(REQUEST_UPDATE_SCREEN);
- }
-
- /**
- * @return true if we're in restricted / emergency dialing only mode.
- */
- public boolean isPhoneStateRestricted() {
- // TODO: This needs to work IN TANDEM with the KeyGuardViewMediator Code.
- // Right now, it looks like the mInputRestricted flag is INTERNAL to the
- // KeyGuardViewMediator and SPECIFICALLY set to be FALSE while the emergency
- // phone call is being made, to allow for input into the InCallScreen.
- // Having the InCallScreen judge the state of the device from this flag
- // becomes meaningless since it is always false for us. The mediator should
- // have an additional API to let this app know that it should be restricted.
- int serviceState = mCM.getServiceState();
- return ((serviceState == ServiceState.STATE_EMERGENCY_ONLY) ||
- (serviceState == ServiceState.STATE_OUT_OF_SERVICE) ||
- (mApp.getKeyguardManager().inKeyguardRestrictedInputMode()));
- }
-
- /**
- * Posts a handler message telling the InCallScreen to close
- * the OTA failure notice after the specified delay.
- * @see OtaUtils.otaShowProgramFailureNotice
- */
- /* package */ void requestCloseOtaFailureNotice(long timeout) {
- if (DBG) log("requestCloseOtaFailureNotice() with timeout: " + timeout);
- mHandler.sendEmptyMessageDelayed(REQUEST_CLOSE_OTA_FAILURE_NOTICE, timeout);
-
- // TODO: we probably ought to call removeMessages() for this
- // message code in either onPause or onResume, just to be 100%
- // sure that the message we just posted has no way to affect a
- // *different* call if the user quickly backs out and restarts.
- // (This is also true for requestCloseSpcErrorNotice() below, and
- // probably anywhere else we use mHandler.sendEmptyMessageDelayed().)
- }
-
- /**
- * Posts a handler message telling the InCallScreen to close
- * the SPC error notice after the specified delay.
- * @see OtaUtils.otaShowSpcErrorNotice
- */
- /* package */ void requestCloseSpcErrorNotice(long timeout) {
- if (DBG) log("requestCloseSpcErrorNotice() with timeout: " + timeout);
- mHandler.sendEmptyMessageDelayed(REQUEST_CLOSE_SPC_ERROR_NOTICE, timeout);
- }
-
- public boolean isOtaCallInActiveState() {
- if ((mApp.inCallUiState.inCallScreenMode == InCallScreenMode.OTA_NORMAL)
- || ((mApp.cdmaOtaScreenState != null)
- && (mApp.cdmaOtaScreenState.otaScreenState ==
- CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION))) {
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * Handle OTA Call End scenario when display becomes dark during OTA Call
- * and InCallScreen is in pause mode. CallNotifier will listen for call
- * end indication and call this api to handle OTA Call end scenario
- */
- public void handleOtaCallEnd() {
- if (DBG) log("handleOtaCallEnd entering");
- if (((mApp.inCallUiState.inCallScreenMode == InCallScreenMode.OTA_NORMAL)
- || ((mApp.cdmaOtaScreenState != null)
- && (mApp.cdmaOtaScreenState.otaScreenState !=
- CdmaOtaScreenState.OtaScreenState.OTA_STATUS_UNDEFINED)))
- && ((mApp.cdmaOtaProvisionData != null)
- && (!mApp.cdmaOtaProvisionData.inOtaSpcState))) {
- if (DBG) log("handleOtaCallEnd - Set OTA Call End stater");
- setInCallScreenMode(InCallScreenMode.OTA_ENDED);
- updateScreen();
- }
- }
-
- public boolean isOtaCallInEndState() {
- return (mApp.inCallUiState.inCallScreenMode == InCallScreenMode.OTA_ENDED);
- }
-
-
- /**
- * Upon resuming the in-call UI, check to see if an OTASP call is in
- * progress, and if so enable the special OTASP-specific UI.
- *
- * TODO: have a simple single flag in InCallUiState for this rather than
- * needing to know about all those mApp.cdma*State objects.
- *
- * @return true if any OTASP-related UI is active
- */
- private boolean checkOtaspStateOnResume() {
- // If there's no OtaUtils instance, that means we haven't even tried
- // to start an OTASP call (yet), so there's definitely nothing to do here.
- if (mApp.otaUtils == null) {
- if (DBG) log("checkOtaspStateOnResume: no OtaUtils instance; nothing to do.");
- return false;
- }
-
- if ((mApp.cdmaOtaScreenState == null) || (mApp.cdmaOtaProvisionData == null)) {
- // Uh oh -- something wrong with our internal OTASP state.
- // (Since this is an OTASP-capable device, these objects
- // *should* have already been created by PhoneApp.onCreate().)
- throw new IllegalStateException("checkOtaspStateOnResume: "
- + "app.cdmaOta* objects(s) not initialized");
- }
-
- // The PhoneApp.cdmaOtaInCallScreenUiState instance is the
- // authoritative source saying whether or not the in-call UI should
- // show its OTASP-related UI.
-
- OtaUtils.CdmaOtaInCallScreenUiState.State cdmaOtaInCallScreenState =
- mApp.otaUtils.getCdmaOtaInCallScreenUiState();
- // These states are:
- // - UNDEFINED: no OTASP-related UI is visible
- // - NORMAL: OTASP call in progress, so show in-progress OTASP UI
- // - ENDED: OTASP call just ended, so show success/failure indication
-
- boolean otaspUiActive =
- (cdmaOtaInCallScreenState == OtaUtils.CdmaOtaInCallScreenUiState.State.NORMAL)
- || (cdmaOtaInCallScreenState == OtaUtils.CdmaOtaInCallScreenUiState.State.ENDED);
-
- if (otaspUiActive) {
- // Make sure the OtaUtils instance knows about the InCallScreen's
- // OTASP-related UI widgets.
- //
- // (This call has no effect if the UI widgets have already been set up.
- // It only really matters the very first time that the InCallScreen instance
- // is onResume()d after starting an OTASP call.)
- mApp.otaUtils.updateUiWidgets(this, mInCallTouchUi, mCallCard);
-
- // Also update the InCallScreenMode based on the cdmaOtaInCallScreenState.
-
- if (cdmaOtaInCallScreenState == OtaUtils.CdmaOtaInCallScreenUiState.State.NORMAL) {
- if (DBG) log("checkOtaspStateOnResume - in OTA Normal mode");
- setInCallScreenMode(InCallScreenMode.OTA_NORMAL);
- } else if (cdmaOtaInCallScreenState ==
- OtaUtils.CdmaOtaInCallScreenUiState.State.ENDED) {
- if (DBG) log("checkOtaspStateOnResume - in OTA END mode");
- setInCallScreenMode(InCallScreenMode.OTA_ENDED);
- }
-
- // TODO(OTASP): we might also need to go into OTA_ENDED mode
- // in one extra case:
- //
- // else if (mApp.cdmaOtaScreenState.otaScreenState ==
- // CdmaOtaScreenState.OtaScreenState.OTA_STATUS_SUCCESS_FAILURE_DLG) {
- // if (DBG) log("checkOtaspStateOnResume - set OTA END Mode");
- // setInCallScreenMode(InCallScreenMode.OTA_ENDED);
- // }
-
- } else {
- // OTASP is not active; reset to regular in-call UI.
-
- if (DBG) log("checkOtaspStateOnResume - Set OTA NORMAL Mode");
- setInCallScreenMode(InCallScreenMode.OTA_NORMAL);
-
- if (mApp.otaUtils != null) {
- mApp.otaUtils.cleanOtaScreen(false);
- }
- }
-
- // TODO(OTASP):
- // The original check from checkIsOtaCall() when handling ACTION_MAIN was this:
- //
- // [ . . . ]
- // else if (action.equals(intent.ACTION_MAIN)) {
- // if (DBG) log("checkIsOtaCall action ACTION_MAIN");
- // boolean isRingingCall = mCM.hasActiveRingingCall();
- // if (isRingingCall) {
- // if (DBG) log("checkIsOtaCall isRingingCall: " + isRingingCall);
- // return false;
- // } else if ((mApp.cdmaOtaInCallScreenUiState.state
- // == CdmaOtaInCallScreenUiState.State.NORMAL)
- // || (mApp.cdmaOtaInCallScreenUiState.state
- // == CdmaOtaInCallScreenUiState.State.ENDED)) {
- // if (DBG) log("action ACTION_MAIN, OTA call already in progress");
- // isOtaCall = true;
- // } else {
- // if (mApp.cdmaOtaScreenState.otaScreenState !=
- // CdmaOtaScreenState.OtaScreenState.OTA_STATUS_UNDEFINED) {
- // if (DBG) log("checkIsOtaCall action ACTION_MAIN, "
- // + "OTA call in progress with UNDEFINED");
- // isOtaCall = true;
- // }
- // }
- // }
- //
- // Also, in internalResolveIntent() we used to do this:
- //
- // if ((mApp.inCallUiState.inCallScreenMode == InCallScreenMode.OTA_NORMAL)
- // || (mApp.inCallUiState.inCallScreenMode == InCallScreenMode.OTA_ENDED)) {
- // // If in OTA Call, update the OTA UI
- // updateScreen();
- // return;
- // }
- //
- // We still need more cleanup to simplify the mApp.cdma*State objects.
-
- return otaspUiActive;
- }
-
- /**
- * Updates and returns the InCallControlState instance.
- */
- public InCallControlState getUpdatedInCallControlState() {
- if (VDBG) log("getUpdatedInCallControlState()...");
- mInCallControlState.update();
- return mInCallControlState;
- }
-
- public void resetInCallScreenMode() {
- if (DBG) log("resetInCallScreenMode: setting mode to UNDEFINED...");
- setInCallScreenMode(InCallScreenMode.UNDEFINED);
- }
-
- /**
- * Updates the onscreen hint displayed while the user is dragging one
- * of the handles of the RotarySelector widget used for incoming
- * calls.
- *
- * @param hintTextResId resource ID of the hint text to display,
- * or 0 if no hint should be visible.
- * @param hintColorResId resource ID for the color of the hint text
- */
- /* package */ void updateIncomingCallWidgetHint(int hintTextResId, int hintColorResId) {
- if (VDBG) log("updateIncomingCallWidgetHint(" + hintTextResId + ")...");
- if (mCallCard != null) {
- mCallCard.setIncomingCallWidgetHint(hintTextResId, hintColorResId);
- mCallCard.updateState(mCM);
- // TODO: if hintTextResId == 0, consider NOT clearing the onscreen
- // hint right away, but instead post a delayed handler message to
- // keep it onscreen for an extra second or two. (This might make
- // the hint more helpful if the user quickly taps one of the
- // handles without dragging at all...)
- // (Or, maybe this should happen completely within the RotarySelector
- // widget, since the widget itself probably wants to keep the colored
- // arrow visible for some extra time also...)
- }
- }
-
-
- /**
- * Used when we need to update buttons outside InCallTouchUi's updateInCallControls() along
- * with that method being called. CallCard may call this too because it doesn't have
- * enough information to update buttons inside itself (more specifically, the class cannot
- * obtain mInCallControllState without some side effect. See also
- * {@link #getUpdatedInCallControlState()}. We probably don't want a method like
- * getRawCallControlState() which returns raw intance with no side effect just for this
- * corner case scenario)
- *
- * TODO: need better design for buttons outside InCallTouchUi.
- */
- /* package */ void updateButtonStateOutsideInCallTouchUi() {
- if (mCallCard != null) {
- mCallCard.setSecondaryCallClickable(mInCallControlState.canSwap);
- }
- }
-
- @Override
- public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
- super.dispatchPopulateAccessibilityEvent(event);
- mCallCard.dispatchPopulateAccessibilityEvent(event);
- return true;
- }
-
- /**
- * Manually handle configuration changes.
- *
- * Originally android:configChanges was set to "orientation|keyboardHidden|uiMode"
- * in order "to make sure the system doesn't destroy and re-create us due to the
- * above config changes". However it is currently set to "keyboardHidden" since
- * the system needs to handle rotation when inserted into a compatible cardock.
- * Even without explicitly handling orientation and uiMode, the app still runs
- * and does not drop the call when rotated.
- *
- */
- public void onConfigurationChanged(Configuration newConfig) {
- if (DBG) log("onConfigurationChanged: newConfig = " + newConfig);
-
- // Note: At the time this function is called, our Resources object
- // will have already been updated to return resource values matching
- // the new configuration.
-
- // Watch out: we *can* still get destroyed and recreated if a
- // configuration change occurs that is *not* listed in the
- // android:configChanges attribute. TODO: Any others we need to list?
-
- super.onConfigurationChanged(newConfig);
-
- // Nothing else to do here, since (currently) the InCallScreen looks
- // exactly the same regardless of configuration.
- // (Specifically, we'll never be in landscape mode because we set
- // android:screenOrientation="portrait" in our manifest, and we don't
- // change our UI at all based on newConfig.keyboardHidden or
- // newConfig.uiMode.)
-
- // TODO: we do eventually want to handle at least some config changes, such as:
- boolean isKeyboardOpen = (newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO);
- if (DBG) log(" - isKeyboardOpen = " + isKeyboardOpen);
- boolean isLandscape = (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE);
- if (DBG) log(" - isLandscape = " + isLandscape);
- if (DBG) log(" - uiMode = " + newConfig.uiMode);
- // See bug 2089513.
- }
-
- /**
- * Handles an incoming RING event from the telephony layer.
- */
- private void onIncomingRing() {
- if (DBG) log("onIncomingRing()...");
- // IFF we're visible, forward this event to the InCallTouchUi
- // instance (which uses this event to drive the animation of the
- // incoming-call UI.)
- if (mIsForegroundActivity && (mInCallTouchUi != null)) {
- mInCallTouchUi.onIncomingRing();
- }
- }
-
- /**
- * Handles a "new ringing connection" event from the telephony layer.
- *
- * This event comes in right at the start of the incoming-call sequence,
- * exactly once per incoming call.
- *
- * Watch out: this won't be called if InCallScreen isn't ready yet,
- * which typically happens for the first incoming phone call (even before
- * the possible first outgoing call).
- */
- private void onNewRingingConnection() {
- if (DBG) log("onNewRingingConnection()...");
-
- // We use this event to reset any incoming-call-related UI elements
- // that might have been left in an inconsistent state after a prior
- // incoming call.
- // (Note we do this whether or not we're the foreground activity,
- // since this event comes in *before* we actually get launched to
- // display the incoming-call UI.)
-
- // If there's a "Respond via SMS" popup still around since the
- // last time we were the foreground activity, make sure it's not
- // still active(!) since that would interfere with *this* incoming
- // call.
- // (Note that we also do this same check in onResume(). But we
- // need it here too, to make sure the popup gets reset in the case
- // where a call-waiting call comes in while the InCallScreen is
- // already in the foreground.)
- mRespondViaSmsManager.dismissPopup(); // safe even if already dismissed
- }
-
- /**
- * Enables or disables the status bar "window shade" based on the current situation.
- */
- private void updateExpandedViewState() {
- if (mIsForegroundActivity) {
- // We should not enable notification's expanded view on RINGING state.
- mApp.notificationMgr.statusBarHelper.enableExpandedView(
- mCM.getState() != PhoneConstants.State.RINGING);
- } else {
- mApp.notificationMgr.statusBarHelper.enableExpandedView(true);
- }
- }
-
- private void log(String msg) {
- Log.d(LOG_TAG, msg);
- }
-
- /**
- * Requests to remove provider info frame after having
- * {@link #PROVIDER_INFO_TIMEOUT}) msec delay.
- */
- /* package */ void requestRemoveProviderInfoWithDelay() {
- // Remove any zombie messages and then send a message to
- // self to remove the provider info after some time.
- mHandler.removeMessages(EVENT_HIDE_PROVIDER_INFO);
- Message msg = Message.obtain(mHandler, EVENT_HIDE_PROVIDER_INFO);
- mHandler.sendMessageDelayed(msg, PROVIDER_INFO_TIMEOUT);
- if (DBG) {
- log("Requested to remove provider info after " + PROVIDER_INFO_TIMEOUT + " msec.");
- }
- }
-
- /**
- * Indicates whether or not the QuickResponseDialog is currently showing in the call screen
- */
- public boolean isQuickResponseDialogShowing() {
- return mRespondViaSmsManager != null && mRespondViaSmsManager.isShowingPopup();
- }
}
diff --git a/src/com/android/phone/InCallTouchUi.java b/src/com/android/phone/InCallTouchUi.java
deleted file mode 100644
index 9cb2a52..0000000
--- a/src/com/android/phone/InCallTouchUi.java
+++ /dev/null
@@ -1,1337 +0,0 @@
-/*
- * Copyright (C) 2009 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.phone;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.content.Context;
-import android.graphics.drawable.LayerDrawable;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewPropertyAnimator;
-import android.view.ViewStub;
-import android.widget.CompoundButton;
-import android.widget.FrameLayout;
-import android.widget.ImageButton;
-import android.widget.PopupMenu;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.android.internal.telephony.Call;
-import com.android.internal.telephony.CallManager;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.widget.multiwaveview.GlowPadView;
-import com.android.internal.widget.multiwaveview.GlowPadView.OnTriggerListener;
-import com.android.phone.InCallUiState.InCallScreenMode;
-
-/**
- * In-call onscreen touch UI elements, used on some platforms.
- *
- * This widget is a fullscreen overlay, drawn on top of the
- * non-touch-sensitive parts of the in-call UI (i.e. the call card).
- */
-public class InCallTouchUi extends FrameLayout
- implements View.OnClickListener, View.OnLongClickListener, OnTriggerListener,
- PopupMenu.OnMenuItemClickListener, PopupMenu.OnDismissListener {
- private static final String LOG_TAG = "InCallTouchUi";
- private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
-
- // Incoming call widget targets
- private static final int ANSWER_CALL_ID = 0; // drag right
- private static final int SEND_SMS_ID = 1; // drag up
- private static final int DECLINE_CALL_ID = 2; // drag left
-
- /**
- * Reference to the InCallScreen activity that owns us. This may be
- * null if we haven't been initialized yet *or* after the InCallScreen
- * activity has been destroyed.
- */
- private InCallScreen mInCallScreen;
-
- // Phone app instance
- private PhoneGlobals mApp;
-
- // UI containers / elements
- private GlowPadView mIncomingCallWidget; // UI used for an incoming call
- private boolean mIncomingCallWidgetIsFadingOut;
- private boolean mIncomingCallWidgetShouldBeReset = true;
-
- /** UI elements while on a regular call (bottom buttons, DTMF dialpad) */
- private View mInCallControls;
- private boolean mShowInCallControlsDuringHidingAnimation;
-
- //
- private ImageButton mAddButton;
- private ImageButton mMergeButton;
- private ImageButton mEndButton;
- private CompoundButton mDialpadButton;
- private CompoundButton mMuteButton;
- private CompoundButton mAudioButton;
- private CompoundButton mHoldButton;
- private ImageButton mSwapButton;
- private View mHoldSwapSpacer;
- private View mVideoSpacer;
- private ImageButton mVideoButton;
-
- // "Extra button row"
- private ViewStub mExtraButtonRow;
- private ViewGroup mCdmaMergeButton;
- private ViewGroup mManageConferenceButton;
- private ImageButton mManageConferenceButtonImage;
-
- // "Audio mode" PopupMenu
- private PopupMenu mAudioModePopup;
- private boolean mAudioModePopupVisible = false;
-
- // Time of the most recent "answer" or "reject" action (see updateState())
- private long mLastIncomingCallActionTime; // in SystemClock.uptimeMillis() time base
-
- // Parameters for the GlowPadView "ping" animation; see triggerPing().
- private static final boolean ENABLE_PING_ON_RING_EVENTS = false;
- private static final boolean ENABLE_PING_AUTO_REPEAT = true;
- private static final long PING_AUTO_REPEAT_DELAY_MSEC = 1200;
-
- private static final int INCOMING_CALL_WIDGET_PING = 101;
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- // If the InCallScreen activity isn't around any more,
- // there's no point doing anything here.
- if (mInCallScreen == null) return;
-
- switch (msg.what) {
- case INCOMING_CALL_WIDGET_PING:
- if (DBG) log("INCOMING_CALL_WIDGET_PING...");
- triggerPing();
- break;
- default:
- Log.wtf(LOG_TAG, "mHandler: unexpected message: " + msg);
- break;
- }
- }
- };
-
- public InCallTouchUi(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- if (DBG) log("InCallTouchUi constructor...");
- if (DBG) log("- this = " + this);
- if (DBG) log("- context " + context + ", attrs " + attrs);
- mApp = PhoneGlobals.getInstance();
- }
-
- void setInCallScreenInstance(InCallScreen inCallScreen) {
- mInCallScreen = inCallScreen;
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- if (DBG) log("InCallTouchUi onFinishInflate(this = " + this + ")...");
-
- // Look up the various UI elements.
-
- // "Drag-to-answer" widget for incoming calls.
- mIncomingCallWidget = (GlowPadView) findViewById(R.id.incomingCallWidget);
- mIncomingCallWidget.setOnTriggerListener(this);
-
- // Container for the UI elements shown while on a regular call.
- mInCallControls = findViewById(R.id.inCallControls);
-
- // Regular (single-tap) buttons, where we listen for click events:
- // Main cluster of buttons:
- mAddButton = (ImageButton) mInCallControls.findViewById(R.id.addButton);
- mAddButton.setOnClickListener(this);
- mAddButton.setOnLongClickListener(this);
- mMergeButton = (ImageButton) mInCallControls.findViewById(R.id.mergeButton);
- mMergeButton.setOnClickListener(this);
- mMergeButton.setOnLongClickListener(this);
- mEndButton = (ImageButton) mInCallControls.findViewById(R.id.endButton);
- mEndButton.setOnClickListener(this);
- mDialpadButton = (CompoundButton) mInCallControls.findViewById(R.id.dialpadButton);
- mDialpadButton.setOnClickListener(this);
- mDialpadButton.setOnLongClickListener(this);
- mMuteButton = (CompoundButton) mInCallControls.findViewById(R.id.muteButton);
- mMuteButton.setOnClickListener(this);
- mMuteButton.setOnLongClickListener(this);
- mAudioButton = (CompoundButton) mInCallControls.findViewById(R.id.audioButton);
- mAudioButton.setOnClickListener(this);
- mAudioButton.setOnLongClickListener(this);
- mHoldButton = (CompoundButton) mInCallControls.findViewById(R.id.holdButton);
- mHoldButton.setOnClickListener(this);
- mHoldButton.setOnLongClickListener(this);
- mSwapButton = (ImageButton) mInCallControls.findViewById(R.id.swapButton);
- mSwapButton.setOnClickListener(this);
- mSwapButton.setOnLongClickListener(this);
- mHoldSwapSpacer = mInCallControls.findViewById(R.id.holdSwapSpacer);
- mVideoButton = (ImageButton) mInCallControls.findViewById(R.id.videoCallButton);
- mVideoButton.setOnClickListener(this);
- mVideoButton.setOnLongClickListener(this);
- mVideoSpacer = mInCallControls.findViewById(R.id.videoCallSpacer);
-
- // TODO: Back when these buttons had text labels, we changed
- // the label of mSwapButton for CDMA as follows:
- //
- // if (PhoneApp.getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
- // // In CDMA we use a generalized text - "Manage call", as behavior on selecting
- // // this option depends entirely on what the current call state is.
- // mSwapButtonLabel.setText(R.string.onscreenManageCallsText);
- // } else {
- // mSwapButtonLabel.setText(R.string.onscreenSwapCallsText);
- // }
- //
- // If this is still needed, consider having a special icon for this
- // button in CDMA.
-
- // Buttons shown on the "extra button row", only visible in certain (rare) states.
- mExtraButtonRow = (ViewStub) mInCallControls.findViewById(R.id.extraButtonRow);
-
- // If in PORTRAIT, add a custom OnTouchListener to shrink the "hit target".
- if (!PhoneUtils.isLandscape(this.getContext())) {
- mEndButton.setOnTouchListener(new SmallerHitTargetTouchListener());
- }
-
- }
-
- /**
- * Updates the visibility and/or state of our UI elements, based on
- * the current state of the phone.
- *
- * TODO: This function should be relying on a state defined by InCallScreen,
- * and not generic call states. The incoming call screen handles more states
- * than Call.State or PhoneConstant.State know about.
- */
- /* package */ void updateState(CallManager cm) {
- if (mInCallScreen == null) {
- log("- updateState: mInCallScreen has been destroyed; bailing out...");
- return;
- }
-
- PhoneConstants.State state = cm.getState(); // IDLE, RINGING, or OFFHOOK
- if (DBG) log("updateState: current state = " + state);
-
- boolean showIncomingCallControls = false;
- boolean showInCallControls = false;
-
- final Call ringingCall = cm.getFirstActiveRingingCall();
- final Call.State fgCallState = cm.getActiveFgCallState();
-
- // If the FG call is dialing/alerting, we should display for that call
- // and ignore the ringing call. This case happens when the telephony
- // layer rejects the ringing call while the FG call is dialing/alerting,
- // but the incoming call *does* briefly exist in the DISCONNECTING or
- // DISCONNECTED state.
- if ((ringingCall.getState() != Call.State.IDLE) && !fgCallState.isDialing()) {
- // A phone call is ringing *or* call waiting.
-
- // Watch out: even if the phone state is RINGING, it's
- // possible for the ringing call to be in the DISCONNECTING
- // state. (This typically happens immediately after the user
- // rejects an incoming call, and in that case we *don't* show
- // the incoming call controls.)
- if (ringingCall.getState().isAlive()) {
- if (DBG) log("- updateState: RINGING! Showing incoming call controls...");
- showIncomingCallControls = true;
- }
-
- // Ugly hack to cover up slow response from the radio:
- // if we get an updateState() call immediately after answering/rejecting a call
- // (via onTrigger()), *don't* show the incoming call
- // UI even if the phone is still in the RINGING state.
- // This covers up a slow response from the radio for some actions.
- // To detect that situation, we are using "500 msec" heuristics.
- //
- // Watch out: we should *not* rely on this behavior when "instant text response" action
- // has been chosen. See also onTrigger() for why.
- long now = SystemClock.uptimeMillis();
- if (now < mLastIncomingCallActionTime + 500) {
- log("updateState: Too soon after last action; not drawing!");
- showIncomingCallControls = false;
- }
-
- // b/6765896
- // If the glowview triggers two hits of the respond-via-sms gadget in
- // quick succession, it can cause the incoming call widget to show and hide
- // twice in a row. However, the second hide doesn't get triggered because
- // we are already attemping to hide. This causes an additional glowview to
- // stay up above all other screens.
- // In reality, we shouldn't even be showing incoming-call UI while we are
- // showing the respond-via-sms popup, so we check for that here.
- //
- // TODO: In the future, this entire state machine
- // should be reworked. Respond-via-sms was stapled onto the current
- // design (and so were other states) and should be made a first-class
- // citizen in a new state machine.
- if (mInCallScreen.isQuickResponseDialogShowing()) {
- log("updateState: quickResponse visible. Cancel showing incoming call controls.");
- showIncomingCallControls = false;
- }
- } else {
- // Ok, show the regular in-call touch UI (with some exceptions):
- if (okToShowInCallControls()) {
- showInCallControls = true;
- } else {
- if (DBG) log("- updateState: NOT OK to show touch UI; disabling...");
- }
- }
-
- // In usual cases we don't allow showing both incoming call controls and in-call controls.
- //
- // There's one exception: if this call is during fading-out animation for the incoming
- // call controls, we need to show both for smoother transition.
- if (showIncomingCallControls && showInCallControls) {
- throw new IllegalStateException(
- "'Incoming' and 'in-call' touch controls visible at the same time!");
- }
- if (mShowInCallControlsDuringHidingAnimation) {
- if (DBG) {
- log("- updateState: FORCE showing in-call controls during incoming call widget"
- + " being hidden with animation");
- }
- showInCallControls = true;
- }
-
- // Update visibility and state of the incoming call controls or
- // the normal in-call controls.
-
- if (showInCallControls) {
- if (DBG) log("- updateState: showing in-call controls...");
- updateInCallControls(cm);
- mInCallControls.setVisibility(View.VISIBLE);
- } else {
- if (DBG) log("- updateState: HIDING in-call controls...");
- mInCallControls.setVisibility(View.GONE);
- }
-
- if (showIncomingCallControls) {
- if (DBG) log("- updateState: showing incoming call widget...");
- showIncomingCallWidget(ringingCall);
-
- // On devices with a system bar (soft buttons at the bottom of
- // the screen), disable navigation while the incoming-call UI
- // is up.
- // This prevents false touches (e.g. on the "Recents" button)
- // from interfering with the incoming call UI, like if you
- // accidentally touch the system bar while pulling the phone
- // out of your pocket.
- mApp.notificationMgr.statusBarHelper.enableSystemBarNavigation(false);
- } else {
- if (DBG) log("- updateState: HIDING incoming call widget...");
- hideIncomingCallWidget();
-
- // The system bar is allowed to work normally in regular
- // in-call states.
- mApp.notificationMgr.statusBarHelper.enableSystemBarNavigation(true);
- }
-
- // Dismiss the "Audio mode" PopupMenu if necessary.
- //
- // The "Audio mode" popup is only relevant in call states that support
- // in-call audio, namely when the phone is OFFHOOK (not RINGING), *and*
- // the foreground call is either ALERTING (where you can hear the other
- // end ringing) or ACTIVE (when the call is actually connected.) In any
- // state *other* than these, the popup should not be visible.
-
- if ((state == PhoneConstants.State.OFFHOOK)
- && (fgCallState == Call.State.ALERTING || fgCallState == Call.State.ACTIVE)) {
- // The audio mode popup is allowed to be visible in this state.
- // So if it's up, leave it alone.
- } else {
- // The Audio mode popup isn't relevant in this state, so make sure
- // it's not visible.
- dismissAudioModePopup(); // safe even if not active
- }
- }
-
- private boolean okToShowInCallControls() {
- // Note that this method is concerned only with the internal state
- // of the InCallScreen. (The InCallTouchUi widget has separate
- // logic to make sure it's OK to display the touch UI given the
- // current telephony state, and that it's allowed on the current
- // device in the first place.)
-
- // The touch UI is available in the following InCallScreenModes:
- // - NORMAL (obviously)
- // - CALL_ENDED (which is intended to look mostly the same as
- // a normal in-call state, even though the in-call
- // buttons are mostly disabled)
- // and is hidden in any of the other modes, like MANAGE_CONFERENCE
- // or one of the OTA modes (which use totally different UIs.)
-
- return ((mApp.inCallUiState.inCallScreenMode == InCallScreenMode.NORMAL)
- || (mApp.inCallUiState.inCallScreenMode == InCallScreenMode.CALL_ENDED));
- }
-
- @Override
- public void onClick(View view) {
- int id = view.getId();
- if (DBG) log("onClick(View " + view + ", id " + id + ")...");
-
- switch (id) {
- case R.id.addButton:
- case R.id.mergeButton:
- case R.id.endButton:
- case R.id.dialpadButton:
- case R.id.muteButton:
- case R.id.holdButton:
- case R.id.swapButton:
- case R.id.cdmaMergeButton:
- case R.id.manageConferenceButton:
- case R.id.videoCallButton:
- // Clicks on the regular onscreen buttons get forwarded
- // straight to the InCallScreen.
- mInCallScreen.handleOnscreenButtonClick(id);
- break;
-
- case R.id.audioButton:
- handleAudioButtonClick();
- break;
-
- default:
- Log.w(LOG_TAG, "onClick: unexpected click: View " + view + ", id " + id);
- break;
- }
- }
-
- @Override
- public boolean onLongClick(View view) {
- final int id = view.getId();
- if (DBG) log("onLongClick(View " + view + ", id " + id + ")...");
-
- switch (id) {
- case R.id.addButton:
- case R.id.mergeButton:
- case R.id.dialpadButton:
- case R.id.muteButton:
- case R.id.holdButton:
- case R.id.swapButton:
- case R.id.audioButton:
- case R.id.videoCallButton: {
- final CharSequence description = view.getContentDescription();
- if (!TextUtils.isEmpty(description)) {
- // Show description as ActionBar's menu buttons do.
- // See also ActionMenuItemView#onLongClick() for the original implementation.
- final Toast cheatSheet =
- Toast.makeText(view.getContext(), description, Toast.LENGTH_SHORT);
- cheatSheet.setGravity(
- Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, view.getHeight());
- cheatSheet.show();
- }
- return true;
- }
- default:
- Log.w(LOG_TAG, "onLongClick() with unexpected View " + view + ". Ignoring it.");
- break;
- }
- return false;
- }
-
- /**
- * Updates the enabledness and "checked" state of the buttons on the
- * "inCallControls" panel, based on the current telephony state.
- */
- private void updateInCallControls(CallManager cm) {
- int phoneType = cm.getActiveFgCall().getPhone().getPhoneType();
-
- // Note we do NOT need to worry here about cases where the entire
- // in-call touch UI is disabled, like during an OTA call or if the
- // dtmf dialpad is up. (That's handled by updateState(), which
- // calls okToShowInCallControls().)
- //
- // If we get here, it *is* OK to show the in-call touch UI, so we
- // now need to update the enabledness and/or "checked" state of
- // each individual button.
- //
-
- // The InCallControlState object tells us the enabledness and/or
- // state of the various onscreen buttons:
- InCallControlState inCallControlState = mInCallScreen.getUpdatedInCallControlState();
-
- if (DBG) {
- log("updateInCallControls()...");
- inCallControlState.dumpState();
- }
-
- // "Add" / "Merge":
- // These two buttons occupy the same space onscreen, so at any
- // given point exactly one of them must be VISIBLE and the other
- // must be GONE.
- if (inCallControlState.canAddCall) {
- mAddButton.setVisibility(View.VISIBLE);
- mAddButton.setEnabled(true);
- mMergeButton.setVisibility(View.GONE);
- } else if (inCallControlState.canMerge) {
- if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
- // In CDMA "Add" option is always given to the user and the
- // "Merge" option is provided as a button on the top left corner of the screen,
- // we always set the mMergeButton to GONE
- mMergeButton.setVisibility(View.GONE);
- } else if ((phoneType == PhoneConstants.PHONE_TYPE_GSM)
- || (phoneType == PhoneConstants.PHONE_TYPE_SIP)) {
- mMergeButton.setVisibility(View.VISIBLE);
- mMergeButton.setEnabled(true);
- mAddButton.setVisibility(View.GONE);
- } else {
- throw new IllegalStateException("Unexpected phone type: " + phoneType);
- }
- } else {
- // Neither "Add" nor "Merge" is available. (This happens in
- // some transient states, like while dialing an outgoing call,
- // and in other rare cases like if you have both lines in use
- // *and* there are already 5 people on the conference call.)
- // Since the common case here is "while dialing", we show the
- // "Add" button in a disabled state so that there won't be any
- // jarring change in the UI when the call finally connects.
- mAddButton.setVisibility(View.VISIBLE);
- mAddButton.setEnabled(false);
- mMergeButton.setVisibility(View.GONE);
- }
- if (inCallControlState.canAddCall && inCallControlState.canMerge) {
- if ((phoneType == PhoneConstants.PHONE_TYPE_GSM)
- || (phoneType == PhoneConstants.PHONE_TYPE_SIP)) {
- // Uh oh, the InCallControlState thinks that "Add" *and* "Merge"
- // should both be available right now. This *should* never
- // happen with GSM, but if it's possible on any
- // future devices we may need to re-layout Add and Merge so
- // they can both be visible at the same time...
- Log.w(LOG_TAG, "updateInCallControls: Add *and* Merge enabled," +
- " but can't show both!");
- } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
- // In CDMA "Add" option is always given to the user and the hence
- // in this case both "Add" and "Merge" options would be available to user
- if (DBG) log("updateInCallControls: CDMA: Add and Merge both enabled");
- } else {
- throw new IllegalStateException("Unexpected phone type: " + phoneType);
- }
- }
-
- // "End call"
- mEndButton.setEnabled(inCallControlState.canEndCall);
-
- // "Dialpad": Enabled only when it's OK to use the dialpad in the
- // first place.
- mDialpadButton.setEnabled(inCallControlState.dialpadEnabled);
- mDialpadButton.setChecked(inCallControlState.dialpadVisible);
-
- // "Mute"
- mMuteButton.setEnabled(inCallControlState.canMute);
- mMuteButton.setChecked(inCallControlState.muteIndicatorOn);
-
- // "Audio"
- updateAudioButton(inCallControlState);
-
- // "Hold" / "Swap":
- // These two buttons occupy the same space onscreen, so at any
- // given point exactly one of them must be VISIBLE and the other
- // must be GONE.
- if (inCallControlState.canHold) {
- mHoldButton.setVisibility(View.VISIBLE);
- mHoldButton.setEnabled(true);
- mHoldButton.setChecked(inCallControlState.onHold);
- mSwapButton.setVisibility(View.GONE);
- mHoldSwapSpacer.setVisibility(View.VISIBLE);
- } else if (inCallControlState.canSwap) {
- mSwapButton.setVisibility(View.VISIBLE);
- mSwapButton.setEnabled(true);
- mHoldButton.setVisibility(View.GONE);
- mHoldSwapSpacer.setVisibility(View.VISIBLE);
- } else {
- // Neither "Hold" nor "Swap" is available. This can happen for two
- // reasons:
- // (1) this is a transient state on a device that *can*
- // normally hold or swap, or
- // (2) this device just doesn't have the concept of hold/swap.
- //
- // In case (1), show the "Hold" button in a disabled state. In case
- // (2), remove the button entirely. (This means that the button row
- // will only have 4 buttons on some devices.)
-
- if (inCallControlState.supportsHold) {
- mHoldButton.setVisibility(View.VISIBLE);
- mHoldButton.setEnabled(false);
- mHoldButton.setChecked(false);
- mSwapButton.setVisibility(View.GONE);
- mHoldSwapSpacer.setVisibility(View.VISIBLE);
- } else {
- mHoldButton.setVisibility(View.GONE);
- mSwapButton.setVisibility(View.GONE);
- mHoldSwapSpacer.setVisibility(View.GONE);
- }
- }
- mInCallScreen.updateButtonStateOutsideInCallTouchUi();
- if (inCallControlState.canSwap && inCallControlState.canHold) {
- // Uh oh, the InCallControlState thinks that Swap *and* Hold
- // should both be available. This *should* never happen with
- // either GSM or CDMA, but if it's possible on any future
- // devices we may need to re-layout Hold and Swap so they can
- // both be visible at the same time...
- Log.w(LOG_TAG, "updateInCallControls: Hold *and* Swap enabled, but can't show both!");
- }
-
- if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
- if (inCallControlState.canSwap && inCallControlState.canMerge) {
- // Uh oh, the InCallControlState thinks that Swap *and* Merge
- // should both be available. This *should* never happen with
- // CDMA, but if it's possible on any future
- // devices we may need to re-layout Merge and Swap so they can
- // both be visible at the same time...
- Log.w(LOG_TAG, "updateInCallControls: Merge *and* Swap" +
- "enabled, but can't show both!");
- }
- }
-
- // Finally, update the "extra button row": It's displayed above the
- // "End" button, but only if necessary. Also, it's never displayed
- // while the dialpad is visible (since it would overlap.)
- //
- // The row contains two buttons:
- //
- // - "Manage conference" (used only on GSM devices)
- // - "Merge" button (used only on CDMA devices)
- //
- // Note that mExtraButtonRow is ViewStub, which will be inflated for the first time when
- // any of its buttons becomes visible.
- final boolean showCdmaMerge =
- (phoneType == PhoneConstants.PHONE_TYPE_CDMA) && inCallControlState.canMerge;
- final boolean showExtraButtonRow =
- showCdmaMerge || inCallControlState.manageConferenceVisible;
- if (showExtraButtonRow && !inCallControlState.dialpadVisible) {
- // This will require the ViewStub inflate itself.
- mExtraButtonRow.setVisibility(View.VISIBLE);
-
- // Need to set up mCdmaMergeButton and mManageConferenceButton if this is the first
- // time they're visible.
- if (mCdmaMergeButton == null) {
- setupExtraButtons();
- }
- mCdmaMergeButton.setVisibility(showCdmaMerge ? View.VISIBLE : View.GONE);
- if (inCallControlState.manageConferenceVisible) {
- mManageConferenceButton.setVisibility(View.VISIBLE);
- mManageConferenceButtonImage.setEnabled(inCallControlState.manageConferenceEnabled);
- } else {
- mManageConferenceButton.setVisibility(View.GONE);
- }
- } else {
- mExtraButtonRow.setVisibility(View.GONE);
- }
-
- setupVideoCallButton();
-
- if (DBG) {
- log("At the end of updateInCallControls().");
- dumpBottomButtonState();
- }
- }
-
- /**
- * Set up the video call button. Checks the system for any video call providers before
- * displaying the video chat button.
- */
- private void setupVideoCallButton() {
- // TODO: Check system to see if there are video chat providers and if not, disable the
- // button.
- }
-
-
- /**
- * Set up the buttons that are part of the "extra button row"
- */
- private void setupExtraButtons() {
- // The two "buttons" here (mCdmaMergeButton and mManageConferenceButton)
- // are actually layouts containing an icon and a text label side-by-side.
- mCdmaMergeButton = (ViewGroup) mInCallControls.findViewById(R.id.cdmaMergeButton);
- if (mCdmaMergeButton == null) {
- Log.wtf(LOG_TAG, "CDMA Merge button is null even after ViewStub being inflated.");
- return;
- }
- mCdmaMergeButton.setOnClickListener(this);
-
- mManageConferenceButton =
- (ViewGroup) mInCallControls.findViewById(R.id.manageConferenceButton);
- mManageConferenceButton.setOnClickListener(this);
- mManageConferenceButtonImage =
- (ImageButton) mInCallControls.findViewById(R.id.manageConferenceButtonImage);
- }
-
- private void dumpBottomButtonState() {
- log(" - dialpad: " + getButtonState(mDialpadButton));
- log(" - speaker: " + getButtonState(mAudioButton));
- log(" - mute: " + getButtonState(mMuteButton));
- log(" - hold: " + getButtonState(mHoldButton));
- log(" - swap: " + getButtonState(mSwapButton));
- log(" - add: " + getButtonState(mAddButton));
- log(" - merge: " + getButtonState(mMergeButton));
- log(" - cdmaMerge: " + getButtonState(mCdmaMergeButton));
- log(" - swap: " + getButtonState(mSwapButton));
- log(" - manageConferenceButton: " + getButtonState(mManageConferenceButton));
- }
-
- private static String getButtonState(View view) {
- if (view == null) {
- return "(null)";
- }
- StringBuilder builder = new StringBuilder();
- builder.append("visibility: " + (view.getVisibility() == View.VISIBLE ? "VISIBLE"
- : view.getVisibility() == View.INVISIBLE ? "INVISIBLE" : "GONE"));
- if (view instanceof ImageButton) {
- builder.append(", enabled: " + ((ImageButton) view).isEnabled());
- } else if (view instanceof CompoundButton) {
- builder.append(", enabled: " + ((CompoundButton) view).isEnabled());
- builder.append(", checked: " + ((CompoundButton) view).isChecked());
- }
- return builder.toString();
- }
-
- /**
- * Updates the onscreen "Audio mode" button based on the current state.
- *
- * - If bluetooth is available, this button's function is to bring up the
- * "Audio mode" popup (which provides a 3-way choice between earpiece /
- * speaker / bluetooth). So it should look like a regular action button,
- * but should also have the small "more_indicator" triangle that indicates
- * that a menu will pop up.
- *
- * - If speaker (but not bluetooth) is available, this button should look like
- * a regular toggle button (and indicate the current speaker state.)
- *
- * - If even speaker isn't available, disable the button entirely.
- */
- private void updateAudioButton(InCallControlState inCallControlState) {
- if (DBG) log("updateAudioButton()...");
-
- // The various layers of artwork for this button come from
- // btn_compound_audio.xml. Keep track of which layers we want to be
- // visible:
- //
- // - This selector shows the blue bar below the button icon when
- // this button is a toggle *and* it's currently "checked".
- boolean showToggleStateIndication = false;
- //
- // - This is visible if the popup menu is enabled:
- boolean showMoreIndicator = false;
- //
- // - Foreground icons for the button. Exactly one of these is enabled:
- boolean showSpeakerOnIcon = false;
- boolean showSpeakerOffIcon = false;
- boolean showHandsetIcon = false;
- boolean showBluetoothIcon = false;
-
- if (inCallControlState.bluetoothEnabled) {
- if (DBG) log("- updateAudioButton: 'popup menu action button' mode...");
-
- mAudioButton.setEnabled(true);
-
- // The audio button is NOT a toggle in this state. (And its
- // setChecked() state is irrelevant since we completely hide the
- // btn_compound_background layer anyway.)
-
- // Update desired layers:
- showMoreIndicator = true;
- if (inCallControlState.bluetoothIndicatorOn) {
- showBluetoothIcon = true;
- } else if (inCallControlState.speakerOn) {
- showSpeakerOnIcon = true;
- } else {
- showHandsetIcon = true;
- // TODO: if a wired headset is plugged in, that takes precedence
- // over the handset earpiece. If so, maybe we should show some
- // sort of "wired headset" icon here instead of the "handset
- // earpiece" icon. (Still need an asset for that, though.)
- }
- } else if (inCallControlState.speakerEnabled) {
- if (DBG) log("- updateAudioButton: 'speaker toggle' mode...");
-
- mAudioButton.setEnabled(true);
-
- // The audio button *is* a toggle in this state, and indicates the
- // current state of the speakerphone.
- mAudioButton.setChecked(inCallControlState.speakerOn);
-
- // Update desired layers:
- showToggleStateIndication = true;
-
- showSpeakerOnIcon = inCallControlState.speakerOn;
- showSpeakerOffIcon = !inCallControlState.speakerOn;
- } else {
- if (DBG) log("- updateAudioButton: disabled...");
-
- // The audio button is a toggle in this state, but that's mostly
- // irrelevant since it's always disabled and unchecked.
- mAudioButton.setEnabled(false);
- mAudioButton.setChecked(false);
-
- // Update desired layers:
- showToggleStateIndication = true;
- showSpeakerOffIcon = true;
- }
-
- // Finally, update the drawable layers (see btn_compound_audio.xml).
-
- // Constants used below with Drawable.setAlpha():
- final int HIDDEN = 0;
- final int VISIBLE = 255;
-
- LayerDrawable layers = (LayerDrawable) mAudioButton.getBackground();
- if (DBG) log("- 'layers' drawable: " + layers);
-
- layers.findDrawableByLayerId(R.id.compoundBackgroundItem)
- .setAlpha(showToggleStateIndication ? VISIBLE : HIDDEN);
-
- layers.findDrawableByLayerId(R.id.moreIndicatorItem)
- .setAlpha(showMoreIndicator ? VISIBLE : HIDDEN);
-
- layers.findDrawableByLayerId(R.id.bluetoothItem)
- .setAlpha(showBluetoothIcon ? VISIBLE : HIDDEN);
-
- layers.findDrawableByLayerId(R.id.handsetItem)
- .setAlpha(showHandsetIcon ? VISIBLE : HIDDEN);
-
- layers.findDrawableByLayerId(R.id.speakerphoneOnItem)
- .setAlpha(showSpeakerOnIcon ? VISIBLE : HIDDEN);
-
- layers.findDrawableByLayerId(R.id.speakerphoneOffItem)
- .setAlpha(showSpeakerOffIcon ? VISIBLE : HIDDEN);
- }
-
- /**
- * Handles a click on the "Audio mode" button.
- * - If bluetooth is available, bring up the "Audio mode" popup
- * (which provides a 3-way choice between earpiece / speaker / bluetooth).
- * - If bluetooth is *not* available, just toggle between earpiece and
- * speaker, with no popup at all.
- */
- private void handleAudioButtonClick() {
- InCallControlState inCallControlState = mInCallScreen.getUpdatedInCallControlState();
- if (inCallControlState.bluetoothEnabled) {
- if (DBG) log("- handleAudioButtonClick: 'popup menu' mode...");
- showAudioModePopup();
- } else {
- if (DBG) log("- handleAudioButtonClick: 'speaker toggle' mode...");
- mInCallScreen.toggleSpeaker();
- }
- }
-
- /**
- * Brings up the "Audio mode" popup.
- */
- private void showAudioModePopup() {
- if (DBG) log("showAudioModePopup()...");
-
- mAudioModePopup = new PopupMenu(mInCallScreen /* context */,
- mAudioButton /* anchorView */);
- mAudioModePopup.getMenuInflater().inflate(R.menu.incall_audio_mode_menu,
- mAudioModePopup.getMenu());
- mAudioModePopup.setOnMenuItemClickListener(this);
- mAudioModePopup.setOnDismissListener(this);
-
- // Update the enabled/disabledness of menu items based on the
- // current call state.
- InCallControlState inCallControlState = mInCallScreen.getUpdatedInCallControlState();
-
- Menu menu = mAudioModePopup.getMenu();
-
- // TODO: Still need to have the "currently active" audio mode come
- // up pre-selected (or focused?) with a blue highlight. Still
- // need exact visual design, and possibly framework support for this.
- // See comments below for the exact logic.
-
- MenuItem speakerItem = menu.findItem(R.id.audio_mode_speaker);
- speakerItem.setEnabled(inCallControlState.speakerEnabled);
- // TODO: Show speakerItem as initially "selected" if
- // inCallControlState.speakerOn is true.
-
- // We display *either* "earpiece" or "wired headset", never both,
- // depending on whether a wired headset is physically plugged in.
- MenuItem earpieceItem = menu.findItem(R.id.audio_mode_earpiece);
- MenuItem wiredHeadsetItem = menu.findItem(R.id.audio_mode_wired_headset);
-
- final boolean usingHeadset = false; //mApp.isHeadsetPlugged();
-
- earpieceItem.setVisible(!usingHeadset);
- earpieceItem.setEnabled(!usingHeadset);
- wiredHeadsetItem.setVisible(usingHeadset);
- wiredHeadsetItem.setEnabled(usingHeadset);
- // TODO: Show the above item (either earpieceItem or wiredHeadsetItem)
- // as initially "selected" if inCallControlState.speakerOn and
- // inCallControlState.bluetoothIndicatorOn are both false.
-
- MenuItem bluetoothItem = menu.findItem(R.id.audio_mode_bluetooth);
- bluetoothItem.setEnabled(inCallControlState.bluetoothEnabled);
- // TODO: Show bluetoothItem as initially "selected" if
- // inCallControlState.bluetoothIndicatorOn is true.
-
- mAudioModePopup.show();
-
- // Unfortunately we need to manually keep track of the popup menu's
- // visiblity, since PopupMenu doesn't have an isShowing() method like
- // Dialogs do.
- mAudioModePopupVisible = true;
- }
-
- /**
- * Dismisses the "Audio mode" popup if it's visible.
- *
- * This is safe to call even if the popup is already dismissed, or even if
- * you never called showAudioModePopup() in the first place.
- */
- public void dismissAudioModePopup() {
- if (mAudioModePopup != null) {
- mAudioModePopup.dismiss(); // safe even if already dismissed
- mAudioModePopup = null;
- mAudioModePopupVisible = false;
- }
- }
-
- /**
- * Refreshes the "Audio mode" popup if it's visible. This is useful
- * (for example) when a wired headset is plugged or unplugged,
- * since we need to switch back and forth between the "earpiece"
- * and "wired headset" items.
- *
- * This is safe to call even if the popup is already dismissed, or even if
- * you never called showAudioModePopup() in the first place.
- */
- public void refreshAudioModePopup() {
- if (mAudioModePopup != null && mAudioModePopupVisible) {
- // Dismiss the previous one
- mAudioModePopup.dismiss(); // safe even if already dismissed
- // And bring up a fresh PopupMenu
- showAudioModePopup();
- }
- }
-
- // PopupMenu.OnMenuItemClickListener implementation; see showAudioModePopup()
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- if (DBG) log("- onMenuItemClick: " + item);
- if (DBG) log(" id: " + item.getItemId());
- if (DBG) log(" title: '" + item.getTitle() + "'");
-
- if (mInCallScreen == null) {
- Log.w(LOG_TAG, "onMenuItemClick(" + item + "), but null mInCallScreen!");
- return true;
- }
-
- switch (item.getItemId()) {
- case R.id.audio_mode_speaker:
-// mInCallScreen.switchInCallAudio(InCallScreen.InCallAudioMode.SPEAKER);
- break;
- case R.id.audio_mode_earpiece:
- case R.id.audio_mode_wired_headset:
- // InCallAudioMode.EARPIECE means either the handset earpiece,
- // or the wired headset (if connected.)
-// mInCallScreen.switchInCallAudio(InCallScreen.InCallAudioMode.EARPIECE);
- break;
- case R.id.audio_mode_bluetooth:
-// mInCallScreen.switchInCallAudio(InCallScreen.InCallAudioMode.BLUETOOTH);
- break;
- default:
- Log.wtf(LOG_TAG,
- "onMenuItemClick: unexpected View ID " + item.getItemId()
- + " (MenuItem = '" + item + "')");
- break;
- }
- return true;
- }
-
- // PopupMenu.OnDismissListener implementation; see showAudioModePopup().
- // This gets called when the PopupMenu gets dismissed for *any* reason, like
- // the user tapping outside its bounds, or pressing Back, or selecting one
- // of the menu items.
- @Override
- public void onDismiss(PopupMenu menu) {
- if (DBG) log("- onDismiss: " + menu);
- mAudioModePopupVisible = false;
- }
-
- /**
- * @return the amount of vertical space (in pixels) that needs to be
- * reserved for the button cluster at the bottom of the screen.
- * (The CallCard uses this measurement to determine how big
- * the main "contact photo" area can be.)
- *
- * NOTE that this returns the "canonical height" of the main in-call
- * button cluster, which may not match the amount of vertical space
- * actually used. Specifically:
- *
- * - If an incoming call is ringing, the button cluster isn't
- * visible at all. (And the GlowPadView widget is actually
- * much taller than the button cluster.)
- *
- * - If the InCallTouchUi widget's "extra button row" is visible
- * (in some rare phone states) the button cluster will actually
- * be slightly taller than the "canonical height".
- *
- * In either of these cases, we allow the bottom edge of the contact
- * photo to be covered up by whatever UI is actually onscreen.
- */
- public int getTouchUiHeight() {
- // Add up the vertical space consumed by the various rows of buttons.
- int height = 0;
-
- // - The main row of buttons:
- height += (int) getResources().getDimension(R.dimen.in_call_button_height);
-
- // - The End button:
- height += (int) getResources().getDimension(R.dimen.in_call_end_button_height);
-
- // - Note we *don't* consider the InCallTouchUi widget's "extra
- // button row" here.
-
- //- And an extra bit of margin:
- height += (int) getResources().getDimension(R.dimen.in_call_touch_ui_upper_margin);
-
- return height;
- }
-
-
- //
- // GlowPadView.OnTriggerListener implementation
- //
-
- @Override
- public void onGrabbed(View v, int handle) {
-
- }
-
- @Override
- public void onReleased(View v, int handle) {
-
- }
-
- /**
- * Handles "Answer" and "Reject" actions for an incoming call.
- * We get this callback from the incoming call widget
- * when the user triggers an action.
- */
- @Override
- public void onTrigger(View view, int whichHandle) {
- if (DBG) log("onTrigger(whichHandle = " + whichHandle + ")...");
-
- if (mInCallScreen == null) {
- Log.wtf(LOG_TAG, "onTrigger(" + whichHandle
- + ") from incoming-call widget, but null mInCallScreen!");
- return;
- }
-
- // The InCallScreen actually implements all of these actions.
- // Each possible action from the incoming call widget corresponds
- // to an R.id value; we pass those to the InCallScreen's "button
- // click" handler (even though the UI elements aren't actually
- // buttons; see InCallScreen.handleOnscreenButtonClick().)
-
- mShowInCallControlsDuringHidingAnimation = false;
- switch (whichHandle) {
- case ANSWER_CALL_ID:
- if (DBG) log("ANSWER_CALL_ID: answer!");
- mInCallScreen.handleOnscreenButtonClick(R.id.incomingCallAnswer);
- mShowInCallControlsDuringHidingAnimation = true;
-
- // ...and also prevent it from reappearing right away.
- // (This covers up a slow response from the radio for some
- // actions; see updateState().)
- mLastIncomingCallActionTime = SystemClock.uptimeMillis();
- break;
-
- case SEND_SMS_ID:
- if (DBG) log("SEND_SMS_ID!");
- mInCallScreen.handleOnscreenButtonClick(R.id.incomingCallRespondViaSms);
-
- // Watch out: mLastIncomingCallActionTime should not be updated for this case.
- //
- // The variable is originally for avoiding a problem caused by delayed phone state
- // update; RINGING state may remain just after answering/declining an incoming
- // call, so we need to wait a bit (500ms) until we get the effective phone state.
- // For this case, we shouldn't rely on that hack.
- //
- // When the user selects this case, there are two possibilities, neither of which
- // should rely on the hack.
- //
- // 1. The first possibility is that, the device eventually sends one of canned
- // responses per the user's "send" request, and reject the call after sending it.
- // At that moment the code introducing the canned responses should handle the
- // case separately.
- //
- // 2. The second possibility is that, the device will show incoming call widget
- // again per the user's "cancel" request, where the incoming call will still
- // remain. At that moment the incoming call will keep its RINGING state.
- // The remaining phone state should never be ignored by the hack for
- // answering/declining calls because the RINGING state is legitimate. If we
- // use the hack for answer/decline cases, the user loses the incoming call
- // widget, until further screen update occurs afterward, which often results in
- // missed calls.
- break;
-
- case DECLINE_CALL_ID:
- if (DBG) log("DECLINE_CALL_ID: reject!");
- mInCallScreen.handleOnscreenButtonClick(R.id.incomingCallReject);
-
- // Same as "answer" case.
- mLastIncomingCallActionTime = SystemClock.uptimeMillis();
- break;
-
- default:
- Log.wtf(LOG_TAG, "onDialTrigger: unexpected whichHandle value: " + whichHandle);
- break;
- }
-
- // On any action by the user, hide the widget.
- //
- // If requested above (i.e. if mShowInCallControlsDuringHidingAnimation is set to true),
- // in-call controls will start being shown too.
- //
- // TODO: The decision to hide this should be made by the controller
- // (InCallScreen), and not this view.
- hideIncomingCallWidget();
-
- // Regardless of what action the user did, be sure to clear out
- // the hint text we were displaying while the user was dragging.
- mInCallScreen.updateIncomingCallWidgetHint(0, 0);
- }
-
- public void onFinishFinalAnimation() {
- // Not used
- }
-
- /**
- * Apply an animation to hide the incoming call widget.
- */
- private void hideIncomingCallWidget() {
- if (DBG) log("hideIncomingCallWidget()...");
- if (mIncomingCallWidget.getVisibility() != View.VISIBLE
- || mIncomingCallWidgetIsFadingOut) {
- if (DBG) log("Skipping hideIncomingCallWidget action");
- // Widget is already hidden or in the process of being hidden
- return;
- }
-
- // Hide the incoming call screen with a transition
- mIncomingCallWidgetIsFadingOut = true;
- ViewPropertyAnimator animator = mIncomingCallWidget.animate();
- animator.cancel();
- animator.setDuration(AnimationUtils.ANIMATION_DURATION);
- animator.setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- if (mShowInCallControlsDuringHidingAnimation) {
- if (DBG) log("IncomingCallWidget's hiding animation started");
- updateInCallControls(mApp.mCM);
- mInCallControls.setVisibility(View.VISIBLE);
- }
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (DBG) log("IncomingCallWidget's hiding animation ended");
- mIncomingCallWidget.setAlpha(1);
- mIncomingCallWidget.setVisibility(View.GONE);
- mIncomingCallWidget.animate().setListener(null);
- mShowInCallControlsDuringHidingAnimation = false;
- mIncomingCallWidgetIsFadingOut = false;
- mIncomingCallWidgetShouldBeReset = true;
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mIncomingCallWidget.animate().setListener(null);
- mShowInCallControlsDuringHidingAnimation = false;
- mIncomingCallWidgetIsFadingOut = false;
- mIncomingCallWidgetShouldBeReset = true;
-
- // Note: the code which reset this animation should be responsible for
- // alpha and visibility.
- }
- });
- animator.alpha(0f);
- }
-
- /**
- * Shows the incoming call widget and cancels any animation that may be fading it out.
- */
- private void showIncomingCallWidget(Call ringingCall) {
- if (DBG) log("showIncomingCallWidget()...");
-
- // TODO: wouldn't be ok to suppress this whole request if the widget is already VISIBLE
- // and we don't need to reset it?
- // log("showIncomingCallWidget(). widget visibility: " + mIncomingCallWidget.getVisibility());
-
- ViewPropertyAnimator animator = mIncomingCallWidget.animate();
- if (animator != null) {
- animator.cancel();
- // If animation is cancelled before it's running,
- // onAnimationCancel will not be called and mIncomingCallWidgetIsFadingOut
- // will be alway true. hideIncomingCallWidget() will not be excuted in this case.
- mIncomingCallWidgetIsFadingOut = false;
- }
- mIncomingCallWidget.setAlpha(1.0f);
-
- // On an incoming call, if the layout is landscape, then align the "incoming call" text
- // to the left, because the incomingCallWidget (black background with glowing ring)
- // is aligned to the right and would cover the "incoming call" text.
- // Note that callStateLabel is within CallCard, outside of the context of InCallTouchUi
- if (PhoneUtils.isLandscape(this.getContext())) {
- TextView callStateLabel = (TextView) mIncomingCallWidget
- .getRootView().findViewById(R.id.callStateLabel);
- if (callStateLabel != null) callStateLabel.setGravity(Gravity.START);
- }
-
- mIncomingCallWidget.setVisibility(View.VISIBLE);
-
- // Finally, manually trigger a "ping" animation.
- //
- // Normally, the ping animation is triggered by RING events from
- // the telephony layer (see onIncomingRing().) But that *doesn't*
- // happen for the very first RING event of an incoming call, since
- // the incoming-call UI hasn't been set up yet at that point!
- //
- // So trigger an explicit ping() here, to force the animation to
- // run when the widget first appears.
- //
- mHandler.removeMessages(INCOMING_CALL_WIDGET_PING);
- mHandler.sendEmptyMessageDelayed(
- INCOMING_CALL_WIDGET_PING,
- // Visual polish: add a small delay here, to make the
- // GlowPadView widget visible for a brief moment
- // *before* starting the ping animation.
- // This value doesn't need to be very precise.
- 250 /* msec */);
- }
-
- /**
- * Handles state changes of the incoming-call widget.
- *
- * In previous releases (where we used a SlidingTab widget) we would
- * display an onscreen hint depending on which "handle" the user was
- * dragging. But we now use a GlowPadView widget, which has only
- * one handle, so for now we don't display a hint at all (see the TODO
- * comment below.)
- */
- @Override
- public void onGrabbedStateChange(View v, int grabbedState) {
- if (mInCallScreen != null) {
- // Look up the hint based on which handle is currently grabbed.
- // (Note we don't simply pass grabbedState thru to the InCallScreen,
- // since *this* class is the only place that knows that the left
- // handle means "Answer" and the right handle means "Decline".)
- int hintTextResId, hintColorResId;
- switch (grabbedState) {
- case GlowPadView.OnTriggerListener.NO_HANDLE:
- case GlowPadView.OnTriggerListener.CENTER_HANDLE:
- hintTextResId = 0;
- hintColorResId = 0;
- break;
- default:
- Log.e(LOG_TAG, "onGrabbedStateChange: unexpected grabbedState: "
- + grabbedState);
- hintTextResId = 0;
- hintColorResId = 0;
- break;
- }
-
- // Tell the InCallScreen to update the CallCard and force the
- // screen to redraw.
- mInCallScreen.updateIncomingCallWidgetHint(hintTextResId, hintColorResId);
- }
- }
-
- /**
- * Handles an incoming RING event from the telephony layer.
- */
- public void onIncomingRing() {
- if (ENABLE_PING_ON_RING_EVENTS) {
- // Each RING from the telephony layer triggers a "ping" animation
- // of the GlowPadView widget. (The intent here is to make the
- // pinging appear to be synchronized with the ringtone, although
- // that only works for non-looping ringtones.)
- triggerPing();
- }
- }
-
- /**
- * Runs a single "ping" animation of the GlowPadView widget,
- * or do nothing if the GlowPadView widget is no longer visible.
- *
- * Also, if ENABLE_PING_AUTO_REPEAT is true, schedule the next ping as
- * well (but again, only if the GlowPadView widget is still visible.)
- */
- public void triggerPing() {
- if (DBG) log("triggerPing: mIncomingCallWidget = " + mIncomingCallWidget);
-
- if (!mInCallScreen.isForegroundActivity()) {
- // InCallScreen has been dismissed; no need to run a ping *or*
- // schedule another one.
- log("- triggerPing: InCallScreen no longer in foreground; ignoring...");
- return;
- }
-
- if (mIncomingCallWidget == null) {
- // This shouldn't happen; the GlowPadView widget should
- // always be present in our layout file.
- Log.w(LOG_TAG, "- triggerPing: null mIncomingCallWidget!");
- return;
- }
-
- if (DBG) log("- triggerPing: mIncomingCallWidget visibility = "
- + mIncomingCallWidget.getVisibility());
-
- if (mIncomingCallWidget.getVisibility() != View.VISIBLE) {
- if (DBG) log("- triggerPing: mIncomingCallWidget no longer visible; ignoring...");
- return;
- }
-
- // Ok, run a ping (and schedule the next one too, if desired...)
-
- mIncomingCallWidget.ping();
-
- if (ENABLE_PING_AUTO_REPEAT) {
- // Schedule the next ping. (ENABLE_PING_AUTO_REPEAT mode
- // allows the ping animation to repeat much faster than in
- // the ENABLE_PING_ON_RING_EVENTS case, since telephony RING
- // events come fairly slowly (about 3 seconds apart.))
-
- // No need to check here if the call is still ringing, by
- // the way, since we hide mIncomingCallWidget as soon as the
- // ringing stops, or if the user answers. (And at that
- // point, any future triggerPing() call will be a no-op.)
-
- // TODO: Rather than having a separate timer here, maybe try
- // having these pings synchronized with the vibrator (see
- // VibratorThread in Ringer.java; we'd just need to get
- // events routed from there to here, probably via the
- // PhoneApp instance.) (But watch out: make sure pings
- // still work even if the Vibrate setting is turned off!)
-
- mHandler.sendEmptyMessageDelayed(INCOMING_CALL_WIDGET_PING,
- PING_AUTO_REPEAT_DELAY_MSEC);
- }
- }
-
- // Debugging / testing code
-
- private void log(String msg) {
- Log.d(LOG_TAG, msg);
- }
-}
diff --git a/src/com/android/phone/InCallUiState.java b/src/com/android/phone/InCallUiState.java
deleted file mode 100644
index 126ef63..0000000
--- a/src/com/android/phone/InCallUiState.java
+++ /dev/null
@@ -1,398 +0,0 @@
-/*
- * Copyright (C) 2011 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.phone;
-
-import com.android.phone.Constants.CallStatusCode;
-
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.text.TextUtils;
-import android.util.Log;
-
-
-/**
- * Helper class to keep track of "persistent state" of the in-call UI.
- *
- * The onscreen appearance of the in-call UI mostly depends on the current
- * Call/Connection state, which is owned by the telephony framework. But
- * there's some application-level "UI state" too, which lives here in the
- * phone app.
- *
- * This application-level state information is *not* maintained by the
- * InCallScreen, since it needs to persist throughout an entire phone call,
- * not just a single resume/pause cycle of the InCallScreen. So instead, that
- * state is stored here, in a singleton instance of this class.
- *
- * The state kept here is a high-level abstraction of in-call UI state: we
- * don't know about implementation details like specific widgets or strings or
- * resources, but we do understand higher level concepts (for example "is the
- * dialpad visible") and high-level modes (like InCallScreenMode) and error
- * conditions (like CallStatusCode).
- *
- * @see InCallControlState for a separate collection of "UI state" that
- * controls all the onscreen buttons of the in-call UI, based on the state of
- * the telephony layer.
- *
- * The singleton instance of this class is owned by the PhoneApp instance.
- */
-public class InCallUiState {
- private static final String TAG = "InCallUiState";
- private static final boolean DBG = false;
-
- /** The singleton InCallUiState instance. */
- private static InCallUiState sInstance;
-
- private Context mContext;
-
- /**
- * Initialize the singleton InCallUiState instance.
- *
- * This is only done once, at startup, from PhoneApp.onCreate().
- * From then on, the InCallUiState instance is available via the
- * PhoneApp's public "inCallUiState" field, which is why there's no
- * getInstance() method here.
- */
- /* package */ static InCallUiState init(Context context) {
- synchronized (InCallUiState.class) {
- if (sInstance == null) {
- sInstance = new InCallUiState(context);
- } else {
- Log.wtf(TAG, "init() called multiple times! sInstance = " + sInstance);
- }
- return sInstance;
- }
- }
-
- /**
- * Private constructor (this is a singleton).
- * @see init()
- */
- private InCallUiState(Context context) {
- mContext = context;
- }
-
-
- //
- // (1) High-level state of the whole in-call UI
- //
-
- /** High-level "modes" of the in-call UI. */
- public enum InCallScreenMode {
- /**
- * Normal in-call UI elements visible.
- */
- NORMAL,
- /**
- * "Manage conference" UI is visible, totally replacing the
- * normal in-call UI.
- */
- MANAGE_CONFERENCE,
- /**
- * Non-interactive UI state. Call card is visible,
- * displaying information about the call that just ended.
- */
- CALL_ENDED,
- /**
- * Normal OTA in-call UI elements visible.
- */
- OTA_NORMAL,
- /**
- * OTA call ended UI visible, replacing normal OTA in-call UI.
- */
- OTA_ENDED,
- /**
- * Default state when not on call
- */
- UNDEFINED
- }
-
- /** Current high-level "mode" of the in-call UI. */
- InCallScreenMode inCallScreenMode = InCallScreenMode.UNDEFINED;
-
-
- //
- // (2) State of specific UI elements
- //
-
- /**
- * Is the onscreen twelve-key dialpad visible?
- */
- boolean showDialpad;
-
- /**
- * The contents of the twelve-key dialpad's "digits" display, which is
- * visible only when the dialpad itself is visible.
- *
- * (This is basically the "history" of DTMF digits you've typed so far
- * in the current call. It's cleared out any time a new call starts,
- * to make sure the digits don't persist between two separate calls.)
- */
- String dialpadDigits;
-
- /**
- * The contact/dialed number information shown in the DTMF digits text
- * when the user has not yet typed any digits.
- *
- * Currently only used for displaying "Voice Mail" since voicemail calls
- * start directly in the dialpad view.
- */
- String dialpadContextText;
-
- //
- // (3) Error / diagnostic indications
- //
-
- // This section provides an abstract concept of an "error status
- // indication" for some kind of exceptional condition that needs to be
- // communicated to the user, in the context of the in-call UI.
- //
- // If mPendingCallStatusCode is any value other than SUCCESS, that
- // indicates that the in-call UI needs to display a dialog to the user
- // with the specified title and message text.
- //
- // When an error occurs outside of the InCallScreen itself (like
- // during CallController.placeCall() for example), we inform the user
- // by doing the following steps:
- //
- // (1) set the "pending call status code" to a value other than SUCCESS
- // (based on the specific error that happened)
- // (2) force the InCallScreen to be launched (or relaunched)
- // (3) InCallScreen.onResume() will notice that pending call status code
- // is set, and will actually bring up the desired dialog.
- //
- // Watch out: any time you set (or change!) the pending call status code
- // field you must be sure to always (re)launch the InCallScreen.
- //
- // Finally, the InCallScreen itself is responsible for resetting the
- // pending call status code, when the user dismisses the dialog (like by
- // hitting the OK button or pressing Back). The pending call status code
- // field is NOT cleared simply by the InCallScreen being paused or
- // finished, since the resulting dialog needs to persist across
- // orientation changes or if the screen turns off.
-
- // TODO: other features we might eventually need here:
- //
- // - Some error status messages stay in force till reset,
- // others may automatically clear themselves after
- // a fixed delay
- //
- // - Some error statuses may be visible as a dialog with an OK
- // button (like "call failed"), others may be an indefinite
- // progress dialog (like "turning on radio for emergency call").
- //
- // - Eventually some error statuses may have extra actions (like a
- // "retry call" button that we might provide at the bottom of the
- // "call failed because you have no signal" dialog.)
-
- /**
- * The current pending "error status indication" that we need to
- * display to the user.
- *
- * If this field is set to a value other than SUCCESS, this indicates to
- * the InCallScreen that we need to show some kind of message to the user
- * (usually an error dialog) based on the specified status code.
- */
- private CallStatusCode mPendingCallStatusCode = CallStatusCode.SUCCESS;
-
- /**
- * @return true if there's a pending "error status indication"
- * that we need to display to the user.
- */
- public boolean hasPendingCallStatusCode() {
- if (DBG) log("hasPendingCallStatusCode() ==> "
- + (mPendingCallStatusCode != CallStatusCode.SUCCESS));
- return (mPendingCallStatusCode != CallStatusCode.SUCCESS);
- }
-
- /**
- * @return the pending "error status indication" code
- * that we need to display to the user.
- */
- public CallStatusCode getPendingCallStatusCode() {
- if (DBG) log("getPendingCallStatusCode() ==> " + mPendingCallStatusCode);
- return mPendingCallStatusCode;
- }
-
- /**
- * Sets the pending "error status indication" code.
- */
- public void setPendingCallStatusCode(CallStatusCode status) {
- if (DBG) log("setPendingCallStatusCode( " + status + " )...");
- if (mPendingCallStatusCode != CallStatusCode.SUCCESS) {
- // Uh oh: mPendingCallStatusCode is already set to some value
- // other than SUCCESS (which indicates that there was some kind of
- // failure), and now we're trying to indicate another (potentially
- // different) failure. But we can only indicate one failure at a
- // time to the user, so the previous pending code is now going to
- // be lost.
- Log.w(TAG, "setPendingCallStatusCode: setting new code " + status
- + ", but a previous code " + mPendingCallStatusCode
- + " was already pending!");
- }
- mPendingCallStatusCode = status;
- }
-
- /**
- * Clears out the pending "error status indication" code.
- *
- * This indicates that there's no longer any error or "exceptional
- * condition" that needs to be displayed to the user. (Typically, this
- * method is called when the user dismisses the error dialog that came up
- * because of a previous call status code.)
- */
- public void clearPendingCallStatusCode() {
- if (DBG) log("clearPendingCallStatusCode()...");
- mPendingCallStatusCode = CallStatusCode.SUCCESS;
- }
-
- /**
- * Flag used to control the CDMA-specific "call lost" dialog.
- *
- * If true, that means that if the *next* outgoing call fails with an
- * abnormal disconnection cause, we need to display the "call lost"
- * dialog. (Normally, in CDMA we handle some types of call failures
- * by automatically retrying the call. This flag is set to true when
- * we're about to auto-retry, which means that if the *retry* also
- * fails we'll give up and display an error.)
- * See the logic in InCallScreen.onDisconnect() for the full story.
- *
- * TODO: the state machine that maintains the needToShowCallLostDialog
- * flag in InCallScreen.onDisconnect() should really be moved into the
- * CallController. Then we can get rid of this extra flag, and
- * instead simply use the CallStatusCode value CDMA_CALL_LOST to
- * trigger the "call lost" dialog.
- */
- boolean needToShowCallLostDialog;
-
-
- //
- // Progress indications
- //
-
- /**
- * Possible messages we might need to display along with
- * an indefinite progress spinner.
- */
- public enum ProgressIndicationType {
- /**
- * No progress indication needs to be shown.
- */
- NONE,
-
- /**
- * Shown when making an emergency call from airplane mode;
- * see CallController$EmergencyCallHelper.
- */
- TURNING_ON_RADIO,
-
- /**
- * Generic "retrying" state. (Specifically, this is shown while
- * retrying after an initial failure from the "emergency call from
- * airplane mode" sequence.)
- */
- RETRYING
- }
-
- /**
- * The current progress indication that should be shown
- * to the user. Any value other than NONE will cause the InCallScreen
- * to bring up an indefinite progress spinner along with a message
- * corresponding to the specified ProgressIndicationType.
- */
- private ProgressIndicationType progressIndication = ProgressIndicationType.NONE;
-
- /** Sets the current progressIndication. */
- public void setProgressIndication(ProgressIndicationType value) {
- progressIndication = value;
- }
-
- /** Clears the current progressIndication. */
- public void clearProgressIndication() {
- progressIndication = ProgressIndicationType.NONE;
- }
-
- /**
- * @return the current progress indication type, or ProgressIndicationType.NONE
- * if no progress indication is currently active.
- */
- public ProgressIndicationType getProgressIndication() {
- return progressIndication;
- }
-
- /** @return true if a progress indication is currently active. */
- public boolean isProgressIndicationActive() {
- return (progressIndication != ProgressIndicationType.NONE);
- }
-
- /**
- * "Call origin" of the most recent phone call.
- *
- * Watch out: right now this is only used to determine where the user should go after the phone
- * call. See also {@link InCallScreen} for more detail. There is *no* specific specification
- * about how this variable will be used.
- *
- * @see PhoneGlobals#setLatestActiveCallOrigin(String)
- * @see PhoneGlobals#createPhoneEndIntentUsingCallOrigin()
- *
- * TODO: we should determine some public behavior for this variable.
- */
- String latestActiveCallOrigin;
-
- /**
- * Timestamp for "Call origin". This will be used to preserve when the call origin was set.
- * {@link android.os.SystemClock#elapsedRealtime()} will be used.
- */
- long latestActiveCallOriginTimeStamp;
-
- /**
- * Flag forcing Phone app to show in-call UI even when there's no phone call and thus Phone
- * is in IDLE state. This will be turned on only when:
- *
- * - the last phone call is hung up, and
- * - the screen is being turned off in the middle of in-call UI (and thus when the screen being
- * turned on in-call UI is expected to be the next foreground activity)
- *
- * At that moment whole UI should show "previously disconnected phone call" for a moment and
- * exit itself. {@link InCallScreen#onPause()} will turn this off and prevent possible weird
- * cases which may happen with that exceptional case.
- */
- boolean showAlreadyDisconnectedState;
-
- //
- // Debugging
- //
-
- public void dumpState() {
- log("dumpState():");
- log(" - showDialpad: " + showDialpad);
- log(" - dialpadContextText: " + dialpadContextText);
- if (hasPendingCallStatusCode()) {
- log(" - status indication is pending!");
- log(" - pending call status code = " + mPendingCallStatusCode);
- } else {
- log(" - pending call status code: none");
- }
- log(" - progressIndication: " + progressIndication);
- log(" - latestActiveCallOrigin: " + latestActiveCallOrigin);
- }
-
- private static void log(String msg) {
- Log.d(TAG, msg);
- }
-}
diff --git a/src/com/android/phone/ManageConferenceUtils.java b/src/com/android/phone/ManageConferenceUtils.java
deleted file mode 100644
index 62e9a99..0000000
--- a/src/com/android/phone/ManageConferenceUtils.java
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
- * Copyright (C) 2009 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.phone;
-
-import android.os.SystemProperties;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewStub;
-import android.widget.Chronometer;
-import android.widget.TextView;
-
-import com.android.internal.telephony.CallerInfo;
-import com.android.internal.telephony.CallerInfoAsyncQuery;
-import com.android.internal.telephony.CallManager;
-import com.android.internal.telephony.Connection;
-
-import java.util.List;
-
-
-/**
- * Helper class to initialize and run the InCallScreen's "Manage conference" UI.
- */
-public class ManageConferenceUtils {
- private static final String LOG_TAG = "ManageConferenceUtils";
- private static final boolean DBG =
- (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
-
- /**
- * CallerInfoAsyncQuery.OnQueryCompleteListener implementation.
- *
- * This object listens for results from the caller-id info queries we
- * fire off in updateManageConferenceRow(), and updates the
- * corresponding conference row.
- */
- private final class QueryCompleteListener
- implements CallerInfoAsyncQuery.OnQueryCompleteListener {
- private final int mConferencCallListIndex;
-
- public QueryCompleteListener(int index) {
- mConferencCallListIndex = index;
- }
-
- @Override
- public void onQueryComplete(int token, Object cookie, CallerInfo ci) {
- if (DBG) log("callerinfo query complete, updating UI." + ci);
-
- Connection connection = (Connection) cookie;
- int presentation = connection.getNumberPresentation();
-
- // get the viewgroup (conference call list item) and make it visible
- ViewGroup viewGroup = mConferenceCallList[mConferencCallListIndex];
- viewGroup.setVisibility(View.VISIBLE);
-
- // update the list item with this information.
- displayCallerInfoForConferenceRow(ci, presentation,
- (TextView) viewGroup.findViewById(R.id.conferenceCallerName),
- (TextView) viewGroup.findViewById(R.id.conferenceCallerNumberType),
- (TextView) viewGroup.findViewById(R.id.conferenceCallerNumber));
- }
- }
-
- private InCallScreen mInCallScreen;
- private CallManager mCM;
-
- // "Manage conference" UI elements and state
- private ViewGroup mManageConferencePanel;
- private View mButtonManageConferenceDone;
- private ViewGroup[] mConferenceCallList;
- private int mNumCallersInConference;
- private Chronometer mConferenceTime;
-
- // See CallTracker.MAX_CONNECTIONS_PER_CALL
- private static final int MAX_CALLERS_IN_CONFERENCE = 5;
-
- public ManageConferenceUtils(InCallScreen inCallScreen, CallManager cm) {
- if (DBG) log("ManageConferenceUtils constructor...");
- mInCallScreen = inCallScreen;
- mCM = cm;
- }
-
- public void initManageConferencePanel() {
- if (DBG) log("initManageConferencePanel()...");
- if (mManageConferencePanel == null) {
- if (DBG) log("initManageConferencePanel: first-time initialization!");
-
- // Inflate the ViewStub, look up and initialize the UI elements.
- ViewStub stub = (ViewStub) mInCallScreen.findViewById(R.id.manageConferencePanelStub);
- stub.inflate();
-
- mManageConferencePanel =
- (ViewGroup) mInCallScreen.findViewById(R.id.manageConferencePanel);
- if (mManageConferencePanel == null) {
- throw new IllegalStateException("Couldn't find manageConferencePanel!");
- }
-
- // set up the Conference Call chronometer
- mConferenceTime =
- (Chronometer) mInCallScreen.findViewById(R.id.manageConferencePanelHeader);
- mConferenceTime.setFormat(mInCallScreen.getString(R.string.caller_manage_header));
-
- // Create list of conference call widgets
- mConferenceCallList = new ViewGroup[MAX_CALLERS_IN_CONFERENCE];
-
- final int[] viewGroupIdList = { R.id.caller0, R.id.caller1, R.id.caller2,
- R.id.caller3, R.id.caller4 };
- for (int i = 0; i < MAX_CALLERS_IN_CONFERENCE; i++) {
- mConferenceCallList[i] =
- (ViewGroup) mInCallScreen.findViewById(viewGroupIdList[i]);
- }
-
- mButtonManageConferenceDone = mInCallScreen.findViewById(R.id.manage_done);
- mButtonManageConferenceDone.setOnClickListener(mInCallScreen);
- }
- }
-
- /**
- * Shows or hides the manageConferencePanel.
- */
- public void setPanelVisible(boolean visible) {
- if (mManageConferencePanel != null) {
- mManageConferencePanel.setVisibility(visible ? View.VISIBLE : View.GONE);
- }
- }
-
- /**
- * Starts the "conference time" chronometer.
- */
- public void startConferenceTime(long base) {
- if (mConferenceTime != null) {
- mConferenceTime.setBase(base);
- mConferenceTime.start();
- }
- }
-
- /**
- * Stops the "conference time" chronometer.
- */
- public void stopConferenceTime() {
- if (mConferenceTime != null) {
- mConferenceTime.stop();
- }
- }
-
- public int getNumCallersInConference() {
- return mNumCallersInConference;
- }
-
- /**
- * Updates the "Manage conference" UI based on the specified List of
- * connections.
- *
- * @param connections the List of connections belonging to
- * the current foreground call; size must be greater than 1
- * (or it wouldn't be a conference call in the first place.)
- */
- public void updateManageConferencePanel(List<Connection> connections) {
- mNumCallersInConference = connections.size();
- if (DBG) log("updateManageConferencePanel()... num connections in conference = "
- + mNumCallersInConference);
-
- // Can we give the user the option to separate out ("go private with") a single
- // caller from this conference?
- final boolean hasActiveCall = mCM.hasActiveFgCall();
- final boolean hasHoldingCall = mCM.hasActiveBgCall();
- boolean canSeparate = !(hasActiveCall && hasHoldingCall);
-
- for (int i = 0; i < MAX_CALLERS_IN_CONFERENCE; i++) {
- if (i < mNumCallersInConference) {
- // Fill in the row in the UI for this caller.
- Connection connection = (Connection) connections.get(i);
- updateManageConferenceRow(i, connection, canSeparate);
- } else {
- // Blank out this row in the UI
- updateManageConferenceRow(i, null, false);
- }
- }
- }
-
- /**
- * Updates a single row of the "Manage conference" UI. (One row in this
- * UI represents a single caller in the conference.)
- *
- * @param i the row to update
- * @param connection the Connection corresponding to this caller.
- * If null, that means this is an "empty slot" in the conference,
- * so hide this row in the UI.
- * @param canSeparate if true, show a "Separate" (i.e. "Private") button
- * on this row in the UI.
- */
- public void updateManageConferenceRow(final int i,
- final Connection connection,
- boolean canSeparate) {
- if (DBG) log("updateManageConferenceRow(" + i + ")... connection = " + connection);
-
- if (connection != null) {
- // Activate this row of the Manage conference panel:
- mConferenceCallList[i].setVisibility(View.VISIBLE);
-
- // get the relevant children views
- View endButton = mConferenceCallList[i].findViewById(R.id.conferenceCallerDisconnect);
- View separateButton = mConferenceCallList[i].findViewById(
- R.id.conferenceCallerSeparate);
- TextView nameTextView = (TextView) mConferenceCallList[i].findViewById(
- R.id.conferenceCallerName);
- TextView numberTextView = (TextView) mConferenceCallList[i].findViewById(
- R.id.conferenceCallerNumber);
- TextView numberTypeTextView = (TextView) mConferenceCallList[i].findViewById(
- R.id.conferenceCallerNumberType);
-
- if (DBG) log("- button: " + endButton + ", nameTextView: " + nameTextView);
-
- // Hook up this row's buttons.
- View.OnClickListener endThisConnection = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- endConferenceConnection(i, connection);
- PhoneGlobals.getInstance().pokeUserActivity();
- }
- };
- endButton.setOnClickListener(endThisConnection);
- //
- if (canSeparate) {
- View.OnClickListener separateThisConnection = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- separateConferenceConnection(i, connection);
- PhoneGlobals.getInstance().pokeUserActivity();
- }
- };
- separateButton.setOnClickListener(separateThisConnection);
- separateButton.setVisibility(View.VISIBLE);
- } else {
- separateButton.setVisibility(View.INVISIBLE);
- }
-
- // Name/number for this caller.
- QueryCompleteListener listener = new QueryCompleteListener(i);
- PhoneUtils.CallerInfoToken info =
- PhoneUtils.startGetCallerInfo(mInCallScreen,
- connection, listener, connection);
- if (DBG) log(" - got info from startGetCallerInfo(): " + info);
-
- // display the CallerInfo.
- displayCallerInfoForConferenceRow(info.currentInfo, connection.getNumberPresentation(),
- nameTextView, numberTypeTextView, numberTextView);
- } else {
- // Disable this row of the Manage conference panel:
- mConferenceCallList[i].setVisibility(View.GONE);
- }
- }
-
- /**
- * Helper function to fill out the Conference Call(er) information
- * for each item in the "Manage Conference Call" list.
- *
- * @param presentation presentation specified by {@link Connection}.
- */
- public final void displayCallerInfoForConferenceRow(CallerInfo ci, int presentation,
- TextView nameTextView, TextView numberTypeTextView, TextView numberTextView) {
- // gather the correct name and number information.
- String callerName = "";
- String callerNumber = "";
- String callerNumberType = "";
- if (ci != null) {
- callerName = ci.name;
- if (TextUtils.isEmpty(callerName)) {
- // Do similar fallback as CallCard does.
- // See also CallCard#updateDisplayForPerson().
- if (TextUtils.isEmpty(ci.phoneNumber)) {
- callerName = PhoneUtils.getPresentationString(mInCallScreen, presentation);
- } else if (!TextUtils.isEmpty(ci.cnapName)) {
- // No name, but we do have a valid CNAP name, so use that.
- callerName = ci.cnapName;
- } else {
- callerName = ci.phoneNumber;
- }
- } else {
- callerNumber = ci.phoneNumber;
- callerNumberType = ci.phoneLabel;
- }
- }
-
- // set the caller name
- nameTextView.setText(callerName);
-
- // set the caller number in subscript, or make the field disappear.
- if (TextUtils.isEmpty(callerNumber)) {
- numberTextView.setVisibility(View.GONE);
- numberTypeTextView.setVisibility(View.GONE);
- } else {
- numberTextView.setVisibility(View.VISIBLE);
- numberTextView.setText(callerNumber);
- numberTypeTextView.setVisibility(View.VISIBLE);
- numberTypeTextView.setText(callerNumberType);
- }
- }
-
- /**
- * Ends the specified connection on a conference call. This method is
- * run (via a closure containing a row index and Connection) when the
- * user clicks the "End" button on a specific row in the Manage
- * conference UI.
- */
- public void endConferenceConnection(int i, Connection connection) {
- if (DBG) log("===> ENDING conference connection " + i
- + ": Connection " + connection);
- // The actual work of ending the connection:
- PhoneUtils.hangup(connection);
- // No need to manually update the "Manage conference" UI here;
- // that'll happen automatically very soon (when we get the
- // onDisconnect() callback triggered by this hangup() call.)
- }
-
- /**
- * Separates out the specified connection on a conference call. This
- * method is run (via a closure containing a row index and Connection)
- * when the user clicks the "Separate" (i.e. "Private") button on a
- * specific row in the Manage conference UI.
- */
- public void separateConferenceConnection(int i, Connection connection) {
- if (DBG) log("===> SEPARATING conference connection " + i
- + ": Connection " + connection);
-
- PhoneUtils.separateCall(connection);
-
- // Note that separateCall() automagically makes the
- // newly-separated call into the foreground call (which is the
- // desired UI), so there's no need to do any further
- // call-switching here.
- // There's also no need to manually update (or hide) the "Manage
- // conference" UI; that'll happen on its own in a moment (when we
- // get the phone state change event triggered by the call to
- // separateCall().)
- }
-
-
- private void log(String msg) {
- Log.d(LOG_TAG, msg);
- }
-}
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index 7771fa9..8ead0ce 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -645,10 +645,6 @@
// activity, since the in-call UI already provides an onscreen
// indication of the speaker state. (This reduces clutter in the
// status bar.)
- if (mApp.isShowingCallScreen()) {
- cancelSpeakerphone();
- return;
- }
if (showNotification) {
notifySpeakerphone();
@@ -684,10 +680,6 @@
// foreground activity, since the in-call UI already provides an
// onscreen indication of the mute state. (This reduces clutter
// in the status bar.)
- if (mApp.isShowingCallScreen()) {
- cancelMute();
- return;
- }
if ((mCM.getState() == PhoneConstants.State.OFFHOOK) && PhoneUtils.getMute()) {
if (DBG) log("updateMuteNotification: MUTED");
diff --git a/src/com/android/phone/OtaUtils.java b/src/com/android/phone/OtaUtils.java
index e713df9..8b67148 100644
--- a/src/com/android/phone/OtaUtils.java
+++ b/src/com/android/phone/OtaUtils.java
@@ -141,17 +141,9 @@
private static final String OTASP_NUMBER = "*228";
private static final String OTASP_NUMBER_NON_INTERACTIVE = "*22899";
- private InCallScreen mInCallScreen;
private Context mContext;
private PhoneGlobals mApplication;
private OtaWidgetData mOtaWidgetData;
- private ViewGroup mInCallTouchUi; // UI controls for regular calls
- private CallCard mCallCard;
-
- // The DTMFTwelveKeyDialer instance. We create this in
- // initOtaInCallScreen(), and attach it to the DTMFTwelveKeyDialerView
- // ("otaDtmfDialerView") that comes from otacall_card.xml.
- private DTMFTwelveKeyDialer mOtaCallCardDtmfDialer;
private static boolean sIsWizardMode = true;
@@ -201,7 +193,6 @@
public AlertDialog otaFailureDialog;
public AlertDialog otaSkipConfirmationDialog;
public TextView otaTitle;
- public DTMFTwelveKeyDialerView otaDtmfDialerView;
public Button otaTryAgainButton;
}
@@ -225,59 +216,6 @@
}
/**
- * Updates the OtaUtils object's references to some UI elements belonging to
- * the InCallScreen. This is used only in interactive mode.
- *
- * Use clearUiWidgets() to clear out these references. (The InCallScreen
- * is responsible for doing this from its onDestroy() method.)
- *
- * This method has no effect if the UI widgets have already been set up.
- * (In other words, it's safe to call this every time through
- * InCallScreen.onResume().)
- */
- public void updateUiWidgets(InCallScreen inCallScreen,
- ViewGroup inCallTouchUi, CallCard callCard) {
- if (DBG) log("updateUiWidgets()... mInCallScreen = " + mInCallScreen);
-
- if (!mInteractive) {
- throw new IllegalStateException("updateUiWidgets() called in non-interactive mode");
- }
-
- if (mInCallScreen != null) {
- if (DBG) log("updateUiWidgets(): widgets already set up, nothing to do...");
- return;
- }
-
- mInCallScreen = inCallScreen;
- mInCallTouchUi = inCallTouchUi;
- mCallCard = callCard;
- mOtaWidgetData = new OtaWidgetData();
-
- // Inflate OTASP-specific UI elements:
- ViewStub otaCallCardStub = (ViewStub) mInCallScreen.findViewById(R.id.otaCallCardStub);
- if (otaCallCardStub != null) {
- // If otaCallCardStub is null here, that means it's already been
- // inflated (which could have happened in the current InCallScreen
- // instance for a *prior* OTASP call.)
- otaCallCardStub.inflate();
- }
-
- readXmlSettings();
- initOtaInCallScreen();
- }
-
- /**
- * Clear out the OtaUtils object's references to any InCallScreen UI
- * elements. This is the opposite of updateUiWidgets().
- */
- public void clearUiWidgets() {
- mInCallScreen = null;
- mInCallTouchUi = null;
- mCallCard = null;
- mOtaWidgetData = null;
- }
-
- /**
* Starts the OTA provisioning call. If the MIN isn't available yet, it returns false and adds
* an event to return the request to the calling app when it becomes available.
*
@@ -583,7 +521,7 @@
// TODO(OTASP): note app.inCallUiState.inCallScreenMode and
// app.cdmaOtaInCallScreenUiState.state are mostly redundant. Combine them.
- app.inCallUiState.inCallScreenMode = InCallUiState.InCallScreenMode.OTA_NORMAL;
+ // app.inCallUiState.inCallScreenMode = InCallUiState.InCallScreenMode.OTA_NORMAL;
// TODO(OTASP / bug 5092031): we ideally should call
// otaShowListeningScreen() here to make sure that the DTMF dialpad
@@ -714,7 +652,7 @@
if (DBG) log("otaShowHome()...");
mApplication.cdmaOtaScreenState.otaScreenState =
CdmaOtaScreenState.OtaScreenState.OTA_STATUS_UNDEFINED;
- mInCallScreen.endInCallScreenSession();
+ // mInCallScreen.endInCallScreenSession();
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory (Intent.CATEGORY_HOME);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -727,7 +665,7 @@
sendOtaspResult(OTASP_USER_SKIPPED);
- if (mInteractive) mInCallScreen.finish();
+ // if (mInteractive) mInCallScreen.finish();
return;
}
@@ -753,7 +691,7 @@
// ...and get the OTASP-specific UI into the right state.
otaShowListeningScreen();
- mInCallScreen.requestUpdateScreen();
+ // mInCallScreen.requestUpdateScreen();
}
return;
}
@@ -804,7 +742,7 @@
otaScreenInitialize();
mOtaWidgetData.otaTextListenProgress.setVisibility(View.VISIBLE);
mOtaWidgetData.otaTextListenProgress.setText(R.string.ota_listen);
- mOtaWidgetData.otaDtmfDialerView.setVisibility(View.VISIBLE);
+ // mOtaWidgetData.otaDtmfDialerView.setVisibility(View.VISIBLE);
mOtaWidgetData.callCardOtaButtonsListenProgress.setVisibility(View.VISIBLE);
mOtaWidgetData.otaSpeakerButton.setVisibility(View.VISIBLE);
boolean speakerOn = PhoneUtils.isSpeakerOn(mContext);
@@ -923,7 +861,7 @@
mApplication.cdmaOtaScreenState.otaScreenState =
CdmaOtaScreenState.OtaScreenState.OTA_STATUS_PROGRESS;
- if ((mOtaWidgetData == null) || (mInCallScreen == null)) {
+ if ((mOtaWidgetData == null) /* || (mInCallScreen == null) */) {
Log.w(LOG_TAG, "otaShowInProgressScreen: UI widgets not set up yet!");
// TODO(OTASP): our CdmaOtaScreenState is now correct; we just set
@@ -1008,9 +946,9 @@
mOtaWidgetData.callCardOtaButtonsFailSuccess.setVisibility(View.VISIBLE);
mOtaWidgetData.otaTryAgainButton.setVisibility(View.VISIBLE);
//close the dialer if open
- if (isDialerOpened()) {
- mOtaCallCardDtmfDialer.closeDialer(false);
- }
+ // if (isDialerOpened()) {
+ // mOtaCallCardDtmfDialer.closeDialer(false);
+ // }
}
/**
@@ -1026,9 +964,9 @@
mOtaWidgetData.callCardOtaButtonsFailSuccess.setVisibility(View.VISIBLE);
mOtaWidgetData.otaNextButton.setVisibility(View.VISIBLE);
//close the dialer if open
- if (isDialerOpened()) {
- mOtaCallCardDtmfDialer.closeDialer(false);
- }
+ // if (isDialerOpened()) {
+ // mOtaCallCardDtmfDialer.closeDialer(false);
+ // }
}
/**
@@ -1047,7 +985,7 @@
log("Ignoring key events...");
return true;
}};
- mOtaWidgetData.spcErrorDialog = new AlertDialog.Builder(mInCallScreen)
+ mOtaWidgetData.spcErrorDialog = new AlertDialog.Builder(null /* mInCallScreen */)
.setMessage(R.string.ota_spc_failure)
.setOnKeyListener(keyListener)
.create();
@@ -1056,12 +994,12 @@
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
mOtaWidgetData.spcErrorDialog.show();
//close the dialer if open
- if (isDialerOpened()) {
- mOtaCallCardDtmfDialer.closeDialer(false);
- }
+ // if (isDialerOpened()) {
+ // mOtaCallCardDtmfDialer.closeDialer(false);
+ // }
long noticeTime = length*1000;
if (DBG) log("otaShowSpcErrorNotice(), remaining SPC noticeTime" + noticeTime);
- mInCallScreen.requestCloseSpcErrorNotice(noticeTime);
+ // mInCallScreen.requestCloseSpcErrorNotice(noticeTime);
}
}
@@ -1083,7 +1021,7 @@
private void otaShowProgramFailureNotice(int length) {
if (DBG) log("otaShowProgramFailureNotice()...");
if (mOtaWidgetData.otaFailureDialog == null) {
- mOtaWidgetData.otaFailureDialog = new AlertDialog.Builder(mInCallScreen)
+ mOtaWidgetData.otaFailureDialog = new AlertDialog.Builder(null /* mInCallScreen */)
.setMessage(R.string.ota_failure)
.create();
mOtaWidgetData.otaFailureDialog.getWindow().addFlags(
@@ -1092,7 +1030,7 @@
mOtaWidgetData.otaFailureDialog.show();
long noticeTime = length*1000;
- mInCallScreen.requestCloseOtaFailureNotice(noticeTime);
+ // mInCallScreen.requestCloseOtaFailureNotice(noticeTime);
}
}
@@ -1124,12 +1062,12 @@
return;
}
- if (mInCallTouchUi != null) mInCallTouchUi.setVisibility(View.GONE);
- if (mCallCard != null) {
- mCallCard.setVisibility(View.GONE);
- // TODO: try removing this.
- mCallCard.hideCallCardElements();
- }
+ // if (mInCallTouchUi != null) mInCallTouchUi.setVisibility(View.GONE);
+ // if (mCallCard != null) {
+ // mCallCard.setVisibility(View.GONE);
+ // // TODO: try removing this.
+ // mCallCard.hideCallCardElements();
+ // }
mOtaWidgetData.otaTitle.setText(R.string.ota_title_activate);
mOtaWidgetData.otaTextActivate.setVisibility(View.GONE);
@@ -1139,7 +1077,7 @@
mOtaWidgetData.callCardOtaButtonsActivate.setVisibility(View.GONE);
mOtaWidgetData.callCardOtaButtonsListenProgress.setVisibility(View.GONE);
mOtaWidgetData.callCardOtaButtonsFailSuccess.setVisibility(View.GONE);
- mOtaWidgetData.otaDtmfDialerView.setVisibility(View.GONE);
+ // mOtaWidgetData.otaDtmfDialerView.setVisibility(View.GONE);
mOtaWidgetData.otaSpeakerButton.setVisibility(View.GONE);
mOtaWidgetData.otaTryAgainButton.setVisibility(View.GONE);
mOtaWidgetData.otaNextButton.setVisibility(View.GONE);
@@ -1157,7 +1095,8 @@
}
public boolean isDialerOpened() {
- boolean retval = (mOtaCallCardDtmfDialer != null && mOtaCallCardDtmfDialer.isOpened());
+ // boolean retval = (mOtaCallCardDtmfDialer != null && mOtaCallCardDtmfDialer.isOpened());
+ boolean retval = false;
if (DBG) log("- isDialerOpened() ==> " + retval);
return retval;
}
@@ -1180,30 +1119,30 @@
return;
}
- if ((mInCallScreen != null) && mInCallScreen.isForegroundActivity()) {
- if (DBG) log("otaShowProperScreen(): InCallScreen in foreground, currentstate = "
- + mApplication.cdmaOtaScreenState.otaScreenState);
- if (mInCallTouchUi != null) {
- mInCallTouchUi.setVisibility(View.GONE);
- }
- if (mCallCard != null) {
- mCallCard.setVisibility(View.GONE);
- }
- if (mApplication.cdmaOtaScreenState.otaScreenState
- == CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION) {
- otaShowActivateScreen();
- } else if (mApplication.cdmaOtaScreenState.otaScreenState
- == CdmaOtaScreenState.OtaScreenState.OTA_STATUS_LISTENING) {
- otaShowListeningScreen();
- } else if (mApplication.cdmaOtaScreenState.otaScreenState
- == CdmaOtaScreenState.OtaScreenState.OTA_STATUS_PROGRESS) {
- otaShowInProgressScreen();
- }
+ // if ((mInCallScreen != null) && mInCallScreen.isForegroundActivity()) {
+ // if (DBG) log("otaShowProperScreen(): InCallScreen in foreground, currentstate = "
+ // + mApplication.cdmaOtaScreenState.otaScreenState);
+ // if (mInCallTouchUi != null) {
+ // mInCallTouchUi.setVisibility(View.GONE);
+ // }
+ // if (mCallCard != null) {
+ // mCallCard.setVisibility(View.GONE);
+ // }
+ // if (mApplication.cdmaOtaScreenState.otaScreenState
+ // == CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION) {
+ // otaShowActivateScreen();
+ // } else if (mApplication.cdmaOtaScreenState.otaScreenState
+ // == CdmaOtaScreenState.OtaScreenState.OTA_STATUS_LISTENING) {
+ // otaShowListeningScreen();
+ // } else if (mApplication.cdmaOtaScreenState.otaScreenState
+ // == CdmaOtaScreenState.OtaScreenState.OTA_STATUS_PROGRESS) {
+ // otaShowInProgressScreen();
+ // }
- if (mApplication.cdmaOtaProvisionData.inOtaSpcState) {
- otaShowSpcErrorNotice(getOtaSpcDisplayTime());
- }
- }
+ // if (mApplication.cdmaOtaProvisionData.inOtaSpcState) {
+ // otaShowSpcErrorNotice(getOtaSpcDisplayTime());
+ // }
+ // }
}
/**
@@ -1292,7 +1231,7 @@
// the screen is not updated by the call disconnect
// handler and we have to do it here
setSpeaker(false);
- mInCallScreen.handleOtaCallEnd();
+ // mInCallScreen.handleOtaCallEnd();
}
}
}
@@ -1320,7 +1259,7 @@
return true;
}
};
- mOtaWidgetData.otaSkipConfirmationDialog = new AlertDialog.Builder(mInCallScreen)
+ mOtaWidgetData.otaSkipConfirmationDialog = new AlertDialog.Builder(null /* mInCallScreen */)
.setTitle(R.string.ota_skip_activation_dialog_title)
.setMessage(R.string.ota_skip_activation_dialog_message)
.setPositiveButton(
@@ -1389,60 +1328,60 @@
*/
private void initOtaInCallScreen() {
if (DBG) log("initOtaInCallScreen()...");
- mOtaWidgetData.otaTitle = (TextView) mInCallScreen.findViewById(R.id.otaTitle);
- mOtaWidgetData.otaTextActivate = (TextView) mInCallScreen.findViewById(R.id.otaActivate);
+ // mOtaWidgetData.otaTitle = (TextView) mInCallScreen.findViewById(R.id.otaTitle);
+ // mOtaWidgetData.otaTextActivate = (TextView) mInCallScreen.findViewById(R.id.otaActivate);
mOtaWidgetData.otaTextActivate.setVisibility(View.GONE);
- mOtaWidgetData.otaTextListenProgress =
- (TextView) mInCallScreen.findViewById(R.id.otaListenProgress);
- mOtaWidgetData.otaTextProgressBar =
- (ProgressBar) mInCallScreen.findViewById(R.id.progress_large);
+ // mOtaWidgetData.otaTextListenProgress =
+ // (TextView) mInCallScreen.findViewById(R.id.otaListenProgress);
+ // mOtaWidgetData.otaTextProgressBar =
+ // (ProgressBar) mInCallScreen.findViewById(R.id.progress_large);
mOtaWidgetData.otaTextProgressBar.setIndeterminate(true);
- mOtaWidgetData.otaTextSuccessFail =
- (TextView) mInCallScreen.findViewById(R.id.otaSuccessFailStatus);
+ // mOtaWidgetData.otaTextSuccessFail =
+ // (TextView) mInCallScreen.findViewById(R.id.otaSuccessFailStatus);
- mOtaWidgetData.otaUpperWidgets =
- (ViewGroup) mInCallScreen.findViewById(R.id.otaUpperWidgets);
- mOtaWidgetData.callCardOtaButtonsListenProgress =
- (View) mInCallScreen.findViewById(R.id.callCardOtaListenProgress);
- mOtaWidgetData.callCardOtaButtonsActivate =
- (View) mInCallScreen.findViewById(R.id.callCardOtaActivate);
- mOtaWidgetData.callCardOtaButtonsFailSuccess =
- (View) mInCallScreen.findViewById(R.id.callCardOtaFailOrSuccessful);
+ // mOtaWidgetData.otaUpperWidgets =
+ // (ViewGroup) mInCallScreen.findViewById(R.id.otaUpperWidgets);
+ // mOtaWidgetData.callCardOtaButtonsListenProgress =
+ // (View) mInCallScreen.findViewById(R.id.callCardOtaListenProgress);
+ // mOtaWidgetData.callCardOtaButtonsActivate =
+ // (View) mInCallScreen.findViewById(R.id.callCardOtaActivate);
+ // mOtaWidgetData.callCardOtaButtonsFailSuccess =
+ // (View) mInCallScreen.findViewById(R.id.callCardOtaFailOrSuccessful);
- mOtaWidgetData.otaEndButton = (Button) mInCallScreen.findViewById(R.id.otaEndButton);
- mOtaWidgetData.otaEndButton.setOnClickListener(mInCallScreen);
- mOtaWidgetData.otaSpeakerButton =
- (ToggleButton) mInCallScreen.findViewById(R.id.otaSpeakerButton);
- mOtaWidgetData.otaSpeakerButton.setOnClickListener(mInCallScreen);
- mOtaWidgetData.otaActivateButton =
- (Button) mInCallScreen.findViewById(R.id.otaActivateButton);
- mOtaWidgetData.otaActivateButton.setOnClickListener(mInCallScreen);
- mOtaWidgetData.otaSkipButton = (Button) mInCallScreen.findViewById(R.id.otaSkipButton);
- mOtaWidgetData.otaSkipButton.setOnClickListener(mInCallScreen);
- mOtaWidgetData.otaNextButton = (Button) mInCallScreen.findViewById(R.id.otaNextButton);
- mOtaWidgetData.otaNextButton.setOnClickListener(mInCallScreen);
- mOtaWidgetData.otaTryAgainButton =
- (Button) mInCallScreen.findViewById(R.id.otaTryAgainButton);
- mOtaWidgetData.otaTryAgainButton.setOnClickListener(mInCallScreen);
+ // mOtaWidgetData.otaEndButton = (Button) mInCallScreen.findViewById(R.id.otaEndButton);
+ // mOtaWidgetData.otaEndButton.setOnClickListener(mInCallScreen);
+ // mOtaWidgetData.otaSpeakerButton =
+ // (ToggleButton) mInCallScreen.findViewById(R.id.otaSpeakerButton);
+ // mOtaWidgetData.otaSpeakerButton.setOnClickListener(mInCallScreen);
+ // mOtaWidgetData.otaActivateButton =
+ // (Button) mInCallScreen.findViewById(R.id.otaActivateButton);
+ // mOtaWidgetData.otaActivateButton.setOnClickListener(mInCallScreen);
+ // mOtaWidgetData.otaSkipButton = (Button) mInCallScreen.findViewById(R.id.otaSkipButton);
+ // mOtaWidgetData.otaSkipButton.setOnClickListener(mInCallScreen);
+ // mOtaWidgetData.otaNextButton = (Button) mInCallScreen.findViewById(R.id.otaNextButton);
+ // mOtaWidgetData.otaNextButton.setOnClickListener(mInCallScreen);
+ // mOtaWidgetData.otaTryAgainButton =
+ // (Button) mInCallScreen.findViewById(R.id.otaTryAgainButton);
+ // mOtaWidgetData.otaTryAgainButton.setOnClickListener(mInCallScreen);
- mOtaWidgetData.otaDtmfDialerView =
- (DTMFTwelveKeyDialerView) mInCallScreen.findViewById(R.id.otaDtmfDialerView);
+ // mOtaWidgetData.otaDtmfDialerView =
+ // (DTMFTwelveKeyDialerView) mInCallScreen.findViewById(R.id.otaDtmfDialerView);
// Sanity-check: the otaDtmfDialerView widget should *always* be present.
- if (mOtaWidgetData.otaDtmfDialerView == null) {
- throw new IllegalStateException("initOtaInCallScreen: couldn't find otaDtmfDialerView");
- }
+ // if (mOtaWidgetData.otaDtmfDialerView == null) {
+ // throw new IllegalStateException("initOtaInCallScreen: couldn't find otaDtmfDialerView");
+ // }
// Create a new DTMFTwelveKeyDialer instance purely for use by the
// DTMFTwelveKeyDialerView ("otaDtmfDialerView") that comes from
// otacall_card.xml.
- mOtaCallCardDtmfDialer = new DTMFTwelveKeyDialer(mInCallScreen,
- mOtaWidgetData.otaDtmfDialerView);
+ // mOtaCallCardDtmfDialer = new DTMFTwelveKeyDialer(mInCallScreen,
+ // mOtaWidgetData.otaDtmfDialerView);
// Initialize the new DTMFTwelveKeyDialer instance. This is
// needed to play local DTMF tones.
- mOtaCallCardDtmfDialer.startDialerSession();
+ // mOtaCallCardDtmfDialer.startDialerSession();
- mOtaWidgetData.otaDtmfDialerView.setDialer(mOtaCallCardDtmfDialer);
+ // mOtaWidgetData.otaDtmfDialerView.setDialer(mOtaCallCardDtmfDialer);
}
/**
@@ -1463,17 +1402,17 @@
mApplication.cdmaOtaInCallScreenUiState.state = State.UNDEFINED;
if (mInteractive && (mOtaWidgetData != null)) {
- if (mInCallTouchUi != null) mInCallTouchUi.setVisibility(View.VISIBLE);
- if (mCallCard != null) {
- mCallCard.setVisibility(View.VISIBLE);
- mCallCard.hideCallCardElements();
- }
+ // if (mInCallTouchUi != null) mInCallTouchUi.setVisibility(View.VISIBLE);
+ // if (mCallCard != null) {
+ // mCallCard.setVisibility(View.VISIBLE);
+ // mCallCard.hideCallCardElements();
+ // }
// Free resources from the DTMFTwelveKeyDialer instance we created
// in initOtaInCallScreen().
- if (mOtaCallCardDtmfDialer != null) {
- mOtaCallCardDtmfDialer.stopDialerSession();
- }
+ // if (mOtaCallCardDtmfDialer != null) {
+ // mOtaCallCardDtmfDialer.stopDialerSession();
+ // }
mOtaWidgetData.otaTextActivate.setVisibility(View.GONE);
mOtaWidgetData.otaTextListenProgress.setVisibility(View.GONE);
@@ -1483,7 +1422,7 @@
mOtaWidgetData.callCardOtaButtonsListenProgress.setVisibility(View.GONE);
mOtaWidgetData.callCardOtaButtonsFailSuccess.setVisibility(View.GONE);
mOtaWidgetData.otaUpperWidgets.setVisibility(View.GONE);
- mOtaWidgetData.otaDtmfDialerView.setVisibility(View.GONE);
+ // mOtaWidgetData.otaDtmfDialerView.setVisibility(View.GONE);
mOtaWidgetData.otaNextButton.setVisibility(View.GONE);
mOtaWidgetData.otaTryAgainButton.setVisibility(View.GONE);
}
diff --git a/src/com/android/phone/OutgoingCallBroadcaster.java b/src/com/android/phone/OutgoingCallBroadcaster.java
index c5e8953..a4f7178 100644
--- a/src/com/android/phone/OutgoingCallBroadcaster.java
+++ b/src/com/android/phone/OutgoingCallBroadcaster.java
@@ -203,7 +203,6 @@
// to take down any OTASP-related UI first.
if (dialogState) app.dismissOtaDialogs();
app.clearOtaState();
- app.clearInCallScreenMode();
} else if (isOtaCallActive) {
// The actual OTASP call is active. Don't allow new
// outgoing calls at all from this state.
@@ -600,17 +599,6 @@
// EXTRA_ALREADY_CALLED extra.)
}
- // Remember the call origin so that users will be able to see an appropriate screen
- // after the phone call. This should affect both phone calls and SIP calls.
- final String callOrigin = intent.getStringExtra(PhoneGlobals.EXTRA_CALL_ORIGIN);
- if (callOrigin != null) {
- if (DBG) Log.v(TAG, " - Call origin is passed (" + callOrigin + ")");
- PhoneGlobals.getInstance().setLatestActiveCallOrigin(callOrigin);
- } else {
- if (DBG) Log.v(TAG, " - Call origin is not passed. Reset current one.");
- PhoneGlobals.getInstance().resetLatestActiveCallOrigin();
- }
-
// For now, SIP calls will be processed directly without a
// NEW_OUTGOING_CALL broadcast.
//
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index b70b159..06b8969 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -163,7 +163,6 @@
CallManager mCM;
CallNotifier notifier;
CallerInfoCache callerInfoCache;
- InCallUiState inCallUiState;
NotificationMgr notificationMgr;
Phone phone;
PhoneInterfaceManager phoneMgr;
@@ -187,10 +186,6 @@
// Internal PhoneApp Call state tracker
CdmaPhoneCallState cdmaPhoneCallState;
- // The InCallScreen instance (or null if the InCallScreen hasn't been
- // created yet.)
- private InCallScreen mInCallScreen;
-
// The currently-active PUK entry activity and progress dialog.
// Normally, these are the Emergency Dialer and the subsequent
// progress dialog. null if there is are no such objects in
@@ -259,15 +254,6 @@
mShouldRestoreMuteOnInCallResume = mode;
}
- /**
- * Get the restore mute state flag.
- * This is used by the InCallScreen {@link InCallScreen#onResume()} to figure
- * out if we need to restore the mute state for the current active call.
- */
- /*package*/boolean getRestoreMuteOnInCallResume () {
- return mShouldRestoreMuteOnInCallResume;
- }
-
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -356,7 +342,6 @@
audioRouter.setSpeaker(inDockMode);
PhoneUtils.turnOnSpeaker(getApplicationContext(), inDockMode, true);
- updateInCallScreen(); // Has no effect if the InCallScreen isn't visible
}
break;
@@ -470,10 +455,6 @@
// (like making outgoing calls.)
callController = CallController.init(this, callLogger, callGatewayManager);
- // ...and also the InCallUiState instance, used by the CallController to
- // keep track of some "persistent state" of the in-call UI.
- inCallUiState = InCallUiState.init(this);
-
// Create the CallerInfoCache singleton, which remembers custom ring tone and
// send-to-voicemail settings.
//
@@ -692,32 +673,6 @@
}
/**
- * Return an Intent that can be used to bring up the in-call screen.
- *
- * This intent can only be used from within the Phone app, since the
- * InCallScreen is not exported from our AndroidManifest.
- */
- /* package */ static Intent createInCallIntent() {
- Intent intent = new Intent(Intent.ACTION_MAIN, null);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
- intent.setClassName("com.android.phone", getCallScreenClassName());
- return intent;
- }
-
- /**
- * Variation of createInCallIntent() that also specifies whether the
- * DTMF dialpad should be initially visible when the InCallScreen
- * comes up.
- */
- /* package */ static Intent createInCallIntent(boolean showDialpad) {
- Intent intent = createInCallIntent();
- intent.putExtra(InCallScreen.SHOW_DIALPAD_EXTRA, showDialpad);
- return intent;
- }
-
- /**
* Returns PendingIntent for hanging up ongoing phone call. This will typically be used from
* Notification context.
*/
@@ -742,37 +697,6 @@
return PendingIntent.getBroadcast(context, 0, intent, 0);
}
- private static String getCallScreenClassName() {
- //InCallScreen.class.getName();
- return "blah";
- }
-
- /**
- * Starts the InCallScreen Activity.
- */
- /* package */ void displayCallScreen() {
- if (VDBG) Log.d(LOG_TAG, "displayCallScreen()...");
-
- // On non-voice-capable devices we shouldn't ever be trying to
- // bring up the InCallScreen in the first place.
- if (!sVoiceCapable) {
- Log.w(LOG_TAG, "displayCallScreen() not allowed: non-voice-capable device",
- new Throwable("stack dump")); // Include a stack trace since this warning
- // indicates a bug in our caller
- return;
- }
-
- try {
- //startActivity(createInCallIntent());
- } catch (ActivityNotFoundException e) {
- // It's possible that the in-call UI might not exist (like on
- // non-voice-capable devices), so don't crash if someone
- // accidentally tries to bring it up...
- Log.w(LOG_TAG, "displayCallScreen: transition to InCallScreen failed: " + e);
- }
- Profiler.callScreenRequested();
- }
-
boolean isSimPinEnabled() {
return mIsSimPinEnabled;
}
@@ -785,59 +709,6 @@
mCachedSimPin = pin;
}
- void setInCallScreenInstance(InCallScreen inCallScreen) {
- mInCallScreen = inCallScreen;
- }
-
- /**
- * @return true if the in-call UI is running as the foreground
- * activity. (In other words, from the perspective of the
- * InCallScreen activity, return true between onResume() and
- * onPause().)
- *
- * Note this method will return false if the screen is currently off,
- * even if the InCallScreen *was* in the foreground just before the
- * screen turned off. (This is because the foreground activity is
- * always "paused" while the screen is off.)
- */
- boolean isShowingCallScreen() {
- if (mInCallScreen == null) return false;
- return mInCallScreen.isForegroundActivity();
- }
-
- /**
- * Dismisses the in-call UI.
- *
- * This also ensures that you won't be able to get back to the in-call
- * UI via the BACK button (since this call removes the InCallScreen
- * from the activity history.)
- * For OTA Call, it call InCallScreen api to handle OTA Call End scenario
- * to display OTA Call End screen.
- */
- /* package */ void dismissCallScreen() {
- if (mInCallScreen != null) {
- if ((TelephonyCapabilities.supportsOtasp(phone)) &&
- (mInCallScreen.isOtaCallInActiveState()
- || mInCallScreen.isOtaCallInEndState()
- || ((cdmaOtaScreenState != null)
- && (cdmaOtaScreenState.otaScreenState
- != CdmaOtaScreenState.OtaScreenState.OTA_STATUS_UNDEFINED)))) {
- // TODO: During OTA Call, display should not become dark to
- // allow user to see OTA UI update. Phone app needs to hold
- // a SCREEN_DIM_WAKE_LOCK wake lock during the entire OTA call.
- wakeUpScreen();
- // If InCallScreen is not in foreground we resume it to show the OTA call end screen
- // Fire off the InCallScreen intent
- displayCallScreen();
-
- mInCallScreen.handleOtaCallEnd();
- return;
- } else {
- mInCallScreen.finish();
- }
- }
- }
-
/**
* Handles OTASP-related events from the telephony layer.
*
@@ -985,16 +856,6 @@
/* package */ void updateWakeState() {
PhoneConstants.State state = mCM.getState();
- // True if the in-call UI is the foreground activity.
- // (Note this will be false if the screen is currently off,
- // since in that case *no* activity is in the foreground.)
- boolean isShowingCallScreen = isShowingCallScreen();
-
- // True if the InCallScreen's DTMF dialer is currently opened.
- // (Note this does NOT imply whether or not the InCallScreen
- // itself is visible.)
- boolean isDialerOpened = (mInCallScreen != null) && mInCallScreen.isDialerOpened();
-
// True if the speakerphone is in use. (If so, we *always* use
// the default timeout. Since the user is obviously not holding
// the phone up to his/her face, we don't need to worry about
@@ -1011,11 +872,6 @@
// user to put the phone straight into a pocket, in which case the
// timeout should probably still be short.)
- if (DBG) Log.d(LOG_TAG, "updateWakeState: callscreen " + isShowingCallScreen
- + ", dialer " + isDialerOpened
- + ", speaker " + isSpeakerInUse + "...");
-
- //
// Decide whether to force the screen on or not.
//
// Force the screen to be on if the phone is ringing or dialing,
@@ -1027,13 +883,7 @@
//
boolean isRinging = (state == PhoneConstants.State.RINGING);
boolean isDialing = (phone.getForegroundCall().getState() == Call.State.DIALING);
- boolean showingDisconnectedConnection =
- PhoneUtils.hasDisconnectedConnections(phone) && isShowingCallScreen;
- boolean keepScreenOn = isRinging || isDialing || showingDisconnectedConnection;
- if (DBG) Log.d(LOG_TAG, "updateWakeState: keepScreenOn = " + keepScreenOn
- + " (isRinging " + isRinging
- + ", isDialing " + isDialing
- + ", showingDisc " + showingDisconnectedConnection + ")");
+ boolean keepScreenOn = isRinging || isDialing;
// keepScreenOn == true means we'll hold a full wake lock:
requestWakeState(keepScreenOn ? WakeState.FULL : WakeState.SLEEP);
}
@@ -1072,23 +922,11 @@
mUpdateLock.acquire();
}
} else {
- if (!isShowingCallScreen()) {
- if (!mUpdateLock.isHeld()) {
- mUpdateLock.release();
- }
+ if (!mUpdateLock.isHeld()) {
+ mUpdateLock.release();
} else {
- // For this case InCallScreen will take care of the release() call.
}
}
-
- // While we are in call, the in-call screen should dismiss the keyguard.
- // This allows the user to press Home to go directly home without going through
- // an insecure lock screen.
- // But we do not want to do this if there is no active call so we do not
- // bypass the keyguard if the call is not answered or declined.
- if (mInCallScreen != null) {
- mInCallScreen.updateKeyguardPolicy(state == PhoneConstants.State.OFFHOOK);
- }
}
}
@@ -1151,9 +989,6 @@
Log.e(LOG_TAG, Log.getStackTraceString(new Throwable()));
}
}
- if (mInCallScreen != null) {
- mInCallScreen.updateAfterRadioTechnologyChange();
- }
// Update registration for ICC status after radio technology change
IccCard sim = phone.getIccCard();
@@ -1277,10 +1112,6 @@
boolean consumed = PhoneUtils.handleHeadsetHook(phone, event);
if (VDBG) Log.d(LOG_TAG, "==> handleHeadsetHook(): consumed = " + consumed);
if (consumed) {
- // If a headset is attached and the press is consumed, also update
- // any UI items (such as an InCallScreen mute button) that may need to
- // be updated if their state changed.
- updateInCallScreen(); // Has no effect if the InCallScreen isn't visible
abortBroadcast();
}
} else {
@@ -1366,18 +1197,12 @@
public boolean isOtaCallInActiveState() {
boolean otaCallActive = false;
- if (mInCallScreen != null) {
- otaCallActive = mInCallScreen.isOtaCallInActiveState();
- }
if (VDBG) Log.d(LOG_TAG, "- isOtaCallInActiveState " + otaCallActive);
return otaCallActive;
}
public boolean isOtaCallInEndState() {
boolean otaCallEnded = false;
- if (mInCallScreen != null) {
- otaCallEnded = mInCallScreen.isOtaCallInEndState();
- }
if (VDBG) Log.d(LOG_TAG, "- isOtaCallInEndState " + otaCallEnded);
return otaCallEnded;
}
@@ -1385,8 +1210,7 @@
// it is safe to call clearOtaState() even if the InCallScreen isn't active
public void clearOtaState() {
if (DBG) Log.d(LOG_TAG, "- clearOtaState ...");
- if ((mInCallScreen != null)
- && (otaUtils != null)) {
+ if (otaUtils != null) {
otaUtils.cleanOtaScreen(true);
if (DBG) Log.d(LOG_TAG, " - clearOtaState clears OTA screen");
}
@@ -1395,45 +1219,12 @@
// it is safe to call dismissOtaDialogs() even if the InCallScreen isn't active
public void dismissOtaDialogs() {
if (DBG) Log.d(LOG_TAG, "- dismissOtaDialogs ...");
- if ((mInCallScreen != null)
- && (otaUtils != null)) {
+ if (otaUtils != null) {
otaUtils.dismissAllOtaDialogs();
if (DBG) Log.d(LOG_TAG, " - dismissOtaDialogs clears OTA dialogs");
}
}
- // it is safe to call clearInCallScreenMode() even if the InCallScreen isn't active
- public void clearInCallScreenMode() {
- if (DBG) Log.d(LOG_TAG, "- clearInCallScreenMode ...");
- if (mInCallScreen != null) {
- mInCallScreen.resetInCallScreenMode();
- }
- }
-
- /**
- * Force the in-call UI to refresh itself, if it's currently visible.
- *
- * This method can be used any time there's a state change anywhere in
- * the phone app that needs to be reflected in the onscreen UI.
- *
- * Note that it's *not* necessary to manually refresh the in-call UI
- * (via this method) for regular telephony state changes like
- * DIALING -> ALERTING -> ACTIVE, since the InCallScreen already
- * listens for those state changes itself.
- *
- * This method does *not* force the in-call UI to come up if it's not
- * already visible. To do that, use displayCallScreen().
- */
- /* package */ void updateInCallScreen() {
- if (DBG) Log.d(LOG_TAG, "- updateInCallScreen()...");
- if (mInCallScreen != null) {
- // Post an updateScreen() request. Note that the
- // updateScreen() call will end up being a no-op if the
- // InCallScreen isn't the foreground activity.
- mInCallScreen.requestUpdateScreen();
- }
- }
-
private void handleQueryTTYModeResponse(Message msg) {
AsyncResult ar = (AsyncResult) msg.obj;
if (ar.exception != null) {
@@ -1499,73 +1290,6 @@
*/
private static final long CALL_ORIGIN_EXPIRATION_MILLIS = 30 * 1000;
- public void setLatestActiveCallOrigin(String callOrigin) {
- inCallUiState.latestActiveCallOrigin = callOrigin;
- if (callOrigin != null) {
- inCallUiState.latestActiveCallOriginTimeStamp = SystemClock.elapsedRealtime();
- } else {
- inCallUiState.latestActiveCallOriginTimeStamp = 0;
- }
- }
-
- /**
- * Reset call origin depending on its timestamp.
- *
- * See if the current call origin preserved by the app is fresh enough or not. If it is,
- * previous call origin will be used as is. If not, call origin will be reset.
- *
- * This will be effective especially for 3rd party apps which want to bypass phone calls with
- * their own telephone lines. In that case Phone app may finish the phone call once and make
- * another for the external apps, which will drop call origin information in Intent.
- * Even in that case we are sure the second phone call should be initiated just after the first
- * phone call, so here we restore it from the previous information iff the second call is done
- * fairly soon.
- */
- public void resetLatestActiveCallOrigin() {
- final long callOriginTimestamp = inCallUiState.latestActiveCallOriginTimeStamp;
- final long currentTimestamp = SystemClock.elapsedRealtime();
- if (VDBG) {
- Log.d(LOG_TAG, "currentTimeMillis: " + currentTimestamp
- + ", saved timestamp for call origin: " + callOriginTimestamp);
- }
- if (inCallUiState.latestActiveCallOriginTimeStamp > 0
- && (currentTimestamp - callOriginTimestamp < CALL_ORIGIN_EXPIRATION_MILLIS)) {
- if (VDBG) {
- Log.d(LOG_TAG, "Resume previous call origin (" +
- inCallUiState.latestActiveCallOrigin + ")");
- }
- // Do nothing toward call origin itself but update the timestamp just in case.
- inCallUiState.latestActiveCallOriginTimeStamp = currentTimestamp;
- } else {
- if (VDBG) Log.d(LOG_TAG, "Drop previous call origin and set the current one to null");
- setLatestActiveCallOrigin(null);
- }
- }
-
- /**
- * @return Intent which will be used when in-call UI is shown and the phone call is hang up.
- * By default CallLog screen will be introduced, but the destination may change depending on
- * its latest call origin state.
- */
- public Intent createPhoneEndIntentUsingCallOrigin() {
- if (TextUtils.equals(inCallUiState.latestActiveCallOrigin, ALLOWED_EXTRA_CALL_ORIGIN)) {
- if (VDBG) Log.d(LOG_TAG, "Valid latestActiveCallOrigin("
- + inCallUiState.latestActiveCallOrigin + ") was found. "
- + "Go back to the previous screen.");
- // Right now we just launch the Activity which launched in-call UI. Note that we're
- // assuming the origin is from "com.android.dialer", which may be incorrect in the
- // future.
- final Intent intent = new Intent();
- intent.setClassName(DEFAULT_CALL_ORIGIN_PACKAGE, inCallUiState.latestActiveCallOrigin);
- return intent;
- } else {
- if (VDBG) Log.d(LOG_TAG, "Current latestActiveCallOrigin ("
- + inCallUiState.latestActiveCallOrigin + ") is not valid. "
- + "Just use CallLog as a default destination.");
- return PhoneGlobals.createCallLogIntent();
- }
- }
-
/** Service connection */
private final ServiceConnection mBluetoothPhoneConnection = new ServiceConnection() {
diff --git a/src/com/android/phone/RespondViaSmsManager.java b/src/com/android/phone/RespondViaSmsManager.java
index ffce899..a842f34 100644
--- a/src/com/android/phone/RespondViaSmsManager.java
+++ b/src/com/android/phone/RespondViaSmsManager.java
@@ -80,39 +80,6 @@
// Do not check in with VDBG = true, since that may write PII to the system log.
private static final boolean VDBG = false;
- private static final String PERMISSION_SEND_RESPOND_VIA_MESSAGE =
- "android.permission.SEND_RESPOND_VIA_MESSAGE";
-
- private int mIconSize = -1;
-
- /**
- * Reference to the InCallScreen activity that owns us. This may be
- * null if we haven't been initialized yet *or* after the InCallScreen
- * activity has been destroyed.
- */
- private InCallScreen mInCallScreen;
-
- /**
- * The popup showing the list of canned responses.
- *
- * This is an AlertDialog containing a ListView showing the possible
- * choices. This may be null if the InCallScreen hasn't ever called
- * showRespondViaSmsPopup() yet, or if the popup was visible once but
- * then got dismissed.
- */
- private Dialog mCannedResponsePopup;
-
- /**
- * The popup dialog allowing the user to chose which app handles respond-via-sms.
- *
- * An AlertDialog showing the Resolve-App UI resource from the framework wchih we then fill in
- * with the appropriate data set. Can be null when not visible.
- */
- private Dialog mPackageSelectionPopup;
-
- /** The array of "canned responses"; see loadCannedResponses(). */
- private String[] mCannedResponses;
-
/** SharedPreferences file name for our persistent settings. */
private static final String SHARED_PREFERENCES_NAME = "respond_via_sms_prefs";
@@ -129,518 +96,6 @@
private static final String KEY_INSTANT_TEXT_DEFAULT_COMPONENT = "instant_text_def_component";
/**
- * RespondViaSmsManager constructor.
- */
- public RespondViaSmsManager() {
- }
-
- public void setInCallScreenInstance(InCallScreen inCallScreen) {
- mInCallScreen = inCallScreen;
-
- if (mInCallScreen != null) {
- // Prefetch shared preferences to make the first canned response lookup faster
- // (and to prevent StrictMode violation)
- mInCallScreen.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
- }
- }
-
- /**
- * Brings up the "Respond via SMS" popup for an incoming call.
- *
- * @param ringingCall the current incoming call
- */
- public void showRespondViaSmsPopup(Call ringingCall) {
- if (DBG) log("showRespondViaSmsPopup()...");
-
- // Very quick succession of clicks can cause this to run twice.
- // Stop here to avoid creating more than one popup.
- if (isShowingPopup()) {
- if (DBG) log("Skip showing popup when one is already shown.");
- return;
- }
-
- ListView lv = new ListView(mInCallScreen);
-
- // Refresh the array of "canned responses".
- mCannedResponses = loadCannedResponses();
-
- // Build the list: start with the canned responses, but manually add
- // the write-your-own option as the last choice.
- int numPopupItems = mCannedResponses.length + 1;
- String[] popupItems = Arrays.copyOf(mCannedResponses, numPopupItems);
- popupItems[numPopupItems - 1] = mInCallScreen.getResources()
- .getString(R.string.respond_via_sms_custom_message);
-
- ArrayAdapter<String> adapter =
- new ArrayAdapter<String>(mInCallScreen,
- android.R.layout.simple_list_item_1,
- android.R.id.text1,
- popupItems);
- lv.setAdapter(adapter);
-
- // Create a RespondViaSmsItemClickListener instance to handle item
- // clicks from the popup.
- // (Note we create a fresh instance for each incoming call, and
- // stash away the call's phone number, since we can't necessarily
- // assume this call will still be ringing when the user finally
- // chooses a response.)
-
- Connection c = ringingCall.getLatestConnection();
- if (VDBG) log("- connection: " + c);
-
- if (c == null) {
- // Uh oh -- the "ringingCall" doesn't have any connections any more.
- // (In other words, it's no longer ringing.) This is rare, but can
- // happen if the caller hangs up right at the exact moment the user
- // selects the "Respond via SMS" option.
- // There's nothing to do here (since the incoming call is gone),
- // so just bail out.
- Log.i(TAG, "showRespondViaSmsPopup: null connection; bailing out...");
- return;
- }
-
- // TODO: at this point we probably should re-check c.getAddress()
- // and c.getNumberPresentation() for validity. (i.e. recheck the
- // same cases in InCallTouchUi.showIncomingCallWidget() where we
- // should have disallowed the "respond via SMS" feature in the
- // first place.)
-
- String phoneNumber = c.getAddress();
- if (VDBG) log("- phoneNumber: " + phoneNumber);
- lv.setOnItemClickListener(new RespondViaSmsItemClickListener(phoneNumber));
-
- AlertDialog.Builder builder = new AlertDialog.Builder(mInCallScreen)
- .setCancelable(true)
- .setOnCancelListener(new RespondViaSmsCancelListener())
- .setView(lv);
- mCannedResponsePopup = builder.create();
- mCannedResponsePopup.show();
- }
-
- /**
- * Dismiss currently visible popups.
- *
- * This is safe to call even if the popup is already dismissed, and
- * even if you never called showRespondViaSmsPopup() in the first
- * place.
- */
- public void dismissPopup() {
- if (mCannedResponsePopup != null) {
- mCannedResponsePopup.dismiss(); // safe even if already dismissed
- mCannedResponsePopup = null;
- }
- if (mPackageSelectionPopup != null) {
- mPackageSelectionPopup.dismiss();
- mPackageSelectionPopup = null;
- }
- }
-
- public boolean isShowingPopup() {
- return (mCannedResponsePopup != null && mCannedResponsePopup.isShowing())
- || (mPackageSelectionPopup != null && mPackageSelectionPopup.isShowing());
- }
-
- /**
- * OnItemClickListener for the "Respond via SMS" popup.
- */
- public class RespondViaSmsItemClickListener implements AdapterView.OnItemClickListener {
- // Phone number to send the SMS to.
- private String mPhoneNumber;
-
- public RespondViaSmsItemClickListener(String phoneNumber) {
- mPhoneNumber = phoneNumber;
- }
-
- /**
- * Handles the user selecting an item from the popup.
- */
- @Override
- public void onItemClick(AdapterView<?> parent, // The ListView
- View view, // The TextView that was clicked
- int position,
- long id) {
- if (DBG) log("RespondViaSmsItemClickListener.onItemClick(" + position + ")...");
- String message = (String) parent.getItemAtPosition(position);
- if (VDBG) log("- message: '" + message + "'");
-
- // The "Custom" choice is a special case.
- // (For now, it's guaranteed to be the last item.)
- if (position == (parent.getCount() - 1)) {
- // Take the user to the standard SMS compose UI.
- launchSmsCompose(mPhoneNumber);
- onPostMessageSent();
- } else {
- sendTextToDefaultActivity(mPhoneNumber, message);
- }
- }
- }
-
-
- /**
- * OnCancelListener for the "Respond via SMS" popup.
- */
- public class RespondViaSmsCancelListener implements DialogInterface.OnCancelListener {
- public RespondViaSmsCancelListener() {
- }
-
- /**
- * Handles the user canceling the popup, either by touching
- * outside the popup or by pressing Back.
- */
- @Override
- public void onCancel(DialogInterface dialog) {
- if (DBG) log("RespondViaSmsCancelListener.onCancel()...");
-
- dismissPopup();
-
- final PhoneConstants.State state = PhoneGlobals.getInstance().mCM.getState();
- if (state == PhoneConstants.State.IDLE) {
- // This means the incoming call is already hung up when the user chooses not to
- // use "Respond via SMS" feature. Let's just exit the whole in-call screen.
- PhoneGlobals.getInstance().dismissCallScreen();
- } else {
-
- // If the user cancels the popup, this presumably means that
- // they didn't actually mean to bring up the "Respond via SMS"
- // UI in the first place (and instead want to go back to the
- // state where they can either answer or reject the call.)
- // So restart the ringer and bring back the regular incoming
- // call UI.
-
- // This will have no effect if the incoming call isn't still ringing.
- PhoneGlobals.getInstance().notifier.restartRinger();
-
- // We hid the GlowPadView widget way back in
- // InCallTouchUi.onTrigger(), when the user first selected
- // the "SMS" trigger.
- //
- // To bring it back, just force the entire InCallScreen to
- // update itself based on the current telephony state.
- // (Assuming the incoming call is still ringing, this will
- // cause the incoming call widget to reappear.)
- mInCallScreen.requestUpdateScreen();
- }
- }
- }
-
- private void sendTextToDefaultActivity(String phoneNumber, String message) {
- if (DBG) log("sendTextToDefaultActivity()...");
- final PackageManager packageManager = mInCallScreen.getPackageManager();
-
- // Check to see if the default component to receive this intent is already saved
- // and check to see if it still has the corrent permissions.
- final SharedPreferences prefs = mInCallScreen.getSharedPreferences(SHARED_PREFERENCES_NAME,
- Context.MODE_PRIVATE);
- final String flattenedName = prefs.getString(KEY_INSTANT_TEXT_DEFAULT_COMPONENT, null);
- if (flattenedName != null) {
- if (DBG) log("Default package was found." + flattenedName);
-
- final ComponentName componentName = ComponentName.unflattenFromString(flattenedName);
- ServiceInfo serviceInfo = null;
- try {
- serviceInfo = packageManager.getServiceInfo(componentName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Default service does not have permission.");
- }
-
- if (serviceInfo != null &&
- PERMISSION_SEND_RESPOND_VIA_MESSAGE.equals(serviceInfo.permission)) {
- sendTextAndExit(phoneNumber, message, componentName, false);
- return;
- } else {
- SharedPreferences.Editor editor = prefs.edit();
- editor.remove(KEY_INSTANT_TEXT_DEFAULT_COMPONENT);
- editor.apply();
- }
- }
-
- final ArrayList<ComponentName> componentsWithPermission =
- getPackagesWithInstantTextPermission();
-
- final int size = componentsWithPermission.size();
- if (size == 0) {
- Log.e(TAG, "No appropriate package receiving the Intent. Don't send anything");
- onPostMessageSent();
- } else if (size == 1) {
- sendTextAndExit(phoneNumber, message, componentsWithPermission.get(0), false);
- } else {
- showPackageSelectionDialog(phoneNumber, message, componentsWithPermission);
- }
- }
-
- /**
- * Queries the System to determine what packages contain services that can handle the instant
- * text response Action AND have permissions to do so.
- */
- private ArrayList<ComponentName> getPackagesWithInstantTextPermission() {
- PackageManager packageManager = mInCallScreen.getPackageManager();
-
- ArrayList<ComponentName> componentsWithPermission = Lists.newArrayList();
-
- // Get list of all services set up to handle the Instant Text intent.
- final List<ResolveInfo> infos = packageManager.queryIntentServices(
- getInstantTextIntent("", null, null), 0);
-
- // Collect all the valid services
- for (ResolveInfo resolveInfo : infos) {
- final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
- if (serviceInfo == null) {
- Log.w(TAG, "Ignore package without proper service.");
- continue;
- }
-
- // A Service is valid only if it requires the permission
- // PERMISSION_SEND_RESPOND_VIA_MESSAGE
- if (PERMISSION_SEND_RESPOND_VIA_MESSAGE.equals(serviceInfo.permission)) {
- componentsWithPermission.add(new ComponentName(serviceInfo.packageName,
- serviceInfo.name));
- }
- }
-
- return componentsWithPermission;
- }
-
- private void showPackageSelectionDialog(String phoneNumber, String message,
- List<ComponentName> components) {
- if (DBG) log("showPackageSelectionDialog()...");
-
- dismissPopup();
-
- BaseAdapter adapter = new PackageSelectionAdapter(mInCallScreen, components);
-
- PackageClickListener clickListener =
- new PackageClickListener(phoneNumber, message, components);
-
- final CharSequence title = mInCallScreen.getResources().getText(
- com.android.internal.R.string.whichApplication);
- LayoutInflater inflater =
- (LayoutInflater) mInCallScreen.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- final View view = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
- final CheckBox alwaysUse = (CheckBox) view.findViewById(
- com.android.internal.R.id.alwaysUse);
- alwaysUse.setText(com.android.internal.R.string.alwaysUse);
- alwaysUse.setOnCheckedChangeListener(clickListener);
-
- AlertDialog.Builder builder = new AlertDialog.Builder(mInCallScreen)
- .setTitle(title)
- .setCancelable(true)
- .setOnCancelListener(new RespondViaSmsCancelListener())
- .setAdapter(adapter, clickListener)
- .setView(view);
- mPackageSelectionPopup = builder.create();
- mPackageSelectionPopup.show();
- }
-
- private class PackageSelectionAdapter extends BaseAdapter {
- private final LayoutInflater mInflater;
- private final List<ComponentName> mComponents;
-
- public PackageSelectionAdapter(Context context, List<ComponentName> components) {
- mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- mComponents = components;
- }
-
- @Override
- public int getCount() {
- return mComponents.size();
- }
-
- @Override
- public Object getItem(int position) {
- return mComponents.get(position);
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = mInflater.inflate(
- com.android.internal.R.layout.resolve_list_item, parent, false);
- }
-
- final ComponentName component = mComponents.get(position);
- final String packageName = component.getPackageName();
- final PackageManager packageManager = mInCallScreen.getPackageManager();
-
- // Set the application label
- final TextView text = (TextView) convertView.findViewById(
- com.android.internal.R.id.text1);
- final TextView text2 = (TextView) convertView.findViewById(
- com.android.internal.R.id.text2);
-
- // Reset any previous values
- text.setText("");
- text2.setVisibility(View.GONE);
- try {
- final ApplicationInfo appInfo = packageManager.getApplicationInfo(packageName, 0);
- final CharSequence label = packageManager.getApplicationLabel(appInfo);
- if (label != null) {
- text.setText(label);
- }
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Failed to load app label because package was not found.");
- }
-
- // Set the application icon
- final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
- Drawable drawable = null;
- try {
- drawable = mInCallScreen.getPackageManager().getApplicationIcon(packageName);
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Failed to load icon because it wasn't found.");
- }
- if (drawable == null) {
- drawable = mInCallScreen.getPackageManager().getDefaultActivityIcon();
- }
- icon.setImageDrawable(drawable);
- ViewGroup.LayoutParams lp = (ViewGroup.LayoutParams) icon.getLayoutParams();
- lp.width = lp.height = getIconSize();
-
- return convertView;
- }
-
- }
-
- private class PackageClickListener implements DialogInterface.OnClickListener,
- CompoundButton.OnCheckedChangeListener {
- /** Phone number to send the SMS to. */
- final private String mPhoneNumber;
- final private String mMessage;
- final private List<ComponentName> mComponents;
- private boolean mMakeDefault = false;
-
- public PackageClickListener(String phoneNumber, String message,
- List<ComponentName> components) {
- mPhoneNumber = phoneNumber;
- mMessage = message;
- mComponents = components;
- }
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- ComponentName component = mComponents.get(which);
- sendTextAndExit(mPhoneNumber, mMessage, component, mMakeDefault);
- }
-
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- Log.i(TAG, "mMakeDefault : " + isChecked);
- mMakeDefault = isChecked;
- }
- }
-
- private void sendTextAndExit(String phoneNumber, String message, ComponentName component,
- boolean setDefaultComponent) {
- // Send the selected message immediately with no user interaction.
- sendText(phoneNumber, message, component);
-
- if (setDefaultComponent) {
- final SharedPreferences prefs = mInCallScreen.getSharedPreferences(
- SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
- prefs.edit()
- .putString(KEY_INSTANT_TEXT_DEFAULT_COMPONENT, component.flattenToString())
- .apply();
- }
-
- // ...and show a brief confirmation to the user (since
- // otherwise it's hard to be sure that anything actually
- // happened.)
- final Resources res = mInCallScreen.getResources();
- final String formatString = res.getString(R.string.respond_via_sms_confirmation_format);
- final String confirmationMsg = String.format(formatString, phoneNumber);
- Toast.makeText(mInCallScreen,
- confirmationMsg,
- Toast.LENGTH_LONG).show();
-
- // TODO: If the device is locked, this toast won't actually ever
- // be visible! (That's because we're about to dismiss the call
- // screen, which means that the device will return to the
- // keyguard. But toasts aren't visible on top of the keyguard.)
- // Possible fixes:
- // (1) Is it possible to allow a specific Toast to be visible
- // on top of the keyguard?
- // (2) Artifically delay the dismissCallScreen() call by 3
- // seconds to allow the toast to be seen?
- // (3) Don't use a toast at all; instead use a transient state
- // of the InCallScreen (perhaps via the InCallUiState
- // progressIndication feature), and have that state be
- // visible for 3 seconds before calling dismissCallScreen().
-
- onPostMessageSent();
- }
-
- /**
- * Sends a text message without any interaction from the user.
- */
- private void sendText(String phoneNumber, String message, ComponentName component) {
- if (VDBG) log("sendText: number "
- + phoneNumber + ", message '" + message + "'");
-
- mInCallScreen.startService(getInstantTextIntent(phoneNumber, message, component));
- }
-
- private void onPostMessageSent() {
- // At this point the user is done dealing with the incoming call, so
- // there's no reason to keep it around. (It's also confusing for
- // the "incoming call" icon in the status bar to still be visible.)
- // So reject the call now.
- mInCallScreen.hangupRingingCall();
-
- dismissPopup();
-
- final PhoneConstants.State state = PhoneGlobals.getInstance().mCM.getState();
- if (state == PhoneConstants.State.IDLE) {
- // There's no other phone call to interact. Exit the entire in-call screen.
- PhoneGlobals.getInstance().dismissCallScreen();
- } else {
- // The user is still in the middle of other phone calls, so we should keep the
- // in-call screen.
- mInCallScreen.requestUpdateScreen();
- }
- }
-
- /**
- * Brings up the standard SMS compose UI.
- */
- private void launchSmsCompose(String phoneNumber) {
- if (VDBG) log("launchSmsCompose: number " + phoneNumber);
-
- Intent intent = getInstantTextIntent(phoneNumber, null, null);
-
- if (VDBG) log("- Launching SMS compose UI: " + intent);
- mInCallScreen.startService(intent);
- }
-
- /**
- * @param phoneNumber Must not be null.
- * @param message Can be null. If message is null, the returned Intent will be configured to
- * launch the SMS compose UI. If non-null, the returned Intent will cause the specified message
- * to be sent with no interaction from the user.
- * @param component The component that should handle this intent.
- * @return Service Intent for the instant response.
- */
- private static Intent getInstantTextIntent(String phoneNumber, String message,
- ComponentName component) {
- final Uri uri = Uri.fromParts(Constants.SCHEME_SMSTO, phoneNumber, null);
- Intent intent = new Intent(TelephonyManager.ACTION_RESPOND_VIA_MESSAGE, uri);
- if (message != null) {
- intent.putExtra(Intent.EXTRA_TEXT, message);
- } else {
- intent.putExtra("exit_on_sent", true);
- intent.putExtra("showUI", true);
- }
- if (component != null) {
- intent.setComponent(component);
- }
- return intent;
- }
-
- /**
* Settings activity under "Call settings" to let you manage the
* canned responses; see respond_via_sms_settings.xml
*/
@@ -737,51 +192,7 @@
}
}
- /**
- * Read the (customizable) canned responses from SharedPreferences,
- * or from defaults if the user has never actually brought up
- * the Settings UI.
- *
- * This method does disk I/O (reading the SharedPreferences file)
- * so don't call it from the main thread.
- *
- * @see RespondViaSmsManager.Settings
- */
- private String[] loadCannedResponses() {
- if (DBG) log("loadCannedResponses()...");
-
- SharedPreferences prefs = mInCallScreen.getSharedPreferences(SHARED_PREFERENCES_NAME,
- Context.MODE_PRIVATE);
- final Resources res = mInCallScreen.getResources();
-
- String[] responses = new String[NUM_CANNED_RESPONSES];
-
- // Note the default values here must agree with the corresponding
- // android:defaultValue attributes in respond_via_sms_settings.xml.
-
- responses[0] = prefs.getString(KEY_CANNED_RESPONSE_PREF_1,
- res.getString(R.string.respond_via_sms_canned_response_1));
- responses[1] = prefs.getString(KEY_CANNED_RESPONSE_PREF_2,
- res.getString(R.string.respond_via_sms_canned_response_2));
- responses[2] = prefs.getString(KEY_CANNED_RESPONSE_PREF_3,
- res.getString(R.string.respond_via_sms_canned_response_3));
- responses[3] = prefs.getString(KEY_CANNED_RESPONSE_PREF_4,
- res.getString(R.string.respond_via_sms_canned_response_4));
- return responses;
- }
-
- private int getIconSize() {
- if (mIconSize < 0) {
- final ActivityManager am =
- (ActivityManager) mInCallScreen.getSystemService(Context.ACTIVITY_SERVICE);
- mIconSize = am.getLauncherLargeIconSize();
- }
-
- return mIconSize;
- }
-
-
private static void log(String msg) {
- Log.d(TAG, msg);
+ Log.e(TAG, msg);
}
}