Merge "Don't include voicemails in ALL call type filter." into mnc-dev
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index aefe01f..47e5234 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -1080,6 +1080,15 @@
             }
             mSearchView.setText(normalizedQuery);
         }
+
+        try {
+            if (mDialpadFragment != null && mDialpadFragment.isVisible()) {
+                mDialpadFragment.process_quote_emergency_unquote(normalizedQuery);
+            }
+        } catch (Exception ignored) {
+            // Skip any exceptions for this piece of code
+        }
+
     }
 
     @Override
diff --git a/src/com/android/dialer/dialpad/DialpadFragment.java b/src/com/android/dialer/dialpad/DialpadFragment.java
index 89cab41..766175c 100644
--- a/src/com/android/dialer/dialpad/DialpadFragment.java
+++ b/src/com/android/dialer/dialpad/DialpadFragment.java
@@ -22,7 +22,6 @@
 import android.app.DialogFragment;
 import android.app.Fragment;
 import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -68,7 +67,6 @@
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
-import com.android.contacts.common.ContactsUtils;
 import com.android.contacts.common.GeoUtil;
 import com.android.contacts.common.util.PermissionsUtil;
 import com.android.contacts.common.util.PhoneNumberFormatter;
@@ -79,14 +77,13 @@
 import com.android.dialer.R;
 import com.android.dialer.SpecialCharSequenceMgr;
 import com.android.dialer.calllog.PhoneAccountUtils;
-import com.android.dialer.util.IntentUtil;
 import com.android.dialer.util.DialerUtils;
+import com.android.dialer.util.IntentUtil;
 import com.android.phone.common.CallLogAsync;
 import com.android.phone.common.HapticFeedback;
 import com.android.phone.common.animation.AnimUtils;
 import com.android.phone.common.dialpad.DialpadKeyButton;
 import com.android.phone.common.dialpad.DialpadView;
-
 import com.google.common.annotations.VisibleForTesting;
 
 import java.util.HashSet;
@@ -157,6 +154,7 @@
     /** Stream type used to play the DTMF tones off call, and mapped to the volume control keys */
     private static final int DIAL_TONE_STREAM_TYPE = AudioManager.STREAM_DTMF;
 
+
     private OnDialpadQueryChangedListener mDialpadQueryListener;
 
     private DialpadView mDialpadView;
@@ -188,6 +186,7 @@
      */
     private String mProhibitedPhoneNumberRegexp;
 
+    private PseudoEmergencyAnimator mPseudoEmergencyAnimator;
 
     // Last number dialed, retrieved asynchronously from the call DB
     // in onCreate. This number is displayed when the user hits the
@@ -307,6 +306,7 @@
         if (mDialpadQueryListener != null) {
             mDialpadQueryListener.onDialpadQueryChanged(mDigits.getText().toString());
         }
+
         updateDeleteButtonEnabledState();
     }
 
@@ -722,6 +722,10 @@
     @Override
     public void onDestroy() {
         super.onDestroy();
+        if (mPseudoEmergencyAnimator != null) {
+            mPseudoEmergencyAnimator.destroy();
+            mPseudoEmergencyAnimator = null;
+        }
         ((Context) getActivity()).unregisterReceiver(mCallStateReceiver);
     }
 
@@ -1659,4 +1663,24 @@
     public void setYFraction(float yFraction) {
         ((DialpadSlidingRelativeLayout) getView()).setYFraction(yFraction);
     }
+
+    public void process_quote_emergency_unquote(String query) {
+        if (PseudoEmergencyAnimator.PSEUDO_EMERGENCY_NUMBER.equals(query)) {
+            if (mPseudoEmergencyAnimator == null) {
+                mPseudoEmergencyAnimator = new PseudoEmergencyAnimator(
+                        new PseudoEmergencyAnimator.ViewProvider() {
+                            @Override
+                            public View getView() {
+                                return DialpadFragment.this.getView();
+                            }
+                        });
+            }
+            mPseudoEmergencyAnimator.start();
+        } else {
+            if (mPseudoEmergencyAnimator != null) {
+                mPseudoEmergencyAnimator.end();
+            }
+        }
+    }
+
 }
