From 450c96108c73569dbe13a9e91dc35b34e940aecc Mon Sep 17 00:00:00 2001 From: weijiaxu Date: Fri, 17 Nov 2017 10:33:02 -0800 Subject: Fix dialer simulator for conference calling funcitonality. Updated the following contents: 1.Fix the order of spawning connections for GSM conference. 2.Make VOLTE conference call more realistic. 3.Fix minor bugs about simulator. 4.Add SimulatorConnectionsBank class to store connection tags created by simulator. 5.Fix tests influenced by SimulatorConnectionsBank. WANT_LGTM=wangqi Bug: 67785540 Test: In dialer lab. PiperOrigin-RevId: 176127584 Change-Id: I846174b97ed9329df6347583c41095f45f43494b --- java/com/android/dialer/simulator/Simulator.java | 13 ++ .../dialer/simulator/SimulatorComponent.java | 2 + .../dialer/simulator/SimulatorConnectionsBank.java | 55 ++++++++ .../simulator/impl/SimulatorConferenceCreator.java | 119 +++++++---------- .../dialer/simulator/impl/SimulatorConnection.java | 6 + .../simulator/impl/SimulatorConnectionService.java | 20 ++- .../impl/SimulatorConnectionsBankImpl.java | 147 +++++++++++++++++++++ .../dialer/simulator/impl/SimulatorModule.java | 6 + .../dialer/simulator/impl/SimulatorVoiceCall.java | 2 + 9 files changed, 297 insertions(+), 73 deletions(-) create mode 100644 java/com/android/dialer/simulator/SimulatorConnectionsBank.java create mode 100644 java/com/android/dialer/simulator/impl/SimulatorConnectionsBankImpl.java diff --git a/java/com/android/dialer/simulator/Simulator.java b/java/com/android/dialer/simulator/Simulator.java index 2094b420e..d75d10e82 100644 --- a/java/com/android/dialer/simulator/Simulator.java +++ b/java/com/android/dialer/simulator/Simulator.java @@ -42,6 +42,19 @@ public interface Simulator { static final int CONFERENCE_TYPE_GSM = 1; static final int CONFERENCE_TYPE_VOLTE = 2; + /** The types of connection service listener events */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + ON_NEW_OUTGOING_CONNECTION, + ON_NEW_INCOMING_CONNECTION, + ON_CONFERENCE, + }) + @interface ConnectionServiceEventType {} + + static final int ON_NEW_OUTGOING_CONNECTION = 1; + static final int ON_NEW_INCOMING_CONNECTION = 2; + static final int ON_CONFERENCE = 3; + /** Information about a connection event. */ public static class Event { /** The type of connection event. */ diff --git a/java/com/android/dialer/simulator/SimulatorComponent.java b/java/com/android/dialer/simulator/SimulatorComponent.java index f14496b80..dee188281 100644 --- a/java/com/android/dialer/simulator/SimulatorComponent.java +++ b/java/com/android/dialer/simulator/SimulatorComponent.java @@ -26,6 +26,8 @@ public abstract class SimulatorComponent { public abstract Simulator getSimulator(); + public abstract SimulatorConnectionsBank getSimulatorConnectionsBank(); + public static SimulatorComponent get(Context context) { return ((HasComponent) ((HasRootComponent) context.getApplicationContext()).component()) .simulatorComponent(); diff --git a/java/com/android/dialer/simulator/SimulatorConnectionsBank.java b/java/com/android/dialer/simulator/SimulatorConnectionsBank.java new file mode 100644 index 000000000..23c00424f --- /dev/null +++ b/java/com/android/dialer/simulator/SimulatorConnectionsBank.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2017 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.simulator; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.telecom.Connection; +import com.android.dialer.simulator.Simulator.ConferenceType; +import java.util.List; + +/** + * Used to create a shared connections bank which contains methods to manipulate connections. This + * is used mainly for conference calling. + */ +public interface SimulatorConnectionsBank { + + /** Add a connection into bank. */ + void add(Connection connection); + + /** Remove a connection from bank. */ + void remove(Connection connection); + + /** Merge all existing connections created by simulator into a conference. */ + void mergeAllConnections(@ConferenceType int conferenceType, Context context); + + /** Set all connections created by simulator to disconnected. */ + void disconnectAllConnections(); + + /** + * Update conferenceable connections for all connections in bank (usually after adding a new + * connection). Before calling this method, make sure all connections are returned by + * ConnectionService. + */ + void updateConferenceableConnections(); + + /** Determine whether a connection is created by simulator. */ + boolean isSimulatorConnection(@NonNull Connection connection); + + /** Get all connections tags from bank. */ + List getConnectionTags(); +} diff --git a/java/com/android/dialer/simulator/impl/SimulatorConferenceCreator.java b/java/com/android/dialer/simulator/impl/SimulatorConferenceCreator.java index d0249938a..36c19956a 100644 --- a/java/com/android/dialer/simulator/impl/SimulatorConferenceCreator.java +++ b/java/com/android/dialer/simulator/impl/SimulatorConferenceCreator.java @@ -19,8 +19,6 @@ package com.android.dialer.simulator.impl; import android.content.Context; import android.os.Bundle; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.telecom.Conferenceable; import android.telecom.Connection; import android.telecom.DisconnectCause; import com.android.dialer.common.Assert; @@ -28,8 +26,9 @@ import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.ThreadUtil; import com.android.dialer.simulator.Simulator; import com.android.dialer.simulator.Simulator.Event; +import com.android.dialer.simulator.SimulatorComponent; +import com.android.dialer.simulator.SimulatorConnectionsBank; import java.util.ArrayList; -import java.util.List; import java.util.Locale; /** Creates a conference with a given number of participants. */ @@ -38,32 +37,59 @@ final class SimulatorConferenceCreator SimulatorConnection.Listener, SimulatorConference.Listener { private static final String EXTRA_CALL_COUNT = "call_count"; - + private static final String RECONNECT = "reconnect"; @NonNull private final Context context; - @NonNull private final List connectionTags = new ArrayList<>(); + + private final SimulatorConnectionsBank simulatorConnectionsBank; + + private boolean onNewIncomingConnectionEnabled = false; + @Simulator.ConferenceType private final int conferenceType; public SimulatorConferenceCreator( @NonNull Context context, @Simulator.ConferenceType int conferenceType) { this.context = Assert.isNotNull(context); this.conferenceType = conferenceType; + simulatorConnectionsBank = SimulatorComponent.get(context).getSimulatorConnectionsBank(); } void start(int callCount) { + onNewIncomingConnectionEnabled = true; SimulatorConnectionService.addListener(this); - addNextCall(callCount); + if (conferenceType == Simulator.CONFERENCE_TYPE_VOLTE) { + addNextCall(callCount, true); + } else if (conferenceType == Simulator.CONFERENCE_TYPE_GSM) { + addNextCall(callCount, false); + } } - - private void addNextCall(int callCount) { + /** + * Add a call in a process of making a conference. + * + * @param callCount the remaining number of calls to make + * @param reconnect whether all connections should reconnect once (connections are reconnected + * once in making VoLTE conference) + */ + private void addNextCall(int callCount, boolean reconnect) { LogUtil.i("SimulatorConferenceCreator.addNextIncomingCall", "callCount: " + callCount); if (callCount <= 0) { LogUtil.i("SimulatorConferenceCreator.addNextCall", "done adding calls"); + if (reconnect) { + simulatorConnectionsBank.disconnectAllConnections(); + addNextCall(simulatorConnectionsBank.getConnectionTags().size(), false); + } else { + simulatorConnectionsBank.mergeAllConnections(conferenceType, context); + SimulatorConnectionService.removeListener(this); + } return; } - String callerId = String.format(Locale.US, "+1-650-234%04d", callCount); Bundle extras = new Bundle(); extras.putInt(EXTRA_CALL_COUNT, callCount - 1); + extras.putBoolean(RECONNECT, reconnect); + addConferenceCall(callerId, extras); + } + + private void addConferenceCall(String number, Bundle extras) { switch (conferenceType) { case Simulator.CONFERENCE_TYPE_VOLTE: extras.putBoolean("ISVOLTE", true); @@ -71,35 +97,25 @@ final class SimulatorConferenceCreator default: break; } - connectionTags.add( - SimulatorSimCallManager.addNewIncomingCall(context, callerId, false /* isVideo */, extras)); + SimulatorSimCallManager.addNewIncomingCall(context, number, false /* isVideo */, extras); } @Override public void onNewIncomingConnection(@NonNull SimulatorConnection connection) { - if (!isMyConnection(connection)) { + if (!onNewIncomingConnectionEnabled) { + return; + } + if (!simulatorConnectionsBank.isSimulatorConnection(connection)) { LogUtil.i("SimulatorConferenceCreator.onNewOutgoingConnection", "unknown connection"); return; } - LogUtil.i("SimulatorConferenceCreator.onNewOutgoingConnection", "connection created"); connection.addListener(this); - // Once the connection is active, go ahead and conference it and add the next call. ThreadUtil.postDelayedOnUiThread( () -> { - SimulatorConference conference = findCurrentConference(); - if (conference == null) { - conference = - SimulatorConference.newGsmConference( - SimulatorSimCallManager.getSystemPhoneAccountHandle(context)); - conference.addListener(this); - SimulatorConnectionService.getInstance().addConference(conference); - } - updateConferenceableConnections(); connection.setActive(); - conference.addConnection(connection); - addNextCall(getCallCount(connection)); + addNextCall(getCallCount(connection), shouldReconnect(connection)); }, 1000); } @@ -115,7 +131,8 @@ final class SimulatorConferenceCreator public void onConference( @NonNull SimulatorConnection connection1, @NonNull SimulatorConnection connection2) { LogUtil.enterBlock("SimulatorConferenceCreator.onConference"); - if (!isMyConnection(connection1) || !isMyConnection(connection2)) { + if (!simulatorConnectionsBank.isSimulatorConnection(connection1) + || !simulatorConnectionsBank.isSimulatorConnection(connection2)) { LogUtil.i("SimulatorConferenceCreator.onConference", "unknown connections, ignoring"); return; } @@ -125,7 +142,6 @@ final class SimulatorConferenceCreator } else if (connection2.getConference() != null) { connection2.getConference().addConnection(connection1); } else { - Assert.checkArgument(conferenceType == Simulator.CONFERENCE_TYPE_GSM); SimulatorConference conference = SimulatorConference.newGsmConference( SimulatorSimCallManager.getSystemPhoneAccountHandle(context)); @@ -136,54 +152,14 @@ final class SimulatorConferenceCreator } } - private boolean isMyConnection(@NonNull Connection connection) { - for (String connectionTag : connectionTags) { - if (connection.getExtras().getBoolean(connectionTag)) { - return true; - } - } - return false; - } - - private void updateConferenceableConnections() { - LogUtil.enterBlock("SimulatorConferenceCreator.updateConferenceableConnections"); - for (String connectionTag : connectionTags) { - SimulatorConnection connection = SimulatorSimCallManager.findConnectionByTag(connectionTag); - List conferenceables = getMyConferenceables(); - conferenceables.remove(connection); - conferenceables.remove(connection.getConference()); - connection.setConferenceables(conferenceables); - } - } - - private List getMyConferenceables() { - List conferenceables = new ArrayList<>(); - for (String connectionTag : connectionTags) { - SimulatorConnection connection = SimulatorSimCallManager.findConnectionByTag(connectionTag); - conferenceables.add(connection); - if (connection.getConference() != null - && !conferenceables.contains(connection.getConference())) { - conferenceables.add(connection.getConference()); - } - } - return conferenceables; - } - - @Nullable - private SimulatorConference findCurrentConference() { - for (String connectionTag : connectionTags) { - SimulatorConnection connection = SimulatorSimCallManager.findConnectionByTag(connectionTag); - if (connection.getConference() != null) { - return (SimulatorConference) connection.getConference(); - } - } - return null; - } - private static int getCallCount(@NonNull Connection connection) { return connection.getExtras().getInt(EXTRA_CALL_COUNT); } + private static boolean shouldReconnect(@NonNull Connection connection) { + return connection.getExtras().getBoolean(RECONNECT); + } + @Override public void onEvent(@NonNull SimulatorConnection connection, @NonNull Event event) { switch (event.type) { @@ -222,6 +198,7 @@ final class SimulatorConferenceCreator for (Connection connection : new ArrayList<>(conference.getConnections())) { connection.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL)); } + conference.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL)); break; default: LogUtil.i( diff --git a/java/com/android/dialer/simulator/impl/SimulatorConnection.java b/java/com/android/dialer/simulator/impl/SimulatorConnection.java index 168f5db98..2a24d8f37 100644 --- a/java/com/android/dialer/simulator/impl/SimulatorConnection.java +++ b/java/com/android/dialer/simulator/impl/SimulatorConnection.java @@ -24,6 +24,8 @@ import android.telecom.VideoProfile; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; import com.android.dialer.simulator.Simulator.Event; +import com.android.dialer.simulator.SimulatorComponent; +import com.android.dialer.simulator.SimulatorConnectionsBank; import java.util.ArrayList; import java.util.List; @@ -31,6 +33,7 @@ import java.util.List; public final class SimulatorConnection extends Connection { private final List listeners = new ArrayList<>(); private final List events = new ArrayList<>(); + private final SimulatorConnectionsBank simulatorConnectionsBank; private int currentState = STATE_NEW; SimulatorConnection(@NonNull Context context, @NonNull ConnectionRequest request) { @@ -47,6 +50,7 @@ public final class SimulatorConnection extends Connection { setConnectionCapabilities(getConnectionCapabilities() | CAPABILITY_SEPARATE_FROM_CONFERENCE); } setVideoProvider(new SimulatorVideoProvider(context, this)); + simulatorConnectionsBank = SimulatorComponent.get(context).getSimulatorConnectionsBank(); } public void addListener(@NonNull Listener listener) { @@ -71,6 +75,7 @@ public final class SimulatorConnection extends Connection { @Override public void onReject() { LogUtil.enterBlock("SimulatorConnection.onReject"); + simulatorConnectionsBank.remove(this); onEvent(new Event(Event.REJECT)); } @@ -89,6 +94,7 @@ public final class SimulatorConnection extends Connection { @Override public void onDisconnect() { LogUtil.enterBlock("SimulatorConnection.onDisconnect"); + simulatorConnectionsBank.remove(this); onEvent(new Event(Event.DISCONNECT)); } diff --git a/java/com/android/dialer/simulator/impl/SimulatorConnectionService.java b/java/com/android/dialer/simulator/impl/SimulatorConnectionService.java index 465890cf0..e6bf99f3a 100644 --- a/java/com/android/dialer/simulator/impl/SimulatorConnectionService.java +++ b/java/com/android/dialer/simulator/impl/SimulatorConnectionService.java @@ -28,6 +28,9 @@ import android.telephony.TelephonyManager; import android.widget.Toast; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; +import com.android.dialer.common.concurrent.ThreadUtil; +import com.android.dialer.simulator.SimulatorComponent; +import com.android.dialer.simulator.SimulatorConnectionsBank; import java.util.ArrayList; import java.util.List; @@ -35,6 +38,7 @@ import java.util.List; public class SimulatorConnectionService extends ConnectionService { private static final List listeners = new ArrayList<>(); private static SimulatorConnectionService instance; + private SimulatorConnectionsBank simulatorConnectionsBank; public static SimulatorConnectionService getInstance() { return instance; @@ -52,12 +56,14 @@ public class SimulatorConnectionService extends ConnectionService { public void onCreate() { super.onCreate(); instance = this; + simulatorConnectionsBank = SimulatorComponent.get(this).getSimulatorConnectionsBank(); } @Override public void onDestroy() { LogUtil.enterBlock("SimulatorConnectionService.onDestroy"); instance = null; + simulatorConnectionsBank = null; super.onDestroy(); } @@ -78,7 +84,12 @@ public class SimulatorConnectionService extends ConnectionService { SimulatorConnection connection = new SimulatorConnection(this, request); connection.setDialing(); connection.setAddress(request.getAddress(), TelecomManager.PRESENTATION_ALLOWED); - + simulatorConnectionsBank.add(connection); + ThreadUtil.postOnUiThread( + () -> + SimulatorComponent.get(instance) + .getSimulatorConnectionsBank() + .updateConferenceableConnections()); for (Listener listener : listeners) { listener.onNewOutgoingConnection(connection); } @@ -102,7 +113,12 @@ public class SimulatorConnectionService extends ConnectionService { SimulatorConnection connection = new SimulatorConnection(this, request); connection.setRinging(); connection.setAddress(getPhoneNumber(request), TelecomManager.PRESENTATION_ALLOWED); - + simulatorConnectionsBank.add(connection); + ThreadUtil.postOnUiThread( + () -> + SimulatorComponent.get(instance) + .getSimulatorConnectionsBank() + .updateConferenceableConnections()); for (Listener listener : listeners) { listener.onNewIncomingConnection(connection); } diff --git a/java/com/android/dialer/simulator/impl/SimulatorConnectionsBankImpl.java b/java/com/android/dialer/simulator/impl/SimulatorConnectionsBankImpl.java new file mode 100644 index 000000000..75f144fde --- /dev/null +++ b/java/com/android/dialer/simulator/impl/SimulatorConnectionsBankImpl.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2017 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.simulator.impl; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.telecom.Conferenceable; +import android.telecom.Connection; +import android.telecom.DisconnectCause; +import com.android.dialer.common.LogUtil; +import com.android.dialer.simulator.Simulator; +import com.android.dialer.simulator.Simulator.ConferenceType; +import com.android.dialer.simulator.Simulator.Event; +import com.android.dialer.simulator.SimulatorConnectionsBank; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import javax.inject.Inject; + +/** Wraps a list of connection tags and common methods around the connection tags list. */ +public class SimulatorConnectionsBankImpl + implements SimulatorConnectionsBank, SimulatorConference.Listener { + private final List connectionTags = new ArrayList<>(); + + @Inject + public SimulatorConnectionsBankImpl() {} + + @Override + public List getConnectionTags() { + return connectionTags; + } + + @Override + public void add(Connection connection) { + connectionTags.add(SimulatorSimCallManager.getConnectionTag(connection)); + } + + @Override + public void remove(Connection connection) { + connectionTags.remove(SimulatorSimCallManager.getConnectionTag(connection)); + } + + @Override + public void mergeAllConnections(@ConferenceType int conferenceType, Context context) { + SimulatorConference simulatorConference = null; + if (conferenceType == Simulator.CONFERENCE_TYPE_GSM) { + simulatorConference = + SimulatorConference.newGsmConference( + SimulatorSimCallManager.getSystemPhoneAccountHandle(context)); + } else if (conferenceType == Simulator.CONFERENCE_TYPE_VOLTE) { + simulatorConference = + SimulatorConference.newVoLteConference( + SimulatorSimCallManager.getSystemPhoneAccountHandle(context)); + } + Collection connections = + SimulatorConnectionService.getInstance().getAllConnections(); + for (Connection connection : connections) { + simulatorConference.addConnection(connection); + } + simulatorConference.addListener(this); + SimulatorConnectionService.getInstance().addConference(simulatorConference); + } + + @Override + public void disconnectAllConnections() { + Collection connections = + SimulatorConnectionService.getInstance().getAllConnections(); + for (Connection connection : connections) { + connection.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL)); + } + } + + @Override + public void updateConferenceableConnections() { + LogUtil.enterBlock("SimulatorConferenceCreator.updateConferenceableConnections"); + for (String connectionTag : connectionTags) { + SimulatorConnection connection = SimulatorSimCallManager.findConnectionByTag(connectionTag); + List conferenceables = getSimulatorConferenceables(); + conferenceables.remove(connection); + conferenceables.remove(connection.getConference()); + connection.setConferenceables(conferenceables); + } + } + + private List getSimulatorConferenceables() { + List conferenceables = new ArrayList<>(); + for (String connectionTag : connectionTags) { + SimulatorConnection connection = SimulatorSimCallManager.findConnectionByTag(connectionTag); + conferenceables.add(connection); + if (connection.getConference() != null + && !conferenceables.contains(connection.getConference())) { + conferenceables.add(connection.getConference()); + } + } + return conferenceables; + } + + @Override + public boolean isSimulatorConnection(@NonNull Connection connection) { + for (String connectionTag : connectionTags) { + if (connection.getExtras().getBoolean(connectionTag)) { + return true; + } + } + return false; + } + + @Override + public void onEvent(@NonNull SimulatorConference conference, @NonNull Event event) { + switch (event.type) { + case Event.MERGE: + int capabilities = conference.getConnectionCapabilities(); + capabilities |= Connection.CAPABILITY_SWAP_CONFERENCE; + conference.setConnectionCapabilities(capabilities); + break; + case Event.SEPARATE: + SimulatorConnection connectionToRemove = + SimulatorSimCallManager.findConnectionByTag(event.data1); + conference.removeConnection(connectionToRemove); + break; + case Event.DISCONNECT: + for (Connection connection : new ArrayList<>(conference.getConnections())) { + connection.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL)); + } + conference.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL)); + break; + default: + LogUtil.i( + "SimulatorConferenceCreator.onEvent", "unexpected conference event: " + event.type); + break; + } + } +} diff --git a/java/com/android/dialer/simulator/impl/SimulatorModule.java b/java/com/android/dialer/simulator/impl/SimulatorModule.java index c0cca271b..2bc72c956 100644 --- a/java/com/android/dialer/simulator/impl/SimulatorModule.java +++ b/java/com/android/dialer/simulator/impl/SimulatorModule.java @@ -17,6 +17,7 @@ package com.android.dialer.simulator.impl; import com.android.dialer.simulator.Simulator; +import com.android.dialer.simulator.SimulatorConnectionsBank; import dagger.Binds; import dagger.Module; import javax.inject.Singleton; @@ -27,4 +28,9 @@ public abstract class SimulatorModule { @Binds @Singleton public abstract Simulator bindsSimulator(SimulatorImpl simulator); + + @Binds + @Singleton + public abstract SimulatorConnectionsBank bindsSimulatorConnectionsBank( + SimulatorConnectionsBankImpl simulatorConnectionsBank); } diff --git a/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java b/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java index d2eba6b03..451896b73 100644 --- a/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java +++ b/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java @@ -52,6 +52,8 @@ final class SimulatorVoiceCall private SimulatorVoiceCall(@NonNull Context context) { this.context = Assert.isNotNull(context); SimulatorConnectionService.addListener(this); + SimulatorConnectionService.addListener( + new SimulatorConferenceCreator(context, Simulator.CONFERENCE_TYPE_GSM)); } private void addNewIncomingCall(boolean isSpam) { -- cgit v1.2.3 From c170e2df0230f46e06ed066becc0001f5b8eb44e Mon Sep 17 00:00:00 2001 From: twyen Date: Fri, 17 Nov 2017 10:43:38 -0800 Subject: Implement suggested SIM This CL adds a module that can query providers to aid users choosing which SIM to call with. Bug: 64214592 Test: MotoSuggestionProviderTest PiperOrigin-RevId: 176129303 Change-Id: Idb6314ad7f5a1bcd20b5b9173d3bfd873383cc84 --- .../binary/aosp/AospDialerRootComponent.java | 4 +- .../basecomponent/BaseDialerRootComponent.java | 2 + .../google/GoogleStubDialerRootComponent.java | 2 + .../precall/impl/CallingAccountSelector.java | 64 ++++++++++++++++++++-- .../suggestion/SimSuggestionComponent.java | 41 ++++++++++++++ .../suggestion/SuggestionProvider.java | 58 ++++++++++++++++++++ .../suggestion/stub/StubSimSuggestionModule.java | 32 +++++++++++ .../suggestion/stub/StubSuggestionProvider.java | 44 +++++++++++++++ 8 files changed, 240 insertions(+), 7 deletions(-) create mode 100644 java/com/android/dialer/preferredsim/suggestion/SimSuggestionComponent.java create mode 100644 java/com/android/dialer/preferredsim/suggestion/SuggestionProvider.java create mode 100644 java/com/android/dialer/preferredsim/suggestion/stub/StubSimSuggestionModule.java create mode 100644 java/com/android/dialer/preferredsim/suggestion/stub/StubSuggestionProvider.java diff --git a/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java b/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java index a02727ea8..a0fb604cf 100644 --- a/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java +++ b/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java @@ -26,6 +26,7 @@ import com.android.dialer.inject.ContextModule; import com.android.dialer.phonelookup.PhoneLookupModule; import com.android.dialer.phonenumbergeoutil.impl.PhoneNumberGeoUtilModule; import com.android.dialer.precall.impl.PreCallModule; +import com.android.dialer.preferredsim.suggestion.stub.StubSimSuggestionModule; import com.android.dialer.simulator.impl.SimulatorModule; import com.android.dialer.storage.StorageModule; import com.android.dialer.strictmode.impl.SystemStrictModeModule; @@ -53,7 +54,8 @@ import javax.inject.Singleton; StubDuoModule.class, StubEnrichedCallModule.class, StubMapsModule.class, - VoicemailModule.class + VoicemailModule.class, + StubSimSuggestionModule.class } ) public interface AospDialerRootComponent extends BaseDialerRootComponent {} diff --git a/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java b/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java index 387fca530..d5c91c90a 100644 --- a/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java +++ b/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java @@ -26,6 +26,7 @@ import com.android.dialer.main.MainComponent; import com.android.dialer.phonelookup.PhoneLookupComponent; import com.android.dialer.phonenumbergeoutil.PhoneNumberGeoUtilComponent; import com.android.dialer.precall.PreCallComponent; +import com.android.dialer.preferredsim.suggestion.SimSuggestionComponent; import com.android.dialer.simulator.SimulatorComponent; import com.android.dialer.storage.StorageComponent; import com.android.dialer.strictmode.StrictModeComponent; @@ -50,6 +51,7 @@ public interface BaseDialerRootComponent PhoneLookupComponent.HasComponent, PhoneNumberGeoUtilComponent.HasComponent, PreCallComponent.HasComponent, + SimSuggestionComponent.HasComponent, SimulatorComponent.HasComponent, StorageComponent.HasComponent, StrictModeComponent.HasComponent, diff --git a/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java b/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java index 273d1e4fc..1ae80f4aa 100644 --- a/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java +++ b/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java @@ -26,6 +26,7 @@ import com.android.dialer.inject.ContextModule; import com.android.dialer.phonelookup.PhoneLookupModule; import com.android.dialer.phonenumbergeoutil.impl.PhoneNumberGeoUtilModule; import com.android.dialer.precall.impl.PreCallModule; +import com.android.dialer.preferredsim.suggestion.stub.StubSimSuggestionModule; import com.android.dialer.simulator.impl.SimulatorModule; import com.android.dialer.storage.StorageModule; import com.android.dialer.strictmode.impl.SystemStrictModeModule; @@ -49,6 +50,7 @@ import javax.inject.Singleton; PhoneLookupModule.class, // TODO(zachh): Module which uses APDL? PhoneNumberGeoUtilModule.class, PreCallModule.class, + StubSimSuggestionModule.class, SharedPrefConfigProviderModule.class, SimulatorModule.class, StorageModule.class, diff --git a/java/com/android/dialer/precall/impl/CallingAccountSelector.java b/java/com/android/dialer/precall/impl/CallingAccountSelector.java index d763c7a5f..e0fe0c488 100644 --- a/java/com/android/dialer/precall/impl/CallingAccountSelector.java +++ b/java/com/android/dialer/precall/impl/CallingAccountSelector.java @@ -44,6 +44,8 @@ import com.android.dialer.precall.PreCallCoordinator; import com.android.dialer.precall.PreCallCoordinator.PendingAction; import com.android.dialer.preferredsim.PreferredSimFallbackContract; import com.android.dialer.preferredsim.PreferredSimFallbackContract.PreferredSim; +import com.android.dialer.preferredsim.suggestion.SimSuggestionComponent; +import com.android.dialer.preferredsim.suggestion.SuggestionProvider.Suggestion; import com.google.common.base.Optional; import java.util.List; import java.util.Set; @@ -84,7 +86,7 @@ public class CallingAccountSelector implements PreCallAction { } switch (builder.getUri().getScheme()) { case PhoneAccount.SCHEME_VOICEMAIL: - showDialog(coordinator, coordinator.startPendingAction(), null); + showDialog(coordinator, coordinator.startPendingAction(), null, null, null); break; case PhoneAccount.SCHEME_TEL: processPreferredAccount(coordinator); @@ -128,7 +130,17 @@ public class CallingAccountSelector implements PreCallAction { pendingAction.finish(); return; } - showDialog(coordinator, pendingAction, result.dataId.orNull()); + if (result.suggestion.isPresent()) { + LogUtil.i( + "CallingAccountSelector.processPreferredAccount", + "SIM suggested: " + result.suggestion.get().reason); + } + showDialog( + coordinator, + pendingAction, + result.dataId.orNull(), + phoneNumber, + result.suggestion.orNull()); })) .build() .executeParallel(activity); @@ -136,7 +148,11 @@ public class CallingAccountSelector implements PreCallAction { @MainThread private void showDialog( - PreCallCoordinator coordinator, PendingAction pendingAction, @Nullable String dataId) { + PreCallCoordinator coordinator, + PendingAction pendingAction, + @Nullable String dataId, + @Nullable String number, + @Nullable Suggestion unusedSuggestion) { // TODO(twyen): incoporate suggestion in dialog Assert.isMainThread(); selectPhoneAccountDialogFragment = SelectPhoneAccountDialogFragment.newInstance( @@ -146,7 +162,7 @@ public class CallingAccountSelector implements PreCallAction { .getActivity() .getSystemService(TelecomManager.class) .getCallCapablePhoneAccounts(), - new SelectedListener(coordinator, pendingAction, dataId), + new SelectedListener(coordinator, pendingAction, dataId, number), null /* call ID */); selectPhoneAccountDialogFragment.show( coordinator.getActivity().getFragmentManager(), TAG_CALLING_ACCOUNT_SELECTOR); @@ -169,6 +185,8 @@ public class CallingAccountSelector implements PreCallAction { * preferred account is to be set it should be stored in this row */ Optional dataId = Optional.absent(); + + Optional suggestion = Optional.absent(); } private static class PreferredAccountWorker @@ -189,6 +207,12 @@ public class CallingAccountSelector implements PreCallAction { if (result.dataId.isPresent()) { result.phoneAccountHandle = getPreferredAccount(context, result.dataId.get()); } + if (!result.phoneAccountHandle.isPresent()) { + result.suggestion = + SimSuggestionComponent.get(context) + .getSuggestionProvider() + .getSuggestion(context, phoneNumber); + } return result; } } @@ -257,14 +281,17 @@ public class CallingAccountSelector implements PreCallAction { private final PreCallCoordinator coordinator; private final PreCallCoordinator.PendingAction listener; private final String dataId; + private final String number; public SelectedListener( @NonNull PreCallCoordinator builder, @NonNull PreCallCoordinator.PendingAction listener, - @Nullable String dataId) { + @Nullable String dataId, + @Nullable String number) { this.coordinator = Assert.isNotNull(builder); this.listener = Assert.isNotNull(listener); this.dataId = dataId; + this.number = number; } @MainThread @@ -282,7 +309,11 @@ public class CallingAccountSelector implements PreCallAction { new WritePreferredAccountWorkerInput( coordinator.getActivity(), dataId, selectedAccountHandle)); } - + DialerExecutorComponent.get(coordinator.getActivity()) + .dialerExecutorFactory() + .createNonUiTaskBuilder(new UserSelectionReporter(selectedAccountHandle, number)) + .build() + .executeParallel(coordinator.getActivity()); listener.finish(); } @@ -297,6 +328,27 @@ public class CallingAccountSelector implements PreCallAction { } } + private static class UserSelectionReporter implements Worker { + + private final String number; + private final PhoneAccountHandle phoneAccountHandle; + + public UserSelectionReporter( + @NonNull PhoneAccountHandle phoneAccountHandle, @Nullable String number) { + this.phoneAccountHandle = Assert.isNotNull(phoneAccountHandle); + this.number = Assert.isNotNull(number); + } + + @Nullable + @Override + public Void doInBackground(@NonNull Context context) throws Throwable { + SimSuggestionComponent.get(context) + .getSuggestionProvider() + .reportUserSelection(context, number, phoneAccountHandle); + return null; + } + } + private static class WritePreferredAccountWorkerInput { private final Context context; private final String dataId; diff --git a/java/com/android/dialer/preferredsim/suggestion/SimSuggestionComponent.java b/java/com/android/dialer/preferredsim/suggestion/SimSuggestionComponent.java new file mode 100644 index 000000000..4b3f7b26f --- /dev/null +++ b/java/com/android/dialer/preferredsim/suggestion/SimSuggestionComponent.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 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.preferredsim.suggestion; + +import android.content.Context; +import android.support.annotation.WorkerThread; +import com.android.dialer.common.Assert; +import com.android.dialer.inject.HasRootComponent; +import dagger.Subcomponent; + +/** Dagger component for {@link SuggestionProvider} */ +@Subcomponent +public abstract class SimSuggestionComponent { + public abstract SuggestionProvider getSuggestionProvider(); + + @WorkerThread + public static SimSuggestionComponent get(Context context) { + Assert.isWorkerThread(); + return ((HasComponent) ((HasRootComponent) context.getApplicationContext()).component()) + .simSuggestionComponent(); + } + + /** Used to refer to the root application component. */ + public interface HasComponent { + SimSuggestionComponent simSuggestionComponent(); + } +} diff --git a/java/com/android/dialer/preferredsim/suggestion/SuggestionProvider.java b/java/com/android/dialer/preferredsim/suggestion/SuggestionProvider.java new file mode 100644 index 000000000..61a831b5e --- /dev/null +++ b/java/com/android/dialer/preferredsim/suggestion/SuggestionProvider.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2017 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.preferredsim.suggestion; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.WorkerThread; +import android.telecom.PhoneAccountHandle; +import com.android.dialer.common.Assert; +import com.google.common.base.Optional; + +/** Provides hints to the user when selecting a SIM to make a call. */ +public interface SuggestionProvider { + + /** The reason the suggestion is made. */ + enum Reason { + UNKNOWN, + // The SIM has the same carrier as the callee. + INTRA_CARRIER, + // The user has selected the SIM for the callee multiple times. + FREQUENT + } + + /** The suggestion. */ + class Suggestion { + @NonNull public final PhoneAccountHandle phoneAccountHandle; + @NonNull public final Reason reason; + + public Suggestion(@NonNull PhoneAccountHandle phoneAccountHandle, @NonNull Reason reason) { + this.phoneAccountHandle = Assert.isNotNull(phoneAccountHandle); + this.reason = Assert.isNotNull(reason); + } + } + + @WorkerThread + @NonNull + Optional getSuggestion(@NonNull Context context, @NonNull String number); + + @WorkerThread + void reportUserSelection( + @NonNull Context context, + @NonNull String number, + @NonNull PhoneAccountHandle phoneAccountHandle); +} diff --git a/java/com/android/dialer/preferredsim/suggestion/stub/StubSimSuggestionModule.java b/java/com/android/dialer/preferredsim/suggestion/stub/StubSimSuggestionModule.java new file mode 100644 index 000000000..2f0e9b238 --- /dev/null +++ b/java/com/android/dialer/preferredsim/suggestion/stub/StubSimSuggestionModule.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2017 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.preferredsim.suggestion.stub; + +import com.android.dialer.preferredsim.suggestion.SuggestionProvider; +import dagger.Binds; +import dagger.Module; +import javax.inject.Singleton; + +/** Stub module for {@link com.android.dialer.preferredsim.suggestion.SimSuggestionComponent} */ +@Module +public abstract class StubSimSuggestionModule { + + @Binds + @Singleton + public abstract SuggestionProvider bindSuggestionProvider( + StubSuggestionProvider suggestionProvider); +} diff --git a/java/com/android/dialer/preferredsim/suggestion/stub/StubSuggestionProvider.java b/java/com/android/dialer/preferredsim/suggestion/stub/StubSuggestionProvider.java new file mode 100644 index 000000000..e3240448d --- /dev/null +++ b/java/com/android/dialer/preferredsim/suggestion/stub/StubSuggestionProvider.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2017 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.preferredsim.suggestion.stub; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.WorkerThread; +import android.telecom.PhoneAccountHandle; +import com.android.dialer.preferredsim.suggestion.SuggestionProvider; +import com.google.common.base.Optional; +import javax.inject.Inject; + +/** {@link SuggestionProvider} that does nothing. */ +public class StubSuggestionProvider implements SuggestionProvider { + + @Inject + public StubSuggestionProvider() {} + + @WorkerThread + @Override + public Optional getSuggestion(Context context, String number) { + return Optional.absent(); + } + + @Override + public void reportUserSelection( + @NonNull Context context, + @NonNull String number, + @NonNull PhoneAccountHandle phoneAccountHandle) {} +} -- cgit v1.2.3 From 6a63e8d6830ff6e01454ee116c6df36294818cdb Mon Sep 17 00:00:00 2001 From: mdooley Date: Fri, 17 Nov 2017 10:49:59 -0800 Subject: Adding exponential backoff utilities This cl adds two classes, one to calculate a base multiplier for an exponential backoff series and the other to return sequential values in an exponential backoff series. This cl has a number of advantages over the previous one, such as: - separating the base multiplier calculation from the exponential backoff class so that a standard exponential backoff class like com.google.common.labs.concurrent.RetryStrategy.ExponentialBackoff could be used instead if it becomes open source - allows the backoff sequence to be scaled by specify an initial backoff delay - uses milliseconds instead of arbitrary time units which allows the tolerance to be set automatically when calculating the base multiplier WANT_LGTM=zachh Bug: 66966157 Test: unit tests PiperOrigin-RevId: 176130268 Change-Id: I75135d4df16f642ea9dd3ef9ff9498981beae2c6 --- .../dialer/common/backoff/ExponentialBackoff.java | 96 ++++++++++++++++ .../common/backoff/ExponentialBaseCalculator.java | 126 +++++++++++++++++++++ 2 files changed, 222 insertions(+) create mode 100644 java/com/android/dialer/common/backoff/ExponentialBackoff.java create mode 100644 java/com/android/dialer/common/backoff/ExponentialBaseCalculator.java diff --git a/java/com/android/dialer/common/backoff/ExponentialBackoff.java b/java/com/android/dialer/common/backoff/ExponentialBackoff.java new file mode 100644 index 000000000..67727d8c2 --- /dev/null +++ b/java/com/android/dialer/common/backoff/ExponentialBackoff.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2017 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.common.backoff; + +import com.android.dialer.common.Assert; + +/** + * Given an initial backoff delay, D, a base multiplier, B, and a total number of backoffs, N, this + * class returns values in the exponential sequence, D, D*B, D*B^2, ... D*B^(N-1), ... + * + *

Example usage: + * + *

+ *   long initialDelayMillis = 1000;
+ *   double multiplier = 1.2;
+ *   int backoffs = 10;
+ *   ExponentialBackoff backoff = new ExponentialBackoff(initialDelayMillis, multiplier, backoffs);
+ *   while (backoff.isInRange()) {
+ *     ...
+ *     sleep(backoff.getNextBackoff());
+ *   }
+ * 
+ * + *

Note: the base multiplier can be calculated using {@code ExponentialBaseCalculator} + */ +public final class ExponentialBackoff { + public final long initialDelayMillis; + public final double baseMultiplier; + public final int maximumBackoffs; + private double nextBackoff; + private int backoffCount; + + /** + * Setup an exponential backoff with an initial delay, a base multiplier and a maximum number of + * backoff steps. + * + * @throws IllegalArgumentException for negative argument values + */ + public ExponentialBackoff(long initialDelayMillis, double baseMultiplier, int maximumBackoffs) { + Assert.checkArgument(initialDelayMillis > 0); + Assert.checkArgument(baseMultiplier > 0); + Assert.checkArgument(maximumBackoffs > 0); + this.initialDelayMillis = initialDelayMillis; + this.baseMultiplier = baseMultiplier; + this.maximumBackoffs = maximumBackoffs; + reset(); + } + + /** + * @return the next backoff time in the exponential sequence. Specifically, if D is the initial + * delay, B is the base multiplier and N is the total number of backoffs, then the return + * values will be: D, D*B, D*B^2, ... D*B^(N-1), ... + */ + public long getNextBackoff() { + long backoff = Math.round(nextBackoff); + backoffCount++; + nextBackoff *= baseMultiplier; + return backoff; + } + + /** @return the number of times getNextBackoff() has been called */ + public int getBackoffCount() { + return backoffCount; + } + + /** + * @return {@code true} if getNextBackoff() has been called less than the maximumBackoffs value + * specified in the constructor. + */ + public boolean isInRange() { + return backoffCount < maximumBackoffs; + } + + /** + * Reset the sequence of backoff values so the next call to getNextBackoff() will return the + * initial delay and getBackoffCount() will return 0 + */ + public void reset() { + nextBackoff = initialDelayMillis; + backoffCount = 0; + } +} diff --git a/java/com/android/dialer/common/backoff/ExponentialBaseCalculator.java b/java/com/android/dialer/common/backoff/ExponentialBaseCalculator.java new file mode 100644 index 000000000..a1ae8a053 --- /dev/null +++ b/java/com/android/dialer/common/backoff/ExponentialBaseCalculator.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2017 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.common.backoff; + +import com.android.dialer.common.Assert; + +/** + * Given an initial delay, D, a maximum total backoff time, T, and a maximum number of backoffs, N, + * this class calculates a base multiplier, b, and a scaling factor, g, such that the initial + * backoff is g*b = D and the sum of the scaled backoffs is T = g*b + g*b^2 + g*b^3 + ... g*b^N + * + *

T/D = (1 - b^N)/(1 - b) but this cannot be written as a simple equation for b in terms of T, N + * and D so instead use Newton's method (https://en.wikipedia.org/wiki/Newton%27s_method) to find an + * approximate value for b. + * + *

Example usage using the {@code ExponentialBackoff} would be: + * + *

+ *   // Retry with exponential backoff for up to 2 minutes, with an initial delay of 100 millis
+ *   // and a maximum of 10 retries
+ *   long initialDelayMillis = 100;
+ *   int maxTries = 10;
+ *   double base = ExponentialBaseCalculator.findBase(
+ *       initialDelayMillis,
+ *       TimeUnit.MINUTES.toMillis(2),
+ *       maxTries);
+ *   ExponentialBackoff backoff = new ExponentialBackoff(initialDelayMillis, base, maxTries);
+ *   while (backoff.isInRange()) {
+ *     ...
+ *     long delay = backoff.getNextBackoff();
+ *     // Wait for the indicated time...
+ *   }
+ * 
+ */ +public final class ExponentialBaseCalculator { + private static final int MAX_STEPS = 1000; + private static final double DEFAULT_TOLERANCE_MILLIS = 1; + + /** + * Calculate an exponential backoff base multiplier such that the first backoff delay will be as + * specified and the sum of the delays after doing the indicated maximum number of backoffs will + * be as specified. + * + * @throws IllegalArgumentException if the initial delay is greater than the total backoff time + * @throws IllegalArgumentException if the maximum number of backoffs is not greater than 1 + * @throws IllegalStateException if it fails to find an acceptable base multiplier + */ + public static double findBase( + long initialDelayMillis, long totalBackoffTimeMillis, int maximumBackoffs) { + Assert.checkArgument(initialDelayMillis < totalBackoffTimeMillis); + Assert.checkArgument(maximumBackoffs > 1); + long scaledTotalTime = Math.round(((double) totalBackoffTimeMillis) / initialDelayMillis); + double scaledTolerance = DEFAULT_TOLERANCE_MILLIS / initialDelayMillis; + return getBaseImpl(scaledTotalTime, maximumBackoffs, scaledTolerance); + } + + /** + * T/D = (1 - b^N)/(1 - b) but this cannot be written as a simple equation for b in terms of T, D + * and N so instead we use Newtons method to find an approximate value for b. + * + *

Let f(b) = (1 - b^N)/(1 - b) - T/D then we want to find b* such that f(b*) = 0, or more + * precisely |f(b*)| < tolerance + * + *

Using Newton's method we can interatively find b* as follows: b1 = b0 - f(b0)/f'(b0), where + * b0 is the current best guess for b* and b1 is the next best guess. + * + *

f'(b) = (f(b) + T/D - N*b^(N - 1))/(1 - b) + * + *

so + * + *

b1 = b0 - f(b0)(1 - b0)/(f(b0) + T/D - N*b0^(N - 1)) + */ + private static double getBaseImpl(long t, int n, double tolerance) { + double b0 = 2; // Initial guess for b* + double b0n = Math.pow(b0, n); + double fb0 = f(b0, t, b0n); + if (Math.abs(fb0) < tolerance) { + // Initial guess was pretty good + return b0; + } + + for (int i = 0; i < MAX_STEPS; i++) { + double fpb0 = fp(b0, t, n, fb0, b0n); + double b1 = b0 - fb0 / fpb0; + double b1n = Math.pow(b1, n); + double fb1 = f(b1, t, b1n); + + if (Math.abs(fb1) < tolerance) { + // Found an acceptable value + return b1; + } + + b0 = b1; + b0n = b1n; + fb0 = fb1; + } + + throw new IllegalStateException("Failed to find base. Too many iterations."); + } + + // Evaluate f(b), the function we are trying to find the zero for. + // Note: passing b^N as a parameter so it only has to be calculated once + private static double f(double b, long t, double bn) { + return (1 - bn) / (1 - b) - t; + } + + // Evaluate f'(b), the derivative of the function we are trying to find the zero for. + // Note: passing f(b) and b^N as parameters for efficiency + private static double fp(double b, long t, int n, double fb, double bn) { + return (fb + t - n * bn / b) / (1 - b); + } +} -- cgit v1.2.3 From 7e1a0d155eecc8a407e11f91605db135911c8eb6 Mon Sep 17 00:00:00 2001 From: mdooley Date: Fri, 17 Nov 2017 11:02:48 -0800 Subject: Refactoring and adding TOS check before transcribing This cl just adds a check before starting a transcription job to verify that the user has accepted the appropriate TOS. Its a rather big cl just because it had to expose some TOS acceptance details that were previously encapsulated in the VoicemailTosMessageCreator. Bug: 69267260 Test: manual and unit tests PiperOrigin-RevId: 176132370 Change-Id: Ic28fb197a07f9df7a9b5f1729c84566cba0890fc --- .../dialer/app/voicemail/error/AndroidManifest.xml | 8 -- .../voicemail/error/PackageReplacedReceiver.java | 109 --------------------- .../error/VoicemailTosMessageCreator.java | 46 ++++----- java/com/android/voicemail/VoicemailClient.java | 2 + .../voicemail/VoicemailVersionConstants.java | 38 +++++++ .../voicemail/impl/PackageReplacedReceiver.java | 84 +++++++++++++++- .../voicemail/impl/VoicemailClientImpl.java | 18 ++++ .../impl/transcribe/TranscriptionService.java | 30 +++--- .../voicemail/stub/StubVoicemailClient.java | 5 + 9 files changed, 183 insertions(+), 157 deletions(-) delete mode 100644 java/com/android/dialer/app/voicemail/error/PackageReplacedReceiver.java create mode 100644 java/com/android/voicemail/VoicemailVersionConstants.java diff --git a/java/com/android/dialer/app/voicemail/error/AndroidManifest.xml b/java/com/android/dialer/app/voicemail/error/AndroidManifest.xml index bb6c55f5c..07cb77f58 100644 --- a/java/com/android/dialer/app/voicemail/error/AndroidManifest.xml +++ b/java/com/android/dialer/app/voicemail/error/AndroidManifest.xml @@ -18,12 +18,4 @@ - - - - - - - - diff --git a/java/com/android/dialer/app/voicemail/error/PackageReplacedReceiver.java b/java/com/android/dialer/app/voicemail/error/PackageReplacedReceiver.java deleted file mode 100644 index 64d72b18f..000000000 --- a/java/com/android/dialer/app/voicemail/error/PackageReplacedReceiver.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2017 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.app.voicemail.error; - -import android.annotation.TargetApi; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.database.Cursor; -import android.net.Uri; -import android.preference.PreferenceManager; -import android.provider.CallLog.Calls; -import android.provider.VoicemailContract.Voicemails; -import com.android.dialer.common.LogUtil; -import com.android.dialer.common.concurrent.DialerExecutor.Worker; -import com.android.dialer.common.concurrent.DialerExecutorComponent; - -/** Receives MY_PACKAGE_REPLACED to check for legacy voicemail users. */ -public class PackageReplacedReceiver extends BroadcastReceiver { - - @Override - public void onReceive(Context context, Intent intent) { - LogUtil.enterBlock("PackageReplacedReceiver.onReceive"); - - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - if (!prefs.contains(VoicemailTosMessageCreator.PREF_DIALER_FEATURE_VERSION_ACKNOWLEDGED_KEY)) { - setVoicemailFeatureVersionAsync(context); - } - } - - private void setVoicemailFeatureVersionAsync(Context context) { - LogUtil.enterBlock("PackageReplacedReceiver.setVoicemailFeatureVersionAsync"); - - // Check if user is already using voicemail (ie do they have any voicemails), and set the - // acknowledged feature value accordingly. - PendingResult pendingResult = goAsync(); - DialerExecutorComponent.get(context) - .dialerExecutorFactory() - .createNonUiTaskBuilder(new ExistingVoicemailCheck(context)) - .onSuccess( - output -> { - LogUtil.i("PackageReplacedReceiver.setVoicemailFeatureVersionAsync", "success"); - pendingResult.finish(); - }) - .onFailure( - throwable -> { - LogUtil.i("PackageReplacedReceiver.setVoicemailFeatureVersionAsync", "failure"); - pendingResult.finish(); - }) - .build() - .executeParallel(null); - } - - private static class ExistingVoicemailCheck implements Worker { - private static final String[] PROJECTION = new String[] {Voicemails._ID}; - - private final Context context; - - ExistingVoicemailCheck(Context context) { - this.context = context; - } - - @TargetApi(android.os.Build.VERSION_CODES.M) // used for try with resources - @Override - public Void doInBackground(Void arg) throws Throwable { - LogUtil.i("PackageReplacedReceiver.ExistingVoicemailCheck.doInBackground", ""); - - // Check the database for existing voicemails. - boolean hasVoicemails = false; - Uri uri = Voicemails.buildSourceUri(context.getPackageName()); - String whereClause = Calls.TYPE + " = " + Calls.VOICEMAIL_TYPE; - try (Cursor cursor = - context.getContentResolver().query(uri, PROJECTION, whereClause, null, null)) { - if (cursor == null) { - LogUtil.e( - "PackageReplacedReceiver.ExistingVoicemailCheck.doInBackground", - "failed to check for existing voicemails"); - } else if (cursor.moveToNext()) { - hasVoicemails = true; - } - } - - LogUtil.i( - "PackageReplacedReceiver.ExistingVoicemailCheck.doInBackground", - "has voicemails: " + hasVoicemails); - int version = hasVoicemails ? VoicemailTosMessageCreator.LEGACY_VOICEMAIL_FEATURE_VERSION : 0; - PreferenceManager.getDefaultSharedPreferences(context) - .edit() - .putInt(VoicemailTosMessageCreator.PREF_DIALER_FEATURE_VERSION_ACKNOWLEDGED_KEY, version) - .apply(); - return null; - } - } -} diff --git a/java/com/android/dialer/app/voicemail/error/VoicemailTosMessageCreator.java b/java/com/android/dialer/app/voicemail/error/VoicemailTosMessageCreator.java index 3e4321309..b7c8ed721 100644 --- a/java/com/android/dialer/app/voicemail/error/VoicemailTosMessageCreator.java +++ b/java/com/android/dialer/app/voicemail/error/VoicemailTosMessageCreator.java @@ -48,6 +48,7 @@ import com.android.dialer.voicemail.settings.VoicemailSettingsFragment; import com.android.voicemail.VisualVoicemailTypeExtensions; import com.android.voicemail.VoicemailClient; import com.android.voicemail.VoicemailComponent; +import com.android.voicemail.VoicemailVersionConstants; import java.util.Locale; /** @@ -55,22 +56,6 @@ import java.util.Locale; * terms of service for Verizon and for other carriers. */ public class VoicemailTosMessageCreator { - // Preference key to check which version of the Verizon ToS that the user has accepted. - static final String PREF_VVM3_TOS_VERSION_ACCEPTED_KEY = "vvm3_tos_version_accepted"; - - // Preference key to check which version of the Google Dialer ToS that the user has accepted. - static final String PREF_DIALER_TOS_VERSION_ACCEPTED_KEY = "dialer_tos_version_accepted"; - - // Preference key to check which feature version the user has acknowledged - static final String PREF_DIALER_FEATURE_VERSION_ACKNOWLEDGED_KEY = - "dialer_feature_version_acknowledged"; - - static final int CURRENT_VVM3_TOS_VERSION = 2; - static final int CURRENT_DIALER_TOS_VERSION = 1; - static final int LEGACY_VOICEMAIL_FEATURE_VERSION = 1; // original visual voicemail - static final int TRANSCRIPTION_VOICEMAIL_FEATURE_VERSION = 2; // adds voicemail transcription - static final int CURRENT_VOICEMAIL_FEATURE_VERSION = TRANSCRIPTION_VOICEMAIL_FEATURE_VERSION; - private static final String ISO639_SPANISH = "es"; private final Context context; @@ -328,10 +313,11 @@ public class VoicemailTosMessageCreator { private boolean hasAcceptedTos() { if (isVvm3()) { - return preferences.getInt(PREF_VVM3_TOS_VERSION_ACCEPTED_KEY, 0) >= CURRENT_VVM3_TOS_VERSION; + return preferences.getInt(VoicemailVersionConstants.PREF_VVM3_TOS_VERSION_ACCEPTED_KEY, 0) + >= VoicemailVersionConstants.CURRENT_VVM3_TOS_VERSION; } else { - return preferences.getInt(PREF_DIALER_TOS_VERSION_ACCEPTED_KEY, 0) - >= CURRENT_DIALER_TOS_VERSION; + return preferences.getInt(VoicemailVersionConstants.PREF_DIALER_TOS_VERSION_ACCEPTED_KEY, 0) + >= VoicemailVersionConstants.CURRENT_DIALER_TOS_VERSION; } } @@ -339,12 +325,16 @@ public class VoicemailTosMessageCreator { if (isVvm3()) { preferences .edit() - .putInt(PREF_VVM3_TOS_VERSION_ACCEPTED_KEY, CURRENT_VVM3_TOS_VERSION) + .putInt( + VoicemailVersionConstants.PREF_VVM3_TOS_VERSION_ACCEPTED_KEY, + VoicemailVersionConstants.CURRENT_VVM3_TOS_VERSION) .apply(); } else { preferences .edit() - .putInt(PREF_DIALER_TOS_VERSION_ACCEPTED_KEY, CURRENT_DIALER_TOS_VERSION) + .putInt( + VoicemailVersionConstants.PREF_DIALER_TOS_VERSION_ACCEPTED_KEY, + VoicemailVersionConstants.CURRENT_DIALER_TOS_VERSION) .apply(); } @@ -360,20 +350,24 @@ public class VoicemailTosMessageCreator { return true; } - return preferences.getInt(PREF_DIALER_FEATURE_VERSION_ACKNOWLEDGED_KEY, 0) - >= CURRENT_VOICEMAIL_FEATURE_VERSION; + return preferences.getInt( + VoicemailVersionConstants.PREF_DIALER_FEATURE_VERSION_ACKNOWLEDGED_KEY, 0) + >= VoicemailVersionConstants.CURRENT_VOICEMAIL_FEATURE_VERSION; } private void recordFeatureAcknowledgement() { preferences .edit() - .putInt(PREF_DIALER_FEATURE_VERSION_ACKNOWLEDGED_KEY, CURRENT_VOICEMAIL_FEATURE_VERSION) + .putInt( + VoicemailVersionConstants.PREF_DIALER_FEATURE_VERSION_ACKNOWLEDGED_KEY, + VoicemailVersionConstants.CURRENT_VOICEMAIL_FEATURE_VERSION) .apply(); } private boolean isLegacyVoicemailUser() { - return preferences.getInt(PREF_DIALER_FEATURE_VERSION_ACKNOWLEDGED_KEY, 0) - == LEGACY_VOICEMAIL_FEATURE_VERSION; + return preferences.getInt( + VoicemailVersionConstants.PREF_DIALER_FEATURE_VERSION_ACKNOWLEDGED_KEY, 0) + == VoicemailVersionConstants.LEGACY_VOICEMAIL_FEATURE_VERSION; } private void logTosCreatedImpression() { diff --git a/java/com/android/voicemail/VoicemailClient.java b/java/com/android/voicemail/VoicemailClient.java index db5d74555..1ce7ef75a 100644 --- a/java/com/android/voicemail/VoicemailClient.java +++ b/java/com/android/voicemail/VoicemailClient.java @@ -179,6 +179,8 @@ public interface VoicemailClient { void onTosAccepted(Context context, PhoneAccountHandle phoneAccountHandle); + boolean hasAcceptedTos(Context context, PhoneAccountHandle phoneAccountHandle); + /** * @return arbitrary carrier configuration String value associate with the indicated key. See * {@code CarrierConfigKeys.java} diff --git a/java/com/android/voicemail/VoicemailVersionConstants.java b/java/com/android/voicemail/VoicemailVersionConstants.java new file mode 100644 index 000000000..44ef661d7 --- /dev/null +++ b/java/com/android/voicemail/VoicemailVersionConstants.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2017 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.voicemail; + +/** + * Shared preference keys and values relating to the voicemail version that the user has accepted. + * Note: these can be carrier dependent. + */ +public interface VoicemailVersionConstants { + // Preference key to check which version of the Verizon ToS that the user has accepted. + String PREF_VVM3_TOS_VERSION_ACCEPTED_KEY = "vvm3_tos_version_accepted"; + + // Preference key to check which version of the Google Dialer ToS that the user has accepted. + String PREF_DIALER_TOS_VERSION_ACCEPTED_KEY = "dialer_tos_version_accepted"; + + // Preference key to check which feature version the user has acknowledged + String PREF_DIALER_FEATURE_VERSION_ACKNOWLEDGED_KEY = "dialer_feature_version_acknowledged"; + + int CURRENT_VVM3_TOS_VERSION = 2; + int CURRENT_DIALER_TOS_VERSION = 1; + int LEGACY_VOICEMAIL_FEATURE_VERSION = 1; // original visual voicemail + int TRANSCRIPTION_VOICEMAIL_FEATURE_VERSION = 2; + int CURRENT_VOICEMAIL_FEATURE_VERSION = TRANSCRIPTION_VOICEMAIL_FEATURE_VERSION; +} diff --git a/java/com/android/voicemail/impl/PackageReplacedReceiver.java b/java/com/android/voicemail/impl/PackageReplacedReceiver.java index 6a7ca4a7b..bc56286fb 100644 --- a/java/com/android/voicemail/impl/PackageReplacedReceiver.java +++ b/java/com/android/voicemail/impl/PackageReplacedReceiver.java @@ -16,14 +16,27 @@ package com.android.voicemail.impl; +import android.annotation.TargetApi; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.net.Uri; +import android.preference.PreferenceManager; +import android.provider.CallLog.Calls; +import android.provider.VoicemailContract.Voicemails; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; +import com.android.dialer.common.LogUtil; +import com.android.dialer.common.concurrent.DialerExecutor.Worker; +import com.android.dialer.common.concurrent.DialerExecutorComponent; import com.android.voicemail.VoicemailComponent; +import com.android.voicemail.VoicemailVersionConstants; -/** Receives MY_PACKAGE_REPLACED to trigger VVM activation. */ +/** + * Receives MY_PACKAGE_REPLACED to trigger VVM activation and to check for legacy voicemail users. + */ public class PackageReplacedReceiver extends BroadcastReceiver { @Override @@ -39,5 +52,74 @@ public class PackageReplacedReceiver extends BroadcastReceiver { context.getSystemService(TelecomManager.class).getCallCapablePhoneAccounts()) { ActivationTask.start(context, phoneAccountHandle, null); } + + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + if (!prefs.contains(VoicemailVersionConstants.PREF_DIALER_FEATURE_VERSION_ACKNOWLEDGED_KEY)) { + setVoicemailFeatureVersionAsync(context); + } + } + + private void setVoicemailFeatureVersionAsync(Context context) { + LogUtil.enterBlock("PackageReplacedReceiver.setVoicemailFeatureVersionAsync"); + + // Check if user is already using voicemail (ie do they have any voicemails), and set the + // acknowledged feature value accordingly. + PendingResult pendingResult = goAsync(); + DialerExecutorComponent.get(context) + .dialerExecutorFactory() + .createNonUiTaskBuilder(new ExistingVoicemailCheck(context)) + .onSuccess( + output -> { + LogUtil.i("PackageReplacedReceiver.setVoicemailFeatureVersionAsync", "success"); + pendingResult.finish(); + }) + .onFailure( + throwable -> { + LogUtil.i("PackageReplacedReceiver.setVoicemailFeatureVersionAsync", "failure"); + pendingResult.finish(); + }) + .build() + .executeParallel(null); + } + + private static class ExistingVoicemailCheck implements Worker { + private static final String[] PROJECTION = new String[] {Voicemails._ID}; + + private final Context context; + + ExistingVoicemailCheck(Context context) { + this.context = context; + } + + @TargetApi(android.os.Build.VERSION_CODES.M) // used for try with resources + @Override + public Void doInBackground(Void arg) throws Throwable { + LogUtil.i("PackageReplacedReceiver.ExistingVoicemailCheck.doInBackground", ""); + + // Check the database for existing voicemails. + boolean hasVoicemails = false; + Uri uri = Voicemails.buildSourceUri(context.getPackageName()); + String whereClause = Calls.TYPE + " = " + Calls.VOICEMAIL_TYPE; + try (Cursor cursor = + context.getContentResolver().query(uri, PROJECTION, whereClause, null, null)) { + if (cursor == null) { + LogUtil.e( + "PackageReplacedReceiver.ExistingVoicemailCheck.doInBackground", + "failed to check for existing voicemails"); + } else if (cursor.moveToFirst()) { + hasVoicemails = true; + } + } + + LogUtil.i( + "PackageReplacedReceiver.ExistingVoicemailCheck.doInBackground", + "has voicemails: " + hasVoicemails); + int version = hasVoicemails ? VoicemailVersionConstants.LEGACY_VOICEMAIL_FEATURE_VERSION : 0; + PreferenceManager.getDefaultSharedPreferences(context) + .edit() + .putInt(VoicemailVersionConstants.PREF_DIALER_FEATURE_VERSION_ACKNOWLEDGED_KEY, version) + .apply(); + return null; + } } } diff --git a/java/com/android/voicemail/impl/VoicemailClientImpl.java b/java/com/android/voicemail/impl/VoicemailClientImpl.java index 330543837..60fc80692 100644 --- a/java/com/android/voicemail/impl/VoicemailClientImpl.java +++ b/java/com/android/voicemail/impl/VoicemailClientImpl.java @@ -16,8 +16,10 @@ package com.android.voicemail.impl; import android.annotation.TargetApi; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.os.Build.VERSION_CODES; import android.os.PersistableBundle; +import android.preference.PreferenceManager; import android.provider.VoicemailContract.Status; import android.provider.VoicemailContract.Voicemails; import android.support.annotation.MainThread; @@ -32,6 +34,7 @@ import com.android.dialer.configprovider.ConfigProviderBindings; import com.android.voicemail.PinChanger; import com.android.voicemail.VisualVoicemailTypeExtensions; import com.android.voicemail.VoicemailClient; +import com.android.voicemail.VoicemailVersionConstants; import com.android.voicemail.impl.configui.VoicemailSecretCodeActivity; import com.android.voicemail.impl.settings.VisualVoicemailSettingsUtil; import com.android.voicemail.impl.sync.VvmAccountManager; @@ -291,6 +294,21 @@ public class VoicemailClientImpl implements VoicemailClient { TranscriptionBackfillService.scheduleTask(context, account); } + @Override + public boolean hasAcceptedTos(Context context, PhoneAccountHandle phoneAccountHandle) { + SharedPreferences preferences = + PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()); + OmtpVvmCarrierConfigHelper helper = new OmtpVvmCarrierConfigHelper(context, phoneAccountHandle); + boolean isVvm3 = VisualVoicemailTypeExtensions.VVM_TYPE_VVM3.equals(helper.getVvmType()); + if (isVvm3) { + return preferences.getInt(VoicemailVersionConstants.PREF_VVM3_TOS_VERSION_ACCEPTED_KEY, 0) + >= VoicemailVersionConstants.CURRENT_VVM3_TOS_VERSION; + } else { + return preferences.getInt(VoicemailVersionConstants.PREF_DIALER_TOS_VERSION_ACCEPTED_KEY, 0) + >= VoicemailVersionConstants.CURRENT_DIALER_TOS_VERSION; + } + } + @Override @Nullable public String getCarrierConfigString(Context context, PhoneAccountHandle account, String key) { diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionService.java b/java/com/android/voicemail/impl/transcribe/TranscriptionService.java index 33c9676a4..a19ab6208 100644 --- a/java/com/android/voicemail/impl/transcribe/TranscriptionService.java +++ b/java/com/android/voicemail/impl/transcribe/TranscriptionService.java @@ -69,15 +69,7 @@ public class TranscriptionService extends JobService { public static boolean scheduleNewVoicemailTranscriptionJob( Context context, Uri voicemailUri, PhoneAccountHandle account, boolean highPriority) { Assert.isMainThread(); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { - LogUtil.i( - "TranscriptionService.scheduleNewVoicemailTranscriptionJob", "not supported by sdk"); - return false; - } - if (!carrierAllowsOttTranscription(context, account)) { - LogUtil.i( - "TranscriptionService.scheduleNewVoicemailTranscriptionJob", - "carrier doesn't allow transcription"); + if (!canTranscribeVoicemail(context, account)) { return false; } @@ -101,12 +93,24 @@ public class TranscriptionService extends JobService { return scheduler.enqueue(builder.build(), workItem) == JobScheduler.RESULT_SUCCESS; } - private static boolean carrierAllowsOttTranscription( - Context context, PhoneAccountHandle account) { + private static boolean canTranscribeVoicemail(Context context, PhoneAccountHandle account) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + LogUtil.i("TranscriptionService.canTranscribeVoicemail", "not supported by sdk"); + return false; + } VoicemailClient client = VoicemailComponent.get(context).getVoicemailClient(); - return Boolean.parseBoolean( + if (!client.hasAcceptedTos(context, account)) { + LogUtil.i("TranscriptionService.canTranscribeVoicemail", "hasn't accepted TOS"); + return false; + } + if (!Boolean.parseBoolean( client.getCarrierConfigString( - context, account, CarrierConfigKeys.VVM_CARRIER_ALLOWS_OTT_TRANSCRIPTION_STRING)); + context, account, CarrierConfigKeys.VVM_CARRIER_ALLOWS_OTT_TRANSCRIPTION_STRING))) { + LogUtil.i( + "TranscriptionService.canTranscribeVoicemail", "carrier doesn't allow transcription"); + return false; + } + return true; } // Cancel all transcription tasks diff --git a/java/com/android/voicemail/stub/StubVoicemailClient.java b/java/com/android/voicemail/stub/StubVoicemailClient.java index cfbb3ec00..3069ea4ba 100644 --- a/java/com/android/voicemail/stub/StubVoicemailClient.java +++ b/java/com/android/voicemail/stub/StubVoicemailClient.java @@ -132,6 +132,11 @@ public final class StubVoicemailClient implements VoicemailClient { @Override public void onTosAccepted(Context context, PhoneAccountHandle account) {} + @Override + public boolean hasAcceptedTos(Context context, PhoneAccountHandle phoneAccountHandle) { + return false; + } + @Override @Nullable public String getCarrierConfigString(Context context, PhoneAccountHandle account, String key) { -- cgit v1.2.3 From a430bba3873f556ba895b172a32048030fa7f4d0 Mon Sep 17 00:00:00 2001 From: zachh Date: Fri, 17 Nov 2017 12:50:52 -0800 Subject: Automated rollback of changelist 174944384 Bug: 68942044 Test: NO PiperOrigin-RevId: 176147553 Change-Id: Ia22751f02cdd621c2095e8e476a3077db2c541fa --- .../dialer/searchfragment/list/SearchAdapter.java | 36 ---------------------- .../remote/RemoteDirectoriesCursorLoader.java | 17 ++-------- 2 files changed, 2 insertions(+), 51 deletions(-) diff --git a/java/com/android/dialer/searchfragment/list/SearchAdapter.java b/java/com/android/dialer/searchfragment/list/SearchAdapter.java index 4254baece..17cab6db1 100644 --- a/java/com/android/dialer/searchfragment/list/SearchAdapter.java +++ b/java/com/android/dialer/searchfragment/list/SearchAdapter.java @@ -17,7 +17,6 @@ package com.android.dialer.searchfragment.list; import android.content.Context; -import android.database.Cursor; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; import android.support.v7.widget.RecyclerView; @@ -28,15 +27,12 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import com.android.dialer.common.Assert; -import com.android.dialer.common.LogUtil; -import com.android.dialer.searchfragment.common.Projections; import com.android.dialer.searchfragment.common.RowClickListener; import com.android.dialer.searchfragment.common.SearchCursor; import com.android.dialer.searchfragment.cp2.SearchContactViewHolder; import com.android.dialer.searchfragment.list.SearchCursorManager.RowType; import com.android.dialer.searchfragment.nearbyplaces.NearbyPlaceViewHolder; import com.android.dialer.searchfragment.remote.RemoteContactViewHolder; -import com.android.dialer.searchfragment.remote.RemoteDirectoriesCursorLoader; import java.util.List; /** RecyclerView adapter for {@link NewSearchFragment}. */ @@ -107,23 +103,6 @@ public final class SearchAdapter extends RecyclerView.Adapter { } else if (holder instanceof NearbyPlaceViewHolder) { ((NearbyPlaceViewHolder) holder).bind(searchCursorManager.getCursor(position), query); } else if (holder instanceof RemoteContactViewHolder) { - Cursor cursor = searchCursorManager.getCursor(position); - // Temporary logging to identify cause of a bug: - if (cursor.getString(Projections.PHONE_NUMBER) == null) { - LogUtil.e( - "SearchAdapter.onBindViewHolder", "cursor class: %s", cursor.getClass().getName()); - LogUtil.e("SearchAdapter.onBindViewHolder", "position: %d", position); - LogUtil.e( - "SearchAdapter.onBindViewHolder", - "query length: %s", - query == null ? "null" : query.length()); - logDirectories(); - LogUtil.e( - "SearchAdapter.onBindViewHolder", - "directory id: %d", - ((SearchCursor) cursor).getDirectoryId()); - throw new IllegalStateException("Null phone number reading remote contact"); - } ((RemoteContactViewHolder) holder).bind(searchCursorManager.getCursor(position), query); } else if (holder instanceof HeaderViewHolder) { String header = @@ -142,21 +121,6 @@ public final class SearchAdapter extends RecyclerView.Adapter { } } - private void logDirectories() { - try (Cursor directories = new RemoteDirectoriesCursorLoader(context).loadInBackground()) { - if (directories.moveToFirst()) { - do { - LogUtil.e( - "SearchAdapter.logDirectories", - "directory: %s", - RemoteDirectoriesCursorLoader.readDirectory(directories)); - } while (directories.moveToNext()); - } else { - LogUtil.e("SearchAdapter.logDirectories", "no directories found"); - } - } - } - public void setContactsCursor(SearchCursor cursor) { if (searchCursorManager.setContactsCursor(cursor)) { // Since this is a new contacts cursor, we need to reapply the filter. diff --git a/java/com/android/dialer/searchfragment/remote/RemoteDirectoriesCursorLoader.java b/java/com/android/dialer/searchfragment/remote/RemoteDirectoriesCursorLoader.java index 3d16c4351..de71025cd 100644 --- a/java/com/android/dialer/searchfragment/remote/RemoteDirectoriesCursorLoader.java +++ b/java/com/android/dialer/searchfragment/remote/RemoteDirectoriesCursorLoader.java @@ -36,14 +36,12 @@ public final class RemoteDirectoriesCursorLoader extends CursorLoader { private static final int DISPLAY_NAME = 1; private static final int PHOTO_SUPPORT = 2; - private static final int AUTHORITY = 3; @VisibleForTesting static final String[] PROJECTION = { ContactsContract.Directory._ID, ContactsContract.Directory.DISPLAY_NAME, ContactsContract.Directory.PHOTO_SUPPORT, - ContactsContract.Directory.DIRECTORY_AUTHORITY, }; public RemoteDirectoriesCursorLoader(Context context) { @@ -53,10 +51,7 @@ public final class RemoteDirectoriesCursorLoader extends CursorLoader { /** @return current cursor row represented as a {@link Directory}. */ public static Directory readDirectory(Cursor cursor) { return Directory.create( - cursor.getInt(ID), - cursor.getString(DISPLAY_NAME), - cursor.getInt(PHOTO_SUPPORT) != 0, - cursor.getString(AUTHORITY)); + cursor.getInt(ID), cursor.getString(DISPLAY_NAME), cursor.getInt(PHOTO_SUPPORT) != 0); } private static Uri getContentUri() { @@ -68,14 +63,8 @@ public final class RemoteDirectoriesCursorLoader extends CursorLoader { /** POJO representing the results returned from {@link RemoteDirectoriesCursorLoader}. */ @AutoValue public abstract static class Directory { - public static Directory create( - int id, @Nullable String displayName, boolean supportsPhotos, @Nullable String authority) { - return new AutoValue_RemoteDirectoriesCursorLoader_Directory( - id, displayName, supportsPhotos, authority); - } - public static Directory create(int id, @Nullable String displayName, boolean supportsPhotos) { - return create(id, displayName, supportsPhotos, null); + return new AutoValue_RemoteDirectoriesCursorLoader_Directory(id, displayName, supportsPhotos); } public abstract int getId(); @@ -84,7 +73,5 @@ public final class RemoteDirectoriesCursorLoader extends CursorLoader { abstract @Nullable String getDisplayName(); abstract boolean supportsPhotos(); - - abstract @Nullable String authority(); } } -- cgit v1.2.3 From 6b96d28ddf93d87ad1253e633771946ea770dfb3 Mon Sep 17 00:00:00 2001 From: Android Dialer Date: Fri, 17 Nov 2017 13:56:31 -0800 Subject: Add contact source options. Bug: 66900825 Test: manual PiperOrigin-RevId: 176155772 Change-Id: Ibe722477b90671b55ff7bf5405de9fb66569d43e --- .../dialer/logging/contact_lookup_result.proto | 35 ++++++++++++++++++++++ .../android/dialer/logging/contact_source.proto | 17 +++++++++++ 2 files changed, 52 insertions(+) diff --git a/java/com/android/dialer/logging/contact_lookup_result.proto b/java/com/android/dialer/logging/contact_lookup_result.proto index 6c83908b9..673ade760 100644 --- a/java/com/android/dialer/logging/contact_lookup_result.proto +++ b/java/com/android/dialer/logging/contact_lookup_result.proto @@ -65,5 +65,40 @@ message ContactLookupResult { // Number was found in Dialer's local cache and was originally identified // via Cequint caller ID. LOCAL_CACHE_CEQUINT = 15; + + // Number was identified by a remote data source not listed below + REMOTE_OTHER = 16; + + // Number was found in Dialer's local cache and was originally identified + // as REMOTE_OTHER + LOCAL_CACHE_REMOTE_OTHER = 17; + + // Number was identified by manually-entered caller ID data + REMOTE_MANUAL = 18; + + // Number was found in Dialer's local cache and was originally identified + // as REMOTE_MANUAL + LOCAL_CACHE_REMOTE_MANUAL = 19; + + // Number was identified by Google Voice data + REMOTE_GOOGLE_VOICE = 20; + + // Number was found in Dialer's local cache and was originally identified + // as REMOTE_GOOGLE_VOICE + LOCAL_CACHE_REMOTE_GOOGLE_VOICE = 21; + + // Number was identified by Customer Service Apps data + REMOTE_CSA = 22; + + // Number was found in Dialer's local cache and was originally identified + // as REMOTE_CSA + LOCAL_CACHE_REMOTE_CSA = 23; + + // Number was identified by Knowledge Graph data + REMOTE_KNOWLEDGE_GRAPH = 24; + + // Number was found in Dialer's local cache and was originally identified + // as REMOTE_KNOWLEDGE_GRAPH + LOCAL_CACHE_REMOTE_KNOWLEDGE_GRAPH = 25; } } diff --git a/java/com/android/dialer/logging/contact_source.proto b/java/com/android/dialer/logging/contact_source.proto index 96ef9e18e..7b90730ab 100644 --- a/java/com/android/dialer/logging/contact_source.proto +++ b/java/com/android/dialer/logging/contact_source.proto @@ -16,6 +16,8 @@ message ContactSource { // number's status at the time they made or received the call. // Type definitions are from the CachedContactInfo interface in // CachedNumberLookupService.java + // When adding new sources here, consider updating the isPeopleApiSource() and + // isBusiness() methods in LookupSourceUtils.java if needed. enum Type { UNKNOWN_SOURCE_TYPE = 0; @@ -36,5 +38,20 @@ message ContactSource { SOURCE_TYPE_CNAP = 5; SOURCE_TYPE_CEQUINT_CALLER_ID = 6; + + // A remote source not listed below + SOURCE_TYPE_REMOTE_OTHER = 7; + + // Manually-entered caller ID data + SOURCE_TYPE_REMOTE_MANUAL = 8; + + // Google Voice short code data + SOURCE_TYPE_REMOTE_GOOGLE_VOICE = 9; + + // Customer Service Applications data + SOURCE_TYPE_REMOTE_CSA = 10; + + // Knowledge Graph data + SOURCE_TYPE_REMOTE_KNOWLEDGE_GRAPH = 11; } } -- cgit v1.2.3 From 57b093b4815afd2d169403c56be1702fae25ad51 Mon Sep 17 00:00:00 2001 From: linyuh Date: Fri, 17 Nov 2017 14:32:32 -0800 Subject: Merge the following methods in InCallActivityCommon into InCallActivity: 01. handleDialerKeyDown(int, KeyEvent), 02. onBackPressed(boolean), 03. onDestroy(), 04. onKeyDown(int, KeyEvent), 05. onKeyUp(int, KeyEvent), 06. onPause(), 07. onSaveInstanceState(Bundle), 08. onStart(), 09. onStop(), 10. updateNavigationBar(boolean), and 11. updateTaskDescription(). Note that InCallActivityCommon#handleDialerKeyDown(int, KeyEvent) is merged into InCallActivity#onKeyDown(int, KeyEvent). This is part of the effort to delete InCallActivityCommon. Bug: 69272096 Test: None PiperOrigin-RevId: 176161247 Change-Id: I6e74c7feec5414fb4bc58814eef3bd297d6eb96f --- java/com/android/incallui/InCallActivity.java | 217 +++++++++++++++++--- .../com/android/incallui/InCallActivityCommon.java | 226 +-------------------- 2 files changed, 197 insertions(+), 246 deletions(-) diff --git a/java/com/android/incallui/InCallActivity.java b/java/com/android/incallui/InCallActivity.java index ed10ed0bb..1e5a5fc02 100644 --- a/java/com/android/incallui/InCallActivity.java +++ b/java/com/android/incallui/InCallActivity.java @@ -16,8 +16,10 @@ package com.android.incallui; +import android.app.ActivityManager.TaskDescription; import android.app.AlertDialog; import android.app.Dialog; +import android.app.KeyguardManager; import android.content.Context; import android.content.Intent; import android.graphics.drawable.GradientDrawable; @@ -30,6 +32,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; +import android.support.v4.content.res.ResourcesCompat; import android.support.v4.graphics.ColorUtils; import android.telephony.TelephonyManager; import android.view.KeyEvent; @@ -54,9 +57,11 @@ import com.android.incallui.answer.protocol.AnswerScreen; import com.android.incallui.answer.protocol.AnswerScreenDelegate; import com.android.incallui.answer.protocol.AnswerScreenDelegateFactory; import com.android.incallui.answerproximitysensor.PseudoScreenState; +import com.android.incallui.audiomode.AudioModeProvider; import com.android.incallui.call.CallList; import com.android.incallui.call.DialerCall; import com.android.incallui.call.DialerCall.State; +import com.android.incallui.call.TelecomAdapter; import com.android.incallui.callpending.CallPendingActivity; import com.android.incallui.disconnectdialog.DisconnectMessage; import com.android.incallui.incall.bindings.InCallBindings; @@ -84,6 +89,10 @@ public class InCallActivity extends TransactionSafeFragmentActivity public static final int PENDING_INTENT_REQUEST_CODE_FULL_SCREEN = 1; public static final int PENDING_INTENT_REQUEST_CODE_BUBBLE = 2; + private static final String DIALPAD_TEXT_KEY = "InCallActivity.dialpad_text"; + + private static final String INTENT_EXTRA_SHOW_DIALPAD = "InCallActivity.show_dialpad"; + private static final String TAG_ANSWER_SCREEN = "tag_answer_screen"; private static final String TAG_DIALPAD_FRAGMENT = "tag_dialpad_fragment"; private static final String TAG_INTERNATIONAL_CALL_ON_WIFI = "tag_international_call_on_wifi"; @@ -168,11 +177,19 @@ public class InCallActivity extends TransactionSafeFragmentActivity @Override protected void onSaveInstanceState(Bundle out) { - LogUtil.i("InCallActivity.onSaveInstanceState", ""); - common.onSaveInstanceState(out); + LogUtil.enterBlock("InCallActivity.onSaveInstanceState"); + + // TODO: DialpadFragment should handle this as part of its own state + out.putBoolean(INTENT_EXTRA_SHOW_DIALPAD, isDialpadVisible()); + DialpadFragment dialpadFragment = getDialpadFragment(); + if (dialpadFragment != null) { + out.putString(DIALPAD_TEXT_KEY, dialpadFragment.getDtmfText()); + } + out.putBoolean(DID_SHOW_ANSWER_SCREEN_KEY, didShowAnswerScreen); out.putBoolean(DID_SHOW_IN_CALL_SCREEN_KEY, didShowInCallScreen); out.putBoolean(DID_SHOW_VIDEO_CALL_SCREEN_KEY, didShowVideoCallScreen); + super.onSaveInstanceState(out); isVisible = false; } @@ -180,18 +197,23 @@ public class InCallActivity extends TransactionSafeFragmentActivity @Override protected void onStart() { Trace.beginSection("InCallActivity.onStart"); - LogUtil.i("InCallActivity.onStart", ""); - Trace.beginSection("call super"); super.onStart(); - Trace.endSection(); + isVisible = true; showMainInCallFragment(); - common.onStart(); + + InCallPresenter.getInstance().setActivity(this); + enableInCallOrientationEventListener( + getRequestedOrientation() + == InCallOrientationEventListener.ACTIVITY_PREFERENCE_ALLOW_ROTATION); + InCallPresenter.getInstance().onActivityStarted(); + if (ActivityCompat.isInMultiWindowMode(this) && !getResources().getBoolean(R.bool.incall_dialpad_allowed)) { // Hide the dialpad because there may not be enough room showDialpadFragment(false, false); } + Trace.endSection(); } @@ -213,13 +235,21 @@ public class InCallActivity extends TransactionSafeFragmentActivity 1000); } - /** onPause is guaranteed to be called when the InCallActivity goes in the background. */ @Override protected void onPause() { Trace.beginSection("InCallActivity.onPause"); - LogUtil.i("InCallActivity.onPause", ""); super.onPause(); - common.onPause(); + + DialpadFragment dialpadFragment = getDialpadFragment(); + if (dialpadFragment != null) { + dialpadFragment.onDialerKeyUp(null); + } + + InCallPresenter.getInstance().onUiShowing(false); + if (isFinishing()) { + InCallPresenter.getInstance().unsetActivity(this); + } + InCallPresenter.getInstance().getPseudoScreenState().removeListener(this); Trace.endSection(); } @@ -227,19 +257,42 @@ public class InCallActivity extends TransactionSafeFragmentActivity @Override protected void onStop() { Trace.beginSection("InCallActivity.onStop"); - LogUtil.i("InCallActivity.onStop", ""); isVisible = false; super.onStop(); - common.onStop(); + + // Disconnects the call waiting for a phone account when the activity is hidden (e.g., after the + // user presses the home button). + // Without this the pending call will get stuck on phone account selection and new calls can't + // be created. + // Skip this when the screen is locked since the activity may complete its current life cycle + // and restart. + if (!common.getIsRecreating() && !getSystemService(KeyguardManager.class).isKeyguardLocked()) { + DialerCall waitingForAccountCall = CallList.getInstance().getWaitingForAccountCall(); + if (waitingForAccountCall != null) { + waitingForAccountCall.disconnect(); + } + } + + enableInCallOrientationEventListener(false); + InCallPresenter.getInstance().updateIsChangingConfigurations(); + InCallPresenter.getInstance().onActivityStopped(); + if (!common.getIsRecreating()) { + Dialog errorDialog = common.getErrorDialog(); + if (errorDialog != null) { + errorDialog.dismiss(); + } + } + Trace.endSection(); } @Override protected void onDestroy() { Trace.beginSection("InCallActivity.onDestroy"); - LogUtil.i("InCallActivity.onDestroy", ""); super.onDestroy(); - common.onDestroy(); + + InCallPresenter.getInstance().unsetActivity(this); + InCallPresenter.getInstance().updateIsChangingConfigurations(); Trace.endSection(); } @@ -306,10 +359,31 @@ public class InCallActivity extends TransactionSafeFragmentActivity @Override public void onBackPressed() { - LogUtil.i("InCallActivity.onBackPressed", ""); - if (!common.onBackPressed(didShowInCallScreen || didShowVideoCallScreen)) { - super.onBackPressed(); + LogUtil.enterBlock("InCallActivity.onBackPressed"); + + if (!isVisible()) { + return; + } + + if (!getCallCardFragmentVisible()) { + return; + } + + DialpadFragment dialpadFragment = getDialpadFragment(); + if (dialpadFragment != null && dialpadFragment.isVisible()) { + showDialpadFragment(false /* show */, true /* animate */); + return; } + + if (CallList.getInstance().getIncomingCall() != null) { + LogUtil.i( + "InCallActivity.onBackPressed", + "Ignore the press of the back key when an incoming call is ringing"); + return; + } + + // Nothing special to do. Fall back to the default behavior. + super.onBackPressed(); } @Override @@ -324,12 +398,79 @@ public class InCallActivity extends TransactionSafeFragmentActivity @Override public boolean onKeyUp(int keyCode, KeyEvent event) { - return common.onKeyUp(keyCode, event) || super.onKeyUp(keyCode, event); + DialpadFragment dialpadFragment = getDialpadFragment(); + if (dialpadFragment != null + && dialpadFragment.isVisible() + && dialpadFragment.onDialerKeyUp(event)) { + return true; + } + + if (keyCode == KeyEvent.KEYCODE_CALL) { + // Always consume KEYCODE_CALL to ensure the PhoneWindow won't do anything with it. + return true; + } + + return super.onKeyUp(keyCode, event); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { - return common.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event); + switch (keyCode) { + case KeyEvent.KEYCODE_CALL: + if (!InCallPresenter.getInstance().handleCallKey()) { + LogUtil.e( + "InCallActivity.onKeyDown", + "InCallPresenter should always handle KEYCODE_CALL in onKeyDown"); + } + // Always consume KEYCODE_CALL to ensure the PhoneWindow won't do anything with it. + return true; + + // Note that KEYCODE_ENDCALL isn't handled here as the standard system-wide handling of it + // is exactly what's needed, namely + // (1) "hang up" if there's an active call, or + // (2) "don't answer" if there's an incoming call. + // (See PhoneWindowManager for implementation details.) + + case KeyEvent.KEYCODE_CAMERA: + // Consume KEYCODE_CAMERA since it's easy to accidentally press the camera button. + return true; + + case KeyEvent.KEYCODE_VOLUME_UP: + case KeyEvent.KEYCODE_VOLUME_DOWN: + case KeyEvent.KEYCODE_VOLUME_MUTE: + // Ringer silencing handled by PhoneWindowManager. + break; + + case KeyEvent.KEYCODE_MUTE: + TelecomAdapter.getInstance() + .mute(!AudioModeProvider.getInstance().getAudioState().isMuted()); + return true; + + case KeyEvent.KEYCODE_SLASH: + // When verbose logging is enabled, dump the view for debugging/testing purposes. + if (LogUtil.isVerboseEnabled()) { + View decorView = getWindow().getDecorView(); + LogUtil.v("InCallActivity.onKeyDown", "View dump:\n%s", decorView); + return true; + } + break; + + case KeyEvent.KEYCODE_EQUALS: + break; + + default: // fall out + } + + // Pass other key events to DialpadFragment's "onDialerKeyDown" method in case the user types + // in DTMF (Dual-tone multi-frequency signaling) code. + DialpadFragment dialpadFragment = getDialpadFragment(); + if (dialpadFragment != null + && dialpadFragment.isVisible() + && dialpadFragment.onDialerKeyDown(event)) { + return true; + } + + return super.onKeyDown(keyCode, event); } public boolean isInCallScreenAnimating() { @@ -371,20 +512,26 @@ public class InCallActivity extends TransactionSafeFragmentActivity } public void onForegroundCallChanged(DialerCall newForegroundCall) { - common.updateTaskDescription(); - if (didShowAnswerScreen && newForegroundCall != null) { - if (newForegroundCall.getState() == State.DISCONNECTED - || newForegroundCall.getState() == State.IDLE) { - LogUtil.i( - "InCallActivity.onForegroundCallChanged", - "rejecting incoming call, not updating " + "window background color"); - } - } else { + updateTaskDescription(); + + if (newForegroundCall == null || !didShowAnswerScreen) { LogUtil.v("InCallActivity.onForegroundCallChanged", "resetting background color"); - updateWindowBackgroundColor(0); + updateWindowBackgroundColor(0 /* progress */); } } + // TODO(a bug): Make this method private after InCallActivityCommon is deleted. + void updateTaskDescription() { + int color = + getResources().getBoolean(R.bool.is_layout_landscape) + ? ResourcesCompat.getColor( + getResources(), R.color.statusbar_background_color, getTheme()) + : InCallPresenter.getInstance().getThemeColorManager().getSecondaryColor(); + setTaskDescription( + new TaskDescription( + getResources().getString(R.string.notification_ongoing_call), null /* icon */, color)); + } + public void updateWindowBackgroundColor(@FloatRange(from = -1f, to = 1.0f) float progress) { ThemeColorManager themeColorManager = InCallPresenter.getInstance().getThemeColorManager(); @ColorInt int top; @@ -680,8 +827,18 @@ public class InCallActivity extends TransactionSafeFragmentActivity @Override public void onMultiWindowModeChanged(boolean isInMultiWindowMode) { super.onMultiWindowModeChanged(isInMultiWindowMode); - if (!isInMultiWindowMode) { - common.updateNavigationBar(isDialpadVisible()); + updateNavigationBar(isDialpadVisible()); + } + + // TODO(a bug): Make this method private after InCallActivityCommon is deleted. + void updateNavigationBar(boolean isDialpadVisible) { + if (ActivityCompat.isInMultiWindowMode(this)) { + return; + } + + View navigationBarBackground = getWindow().findViewById(R.id.navigation_bar_background); + if (navigationBarBackground != null) { + navigationBarBackground.setVisibility(isDialpadVisible ? View.VISIBLE : View.GONE); } } diff --git a/java/com/android/incallui/InCallActivityCommon.java b/java/com/android/incallui/InCallActivityCommon.java index 8f82295ed..e8588a67a 100644 --- a/java/com/android/incallui/InCallActivityCommon.java +++ b/java/com/android/incallui/InCallActivityCommon.java @@ -18,12 +18,9 @@ package com.android.incallui; import android.app.ActivityManager; import android.app.ActivityManager.AppTask; -import android.app.ActivityManager.TaskDescription; import android.app.Dialog; -import android.app.KeyguardManager; import android.content.Intent; import android.content.res.Configuration; -import android.content.res.Resources; import android.os.Bundle; import android.os.Trace; import android.support.annotation.IntDef; @@ -33,12 +30,8 @@ import android.support.annotation.VisibleForTesting; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; -import android.support.v4.content.res.ResourcesCompat; import android.telecom.CallAudioState; import android.telecom.PhoneAccountHandle; -import android.view.KeyEvent; -import android.view.View; -import android.view.Window; import android.view.WindowManager; import android.view.animation.Animation; import android.view.animation.AnimationUtils; @@ -47,7 +40,6 @@ import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment.Selec import com.android.dialer.animation.AnimUtils; import com.android.dialer.animation.AnimationListenerAdapter; import com.android.dialer.common.LogUtil; -import com.android.dialer.compat.ActivityCompat; import com.android.dialer.compat.CompatUtils; import com.android.dialer.logging.Logger; import com.android.dialer.logging.ScreenEvent; @@ -56,7 +48,6 @@ import com.android.incallui.audiomode.AudioModeProvider; import com.android.incallui.call.CallList; import com.android.incallui.call.DialerCall; import com.android.incallui.call.DialerCall.State; -import com.android.incallui.call.TelecomAdapter; import com.android.incallui.telecomeventui.InternationalCallOnWifiDialogFragment; import com.android.incallui.telecomeventui.InternationalCallOnWifiDialogFragment.Callback; import com.google.common.base.Optional; @@ -235,27 +226,6 @@ public class InCallActivityCommon { } } - public void onSaveInstanceState(Bundle out) { - // TODO: The dialpad fragment should handle this as part of its own state - out.putBoolean(INTENT_EXTRA_SHOW_DIALPAD, inCallActivity.isDialpadVisible()); - DialpadFragment dialpadFragment = inCallActivity.getDialpadFragment(); - if (dialpadFragment != null) { - out.putString(DIALPAD_TEXT_KEY, dialpadFragment.getDtmfText()); - } - } - - public void onStart() { - Trace.beginSection("InCallActivityCommon.onStart"); - // setting activity should be last thing in setup process - InCallPresenter.getInstance().setActivity(inCallActivity); - inCallActivity.enableInCallOrientationEventListener( - inCallActivity.getRequestedOrientation() - == InCallOrientationEventListener.ACTIVITY_PREFERENCE_ALLOW_ROTATION); - - InCallPresenter.getInstance().onActivityStarted(); - Trace.endSection(); - } - public void onResume() { Trace.beginSection("InCallActivityCommon.onResume"); if (InCallPresenter.getInstance().isReadyForTearDown()) { @@ -263,7 +233,7 @@ public class InCallActivityCommon { "InCallActivityCommon.onResume", "InCallPresenter is ready for tear down, not sending updates"); } else { - updateTaskDescription(); + inCallActivity.updateTaskDescription(); InCallPresenter.getInstance().onUiShowing(true); } @@ -290,7 +260,7 @@ public class InCallActivityCommon { } showDialpadRequest = DIALPAD_REQUEST_NONE; } - updateNavigationBar(inCallActivity.isDialpadVisible()); + inCallActivity.updateNavigationBar(inCallActivity.isDialpadVisible()); if (showPostCharWaitDialogOnResume) { showPostCharWaitDialog(showPostCharWaitDialogCallId, showPostCharWaitDialogChars); @@ -302,48 +272,6 @@ public class InCallActivityCommon { Trace.endSection(); } - // onPause is guaranteed to be called when the InCallActivity goes - // in the background. - public void onPause() { - DialpadFragment dialpadFragment = inCallActivity.getDialpadFragment(); - if (dialpadFragment != null) { - dialpadFragment.onDialerKeyUp(null); - } - - InCallPresenter.getInstance().onUiShowing(false); - if (inCallActivity.isFinishing()) { - InCallPresenter.getInstance().unsetActivity(inCallActivity); - } - } - - public void onStop() { - // Disconnects call waiting for account when activity is hidden e.g. user press home button. - // This is necessary otherwise the pending call will stuck on account choose and no new call - // will be able to create. See a bug for more details. - // Skip this on locked screen since the activity may go over life cycle and start again. - if (!isRecreating - && !inCallActivity.getSystemService(KeyguardManager.class).isKeyguardLocked()) { - DialerCall waitingForAccountCall = CallList.getInstance().getWaitingForAccountCall(); - if (waitingForAccountCall != null) { - waitingForAccountCall.disconnect(); - } - } - - inCallActivity.enableInCallOrientationEventListener(false); - InCallPresenter.getInstance().updateIsChangingConfigurations(); - InCallPresenter.getInstance().onActivityStopped(); - if (!isRecreating) { - if (errorDialog != null) { - errorDialog.dismiss(); - } - } - } - - public void onDestroy() { - InCallPresenter.getInstance().unsetActivity(inCallActivity); - InCallPresenter.getInstance().updateIsChangingConfigurations(); - } - void onNewIntent(Intent intent, boolean isRecreating) { LogUtil.i("InCallActivityCommon.onNewIntent", ""); this.isRecreating = isRecreating; @@ -368,106 +296,6 @@ public class InCallActivityCommon { } } - public boolean onBackPressed(boolean isInCallScreenVisible) { - LogUtil.i("InCallActivityCommon.onBackPressed", ""); - - // BACK is also used to exit out of any "special modes" of the - // in-call UI: - if (!inCallActivity.isVisible()) { - return true; - } - - if (!isInCallScreenVisible) { - return true; - } - - DialpadFragment dialpadFragment = inCallActivity.getDialpadFragment(); - if (dialpadFragment != null && dialpadFragment.isVisible()) { - inCallActivity.showDialpadFragment(false /* show */, true /* animate */); - return true; - } - - // Always disable the Back key while an incoming call is ringing - DialerCall call = CallList.getInstance().getIncomingCall(); - if (call != null) { - LogUtil.i("InCallActivityCommon.onBackPressed", "consume Back press for an incoming call"); - return true; - } - - // Nothing special to do. Fall back to the default behavior. - return false; - } - - public boolean onKeyUp(int keyCode, KeyEvent event) { - DialpadFragment dialpadFragment = inCallActivity.getDialpadFragment(); - // push input to the dialer. - if (dialpadFragment != null - && (dialpadFragment.isVisible()) - && (dialpadFragment.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 false; - } - - public boolean onKeyDown(int keyCode, KeyEvent event) { - switch (keyCode) { - case KeyEvent.KEYCODE_CALL: - boolean handled = InCallPresenter.getInstance().handleCallKey(); - if (!handled) { - LogUtil.e( - "InCallActivityCommon.onKeyDown", - "InCallPresenter 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: - // Ringer silencing handled by PhoneWindowManager. - break; - - case KeyEvent.KEYCODE_MUTE: - TelecomAdapter.getInstance() - .mute(!AudioModeProvider.getInstance().getAudioState().isMuted()); - return true; - - // Various testing/debugging features, enabled ONLY when VERBOSE == true. - case KeyEvent.KEYCODE_SLASH: - if (LogUtil.isVerboseEnabled()) { - LogUtil.v( - "InCallActivityCommon.onKeyDown", - "----------- InCallActivity View dump --------------"); - // Dump starting from the top-level view of the entire activity: - Window w = inCallActivity.getWindow(); - View decorView = w.getDecorView(); - LogUtil.v("InCallActivityCommon.onKeyDown", "View dump:" + decorView); - return true; - } - break; - case KeyEvent.KEYCODE_EQUALS: - break; - default: // fall out - } - - return event.getRepeatCount() == 0 && handleDialerKeyDown(keyCode, event); - } - private void setWindowFlags() { // Allow the activity to be shown when the screen is locked and filter out touch events that are // "too fat". @@ -498,20 +326,6 @@ public class InCallActivityCommon { audioRouteForTesting = Optional.of(audioRoute); } - private boolean handleDialerKeyDown(int keyCode, KeyEvent event) { - LogUtil.v("InCallActivityCommon.handleDialerKeyDown", "keyCode %d, event: %s", keyCode, 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. - DialpadFragment dialpadFragment = inCallActivity.getDialpadFragment(); - if (dialpadFragment != null && dialpadFragment.isVisible()) { - return dialpadFragment.onDialerKeyDown(event); - } - - return false; - } - public void showPostCharWaitDialog(String callId, String chars) { if (inCallActivity.isVisible()) { PostCharDialogFragment fragment = new PostCharDialogFragment(callId, chars); @@ -566,16 +380,6 @@ public class InCallActivityCommon { } } - void updateNavigationBar(boolean isDialpadVisible) { - if (!ActivityCompat.isInMultiWindowMode(inCallActivity)) { - View navigationBarBackground = - inCallActivity.getWindow().findViewById(R.id.navigation_bar_background); - if (navigationBarBackground != null) { - navigationBarBackground.setVisibility(isDialpadVisible ? View.VISIBLE : View.GONE); - } - } - } - public boolean showDialpadFragment(boolean show, boolean animate) { // If the dialpad is already visible, don't animate in. If it's gone, don't animate out. boolean isDialpadVisible = inCallActivity.isDialpadVisible(); @@ -637,7 +441,7 @@ public class InCallActivityCommon { dialpadFragmentManager.executePendingTransactions(); Logger.get(inCallActivity).logScreenView(ScreenEvent.Type.INCALL_DIALPAD, inCallActivity); - updateNavigationBar(true /* isDialpadVisible */); + inCallActivity.updateNavigationBar(true /* isDialpadVisible */); } private void performHideDialpadFragment() { @@ -655,23 +459,7 @@ public class InCallActivityCommon { transaction.commitAllowingStateLoss(); fragmentManager.executePendingTransactions(); } - updateNavigationBar(false /* isDialpadVisible */); - } - - public void updateTaskDescription() { - Resources resources = inCallActivity.getResources(); - int color; - if (resources.getBoolean(R.bool.is_layout_landscape)) { - color = - ResourcesCompat.getColor( - resources, R.color.statusbar_background_color, inCallActivity.getTheme()); - } else { - color = InCallPresenter.getInstance().getThemeColorManager().getSecondaryColor(); - } - - TaskDescription td = - new TaskDescription(resources.getString(R.string.notification_ongoing_call), null, color); - inCallActivity.setTaskDescription(td); + inCallActivity.updateNavigationBar(false /* isDialpadVisible */); } private void internalResolveIntent(Intent intent) { @@ -757,6 +545,12 @@ public class InCallActivityCommon { this.errorDialog = errorDialog; } + /** @deprecated Only for temporary use during the deprecation of {@link InCallActivityCommon} */ + @Deprecated + boolean getIsRecreating() { + return isRecreating; + } + /** @deprecated Only for temporary use during the deprecation of {@link InCallActivityCommon} */ @Deprecated @Nullable -- cgit v1.2.3 From 7f1076d08b39467ac8c550f60a7fbbad604ab4fa Mon Sep 17 00:00:00 2001 From: linyuh Date: Fri, 17 Nov 2017 17:13:47 -0800 Subject: DialpadView cleanup. Changes: 1. Rewrote the logic to pick a NumberFormat. 2. Deleted dead method setShowVoicemailButton(boolean) and field mCanDigitsBeEdited. 3. Deleted the redundant SDK version check when getting a Drawable. Test: DialpadFragmentIntegrationTest PiperOrigin-RevId: 176181808 Change-Id: Iee4e3db8535ebbea3d4c63c8d8c1d3eb342b538e --- .../android/dialer/dialpadview/DialpadView.java | 48 +++++----------------- 1 file changed, 11 insertions(+), 37 deletions(-) diff --git a/java/com/android/dialer/dialpadview/DialpadView.java b/java/com/android/dialer/dialpadview/DialpadView.java index 4cbf42f2e..1d48066ad 100644 --- a/java/com/android/dialer/dialpadview/DialpadView.java +++ b/java/com/android/dialer/dialpadview/DialpadView.java @@ -22,9 +22,7 @@ import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; -import android.graphics.drawable.Drawable; import android.graphics.drawable.RippleDrawable; -import android.os.Build; import android.text.Spannable; import android.text.TextUtils; import android.text.style.TtsSpan; @@ -33,7 +31,6 @@ import android.util.TypedValue; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.ViewGroup.LayoutParams; import android.view.ViewPropertyAnimator; import android.view.ViewTreeObserver.OnPreDrawListener; import android.view.accessibility.AccessibilityManager; @@ -90,7 +87,6 @@ public class DialpadView extends LinearLayout { private ViewGroup mRateContainer; private TextView mIldCountry; private TextView mIldRate; - private boolean mCanDigitsBeEdited; public DialpadView(Context context) { this(context, null); @@ -154,16 +150,7 @@ public class DialpadView extends LinearLayout { private void setupKeypad() { final Resources resources = getContext().getResources(); - - final Locale currentLocale = resources.getConfiguration().locale; - final NumberFormat nf; - // We translate dialpad numbers only for "fa" and not any other locale - // ("ar" anybody ?). - if ("fa".equals(currentLocale.getLanguage())) { - nf = DecimalFormat.getInstance(CompatUtils.getLocale(getContext())); - } else { - nf = DecimalFormat.getInstance(Locale.ENGLISH); - } + final NumberFormat numberFormat = getNumberFormat(); for (int i = 0; i < BUTTON_IDS.length; i++) { DialpadKeyButton dialpadKey = (DialpadKeyButton) findViewById(BUTTON_IDS[i]); @@ -178,7 +165,7 @@ public class DialpadView extends LinearLayout { numberString = resources.getString(R.string.dialpad_star_number); numberContentDescription = numberString; } else { - numberString = nf.format(i); + numberString = numberFormat.format(i); // The content description is used for Talkback key presses. The number is // separated by a "," to introduce a slight delay. Convert letters into a verbatim // span so that they are read as letters instead of as one word. @@ -194,7 +181,7 @@ public class DialpadView extends LinearLayout { } final RippleDrawable rippleBackground = - (RippleDrawable) getDrawableCompat(getContext(), R.drawable.btn_dialpad_key); + (RippleDrawable) getContext().getDrawable(R.drawable.btn_dialpad_key); if (mRippleColor != null) { rippleBackground.setColor(mRippleColor); } @@ -239,26 +226,19 @@ public class DialpadView extends LinearLayout { zero.setLongHoverContentDescription(resources.getText(R.string.description_image_button_plus)); } - private Drawable getDrawableCompat(Context context, int id) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - return context.getDrawable(id); - } else { - return context.getResources().getDrawable(id); - } - } + private NumberFormat getNumberFormat() { + Locale locale = CompatUtils.getLocale(getContext()); - public void setShowVoicemailButton(boolean show) { - View view = findViewById(R.id.dialpad_key_voicemail); - if (view != null) { - view.setVisibility(show ? View.VISIBLE : View.INVISIBLE); - } + // Return the Persian number format if the current language is Persian. + return "fas".equals(locale.getISO3Language()) + ? DecimalFormat.getInstance(locale) + : DecimalFormat.getInstance(Locale.ENGLISH); } /** - * Whether or not the digits above the dialer can be edited. + * Configure whether or not the digits above the dialpad can be edited. * - * @param canBeEdited If true, the backspace button will be shown and the digits EditText will be - * configured to allow text manipulation. + *

If we allow editing digits, the backspace button will be shown. */ public void setCanDigitsBeEdited(boolean canBeEdited) { View deleteButton = findViewById(R.id.deleteButton); @@ -271,8 +251,6 @@ public class DialpadView extends LinearLayout { digits.setLongClickable(canBeEdited); digits.setFocusableInTouchMode(canBeEdited); digits.setCursorVisible(false); - - mCanDigitsBeEdited = canBeEdited; } public void setCallRateInformation(String countryName, String displayRate) { @@ -285,10 +263,6 @@ public class DialpadView extends LinearLayout { mIldRate.setText(displayRate); } - public boolean canDigitsBeEdited() { - return mCanDigitsBeEdited; - } - /** * Always returns true for onHoverEvent callbacks, to fix problems with accessibility due to the * dialpad overlaying other fragments. -- cgit v1.2.3