diff options
5 files changed, 234 insertions, 24 deletions
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java index aefe01f7a..47e52342a 100644 --- a/src/com/android/dialer/DialtactsActivity.java +++ b/src/com/android/dialer/DialtactsActivity.java @@ -1080,6 +1080,15 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O } 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 89cab4162..766175c9b 100644 --- a/src/com/android/dialer/dialpad/DialpadFragment.java +++ b/src/com/android/dialer/dialpad/DialpadFragment.java @@ -22,7 +22,6 @@ import android.app.Dialog; 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.PopupMenu; 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.NeededForReflection; 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 @@ public class DialpadFragment extends Fragment /** 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 @@ public class DialpadFragment extends Fragment */ 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 @@ public class DialpadFragment extends Fragment if (mDialpadQueryListener != null) { mDialpadQueryListener.onDialpadQueryChanged(mDigits.getText().toString()); } + updateDeleteButtonEnabledState(); } @@ -722,6 +722,10 @@ public class DialpadFragment extends Fragment @Override public void onDestroy() { super.onDestroy(); + if (mPseudoEmergencyAnimator != null) { + mPseudoEmergencyAnimator.destroy(); + mPseudoEmergencyAnimator = null; + } ((Context) getActivity()).unregisterReceiver(mCallStateReceiver); } @@ -1659,4 +1663,24 @@ public class DialpadFragment extends Fragment 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 000000000..d4f32b5d4 --- /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 0313f4004..73f4b3b1c 100644 --- a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java +++ b/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java @@ -69,17 +69,13 @@ public class VoicemailPlaybackLayout extends LinearLayout /** 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 class VoicemailPlaybackLayout extends LinearLayout 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 @@ public class VoicemailPlaybackLayout extends LinearLayout } @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 @@ public class VoicemailPlaybackLayout extends LinearLayout 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 a0e48546f..d47e9e213 100644 --- a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java +++ b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java @@ -78,7 +78,7 @@ public class VoicemailPlaybackPresenter 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 @@ public class VoicemailPlaybackPresenter 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 @@ public class VoicemailPlaybackPresenter 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 @@ public class VoicemailPlaybackPresenter } 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 @@ public class VoicemailPlaybackPresenter @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 @@ public class VoicemailPlaybackPresenter * playing. */ public void resumePlayback() { + if (!mIsPrepared) { + return; + } + mIsPlaying = true; if (!mMediaPlayer.isPlaying()) { @@ -484,13 +499,17 @@ public class VoicemailPlaybackPresenter } 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 @@ public class VoicemailPlaybackPresenter } private void enableProximitySensor() { - if (mProximityWakeLock == null || isSpeakerphoneOn() || - !mIsPrepared || !mMediaPlayer.isPlaying()) { + if (mProximityWakeLock == null || isSpeakerphoneOn() || !mIsPrepared + || !mMediaPlayer.isPlaying()) { return; } @@ -566,6 +585,10 @@ public class VoicemailPlaybackPresenter 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); |