diff --git a/src/com/android/dialer/dialpad/PseudoEmergencyAnimator.java b/src/com/android/dialer/dialpad/PseudoEmergencyAnimator.java
new file mode 100644
index 0000000..d4f32b5
--- /dev/null
+++ b/src/com/android/dialer/dialpad/PseudoEmergencyAnimator.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dialer.dialpad;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.ArgbEvaluator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.LightingColorFilter;
+import android.os.Handler;
+import android.os.Vibrator;
+import android.view.View;
+
+import com.android.dialer.R;
+
+/**
+ * Animates the dial button on "emergency" phone numbers.
+ */
+public class PseudoEmergencyAnimator {
+    public interface ViewProvider {
+        View getView();
+    }
+
+    public static final String PSEUDO_EMERGENCY_NUMBER = "01189998819991197253";
+
+    private static final int VIBRATE_LENGTH_MILLIS = 200;
+    private static final int ITERATION_LENGTH_MILLIS = 1000;
+    private static final int ANIMATION_ITERATION_COUNT = 6;
+
+    private ViewProvider mViewProvider;
+    private ValueAnimator mPseudoEmergencyColorAnimator;
+
+    PseudoEmergencyAnimator(ViewProvider viewProvider) {
+        mViewProvider = viewProvider;
+    }
+
+    public void destroy() {
+        end();
+        mViewProvider = null;
+    }
+
+    public void start() {
+        if (mPseudoEmergencyColorAnimator == null) {
+            Integer colorFrom = Color.BLUE;
+            Integer colorTo = Color.RED;
+            mPseudoEmergencyColorAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
+
+            mPseudoEmergencyColorAnimator.addUpdateListener(new AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animator) {
+                    try {
+                        int color = (int) animator.getAnimatedValue();
+                        ColorFilter colorFilter =
+                                new LightingColorFilter(Color.BLACK, color);
+
+                        View floatingActionButtonContainer = getView().findViewById(
+                                R.id.dialpad_floating_action_button_container);
+                        if (floatingActionButtonContainer != null) {
+                            floatingActionButtonContainer.getBackground().setColorFilter(
+                                    colorFilter);
+                        }
+                    } catch (Exception e) {
+                        animator.cancel();
+                    }
+                }
+            });
+
+            mPseudoEmergencyColorAnimator.addListener(new AnimatorListener() {
+                @Override
+                public void onAnimationCancel(Animator animation) { }
+
+                @Override
+                public void onAnimationRepeat(Animator animation) {
+                    try {
+                        vibrate(VIBRATE_LENGTH_MILLIS);
+                    } catch (Exception e) {
+                        animation.cancel();
+                    }
+                }
+
+                @Override
+                public void onAnimationStart(Animator animation) { }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    try {
+                        View floatingActionButtonContainer = getView().findViewById(
+                                R.id.dialpad_floating_action_button_container);
+                        if (floatingActionButtonContainer != null) {
+                            floatingActionButtonContainer.getBackground().clearColorFilter();
+                        }
+
+                        new Handler().postDelayed(new Runnable() {
+                            @Override public void run() {
+                                try {
+                                    vibrate(VIBRATE_LENGTH_MILLIS);
+                                } catch (Exception e) {
+                                    // ignored
+                                }
+                            }
+                        }, ITERATION_LENGTH_MILLIS);
+                    } catch (Exception e) {
+                        animation.cancel();
+                    }
+                }
+            });
+
+            mPseudoEmergencyColorAnimator.setDuration(VIBRATE_LENGTH_MILLIS);
+            mPseudoEmergencyColorAnimator.setRepeatMode(ValueAnimator.REVERSE);
+            mPseudoEmergencyColorAnimator.setRepeatCount(ANIMATION_ITERATION_COUNT);
+        }
+        if (!mPseudoEmergencyColorAnimator.isStarted()) {
+            mPseudoEmergencyColorAnimator.start();
+        }
+    }
+
+    public void end() {
+        if (mPseudoEmergencyColorAnimator != null && mPseudoEmergencyColorAnimator.isStarted()) {
+            mPseudoEmergencyColorAnimator.end();
+        }
+    }
+
+    private View getView() {
+        return mViewProvider == null ? null : mViewProvider.getView();
+    }
+
+    private Context getContext() {
+        View view = getView();
+        return view != null ? view.getContext() : null;
+    }
+
+    private void vibrate(long milliseconds) {
+        Context context = getContext();
+        if (context != null) {
+            Vibrator vibrator =
+                    (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
+            if (vibrator != null) {
+                vibrator.vibrate(milliseconds);
+            }
+        }
+    }
+}
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java b/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
index 0313f40..73f4b3b 100644
--- a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
+++ b/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
@@ -69,17 +69,13 @@
         /** Update rate for the slider, 30fps. */
         private static final int SLIDER_UPDATE_PERIOD_MILLIS = 1000 / 30;
 
-        private final MediaPlayer mMediaPlayer;
-        private final int mDuration;
+        private int mDuration;
         private final ScheduledExecutorService mExecutorService;
         private final Object mLock = new Object();
         @GuardedBy("mLock") private ScheduledFuture<?> mScheduledFuture;
 
