diff options
Diffstat (limited to 'java/com/android/incallui/answer/impl')
17 files changed, 161 insertions, 212 deletions
diff --git a/java/com/android/incallui/answer/impl/AnswerFragment.java b/java/com/android/incallui/answer/impl/AnswerFragment.java index 98439ee7f..6874daea3 100644 --- a/java/com/android/incallui/answer/impl/AnswerFragment.java +++ b/java/com/android/incallui/answer/impl/AnswerFragment.java @@ -37,7 +37,6 @@ import android.support.annotation.StringRes; import android.support.annotation.VisibleForTesting; import android.support.transition.TransitionManager; import android.support.v4.app.Fragment; -import android.telecom.VideoProfile; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; @@ -79,10 +78,11 @@ import com.android.incallui.incall.protocol.InCallScreenDelegateFactory; import com.android.incallui.incall.protocol.PrimaryCallState; import com.android.incallui.incall.protocol.PrimaryInfo; import com.android.incallui.incall.protocol.SecondaryInfo; -import com.android.incallui.maps.StaticMapBinding; +import com.android.incallui.maps.MapsComponent; import com.android.incallui.sessiondata.AvatarPresenter; import com.android.incallui.sessiondata.MultimediaFragment; import com.android.incallui.util.AccessibilityUtil; +import com.android.incallui.video.protocol.VideoCallScreen; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -101,7 +101,7 @@ public class AnswerFragment extends Fragment static final String ARG_CALL_ID = "call_id"; @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - static final String ARG_VIDEO_STATE = "video_state"; + static final String ARG_IS_VIDEO_CALL = "is_video_call"; @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) static final String ARG_IS_VIDEO_UPGRADE_REQUEST = "is_video_upgrade_request"; @@ -143,7 +143,7 @@ public class AnswerFragment extends Fragment private CreateCustomSmsDialogFragment createCustomSmsDialogFragment; private SecondaryBehavior secondaryBehavior = SecondaryBehavior.REJECT_WITH_SMS; private ContactGridManager contactGridManager; - private AnswerVideoCallScreen answerVideoCallScreen; + private VideoCallScreen answerVideoCallScreen; private Handler handler = new Handler(Looper.getMainLooper()); private enum SecondaryBehavior { @@ -288,10 +288,10 @@ public class AnswerFragment extends Fragment } public static AnswerFragment newInstance( - String callId, int videoState, boolean isVideoUpgradeRequest) { + String callId, boolean isVideoCall, boolean isVideoUpgradeRequest) { Bundle bundle = new Bundle(); bundle.putString(ARG_CALL_ID, Assert.isNotNull(callId)); - bundle.putInt(ARG_VIDEO_STATE, videoState); + bundle.putBoolean(ARG_IS_VIDEO_CALL, isVideoCall); bundle.putBoolean(ARG_IS_VIDEO_UPGRADE_REQUEST, isVideoUpgradeRequest); AnswerFragment instance = new AnswerFragment(); @@ -306,18 +306,13 @@ public class AnswerFragment extends Fragment } @Override - public int getVideoState() { - return getArguments().getInt(ARG_VIDEO_STATE); - } - - @Override public boolean isVideoUpgradeRequest() { return getArguments().getBoolean(ARG_IS_VIDEO_UPGRADE_REQUEST); } @Override public void setTextResponses(List<String> textResponses) { - if (isVideoCall()) { + if (isVideoCall() || isVideoUpgradeRequest()) { LogUtil.i("AnswerFragment.setTextResponses", "no-op for video calls"); } else if (textResponses == null) { LogUtil.i("AnswerFragment.setTextResponses", "no text responses, hiding secondary button"); @@ -336,7 +331,9 @@ public class AnswerFragment extends Fragment private void initSecondaryButton() { secondaryBehavior = - isVideoCall() ? SecondaryBehavior.ANSWER_VIDEO_AS_AUDIO : SecondaryBehavior.REJECT_WITH_SMS; + isVideoCall() || isVideoUpgradeRequest() + ? SecondaryBehavior.ANSWER_VIDEO_AS_AUDIO + : SecondaryBehavior.REJECT_WITH_SMS; secondaryBehavior.applyToView(secondaryButton); secondaryButton.setOnClickListener( @@ -351,12 +348,9 @@ public class AnswerFragment extends Fragment secondaryButton.setAccessibilityDelegate(accessibilityDelegate); if (isVideoCall()) { - //noinspection WrongConstant - if (!isVideoUpgradeRequest() && VideoProfile.isTransmissionEnabled(getVideoState())) { - secondaryButton.setVisibility(View.VISIBLE); - } else { - secondaryButton.setVisibility(View.INVISIBLE); - } + secondaryButton.setVisibility(View.VISIBLE); + } else { + secondaryButton.setVisibility(View.INVISIBLE); } } @@ -448,11 +442,11 @@ public class AnswerFragment extends Fragment MultimediaData multimediaData = getSessionData(); if (multimediaData != null - && (!TextUtils.isEmpty(multimediaData.getSubject()) + && (!TextUtils.isEmpty(multimediaData.getText()) || (multimediaData.getImageUri() != null) || (multimediaData.getLocation() != null && canShowMap()))) { // Need message fragment - String subject = multimediaData.getSubject(); + String subject = multimediaData.getText(); Uri imageUri = multimediaData.getImageUri(); Location location = multimediaData.getLocation(); if (!(current instanceof MultimediaFragment) @@ -487,11 +481,11 @@ public class AnswerFragment extends Fragment } private boolean shouldShowAvatar() { - return !isVideoCall(); + return !isVideoCall() && !isVideoUpgradeRequest(); } private boolean canShowMap() { - return StaticMapBinding.get(getActivity().getApplication()) != null; + return MapsComponent.get(getContext()).getMaps().isAvailable(); } @Override @@ -564,7 +558,7 @@ public class AnswerFragment extends Fragment LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Bundle arguments = getArguments(); Assert.checkState(arguments.containsKey(ARG_CALL_ID)); - Assert.checkState(arguments.containsKey(ARG_VIDEO_STATE)); + Assert.checkState(arguments.containsKey(ARG_IS_VIDEO_CALL)); Assert.checkState(arguments.containsKey(ARG_IS_VIDEO_UPGRADE_REQUEST)); buttonAcceptClicked = false; @@ -596,7 +590,6 @@ public class AnswerFragment extends Fragment }); updateImportanceBadgeVisibility(); - boolean isVideoCall = isVideoCall(); contactGridManager = new ContactGridManager(view, null, 0, false /* showAnonymousAvatar */); Fragment answerMethod = @@ -625,9 +618,9 @@ public class AnswerFragment extends Fragment flags |= STATUS_BAR_DISABLE_BACK | STATUS_BAR_DISABLE_HOME | STATUS_BAR_DISABLE_RECENT; } view.setSystemUiVisibility(flags); - if (isVideoCall) { + if (isVideoCall() || isVideoUpgradeRequest()) { if (VideoUtils.hasCameraPermissionAndAllowedByUser(getContext())) { - answerVideoCallScreen = new AnswerVideoCallScreen(this, view); + answerVideoCallScreen = new AnswerVideoCallScreen(getCallId(), this, view); } else { view.findViewById(R.id.videocall_video_off).setVisibility(View.VISIBLE); } @@ -649,7 +642,7 @@ public class AnswerFragment extends Fragment updateUI(); if (savedInstanceState == null || !savedInstanceState.getBoolean(STATE_HAS_ANIMATED_ENTRY)) { - ViewUtil.doOnPreDraw(view, false, this::animateEntry); + ViewUtil.doOnGlobalLayout(view, this::animateEntry); } } @@ -667,7 +660,7 @@ public class AnswerFragment extends Fragment updateUI(); if (answerVideoCallScreen != null) { - answerVideoCallScreen.onStart(); + answerVideoCallScreen.onVideoScreenStart(); } } @@ -678,7 +671,7 @@ public class AnswerFragment extends Fragment handler.removeCallbacks(swipeHintRestoreTimer); if (answerVideoCallScreen != null) { - answerVideoCallScreen.onStop(); + answerVideoCallScreen.onVideoScreenStop(); } } @@ -722,7 +715,7 @@ public class AnswerFragment extends Fragment @Override public boolean isVideoCall() { - return VideoUtils.isVideoCall(getVideoState()); + return getArguments().getBoolean(ARG_IS_VIDEO_CALL); } @Override @@ -775,14 +768,12 @@ public class AnswerFragment extends Fragment Animator dataContainer = createTranslation(rootView.findViewById(R.id.incall_data_container)); AnimatorSet animatorSet = new AnimatorSet(); - animatorSet - .play(alpha) - .with(topRow) - .with(contactName) - .with(bottomRow) - .with(important) - .with(dataContainer); - animatorSet.setDuration(getResources().getInteger(android.R.integer.config_longAnimTime)); + AnimatorSet.Builder builder = animatorSet.play(alpha); + builder.with(topRow).with(contactName).with(bottomRow).with(important).with(dataContainer); + if (isShowingLocationUi()) { + builder.with(createTranslation(rootView.findViewById(R.id.incall_location_holder))); + } + animatorSet.setDuration(getResources().getInteger(R.integer.answer_animate_entry_millis)); animatorSet.addListener( new AnimatorListenerAdapter() { @Override @@ -803,14 +794,7 @@ public class AnswerFragment extends Fragment private void acceptCallByUser(boolean answerVideoAsAudio) { LogUtil.i("AnswerFragment.acceptCallByUser", answerVideoAsAudio ? " answerVideoAsAudio" : ""); if (!buttonAcceptClicked) { - int desiredVideoState = getVideoState(); - if (answerVideoAsAudio) { - desiredVideoState = VideoProfile.STATE_AUDIO_ONLY; - } - - // Notify the lower layer first to start signaling ASAP. - answerScreenDelegate.onAnswer(desiredVideoState); - + answerScreenDelegate.onAnswer(answerVideoAsAudio); buttonAcceptClicked = true; } } diff --git a/java/com/android/incallui/answer/impl/AnswerVideoCallScreen.java b/java/com/android/incallui/answer/impl/AnswerVideoCallScreen.java index 0316a5fab..06502daab 100644 --- a/java/com/android/incallui/answer/impl/AnswerVideoCallScreen.java +++ b/java/com/android/incallui/answer/impl/AnswerVideoCallScreen.java @@ -32,12 +32,15 @@ import com.android.incallui.videosurface.bindings.VideoSurfaceBindings; /** Shows a video preview for an incoming call. */ public class AnswerVideoCallScreen implements VideoCallScreen { + @NonNull private final String callId; @NonNull private final Fragment fragment; @NonNull private final TextureView textureView; @NonNull private final VideoCallScreenDelegate delegate; - public AnswerVideoCallScreen(@NonNull Fragment fragment, @NonNull View view) { - this.fragment = fragment; + public AnswerVideoCallScreen( + @NonNull String callId, @NonNull Fragment fragment, @NonNull View view) { + this.callId = Assert.isNotNull(callId); + this.fragment = Assert.isNotNull(fragment); textureView = Assert.isNotNull((TextureView) view.findViewById(R.id.incoming_preview_texture_view)); @@ -53,13 +56,15 @@ public class AnswerVideoCallScreen implements VideoCallScreen { overlayView.setVisibility(View.VISIBLE); } - public void onStart() { + @Override + public void onVideoScreenStart() { LogUtil.i("AnswerVideoCallScreen.onStart", null); delegate.onVideoCallScreenUiReady(); delegate.getLocalVideoSurfaceTexture().attachToTextureView(textureView); } - public void onStop() { + @Override + public void onVideoScreenStop() { LogUtil.i("AnswerVideoCallScreen.onStop", null); delegate.onVideoCallScreenUiUnready(); } @@ -98,6 +103,12 @@ public class AnswerVideoCallScreen implements VideoCallScreen { return fragment; } + @NonNull + @Override + public String getCallId() { + return callId; + } + private void updatePreviewVideoScaling() { if (textureView.getWidth() == 0 || textureView.getHeight() == 0) { LogUtil.i( diff --git a/java/com/android/incallui/answer/impl/affordance/SwipeButtonHelper.java b/java/com/android/incallui/answer/impl/affordance/SwipeButtonHelper.java index 62845b748..ff20d3a05 100644 --- a/java/com/android/incallui/answer/impl/affordance/SwipeButtonHelper.java +++ b/java/com/android/incallui/answer/impl/affordance/SwipeButtonHelper.java @@ -190,7 +190,7 @@ public class SwipeButtonHelper { case MotionEvent.ACTION_UP: isUp = true; - //fallthrough_intended + // fall through case MotionEvent.ACTION_CANCEL: boolean hintOnTheRight = targetedView == rightIcon; trackMovement(event); diff --git a/java/com/android/incallui/answer/impl/answermethod/AnswerMethodHolder.java b/java/com/android/incallui/answer/impl/answermethod/AnswerMethodHolder.java index 4052281b7..afa194f2e 100644 --- a/java/com/android/incallui/answer/impl/answermethod/AnswerMethodHolder.java +++ b/java/com/android/incallui/answer/impl/answermethod/AnswerMethodHolder.java @@ -44,4 +44,6 @@ public interface AnswerMethodHolder { * @return true iff the current call is a video call. */ boolean isVideoCall(); + + boolean isVideoUpgradeRequest(); } diff --git a/java/com/android/incallui/answer/impl/answermethod/FlingUpDownMethod.java b/java/com/android/incallui/answer/impl/answermethod/FlingUpDownMethod.java index 0bc65818c..6e8e1f7bf 100644 --- a/java/com/android/incallui/answer/impl/answermethod/FlingUpDownMethod.java +++ b/java/com/android/incallui/answer/impl/answermethod/FlingUpDownMethod.java @@ -60,7 +60,7 @@ import com.android.incallui.answer.impl.answermethod.FlingUpDownTouchHandler.OnP import com.android.incallui.answer.impl.classifier.FalsingManager; import com.android.incallui.answer.impl.hint.AnswerHint; import com.android.incallui.answer.impl.hint.AnswerHintFactory; -import com.android.incallui.answer.impl.hint.EventPayloadLoaderImpl; +import com.android.incallui.answer.impl.hint.PawImageLoaderImpl; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -228,7 +228,7 @@ public class FlingUpDownMethod extends AnswerMethod implements OnProgressChanged touchHandler = FlingUpDownTouchHandler.attach(view, this, falsingManager); answerHint = - new AnswerHintFactory(new EventPayloadLoaderImpl()) + new AnswerHintFactory(new PawImageLoaderImpl()) .create(getContext(), ANIMATE_DURATION_LONG_MILLIS, BOUNCE_ANIMATION_DELAY); answerHint.onCreateView( layoutInflater, @@ -319,7 +319,7 @@ public class FlingUpDownMethod extends AnswerMethod implements OnProgressChanged if (contactPuckIcon == null) { return; } - if (getParent().isVideoCall()) { + if (getParent().isVideoCall() || getParent().isVideoUpgradeRequest()) { contactPuckIcon.setImageResource(R.drawable.quantum_ic_videocam_white_24); } else { contactPuckIcon.setImageResource(R.drawable.quantum_ic_call_white_24); @@ -348,7 +348,8 @@ public class FlingUpDownMethod extends AnswerMethod implements OnProgressChanged } private boolean shouldShowPhotoInPuck() { - return getParent().isVideoCall() && contactPhoto != null; + return (getParent().isVideoCall() || getParent().isVideoUpgradeRequest()) + && contactPhoto != null; } @Override @@ -387,6 +388,10 @@ public class FlingUpDownMethod extends AnswerMethod implements OnProgressChanged // Since the animation progression is controlled by user gesture instead of real timeline, the // spec timeline can be divided into 9 slots. Each slot is equivalent to 83ms in the spec. // Therefore, we use 9 slots of 83ms to map user gesture into the spec timeline. + // + // See specs - + // Accept: https://direct.googleplex.com/#/spec/8510001 + // Decline: https://direct.googleplex.com/#/spec/3850001 final float progressSlots = 9; // Fade out the "swipe up to answer". It only takes 1 slot to complete the fade. @@ -414,7 +419,7 @@ public class FlingUpDownMethod extends AnswerMethod implements OnProgressChanged contactPuckBackground.setColorFilter(destPuckColor); // Animate decline icon - if (isAcceptingFlow || getParent().isVideoCall()) { + if (isAcceptingFlow || getParent().isVideoCall() || getParent().isVideoUpgradeRequest()) { rotateToward(contactPuckIcon, 0f); } else { rotateToward(contactPuckIcon, positiveAdjustedProgress * ICON_END_CALL_ROTATION_DEGREES); diff --git a/java/com/android/incallui/answer/impl/hint/AndroidManifest.xml b/java/com/android/incallui/answer/impl/hint/AndroidManifest.xml index b5fa6da8f..dfbba1cbf 100644 --- a/java/com/android/incallui/answer/impl/hint/AndroidManifest.xml +++ b/java/com/android/incallui/answer/impl/hint/AndroidManifest.xml @@ -3,7 +3,7 @@ xmlns:android="http://schemas.android.com/apk/res/android"> <application> - <receiver android:name=".EventSecretCodeListener"> + <receiver android:name=".PawSecretCodeListener"> <intent-filter> <action android:name="android.provider.Telephony.SECRET_CODE" /> <data android:scheme="android_secret_code" /> diff --git a/java/com/android/incallui/answer/impl/hint/AnswerHintFactory.java b/java/com/android/incallui/answer/impl/hint/AnswerHintFactory.java index 45395a71f..eaf5b74e5 100644 --- a/java/com/android/incallui/answer/impl/hint/AnswerHintFactory.java +++ b/java/com/android/incallui/answer/impl/hint/AnswerHintFactory.java @@ -28,7 +28,6 @@ import com.android.dialer.common.ConfigProvider; import com.android.dialer.common.ConfigProviderBindings; import com.android.dialer.common.LogUtil; import com.android.incallui.util.AccessibilityUtil; -import java.util.Calendar; /** * Selects a AnswerHint to show. If there's no suitable hints {@link EmptyAnswerHint} will be used, @@ -51,10 +50,10 @@ public class AnswerHintFactory { @VisibleForTesting static final String ANSWERED_COUNT_PREFERENCE_KEY = "answer_hint_answered_count"; - private final EventPayloadLoader eventPayloadLoader; + private final PawImageLoader pawImageLoader; - public AnswerHintFactory(@NonNull EventPayloadLoader eventPayloadLoader) { - this.eventPayloadLoader = Assert.isNotNull(eventPayloadLoader); + public AnswerHintFactory(@NonNull PawImageLoader pawImageLoader) { + this.pawImageLoader = Assert.isNotNull(pawImageLoader); } @NonNull @@ -69,11 +68,9 @@ public class AnswerHintFactory { } // Display the event answer hint if the payload is available. - Drawable eventPayload = - eventPayloadLoader.loadPayload( - context, System.currentTimeMillis(), Calendar.getInstance().getTimeZone()); + Drawable eventPayload = pawImageLoader.loadPayload(context); if (eventPayload != null) { - return new EventAnswerHint(context, eventPayload, puckUpDuration, puckUpDelay); + return new PawAnswerHint(context, eventPayload, puckUpDuration, puckUpDelay); } return new EmptyAnswerHint(); diff --git a/java/com/android/incallui/answer/impl/hint/EventPayloadLoaderImpl.java b/java/com/android/incallui/answer/impl/hint/EventPayloadLoaderImpl.java deleted file mode 100644 index bd8d73645..000000000 --- a/java/com/android/incallui/answer/impl/hint/EventPayloadLoaderImpl.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.incallui.answer.impl.hint; - -import android.annotation.TargetApi; -import android.content.Context; -import android.content.SharedPreferences; -import android.graphics.BitmapFactory; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.os.Build.VERSION_CODES; -import android.preference.PreferenceManager; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.VisibleForTesting; -import com.android.dialer.common.Assert; -import com.android.dialer.common.ConfigProvider; -import com.android.dialer.common.ConfigProviderBindings; -import com.android.dialer.common.LogUtil; -import java.io.InputStream; -import java.util.TimeZone; -import javax.crypto.Cipher; -import javax.crypto.SecretKey; -import javax.crypto.SecretKeyFactory; -import javax.crypto.spec.PBEKeySpec; - -/** Decrypt the event payload to be shown if in a specific time range and the key is received. */ -@TargetApi(VERSION_CODES.M) -public final class EventPayloadLoaderImpl implements EventPayloadLoader { - - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - static final String CONFIG_EVENT_KEY = "event_key"; - - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - static final String CONFIG_EVENT_BINARY = "event_binary"; - - // Time is stored as a UTC UNIX timestamp in milliseconds, but interpreted as local time. - // For example, 946684800 (2000/1/1 00:00:00 @UTC) is the new year midnight at every timezone. - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - static final String CONFIG_EVENT_START_UTC_AS_LOCAL_MILLIS = "event_time_start_millis"; - - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - static final String CONFIG_EVENT_TIME_END_UTC_AS_LOCAL_MILLIS = "event_time_end_millis"; - - @Override - @Nullable - public Drawable loadPayload( - @NonNull Context context, long currentTimeUtcMillis, @NonNull TimeZone timeZone) { - Assert.isNotNull(context); - Assert.isNotNull(timeZone); - ConfigProvider configProvider = ConfigProviderBindings.get(context); - - String pbeKey = configProvider.getString(CONFIG_EVENT_KEY, null); - if (pbeKey == null) { - return null; - } - long timeRangeStart = configProvider.getLong(CONFIG_EVENT_START_UTC_AS_LOCAL_MILLIS, 0); - long timeRangeEnd = configProvider.getLong(CONFIG_EVENT_TIME_END_UTC_AS_LOCAL_MILLIS, 0); - - String eventBinary = configProvider.getString(CONFIG_EVENT_BINARY, null); - if (eventBinary == null) { - return null; - } - - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); - if (!preferences.getBoolean( - EventSecretCodeListener.EVENT_ENABLED_WITH_SECRET_CODE_KEY, false)) { - long localTimestamp = currentTimeUtcMillis + timeZone.getRawOffset(); - - if (localTimestamp < timeRangeStart) { - return null; - } - - if (localTimestamp > timeRangeEnd) { - return null; - } - } - - // Use openssl aes-128-cbc -in <input> -out <output> -pass <PBEKey> to generate the asset - try (InputStream input = context.getAssets().open(eventBinary)) { - byte[] encryptedFile = new byte[input.available()]; - input.read(encryptedFile); - - Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC"); - - byte[] salt = new byte[8]; - System.arraycopy(encryptedFile, 8, salt, 0, 8); - SecretKey key = - SecretKeyFactory.getInstance("PBEWITHMD5AND128BITAES-CBC-OPENSSL", "BC") - .generateSecret(new PBEKeySpec(pbeKey.toCharArray(), salt, 100)); - cipher.init(Cipher.DECRYPT_MODE, key); - - byte[] decryptedFile = cipher.doFinal(encryptedFile, 16, encryptedFile.length - 16); - - return new BitmapDrawable( - context.getResources(), - BitmapFactory.decodeByteArray(decryptedFile, 0, decryptedFile.length)); - } catch (Exception e) { - // Avoid crashing dialer for any reason. - LogUtil.e("EventPayloadLoader.loadPayload", "error decrypting payload:", e); - return null; - } - } -} diff --git a/java/com/android/incallui/answer/impl/hint/EventAnswerHint.java b/java/com/android/incallui/answer/impl/hint/PawAnswerHint.java index 7ee327d50..36b761f57 100644 --- a/java/com/android/incallui/answer/impl/hint/EventAnswerHint.java +++ b/java/com/android/incallui/answer/impl/hint/PawAnswerHint.java @@ -39,7 +39,7 @@ import com.android.dialer.common.Assert; * An Answer hint that animates a {@link Drawable} payload with animation similar to {@link * DotAnswerHint}. */ -public final class EventAnswerHint implements AnswerHint { +public final class PawAnswerHint implements AnswerHint { private static final long FADE_IN_DELAY_SCALE_MILLIS = 380; private static final long FADE_IN_DURATION_SCALE_MILLIS = 200; @@ -53,7 +53,8 @@ public final class EventAnswerHint implements AnswerHint { private static final long FADE_OUT_DELAY_ALPHA_MILLIS = 130; private static final long FADE_OUT_DURATION_ALPHA_MILLIS = 170; - private static final float FADE_SCALE = 1.2f; + private static final float IMAGE_SCALE = 1.5f; + private static final float FADE_SCALE = 2.0f; private final Context context; private final Drawable payload; @@ -65,7 +66,7 @@ public final class EventAnswerHint implements AnswerHint { private View answerHintContainer; private AnimatorSet answerGestureHintAnim; - public EventAnswerHint( + public PawAnswerHint( @NonNull Context context, @NonNull Drawable payload, long puckUpDurationMillis, @@ -80,9 +81,9 @@ public final class EventAnswerHint implements AnswerHint { public void onCreateView( LayoutInflater inflater, ViewGroup container, View puck, TextView hintText) { this.puck = puck; - View view = inflater.inflate(R.layout.event_hint, container, true); + View view = inflater.inflate(R.layout.paw_hint, container, true); answerHintContainer = view.findViewById(R.id.answer_hint_container); - payloadView = view.findViewById(R.id.payload); + payloadView = view.findViewById(R.id.paw_image); hintText.setTextSize( TypedValue.COMPLEX_UNIT_PX, context.getResources().getDimension(R.dimen.hint_text_size)); ((ImageView) payloadView).setImageDrawable(payload); @@ -143,7 +144,7 @@ public final class EventAnswerHint implements AnswerHint { createUniformScaleAnimator( target, FADE_SCALE, - 1.0f, + IMAGE_SCALE, FADE_IN_DURATION_SCALE_MILLIS, FADE_IN_DELAY_SCALE_MILLIS, new LinearInterpolator()); @@ -170,7 +171,7 @@ public final class EventAnswerHint implements AnswerHint { Animator scale = createUniformScaleAnimator( target, - 1.0f, + IMAGE_SCALE, FADE_SCALE, FADE_OUT_DURATION_SCALE_MILLIS, scaleDelay, @@ -178,7 +179,7 @@ public final class EventAnswerHint implements AnswerHint { Animator alpha = createAlphaAnimator( target, - 01.0f, + 1.0f, 0.0f, FADE_OUT_DURATION_ALPHA_MILLIS, FADE_OUT_DELAY_ALPHA_MILLIS, diff --git a/java/com/android/incallui/answer/impl/hint/EventPayloadLoader.java b/java/com/android/incallui/answer/impl/hint/PawImageLoader.java index 09e3bedf2..09e700fe0 100644 --- a/java/com/android/incallui/answer/impl/hint/EventPayloadLoader.java +++ b/java/com/android/incallui/answer/impl/hint/PawImageLoader.java @@ -20,11 +20,9 @@ import android.content.Context; import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import java.util.TimeZone; -/** Loads a {@link Drawable} payload for the {@link EventAnswerHint} if it should be displayed. */ -public interface EventPayloadLoader { +/** Loads a {@link Drawable} payload for the {@link PawAnswerHint} if it should be displayed. */ +public interface PawImageLoader { @Nullable - Drawable loadPayload( - @NonNull Context context, long currentTimeUtcMillis, @NonNull TimeZone timeZone); + Drawable loadPayload(@NonNull Context context); } diff --git a/java/com/android/incallui/answer/impl/hint/PawImageLoaderImpl.java b/java/com/android/incallui/answer/impl/hint/PawImageLoaderImpl.java new file mode 100644 index 000000000..485a9ae37 --- /dev/null +++ b/java/com/android/incallui/answer/impl/hint/PawImageLoaderImpl.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.incallui.answer.impl.hint; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.SharedPreferences; +import android.graphics.drawable.Drawable; +import android.os.Build.VERSION_CODES; +import android.preference.PreferenceManager; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import com.android.dialer.common.Assert; + +/** Decrypt the event payload to be shown if in a specific time range and the key is received. */ +@TargetApi(VERSION_CODES.M) +public final class PawImageLoaderImpl implements PawImageLoader { + + @Override + @Nullable + public Drawable loadPayload(@NonNull Context context) { + Assert.isNotNull(context); + + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); + if (!preferences.getBoolean(PawSecretCodeListener.PAW_ENABLED_WITH_SECRET_CODE_KEY, false)) { + return null; + } + int drawableId = preferences.getInt(PawSecretCodeListener.PAW_DRAWABLE_ID_KEY, 0); + if (drawableId == 0) { + return null; + } + return context.getDrawable(drawableId); + } +} diff --git a/java/com/android/incallui/answer/impl/hint/EventSecretCodeListener.java b/java/com/android/incallui/answer/impl/hint/PawSecretCodeListener.java index 7cf4054a9..b4fc19c0d 100644 --- a/java/com/android/incallui/answer/impl/hint/EventSecretCodeListener.java +++ b/java/com/android/incallui/answer/impl/hint/PawSecretCodeListener.java @@ -24,26 +24,30 @@ import android.preference.PreferenceManager; import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import android.widget.Toast; +import com.android.dialer.common.Assert; import com.android.dialer.common.ConfigProviderBindings; import com.android.dialer.common.LogUtil; import com.android.dialer.logging.Logger; -import com.android.dialer.logging.nano.DialerImpression; +import com.android.dialer.logging.nano.DialerImpression.Type; +import java.util.Random; /** * Listen to the broadcast when the user dials "*#*#[number]#*#*" to toggle the event answer hint. */ -public class EventSecretCodeListener extends BroadcastReceiver { +public class PawSecretCodeListener extends BroadcastReceiver { @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - static final String CONFIG_EVENT_SECRET_CODE = "event_secret_code"; + static final String CONFIG_PAW_SECRET_CODE = "paw_secret_code"; - public static final String EVENT_ENABLED_WITH_SECRET_CODE_KEY = "event_enabled_with_secret_code"; + public static final String PAW_ENABLED_WITH_SECRET_CODE_KEY = "paw_enabled_with_secret_code"; + public static final String PAW_DRAWABLE_ID_KEY = "paw_drawable_id"; @Override public void onReceive(Context context, Intent intent) { String host = intent.getData().getHost(); + Assert.checkState(!TextUtils.isEmpty(host)); String secretCode = - ConfigProviderBindings.get(context).getString(CONFIG_EVENT_SECRET_CODE, null); + ConfigProviderBindings.get(context).getString(CONFIG_PAW_SECRET_CODE, "729"); if (secretCode == null) { return; } @@ -51,17 +55,27 @@ public class EventSecretCodeListener extends BroadcastReceiver { return; } SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); - boolean wasEnabled = preferences.getBoolean(EVENT_ENABLED_WITH_SECRET_CODE_KEY, false); + boolean wasEnabled = preferences.getBoolean(PAW_ENABLED_WITH_SECRET_CODE_KEY, false); if (wasEnabled) { - preferences.edit().putBoolean(EVENT_ENABLED_WITH_SECRET_CODE_KEY, false).apply(); + preferences.edit().putBoolean(PAW_ENABLED_WITH_SECRET_CODE_KEY, false).apply(); Toast.makeText(context, R.string.event_deactivated, Toast.LENGTH_SHORT).show(); - Logger.get(context).logImpression(DialerImpression.Type.EVENT_ANSWER_HINT_DEACTIVATED); - LogUtil.i("EventSecretCodeListener.onReceive", "EventAnswerHint disabled"); + Logger.get(context).logImpression(Type.EVENT_ANSWER_HINT_DEACTIVATED); + LogUtil.i("PawSecretCodeListener.onReceive", "PawAnswerHint disabled"); } else { - preferences.edit().putBoolean(EVENT_ENABLED_WITH_SECRET_CODE_KEY, true).apply(); + int drawableId; + if (new Random().nextBoolean()) { + drawableId = R.drawable.cat_paw; + } else { + drawableId = R.drawable.dog_paw; + } + preferences + .edit() + .putBoolean(PAW_ENABLED_WITH_SECRET_CODE_KEY, true) + .putInt(PAW_DRAWABLE_ID_KEY, drawableId) + .apply(); Toast.makeText(context, R.string.event_activated, Toast.LENGTH_SHORT).show(); - Logger.get(context).logImpression(DialerImpression.Type.EVENT_ANSWER_HINT_ACTIVATED); - LogUtil.i("EventSecretCodeListener.onReceive", "EventAnswerHint enabled"); + Logger.get(context).logImpression(Type.EVENT_ANSWER_HINT_ACTIVATED); + LogUtil.i("PawSecretCodeListener.onReceive", "PawAnswerHint enabled"); } } } diff --git a/java/com/android/incallui/answer/impl/hint/res/drawable-xxhdpi/cat_paw.webp b/java/com/android/incallui/answer/impl/hint/res/drawable-xxhdpi/cat_paw.webp Binary files differnew file mode 100644 index 000000000..f7ff6eb54 --- /dev/null +++ b/java/com/android/incallui/answer/impl/hint/res/drawable-xxhdpi/cat_paw.webp diff --git a/java/com/android/incallui/answer/impl/hint/res/drawable-xxhdpi/dog_paw.webp b/java/com/android/incallui/answer/impl/hint/res/drawable-xxhdpi/dog_paw.webp Binary files differnew file mode 100644 index 000000000..3a232542c --- /dev/null +++ b/java/com/android/incallui/answer/impl/hint/res/drawable-xxhdpi/dog_paw.webp diff --git a/java/com/android/incallui/answer/impl/hint/res/layout/event_hint.xml b/java/com/android/incallui/answer/impl/hint/res/layout/paw_hint.xml index d505014c1..c3b12a01d 100644 --- a/java/com/android/incallui/answer/impl/hint/res/layout/event_hint.xml +++ b/java/com/android/incallui/answer/impl/hint/res/layout/paw_hint.xml @@ -24,9 +24,10 @@ android:clipToPadding="false" android:visibility="gone"> <ImageView - android:id="@+id/payload" - android:layout_width="191dp" - android:layout_height="773dp" + android:id="@+id/paw_image" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/cat_paw" android:layout_gravity="center" android:alpha="0" android:rotation="-30" diff --git a/java/com/android/incallui/answer/impl/proguard.flags b/java/com/android/incallui/answer/impl/proguard.flags new file mode 100644 index 000000000..016352857 --- /dev/null +++ b/java/com/android/incallui/answer/impl/proguard.flags @@ -0,0 +1,5 @@ +# Used in com.android.dialer.answer.impl.SmsBottomSheetFragment +-keep class android.support.design.widget.BottomSheetBehavior { + public <init>(android.content.Context, android.util.AttributeSet); + public <init>(); +}
\ No newline at end of file diff --git a/java/com/android/incallui/answer/impl/res/values/dimens.xml b/java/com/android/incallui/answer/impl/res/values/dimens.xml index c48b68f93..8329707a6 100644 --- a/java/com/android/incallui/answer/impl/res/values/dimens.xml +++ b/java/com/android/incallui/answer/impl/res/values/dimens.xml @@ -22,4 +22,5 @@ <dimen name="answer_avatar_size">0dp</dimen> <dimen name="answer_importance_margin_bottom">0dp</dimen> <bool name="answer_important_call_allowed">false</bool> + <integer name="answer_animate_entry_millis">1000</integer> </resources> |