-        public PositionUpdater(
-                MediaPlayer mediaPlayer,
-                ScheduledExecutorService executorService) {
-            mMediaPlayer = mediaPlayer;
-            mDuration = mediaPlayer.getDuration();
+        public PositionUpdater(int duration, ScheduledExecutorService executorService) {
+            mDuration = duration;
             mExecutorService = executorService;
         }
 
@@ -90,11 +86,11 @@
                 public void run() {
                     int currentPosition = 0;
                     synchronized (mLock) {
-                        if (mScheduledFuture == null) {
+                        if (mScheduledFuture == null || mPresenter == null) {
                             // This task has been canceled. Just stop now.
                             return;
                         }
-                        currentPosition = mMediaPlayer.getCurrentPosition();
+                        currentPosition = mPresenter.getMediaPlayerPosition();
                     }
                     setClipPosition(currentPosition, mDuration);
                 }
@@ -234,9 +230,7 @@
     }
 
     @Override
-    public void onPlaybackStarted(
-            MediaPlayer mediaPlayer,
-            ScheduledExecutorService executorService) {
+    public void onPlaybackStarted(int duration, ScheduledExecutorService executorService) {
         mIsPlaying = true;
 
         mStartStopButton.setImageResource(R.drawable.ic_hold_pause);
@@ -245,7 +239,7 @@
             onSpeakerphoneOn(mPresenter.isSpeakerphoneOn());
         }
 
-        mPositionUpdater = new PositionUpdater(mediaPlayer, executorService);
+        mPositionUpdater = new PositionUpdater(duration, executorService);
         mPositionUpdater.startUpdating();
     }
 
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
index a0e4854..d47e9e2 100644
--- a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
+++ b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
@@ -78,7 +78,7 @@
         void disableUiElements();
         void enableUiElements();
         void onPlaybackError(Exception e);
-        void onPlaybackStarted(MediaPlayer mediaPlayer, ScheduledExecutorService executorService);
+        void onPlaybackStarted(int duration, ScheduledExecutorService executorService);
         void onPlaybackStopped();
         void onSpeakerphoneOn(boolean on);
         void setClipPosition(int clipPositionInMillis, int clipLengthInMillis);
@@ -127,8 +127,10 @@
 
     private Uri mVoicemailUri;
     private int mPosition;
-    private boolean mIsPrepared;
     private boolean mIsPlaying;
+    // MediaPlayer crashes on some method calls if not prepared but does not have a method which
+    // exposes its prepared state. Store this locally, so we can check and prevent crashes.
+    private boolean mIsPrepared;
 
     private boolean mShouldResumePlaybackAfterSeeking;
 
@@ -184,8 +186,6 @@
         mView = view;
         mView.setPresenter(this);
 
-        mView.onSpeakerphoneOn(isSpeakerphoneOn());
-
         if (mVoicemailUri != null && mVoicemailUri.equals(voicemailUri)) {
             // Handles rotation case where playback view is set for the same voicemail.
             if (mIsPrepared) {
@@ -193,11 +193,18 @@
             } else {
                 checkForContent();
             }
+            mView.onSpeakerphoneOn(isSpeakerphoneOn());
         } else {
             mVoicemailUri = voicemailUri;
             mPosition = 0;
+            mDuration.set(0);
             mIsPlaying = startPlayingImmediately;
+
+            // Default to earpiece.
+            mView.onSpeakerphoneOn(false);
+
             checkForContent();
+
         }
     }
 
@@ -444,6 +451,10 @@
 
     @Override
     public void onAudioFocusChange(int focusChange) {
+        if (!mIsPrepared) {
+            return;
+        }
+
         boolean lostFocus = focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT ||
                 focusChange == AudioManager.AUDIOFOCUS_LOSS;
         if (mMediaPlayer.isPlaying() && lostFocus) {
@@ -458,6 +469,10 @@
      * playing.
      */
     public void resumePlayback() {
+        if (!mIsPrepared) {
+            return;
+        }
+
         mIsPlaying = true;
 
         if (!mMediaPlayer.isPlaying()) {
@@ -484,13 +499,17 @@
         }
 
         enableProximitySensor();
-        mView.onPlaybackStarted(mMediaPlayer, getScheduledExecutorServiceInstance());
+        mView.onPlaybackStarted(mDuration.get(), getScheduledExecutorServiceInstance());
     }
 
     /**
      * Pauses voicemail playback at the current position. Null-op if already paused.
      */
     public void pausePlayback() {
+        if (!mIsPrepared) {
+            return;
+        }
+
         mPosition = mMediaPlayer.getCurrentPosition();
         mIsPlaying = false;
 
@@ -523,8 +542,8 @@
     }
 
     private void enableProximitySensor() {
-        if (mProximityWakeLock == null || isSpeakerphoneOn() ||
-                !mIsPrepared || !mMediaPlayer.isPlaying()) {
+        if (mProximityWakeLock == null || isSpeakerphoneOn() || !mIsPrepared
+                || !mMediaPlayer.isPlaying()) {
             return;
         }
 
@@ -566,6 +585,10 @@
         return mVoicemailUri;
     }
 
+    public int getMediaPlayerPosition() {
+        return mIsPrepared ? mMediaPlayer.getCurrentPosition() : 0;
+    }
+
     private static synchronized ScheduledExecutorService getScheduledExecutorServiceInstance() {
         if (mScheduledExecutorService == null) {
             mScheduledExecutorService = Executors.newScheduledThreadPool(NUMBER_OF_THREADS_IN_POOL);