summaryrefslogtreecommitdiff
path: root/java
diff options
context:
space:
mode:
Diffstat (limited to 'java')
-rw-r--r--java/com/android/contacts/common/res/values/dimens.xml3
-rw-r--r--java/com/android/dialer/blocking/FilteredNumberCompat.java5
-rw-r--r--java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java2
-rw-r--r--java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java2
-rw-r--r--java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java33
-rw-r--r--java/com/android/dialer/calllog/ui/res/layout/new_call_log_fragment.xml4
-rw-r--r--java/com/android/dialer/calllogutils/CallLogDates.java14
-rw-r--r--java/com/android/dialer/calllogutils/CallLogEntryText.java2
-rw-r--r--java/com/android/dialer/commandline/Arguments.java144
-rw-r--r--java/com/android/dialer/commandline/Command.java20
-rw-r--r--java/com/android/dialer/commandline/CommandLineModule.java6
-rw-r--r--java/com/android/dialer/commandline/CommandLineReceiver.java28
-rw-r--r--java/com/android/dialer/commandline/impl/Blocking.java96
-rw-r--r--java/com/android/dialer/commandline/impl/Echo.java24
-rw-r--r--java/com/android/dialer/commandline/impl/Help.java36
-rw-r--r--java/com/android/dialer/commandline/impl/Version.java22
-rw-r--r--java/com/android/dialer/common/concurrent/DialerExecutorComponent.java16
-rw-r--r--java/com/android/dialer/common/concurrent/UiListener.java2
-rw-r--r--java/com/android/dialer/phonelookup/PhoneLookup.java6
-rw-r--r--java/com/android/dialer/phonelookup/PhoneLookupModule.java9
-rw-r--r--java/com/android/dialer/phonelookup/blockednumber/DialerBlockedNumberPhoneLookup.java56
-rw-r--r--java/com/android/dialer/phonelookup/blockednumber/MarkDirtyObserver.java47
-rw-r--r--java/com/android/dialer/phonelookup/blockednumber/SystemBlockedNumberPhoneLookup.java181
-rw-r--r--java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java6
-rw-r--r--java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java21
-rw-r--r--java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java47
-rw-r--r--java/com/android/dialer/phonelookup/cp2/Cp2RemotePhoneLookup.java19
-rw-r--r--java/com/android/dialer/speeddial/res/layout/fragment_speed_dial.xml3
-rw-r--r--java/com/android/dialer/theme/res/values/dimens.xml4
-rw-r--r--java/com/android/dialer/voicemail/listui/res/layout/new_voicemail_call_log_fragment.xml5
-rw-r--r--java/com/android/incallui/PhoneLookupHistoryRecorder.java34
31 files changed, 699 insertions, 198 deletions
diff --git a/java/com/android/contacts/common/res/values/dimens.xml b/java/com/android/contacts/common/res/values/dimens.xml
index 1ad9b30b0..df3e53c77 100644
--- a/java/com/android/contacts/common/res/values/dimens.xml
+++ b/java/com/android/contacts/common/res/values/dimens.xml
@@ -49,9 +49,6 @@
<dimen name="floating_action_button_width">56dp</dimen>
<!-- Z translation of the floating action button -->
<dimen name="floating_action_button_translation_z">8dp</dimen>
- <!-- Padding to be applied to the bottom of lists to make space for the floating action
- button -->
- <dimen name="floating_action_button_list_bottom_padding">88dp</dimen>
<!-- Right margin of the floating action button -->
<dimen name="floating_action_button_margin_right">16dp</dimen>
<!-- Bottom margin of the floating action button -->
diff --git a/java/com/android/dialer/blocking/FilteredNumberCompat.java b/java/com/android/dialer/blocking/FilteredNumberCompat.java
index a5f3d7efd..bea84e8db 100644
--- a/java/com/android/dialer/blocking/FilteredNumberCompat.java
+++ b/java/com/android/dialer/blocking/FilteredNumberCompat.java
@@ -34,6 +34,7 @@ import android.support.annotation.VisibleForTesting;
import android.telecom.TelecomManager;
import android.telephony.PhoneNumberUtils;
import com.android.dialer.common.LogUtil;
+import com.android.dialer.configprovider.ConfigProviderBindings;
import com.android.dialer.database.FilteredNumberContract.FilteredNumber;
import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
import com.android.dialer.database.FilteredNumberContract.FilteredNumberSources;
@@ -117,7 +118,9 @@ public class FilteredNumberCompat {
* migration has been performed, {@code false} otherwise.
*/
public static boolean useNewFiltering(Context context) {
- return canUseNewFiltering() && hasMigratedToNewBlocking(context);
+ return !ConfigProviderBindings.get(context).getBoolean("debug_force_dialer_filtering", false)
+ && canUseNewFiltering()
+ && hasMigratedToNewBlocking(context);
}
/**
diff --git a/java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java b/java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java
index d382517cd..96a640918 100644
--- a/java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java
+++ b/java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java
@@ -119,7 +119,7 @@ public class AnnotatedCallLogContract {
*
* <p>TYPE: BLOB
*
- * @see com.android.dialer.calllog.model.NumberAttributes
+ * @see com.android.dialer.NumberAttributes
*/
String NUMBER_ATTRIBUTES = "number_attributes";
diff --git a/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java b/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java
index a0daae141..67fb4f018 100644
--- a/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java
+++ b/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java
@@ -74,7 +74,7 @@ final class NewCallLogViewHolder extends RecyclerView.ViewHolder {
this.clock = clock;
this.realtimeRowProcessor = realtimeRowProcessor;
- uiExecutorService = DialerExecutorComponent.get(context).uiExecutorService();
+ uiExecutorService = DialerExecutorComponent.get(context).uiExecutor();
}
/** @param cursor a cursor from {@link CoalescedAnnotatedCallLogCursorLoader}. */
diff --git a/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java b/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java
index 57ad9657c..9e58e53ad 100644
--- a/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java
+++ b/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java
@@ -25,10 +25,9 @@ import com.android.dialer.calllog.model.CoalescedRow;
import com.android.dialer.common.Assert;
import com.android.dialer.common.concurrent.Annotations.Ui;
import com.android.dialer.inject.ApplicationContext;
+import com.android.dialer.phonelookup.PhoneLookup;
import com.android.dialer.phonelookup.PhoneLookupInfo;
-import com.android.dialer.phonelookup.PhoneLookupInfo.Cp2Info;
import com.android.dialer.phonelookup.consolidator.PhoneLookupInfoConsolidator;
-import com.android.dialer.phonelookup.cp2.Cp2LocalPhoneLookup;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
@@ -49,19 +48,19 @@ import javax.inject.Inject;
public final class RealtimeRowProcessor {
private final Context appContext;
- private final Cp2LocalPhoneLookup cp2LocalPhoneLookup;
+ private final PhoneLookup<PhoneLookupInfo> phoneLookup;
private final ListeningExecutorService uiExecutor;
- private final Map<DialerPhoneNumber, Cp2Info> cache = new ArrayMap<>();
+ private final Map<DialerPhoneNumber, PhoneLookupInfo> cache = new ArrayMap<>();
@Inject
RealtimeRowProcessor(
@ApplicationContext Context appContext,
@Ui ListeningExecutorService uiExecutor,
- Cp2LocalPhoneLookup cp2LocalPhoneLookup) {
+ PhoneLookup<PhoneLookupInfo> phoneLookup) {
this.appContext = appContext;
this.uiExecutor = uiExecutor;
- this.cp2LocalPhoneLookup = cp2LocalPhoneLookup;
+ this.phoneLookup = phoneLookup;
}
/**
@@ -75,17 +74,17 @@ public final class RealtimeRowProcessor {
return Futures.immediateFuture(row);
}
- Cp2Info cachedCp2Info = cache.get(row.number());
- if (cachedCp2Info != null) {
- return Futures.immediateFuture(applyCp2LocalInfoToRow(cachedCp2Info, row));
+ PhoneLookupInfo cachedPhoneLookupInfo = cache.get(row.number());
+ if (cachedPhoneLookupInfo != null) {
+ return Futures.immediateFuture(applyPhoneLookupInfoToRow(cachedPhoneLookupInfo, row));
}
- ListenableFuture<Cp2Info> cp2InfoFuture = cp2LocalPhoneLookup.lookupByNumber(row.number());
+ ListenableFuture<PhoneLookupInfo> phoneLookupInfoFuture = phoneLookup.lookup(row.number());
return Futures.transform(
- cp2InfoFuture,
- cp2Info -> {
- cache.put(row.number(), cp2Info);
- return applyCp2LocalInfoToRow(cp2Info, row);
+ phoneLookupInfoFuture,
+ phoneLookupInfo -> {
+ cache.put(row.number(), phoneLookupInfo);
+ return applyPhoneLookupInfoToRow(phoneLookupInfo, row);
},
uiExecutor /* ensures the cache is updated on a single thread */);
}
@@ -97,13 +96,13 @@ public final class RealtimeRowProcessor {
cache.clear();
}
- private CoalescedRow applyCp2LocalInfoToRow(Cp2Info cp2Info, CoalescedRow row) {
- PhoneLookupInfo phoneLookupInfo = PhoneLookupInfo.newBuilder().setCp2LocalInfo(cp2Info).build();
+ private CoalescedRow applyPhoneLookupInfoToRow(
+ PhoneLookupInfo phoneLookupInfo, CoalescedRow row) {
PhoneLookupInfoConsolidator phoneLookupInfoConsolidator =
new PhoneLookupInfoConsolidator(appContext, phoneLookupInfo);
- // It is safe to overwrite any existing data because CP2 always has highest priority.
return row.toBuilder()
.setNumberAttributes(
+ // TODO(zachh): Put this in a common location.
NumberAttributes.newBuilder()
.setName(phoneLookupInfoConsolidator.getName())
.setPhotoUri(phoneLookupInfoConsolidator.getPhotoUri())
diff --git a/java/com/android/dialer/calllog/ui/res/layout/new_call_log_fragment.xml b/java/com/android/dialer/calllog/ui/res/layout/new_call_log_fragment.xml
index e1d8410b6..1a3a08b9d 100644
--- a/java/com/android/dialer/calllog/ui/res/layout/new_call_log_fragment.xml
+++ b/java/com/android/dialer/calllog/ui/res/layout/new_call_log_fragment.xml
@@ -20,4 +20,6 @@
android:id="@+id/new_call_log_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@color/background_dialer_light"/>
+ android:background="@color/background_dialer_light"
+ android:paddingBottom="@dimen/floating_action_button_list_bottom_padding"
+ android:clipToPadding="false"/>
diff --git a/java/com/android/dialer/calllogutils/CallLogDates.java b/java/com/android/dialer/calllogutils/CallLogDates.java
index bdf621518..fe3c0c3ad 100644
--- a/java/com/android/dialer/calllogutils/CallLogDates.java
+++ b/java/com/android/dialer/calllogutils/CallLogDates.java
@@ -36,7 +36,7 @@ public final class CallLogDates {
*
* <pre>
* if < 1 minute ago: "Just now";
- * else if < 1 hour ago: time relative to now (e.g., "8 min. ago");
+ * else if < 1 hour ago: time relative to now (e.g., "8 min ago");
* else if today: time (e.g., "12:15 PM");
* else if < 7 days: abbreviated day of week (e.g., "Wed");
* else if < 1 year: date with abbreviated month, day, but no year (e.g., "Jan 15");
@@ -50,10 +50,18 @@ public final class CallLogDates {
return context.getString(R.string.just_now);
}
- // For calls logged less than 1 hour ago, display time relative to now (e.g., "8 min. ago").
+ // For calls logged less than 1 hour ago, display time relative to now (e.g., "8 min ago").
if (nowMillis - timestampMillis < TimeUnit.HOURS.toMillis(1)) {
return DateUtils.getRelativeTimeSpanString(
- timestampMillis, nowMillis, DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE);
+ timestampMillis,
+ nowMillis,
+ DateUtils.MINUTE_IN_MILLIS,
+ DateUtils.FORMAT_ABBREV_RELATIVE)
+ .toString()
+ // The platform method DateUtils#getRelativeTimeSpanString adds a dot ('.') after the
+ // abbreviated time unit for some languages (e.g., "8 min. ago") but we prefer not to have
+ // the dot.
+ .replace(".", "");
}
int dayDifference = getDayDifference(nowMillis, timestampMillis);
diff --git a/java/com/android/dialer/calllogutils/CallLogEntryText.java b/java/com/android/dialer/calllogutils/CallLogEntryText.java
index 25fe86452..a7a6bba9a 100644
--- a/java/com/android/dialer/calllogutils/CallLogEntryText.java
+++ b/java/com/android/dialer/calllogutils/CallLogEntryText.java
@@ -58,7 +58,7 @@ public final class CallLogEntryText {
*
* <ul>
* <li>Duo Video, Mobile • Now
- * <li>Duo Video • 10 min. ago
+ * <li>Duo Video • 10 min ago
* <li>Mobile • 11:45 PM
* <li>Mobile • Sun
* <li>Brooklyn, NJ • Jan 15
diff --git a/java/com/android/dialer/commandline/Arguments.java b/java/com/android/dialer/commandline/Arguments.java
new file mode 100644
index 000000000..84ed0e422
--- /dev/null
+++ b/java/com/android/dialer/commandline/Arguments.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2018 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.commandline;
+
+import android.support.annotation.Nullable;
+import com.android.dialer.commandline.Command.IllegalCommandLineArgumentException;
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.PeekingIterator;
+import com.google.common.collect.UnmodifiableIterator;
+
+/**
+ * Parses command line arguments into optional flags (--foo, --key=value, --key value) and required
+ * positionals (which must be passed in order). Flags must start with "--" and are always before
+ * positionals. If flags are used "--" must be placed before positionals.
+ *
+ * <p>--flag will be interpreted as --flag=true, and --noflag as --flag=false
+ *
+ * <p>Grammar:<br>
+ * dialer-cmd.py <cmd> <args><br>
+ * <args> = (<flags> -- <positionals>) | <positionals><br>
+ * <flags> = "no"?<name>(<separator><value>)?<br>
+ * <separator> = " " | "="
+ */
+@AutoValue
+public abstract class Arguments {
+
+ public static final Arguments EMPTY =
+ new AutoValue_Arguments(ImmutableMap.of(), ImmutableList.of());
+
+ public abstract ImmutableMap<String, String> getFlags();
+
+ public abstract ImmutableList<String> getPositionals();
+
+ /**
+ * Return the positional at {@code position}. Throw {@link IllegalCommandLineArgumentException} if
+ * it is absent and reports to the user {@code name} is expected.
+ */
+ public String expectPositional(int position, String name)
+ throws IllegalCommandLineArgumentException {
+ if (getPositionals().size() <= position) {
+ throw new IllegalCommandLineArgumentException(name + " expected");
+ }
+ return getPositionals().get(position);
+ }
+
+ public Boolean getBoolean(String flag, boolean defaultValue)
+ throws IllegalCommandLineArgumentException {
+ if (!getFlags().containsKey(flag)) {
+ return defaultValue;
+ }
+ switch (getFlags().get(flag)) {
+ case "true":
+ return true;
+ case "false":
+ return false;
+ default:
+ throw new IllegalCommandLineArgumentException("boolean value expected for " + flag);
+ }
+ }
+
+ public static Arguments parse(@Nullable String[] rawArguments)
+ throws IllegalCommandLineArgumentException {
+ if (rawArguments == null) {
+ return EMPTY;
+ }
+ return parse(Iterators.forArray(rawArguments));
+ }
+
+ public static Arguments parse(Iterable<String> rawArguments)
+ throws IllegalCommandLineArgumentException {
+ return parse(Iterators.unmodifiableIterator(rawArguments.iterator()));
+ }
+
+ public static Arguments parse(UnmodifiableIterator<String> iterator)
+ throws IllegalCommandLineArgumentException {
+ PeekingIterator<String> peekingIterator = Iterators.peekingIterator(iterator);
+ ImmutableMap<String, String> flags = parseFlags(peekingIterator);
+ ImmutableList<String> positionals = parsePositionals(peekingIterator);
+
+ return new AutoValue_Arguments(flags, positionals);
+ }
+
+ private static ImmutableMap<String, String> parseFlags(PeekingIterator<String> iterator)
+ throws IllegalCommandLineArgumentException {
+ ImmutableMap.Builder<String, String> flags = ImmutableMap.builder();
+ if (!iterator.hasNext()) {
+ return flags.build();
+ }
+ if (!iterator.peek().startsWith("--")) {
+ return flags.build();
+ }
+
+ while (iterator.hasNext()) {
+ String peek = iterator.peek();
+ if (peek.equals("--")) {
+ iterator.next();
+ return flags.build();
+ }
+ if (peek.startsWith("--")) {
+ String key = iterator.next().substring(2);
+ String value;
+ if (iterator.hasNext() && !iterator.peek().startsWith("--")) {
+ value = iterator.next();
+ } else if (key.contains("=")) {
+ String[] entry = key.split("=", 2);
+ key = entry[0];
+ value = entry[1];
+ } else if (key.startsWith("no")) {
+ key = key.substring(2);
+ value = "false";
+ } else {
+ value = "true";
+ }
+ flags.put(key, value);
+ } else {
+ throw new IllegalCommandLineArgumentException("flag or '--' expected");
+ }
+ }
+ return flags.build();
+ }
+
+ private static ImmutableList<String> parsePositionals(PeekingIterator<String> iterator) {
+ ImmutableList.Builder<String> positionals = ImmutableList.builder();
+ positionals.addAll(iterator);
+ return positionals.build();
+ }
+}
diff --git a/java/com/android/dialer/commandline/Command.java b/java/com/android/dialer/commandline/Command.java
index 5a35d4002..83618a255 100644
--- a/java/com/android/dialer/commandline/Command.java
+++ b/java/com/android/dialer/commandline/Command.java
@@ -17,15 +17,31 @@
package com.android.dialer.commandline;
import android.support.annotation.NonNull;
-import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.ListenableFuture;
/** Handles a Command from {@link CommandLineReceiver}. */
public interface Command {
- ListenableFuture<String> run(ImmutableList<String> args);
+ /**
+ * Thrown when {@code args} in {@link #run(Arguments)} does not match the expected format. The
+ * commandline will print {@code message} and {@link #getUsage()}.
+ */
+ class IllegalCommandLineArgumentException extends Exception {
+ public IllegalCommandLineArgumentException(String message) {
+ super(message);
+ }
+ }
/** Describe the command when "help" is listing available commands. */
@NonNull
String getShortDescription();
+
+ /**
+ * Call when 'command --help' is called or when {@link IllegalCommandLineArgumentException} is
+ * thrown to inform the user how should the command be used.
+ */
+ @NonNull
+ String getUsage();
+
+ ListenableFuture<String> run(Arguments args) throws IllegalCommandLineArgumentException;
}
diff --git a/java/com/android/dialer/commandline/CommandLineModule.java b/java/com/android/dialer/commandline/CommandLineModule.java
index 366899e5e..9022d6cd5 100644
--- a/java/com/android/dialer/commandline/CommandLineModule.java
+++ b/java/com/android/dialer/commandline/CommandLineModule.java
@@ -16,6 +16,7 @@
package com.android.dialer.commandline;
+import com.android.dialer.commandline.impl.Blocking;
import com.android.dialer.commandline.impl.Echo;
import com.android.dialer.commandline.impl.Help;
import com.android.dialer.commandline.impl.Version;
@@ -41,18 +42,21 @@ public abstract class CommandLineModule {
private final Help help;
private final Version version;
private final Echo echo;
+ private final Blocking blocking;
@Inject
- AospCommandInjector(Help help, Version version, Echo echo) {
+ AospCommandInjector(Help help, Version version, Echo echo, Blocking blocking) {
this.help = help;
this.version = version;
this.echo = echo;
+ this.blocking = blocking;
}
public CommandSupplier.Builder inject(CommandSupplier.Builder builder) {
builder.addCommand("help", help);
builder.addCommand("version", version);
builder.addCommand("echo", echo);
+ builder.addCommand("blocking", blocking);
return builder;
}
}
diff --git a/java/com/android/dialer/commandline/CommandLineReceiver.java b/java/com/android/dialer/commandline/CommandLineReceiver.java
index baaadf054..e5e78c46a 100644
--- a/java/com/android/dialer/commandline/CommandLineReceiver.java
+++ b/java/com/android/dialer/commandline/CommandLineReceiver.java
@@ -21,8 +21,8 @@ import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import com.android.dialer.buildtype.BuildType;
+import com.android.dialer.commandline.Command.IllegalCommandLineArgumentException;
import com.android.dialer.common.LogUtil;
-import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.MoreExecutors;
@@ -53,17 +53,18 @@ public class CommandLineReceiver extends BroadcastReceiver {
.commandSupplier()
.get()
.get(intent.getStringExtra(COMMAND));
- if (command == null) {
- LogUtil.i(outputTag, "unknown command " + intent.getStringExtra(COMMAND));
- return;
- }
+ try {
+ if (command == null) {
+ LogUtil.i(outputTag, "unknown command " + intent.getStringExtra(COMMAND));
+ return;
+ }
- ImmutableList<String> args =
- intent.hasExtra(ARGS)
- ? ImmutableList.copyOf(intent.getStringArrayExtra(ARGS))
- : ImmutableList.of();
+ Arguments args = Arguments.parse(intent.getStringArrayExtra(ARGS));
- try {
+ if (args.getBoolean("help", false)) {
+ LogUtil.i(outputTag, "usage:\n" + command.getUsage());
+ return;
+ }
Futures.addCallback(
command.run(args),
new FutureCallback<String>() {
@@ -78,12 +79,15 @@ public class CommandLineReceiver extends BroadcastReceiver {
@Override
public void onFailure(Throwable throwable) {
- // LogUtil.e(tag, message, e) prints 2 entries where only the first one can be
- // intercepted by the script. Compose the string instead.
+ if (throwable instanceof IllegalCommandLineArgumentException) {
+ LogUtil.e(outputTag, throwable.getMessage() + "\n\nusage:\n" + command.getUsage());
+ }
LogUtil.e(outputTag, "error running command future", throwable);
}
},
MoreExecutors.directExecutor());
+ } catch (IllegalCommandLineArgumentException e) {
+ LogUtil.e(outputTag, e.getMessage() + "\n\nusage:\n" + command.getUsage());
} catch (Throwable throwable) {
LogUtil.e(outputTag, "error running command", throwable);
}
diff --git a/java/com/android/dialer/commandline/impl/Blocking.java b/java/com/android/dialer/commandline/impl/Blocking.java
new file mode 100644
index 000000000..2afd16522
--- /dev/null
+++ b/java/com/android/dialer/commandline/impl/Blocking.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 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.commandline.impl;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler;
+import com.android.dialer.commandline.Arguments;
+import com.android.dialer.commandline.Command;
+import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
+import com.android.dialer.inject.ApplicationContext;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import javax.inject.Inject;
+
+/** Block or unblock a number. */
+public class Blocking implements Command {
+
+ @NonNull
+ @Override
+ public String getShortDescription() {
+ return "block or unblock numbers";
+ }
+
+ @NonNull
+ @Override
+ public String getUsage() {
+ return "blocking block|unblock|isblocked number\n\n" + "number should be e.164 formatted";
+ }
+
+ private final Context appContext;
+ private final ListeningExecutorService executorService;
+
+ @Inject
+ Blocking(
+ @ApplicationContext Context context,
+ @BackgroundExecutor ListeningExecutorService executorService) {
+ this.appContext = context;
+ this.executorService = executorService;
+ }
+
+ @Override
+ public ListenableFuture<String> run(Arguments args) throws IllegalCommandLineArgumentException {
+ // AsyncQueryHandler must be created on a thread with looper.
+ // TODO(a bug): Use blocking version
+ FilteredNumberAsyncQueryHandler asyncQueryHandler =
+ new FilteredNumberAsyncQueryHandler(appContext);
+ return executorService.submit(() -> doInBackground(args, asyncQueryHandler));
+ }
+
+ private String doInBackground(Arguments args, FilteredNumberAsyncQueryHandler asyncQueryHandler) {
+ if (args.getPositionals().isEmpty()) {
+ return getUsage();
+ }
+
+ String command = args.getPositionals().get(0);
+
+ if ("block".equals(command)) {
+ String number = args.getPositionals().get(1);
+ asyncQueryHandler.blockNumber((unused) -> {}, number, null);
+ return "blocked " + number;
+ }
+
+ if ("unblock".equals(command)) {
+ String number = args.getPositionals().get(1);
+ Integer id = asyncQueryHandler.getBlockedIdSynchronous(number, null);
+ if (id == null) {
+ return number + " is not blocked";
+ }
+ asyncQueryHandler.unblock((unusedRows, unusedValues) -> {}, id);
+ return "unblocked " + number;
+ }
+
+ if ("isblocked".equals(command)) {
+ String number = args.getPositionals().get(1);
+ Integer id = asyncQueryHandler.getBlockedIdSynchronous(number, null);
+ return id == null ? "false" : "true";
+ }
+
+ return getUsage();
+ }
+}
diff --git a/java/com/android/dialer/commandline/impl/Echo.java b/java/com/android/dialer/commandline/impl/Echo.java
index b5f2f084b..2741a4042 100644
--- a/java/com/android/dialer/commandline/impl/Echo.java
+++ b/java/com/android/dialer/commandline/impl/Echo.java
@@ -19,8 +19,8 @@ package com.android.dialer.commandline.impl;
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import android.text.TextUtils;
+import com.android.dialer.commandline.Arguments;
import com.android.dialer.commandline.Command;
-import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import javax.inject.Inject;
@@ -28,18 +28,24 @@ import javax.inject.Inject;
/** Print arguments. */
public class Echo implements Command {
- @VisibleForTesting
- @Inject
- public Echo() {}
-
+ @NonNull
@Override
- public ListenableFuture<String> run(ImmutableList<String> args) {
- return Futures.immediateFuture(TextUtils.join(" ", args));
+ public String getShortDescription() {
+ return "@hide Print all arguments.";
}
@NonNull
@Override
- public String getShortDescription() {
- return "@hide Print all arguments.";
+ public String getUsage() {
+ return "echo [arguments...]";
+ }
+
+ @VisibleForTesting
+ @Inject
+ public Echo() {}
+
+ @Override
+ public ListenableFuture<String> run(Arguments args) throws IllegalCommandLineArgumentException {
+ return Futures.immediateFuture(TextUtils.join(" ", args.getPositionals()));
}
}
diff --git a/java/com/android/dialer/commandline/impl/Help.java b/java/com/android/dialer/commandline/impl/Help.java
index d0e008014..357b10762 100644
--- a/java/com/android/dialer/commandline/impl/Help.java
+++ b/java/com/android/dialer/commandline/impl/Help.java
@@ -18,13 +18,14 @@ package com.android.dialer.commandline.impl;
import android.content.Context;
import android.support.annotation.NonNull;
+import com.android.dialer.commandline.Arguments;
import com.android.dialer.commandline.Command;
import com.android.dialer.commandline.CommandLineComponent;
import com.android.dialer.inject.ApplicationContext;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
+import java.util.Locale;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
import javax.inject.Inject;
@@ -32,6 +33,18 @@ import javax.inject.Inject;
/** List available commands */
public class Help implements Command {
+ @NonNull
+ @Override
+ public String getShortDescription() {
+ return "Print this message";
+ }
+
+ @NonNull
+ @Override
+ public String getUsage() {
+ return "help";
+ }
+
private final Context context;
@Inject
@@ -40,8 +53,8 @@ public class Help implements Command {
}
@Override
- public ListenableFuture<String> run(ImmutableList<String> args) {
- boolean showHidden = args.contains("--showHidden");
+ public ListenableFuture<String> run(Arguments args) throws IllegalCommandLineArgumentException {
+ boolean showHidden = args.getFlags().containsKey("showHidden");
StringBuilder stringBuilder = new StringBuilder();
ImmutableMap<String, Command> commands =
@@ -59,20 +72,15 @@ public class Help implements Command {
if (!showHidden && description.startsWith("@hide ")) {
continue;
}
- stringBuilder
- .append("\t")
- .append(entry.getKey())
- .append("\t")
- .append(description)
- .append("\n");
+ stringBuilder.append(String.format(Locale.US, " %20s %s\n", entry.getKey(), description));
}
return Futures.immediateFuture(stringBuilder.toString());
}
- private static String runOrThrow(Command command) {
+ private static String runOrThrow(Command command) throws IllegalCommandLineArgumentException {
try {
- return command.run(ImmutableList.of()).get();
+ return command.run(Arguments.EMPTY).get();
} catch (InterruptedException e) {
Thread.interrupted();
throw new RuntimeException(e);
@@ -80,10 +88,4 @@ public class Help implements Command {
throw new RuntimeException(e);
}
}
-
- @NonNull
- @Override
- public String getShortDescription() {
- return "Print this message";
- }
}
diff --git a/java/com/android/dialer/commandline/impl/Version.java b/java/com/android/dialer/commandline/impl/Version.java
index 5dfad9ae1..70476ea3e 100644
--- a/java/com/android/dialer/commandline/impl/Version.java
+++ b/java/com/android/dialer/commandline/impl/Version.java
@@ -20,9 +20,9 @@ import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.support.annotation.NonNull;
+import com.android.dialer.commandline.Arguments;
import com.android.dialer.commandline.Command;
import com.android.dialer.inject.ApplicationContext;
-import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.Locale;
@@ -31,6 +31,18 @@ import javax.inject.Inject;
/** Print the version name and code. */
public class Version implements Command {
+ @NonNull
+ @Override
+ public String getShortDescription() {
+ return "Print dialer version";
+ }
+
+ @NonNull
+ @Override
+ public String getUsage() {
+ return "version";
+ }
+
private final Context appContext;
@Inject
@@ -39,7 +51,7 @@ public class Version implements Command {
}
@Override
- public ListenableFuture<String> run(ImmutableList<String> args) {
+ public ListenableFuture<String> run(Arguments args) throws IllegalCommandLineArgumentException {
try {
PackageInfo info =
appContext.getPackageManager().getPackageInfo(appContext.getPackageName(), 0);
@@ -49,10 +61,4 @@ public class Version implements Command {
throw new RuntimeException(e);
}
}
-
- @NonNull
- @Override
- public String getShortDescription() {
- return "Print dialer version";
- }
}
diff --git a/java/com/android/dialer/common/concurrent/DialerExecutorComponent.java b/java/com/android/dialer/common/concurrent/DialerExecutorComponent.java
index 28abf96fd..734891430 100644
--- a/java/com/android/dialer/common/concurrent/DialerExecutorComponent.java
+++ b/java/com/android/dialer/common/concurrent/DialerExecutorComponent.java
@@ -18,6 +18,8 @@ package com.android.dialer.common.concurrent;
import android.app.FragmentManager;
import android.content.Context;
+import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
+import com.android.dialer.common.concurrent.Annotations.LightweightExecutor;
import com.android.dialer.common.concurrent.Annotations.NonUiParallel;
import com.android.dialer.common.concurrent.Annotations.Ui;
import com.android.dialer.inject.HasRootComponent;
@@ -31,17 +33,23 @@ public abstract class DialerExecutorComponent {
public abstract DialerExecutorFactory dialerExecutorFactory();
+ @NonUiParallel
+ public abstract ExecutorService lowPriorityThreadPool();
+
@Ui
- public abstract ListeningExecutorService uiExecutorService();
+ public abstract ListeningExecutorService uiExecutor();
+
+ @BackgroundExecutor
+ public abstract ListeningExecutorService backgroundExecutor();
+
+ @LightweightExecutor
+ public abstract ListeningExecutorService lightweightExecutor();
public <OutputT> UiListener<OutputT> createUiListener(
FragmentManager fragmentManager, String taskId) {
return UiListener.create(fragmentManager, taskId);
}
- @NonUiParallel
- public abstract ExecutorService lowPriorityThreadPool();
-
public static DialerExecutorComponent get(Context context) {
return ((DialerExecutorComponent.HasComponent)
((HasRootComponent) context.getApplicationContext()).component())
diff --git a/java/com/android/dialer/common/concurrent/UiListener.java b/java/com/android/dialer/common/concurrent/UiListener.java
index df791301f..b5922f9c8 100644
--- a/java/com/android/dialer/common/concurrent/UiListener.java
+++ b/java/com/android/dialer/common/concurrent/UiListener.java
@@ -96,7 +96,7 @@ public class UiListener<OutputT> extends Fragment {
Futures.addCallback(
Assert.isNotNull(future),
callbackWrapper,
- DialerExecutorComponent.get(context).uiExecutorService());
+ DialerExecutorComponent.get(context).uiExecutor());
}
private static class CallbackWrapper<OutputT> implements FutureCallback<OutputT> {
diff --git a/java/com/android/dialer/phonelookup/PhoneLookup.java b/java/com/android/dialer/phonelookup/PhoneLookup.java
index 118ae603e..76ff98e7c 100644
--- a/java/com/android/dialer/phonelookup/PhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/PhoneLookup.java
@@ -18,8 +18,6 @@ package com.android.dialer.phonelookup;
import android.content.Context;
import android.support.annotation.MainThread;
-import android.support.annotation.NonNull;
-import android.telecom.Call;
import com.android.dialer.DialerPhoneNumber;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -35,13 +33,13 @@ import com.google.common.util.concurrent.ListenableFuture;
public interface PhoneLookup<T> {
/**
- * Returns a future containing a new info for the provided call.
+ * Returns a future containing a new info for the provided number.
*
* <p>The returned message should contain populated data for the sub-message corresponding to this
* {@link PhoneLookup}. For example, the CP2 implementation returns a {@link
* PhoneLookupInfo.Cp2Info} sub-message.
*/
- ListenableFuture<T> lookup(@NonNull Call call);
+ ListenableFuture<T> lookup(DialerPhoneNumber dialerPhoneNumber);
/**
* Returns a future which returns true if the information for any of the provided phone numbers
diff --git a/java/com/android/dialer/phonelookup/PhoneLookupModule.java b/java/com/android/dialer/phonelookup/PhoneLookupModule.java
index e93ca0f77..8a78ba038 100644
--- a/java/com/android/dialer/phonelookup/PhoneLookupModule.java
+++ b/java/com/android/dialer/phonelookup/PhoneLookupModule.java
@@ -17,6 +17,7 @@
package com.android.dialer.phonelookup;
import com.android.dialer.phonelookup.blockednumber.DialerBlockedNumberPhoneLookup;
+import com.android.dialer.phonelookup.blockednumber.SystemBlockedNumberPhoneLookup;
import com.android.dialer.phonelookup.composite.CompositePhoneLookup;
import com.android.dialer.phonelookup.cp2.Cp2LocalPhoneLookup;
import com.android.dialer.phonelookup.cp2.Cp2RemotePhoneLookup;
@@ -33,9 +34,13 @@ public abstract class PhoneLookupModule {
static ImmutableList<PhoneLookup> providePhoneLookupList(
Cp2LocalPhoneLookup cp2LocalPhoneLookup,
Cp2RemotePhoneLookup cp2RemotePhoneLookup,
- DialerBlockedNumberPhoneLookup dialerBlockedNumberPhoneLookup) {
+ DialerBlockedNumberPhoneLookup dialerBlockedNumberPhoneLookup,
+ SystemBlockedNumberPhoneLookup systemBlockedNumberPhoneLookup) {
return ImmutableList.of(
- cp2LocalPhoneLookup, cp2RemotePhoneLookup, dialerBlockedNumberPhoneLookup);
+ cp2LocalPhoneLookup,
+ cp2RemotePhoneLookup,
+ dialerBlockedNumberPhoneLookup,
+ systemBlockedNumberPhoneLookup);
}
@Provides
diff --git a/java/com/android/dialer/phonelookup/blockednumber/DialerBlockedNumberPhoneLookup.java b/java/com/android/dialer/phonelookup/blockednumber/DialerBlockedNumberPhoneLookup.java
index e6c15e8d9..2271c7580 100644
--- a/java/com/android/dialer/phonelookup/blockednumber/DialerBlockedNumberPhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/blockednumber/DialerBlockedNumberPhoneLookup.java
@@ -17,15 +17,11 @@
package com.android.dialer.phonelookup.blockednumber;
import android.content.Context;
-import android.database.ContentObserver;
import android.database.Cursor;
-import android.net.Uri;
-import android.support.annotation.MainThread;
-import android.support.annotation.NonNull;
import android.support.annotation.WorkerThread;
-import android.telecom.Call;
import android.util.ArraySet;
import com.android.dialer.DialerPhoneNumber;
+import com.android.dialer.blocking.FilteredNumberCompat;
import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
@@ -38,15 +34,12 @@ import com.android.dialer.phonelookup.PhoneLookup;
import com.android.dialer.phonelookup.PhoneLookupInfo;
import com.android.dialer.phonelookup.PhoneLookupInfo.BlockedState;
import com.android.dialer.phonelookup.PhoneLookupInfo.DialerBlockedNumberInfo;
-import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil;
import com.android.dialer.phonenumberproto.PartitionedNumbers;
-import com.android.dialer.telecom.TelecomCallUtil;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
-import com.google.i18n.phonenumbers.PhoneNumberUtil;
import java.util.Set;
import javax.inject.Inject;
@@ -68,18 +61,12 @@ public final class DialerBlockedNumberPhoneLookup implements PhoneLookup<DialerB
}
@Override
- public ListenableFuture<DialerBlockedNumberInfo> lookup(@NonNull Call call) {
+ public ListenableFuture<DialerBlockedNumberInfo> lookup(DialerPhoneNumber dialerPhoneNumber) {
+ if (FilteredNumberCompat.useNewFiltering(appContext)) {
+ return Futures.immediateFuture(DialerBlockedNumberInfo.getDefaultInstance());
+ }
return executorService.submit(
- () -> {
- DialerPhoneNumberUtil dialerPhoneNumberUtil =
- new DialerPhoneNumberUtil(PhoneNumberUtil.getInstance());
-
- DialerPhoneNumber number =
- dialerPhoneNumberUtil.parse(
- TelecomCallUtil.getNumber(call),
- TelecomCallUtil.getCountryCode(appContext, call).orNull());
- return queryNumbers(ImmutableSet.of(number)).get(number);
- });
+ () -> queryNumbers(ImmutableSet.of(dialerPhoneNumber)).get(dialerPhoneNumber));
}
@Override
@@ -92,6 +79,9 @@ public final class DialerBlockedNumberPhoneLookup implements PhoneLookup<DialerB
@Override
public ListenableFuture<ImmutableMap<DialerPhoneNumber, DialerBlockedNumberInfo>>
getMostRecentInfo(ImmutableMap<DialerPhoneNumber, DialerBlockedNumberInfo> existingInfoMap) {
+ if (FilteredNumberCompat.useNewFiltering(appContext)) {
+ return Futures.immediateFuture(existingInfoMap);
+ }
LogUtil.enterBlock("DialerBlockedNumberPhoneLookup.getMostRecentPhoneLookupInfo");
return executorService.submit(() -> queryNumbers(existingInfoMap.keySet()));
}
@@ -141,11 +131,7 @@ public final class DialerBlockedNumberPhoneLookup implements PhoneLookup<DialerB
}
Selection rawSelection =
- Selection.column(FilteredNumberColumns.NUMBER)
- .in(
- partitionedNumbers
- .invalidNumbers()
- .toArray(new String[partitionedNumbers.invalidNumbers().size()]));
+ Selection.column(FilteredNumberColumns.NUMBER).in(partitionedNumbers.invalidNumbers());
try (Cursor cursor =
appContext
.getContentResolver()
@@ -186,26 +172,6 @@ public final class DialerBlockedNumberPhoneLookup implements PhoneLookup<DialerB
.registerContentObserver(
FilteredNumber.CONTENT_URI,
true, // FilteredNumberProvider notifies on the item
- new FilteredNumberObserver(appContext, contentObserverCallbacks));
- }
-
- private static class FilteredNumberObserver extends ContentObserver {
- private final Context appContext;
- private final ContentObserverCallbacks contentObserverCallbacks;
-
- FilteredNumberObserver(Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
- super(null);
- this.appContext = appContext;
- this.contentObserverCallbacks = contentObserverCallbacks;
- }
-
- @MainThread
- @Override
- @SuppressWarnings("FutureReturnValueIgnored") // never throws.
- public void onChange(boolean selfChange, Uri uri) {
- Assert.isMainThread();
- LogUtil.enterBlock("DialerBlockedNumberPhoneLookup.FilteredNumberObserver.onChange");
- contentObserverCallbacks.markDirtyAndNotify(appContext);
- }
+ new MarkDirtyObserver(appContext, contentObserverCallbacks));
}
}
diff --git a/java/com/android/dialer/phonelookup/blockednumber/MarkDirtyObserver.java b/java/com/android/dialer/phonelookup/blockednumber/MarkDirtyObserver.java
new file mode 100644
index 000000000..1c41d8f7f
--- /dev/null
+++ b/java/com/android/dialer/phonelookup/blockednumber/MarkDirtyObserver.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 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.phonelookup.blockednumber;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.support.annotation.MainThread;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.ThreadUtil;
+import com.android.dialer.phonelookup.PhoneLookup.ContentObserverCallbacks;
+
+/** Calls {@link ContentObserverCallbacks#markDirtyAndNotify(Context)} when the content changed */
+class MarkDirtyObserver extends ContentObserver {
+
+ private final Context appContext;
+ private final ContentObserverCallbacks contentObserverCallbacks;
+
+ MarkDirtyObserver(Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
+ super(ThreadUtil.getUiThreadHandler());
+ this.appContext = appContext;
+ this.contentObserverCallbacks = contentObserverCallbacks;
+ }
+
+ @MainThread
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ Assert.isMainThread();
+ LogUtil.enterBlock("SystemBlockedNumberPhoneLookup.FilteredNumberObserver.onChange");
+ contentObserverCallbacks.markDirtyAndNotify(appContext);
+ }
+}
diff --git a/java/com/android/dialer/phonelookup/blockednumber/SystemBlockedNumberPhoneLookup.java b/java/com/android/dialer/phonelookup/blockednumber/SystemBlockedNumberPhoneLookup.java
new file mode 100644
index 000000000..e0ff995e7
--- /dev/null
+++ b/java/com/android/dialer/phonelookup/blockednumber/SystemBlockedNumberPhoneLookup.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2018 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.phonelookup.blockednumber;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.database.Cursor;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.provider.BlockedNumberContract.BlockedNumbers;
+import android.support.annotation.NonNull;
+import android.support.annotation.WorkerThread;
+import android.util.ArraySet;
+import com.android.dialer.DialerPhoneNumber;
+import com.android.dialer.blocking.FilteredNumberCompat;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
+import com.android.dialer.common.database.Selection;
+import com.android.dialer.inject.ApplicationContext;
+import com.android.dialer.phonelookup.PhoneLookup;
+import com.android.dialer.phonelookup.PhoneLookupInfo;
+import com.android.dialer.phonelookup.PhoneLookupInfo.BlockedState;
+import com.android.dialer.phonelookup.PhoneLookupInfo.Builder;
+import com.android.dialer.phonelookup.PhoneLookupInfo.SystemBlockedNumberInfo;
+import com.android.dialer.phonenumberproto.PartitionedNumbers;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import java.util.Set;
+import javax.inject.Inject;
+
+/**
+ * Lookup blocked numbers in the system database. Requires N+ and migration from dialer database
+ * completed (need user consent to move data into system).
+ */
+public class SystemBlockedNumberPhoneLookup implements PhoneLookup<SystemBlockedNumberInfo> {
+
+ private final Context appContext;
+ private final ListeningExecutorService executorService;
+
+ @Inject
+ SystemBlockedNumberPhoneLookup(
+ @ApplicationContext Context appContext,
+ @BackgroundExecutor ListeningExecutorService executorService) {
+ this.appContext = appContext;
+ this.executorService = executorService;
+ }
+
+ @Override
+ public ListenableFuture<SystemBlockedNumberInfo> lookup(@NonNull DialerPhoneNumber number) {
+ if (!FilteredNumberCompat.useNewFiltering(appContext)) {
+ return Futures.immediateFuture(SystemBlockedNumberInfo.getDefaultInstance());
+ }
+ return executorService.submit(
+ () -> {
+ return queryNumbers(ImmutableSet.of(number)).get(number);
+ });
+ }
+
+ @Override
+ public ListenableFuture<Boolean> isDirty(ImmutableSet<DialerPhoneNumber> phoneNumbers) {
+ // Dirty state is recorded with PhoneLookupDataSource.markDirtyAndNotify(), which will force
+ // rebuild with the CallLogFramework
+ return Futures.immediateFuture(false);
+ }
+
+ @Override
+ public ListenableFuture<ImmutableMap<DialerPhoneNumber, SystemBlockedNumberInfo>>
+ getMostRecentInfo(ImmutableMap<DialerPhoneNumber, SystemBlockedNumberInfo> existingInfoMap) {
+ LogUtil.enterBlock("SystemBlockedNumberPhoneLookup.getMostRecentPhoneLookupInfo");
+ if (!FilteredNumberCompat.useNewFiltering(appContext)) {
+ return Futures.immediateFuture(existingInfoMap);
+ }
+ return executorService.submit(() -> queryNumbers(existingInfoMap.keySet()));
+ }
+
+ @WorkerThread
+ @TargetApi(VERSION_CODES.N)
+ private ImmutableMap<DialerPhoneNumber, SystemBlockedNumberInfo> queryNumbers(
+ ImmutableSet<DialerPhoneNumber> numbers) {
+ Assert.isWorkerThread();
+ PartitionedNumbers partitionedNumbers = new PartitionedNumbers(numbers);
+
+ Set<DialerPhoneNumber> blockedNumbers = new ArraySet<>();
+
+ Selection normalizedSelection =
+ Selection.column(BlockedNumbers.COLUMN_E164_NUMBER)
+ .in(partitionedNumbers.validE164Numbers());
+ try (Cursor cursor =
+ appContext
+ .getContentResolver()
+ .query(
+ BlockedNumbers.CONTENT_URI,
+ new String[] {BlockedNumbers.COLUMN_E164_NUMBER},
+ normalizedSelection.getSelection(),
+ normalizedSelection.getSelectionArgs(),
+ null)) {
+ while (cursor != null && cursor.moveToNext()) {
+ blockedNumbers.addAll(
+ partitionedNumbers.dialerPhoneNumbersForValidE164(cursor.getString(0)));
+ }
+ }
+
+ Selection rawSelection =
+ Selection.column(BlockedNumbers.COLUMN_ORIGINAL_NUMBER)
+ .in(partitionedNumbers.invalidNumbers());
+ try (Cursor cursor =
+ appContext
+ .getContentResolver()
+ .query(
+ BlockedNumbers.CONTENT_URI,
+ new String[] {BlockedNumbers.COLUMN_ORIGINAL_NUMBER},
+ rawSelection.getSelection(),
+ rawSelection.getSelectionArgs(),
+ null)) {
+ while (cursor != null && cursor.moveToNext()) {
+ blockedNumbers.addAll(partitionedNumbers.dialerPhoneNumbersForInvalid(cursor.getString(0)));
+ }
+ }
+
+ ImmutableMap.Builder<DialerPhoneNumber, SystemBlockedNumberInfo> result =
+ ImmutableMap.builder();
+
+ for (DialerPhoneNumber number : numbers) {
+ result.put(
+ number,
+ SystemBlockedNumberInfo.newBuilder()
+ .setBlockedState(
+ blockedNumbers.contains(number) ? BlockedState.BLOCKED : BlockedState.NOT_BLOCKED)
+ .build());
+ }
+
+ return result.build();
+ }
+
+ @Override
+ public void setSubMessage(Builder phoneLookupInfo, SystemBlockedNumberInfo subMessage) {
+ phoneLookupInfo.setSystemBlockedNumberInfo(subMessage);
+ }
+
+ @Override
+ public SystemBlockedNumberInfo getSubMessage(PhoneLookupInfo phoneLookupInfo) {
+ return phoneLookupInfo.getSystemBlockedNumberInfo();
+ }
+
+ @Override
+ public ListenableFuture<Void> onSuccessfulBulkUpdate() {
+ return Futures.immediateFuture(null);
+ }
+
+ @Override
+ public void registerContentObservers(
+ Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
+ if (VERSION.SDK_INT < VERSION_CODES.N) {
+ return;
+ }
+ appContext
+ .getContentResolver()
+ .registerContentObserver(
+ BlockedNumbers.CONTENT_URI,
+ true, // BlockedNumbers notifies on the item
+ new MarkDirtyObserver(appContext, contentObserverCallbacks));
+ }
+}
diff --git a/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java b/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java
index 68695b7e9..622b4db10 100644
--- a/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java
@@ -18,8 +18,6 @@ package com.android.dialer.phonelookup.composite;
import android.content.Context;
import android.support.annotation.MainThread;
-import android.support.annotation.NonNull;
-import android.telecom.Call;
import com.android.dialer.DialerPhoneNumber;
import com.android.dialer.common.LogUtil;
import com.android.dialer.common.concurrent.Annotations.LightweightExecutor;
@@ -65,12 +63,12 @@ public final class CompositePhoneLookup implements PhoneLookup<PhoneLookupInfo>
*/
@SuppressWarnings({"unchecked", "rawtype"})
@Override
- public ListenableFuture<PhoneLookupInfo> lookup(@NonNull Call call) {
+ public ListenableFuture<PhoneLookupInfo> lookup(DialerPhoneNumber dialerPhoneNumber) {
// TODO(zachh): Add short-circuiting logic so that this call is not blocked on low-priority
// lookups finishing when a higher-priority one has already finished.
List<ListenableFuture<?>> futures = new ArrayList<>();
for (PhoneLookup<?> phoneLookup : phoneLookups) {
- futures.add(phoneLookup.lookup(call));
+ futures.add(phoneLookup.lookup(dialerPhoneNumber));
}
return Futures.transform(
Futures.allAsList(futures),
diff --git a/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java b/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java
index ccad3e7bc..27f0d21ae 100644
--- a/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java
+++ b/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java
@@ -179,11 +179,7 @@ public final class PhoneLookupInfoConsolidator {
* returned.
*/
public String getNumberLabel() {
- if (phoneLookupInfo.hasDialerBlockedNumberInfo()
- && phoneLookupInfo
- .getDialerBlockedNumberInfo()
- .getBlockedState()
- .equals(BlockedState.BLOCKED)) {
+ if (isBlocked()) {
return appContext.getString(R.string.blocked_number_new_call_log_label);
}
@@ -219,6 +215,21 @@ public final class PhoneLookupInfoConsolidator {
return false;
}
+ public boolean isBlocked() {
+ // If system blocking reported blocked state it always takes priority over the dialer blocking.
+ // It will be absent if dialer blocking should be used.
+ if (phoneLookupInfo.getSystemBlockedNumberInfo().hasBlockedState()) {
+ return phoneLookupInfo
+ .getSystemBlockedNumberInfo()
+ .getBlockedState()
+ .equals(BlockedState.BLOCKED);
+ }
+ return phoneLookupInfo
+ .getDialerBlockedNumberInfo()
+ .getBlockedState()
+ .equals(BlockedState.BLOCKED);
+ }
+
/**
* Returns true if the {@link PhoneLookupInfo} passed to the constructor has incomplete CP2 local
* info.
diff --git a/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java b/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java
index 995950d0e..127569b9f 100644
--- a/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java
@@ -27,7 +27,6 @@ import android.provider.ContactsContract.DeletedContacts;
import android.support.annotation.Nullable;
import android.support.v4.util.ArrayMap;
import android.support.v4.util.ArraySet;
-import android.telecom.Call;
import android.text.TextUtils;
import com.android.dialer.DialerPhoneNumber;
import com.android.dialer.common.Assert;
@@ -43,7 +42,6 @@ import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContra
import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil;
import com.android.dialer.phonenumberproto.PartitionedNumbers;
import com.android.dialer.storage.Unencrypted;
-import com.android.dialer.telecom.TelecomCallUtil;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -93,16 +91,18 @@ public final class Cp2LocalPhoneLookup implements PhoneLookup<Cp2Info> {
}
@Override
- public ListenableFuture<Cp2Info> lookup(Call call) {
- return backgroundExecutorService.submit(() -> lookupInternal(call));
+ public ListenableFuture<Cp2Info> lookup(DialerPhoneNumber dialerPhoneNumber) {
+ return backgroundExecutorService.submit(() -> lookupInternal(dialerPhoneNumber));
}
- private Cp2Info lookupInternal(Call call) {
- String rawNumber = TelecomCallUtil.getNumber(call);
- if (TextUtils.isEmpty(rawNumber)) {
+ private Cp2Info lookupInternal(DialerPhoneNumber dialerPhoneNumber) {
+ DialerPhoneNumberUtil dialerPhoneNumberUtil =
+ new DialerPhoneNumberUtil(PhoneNumberUtil.getInstance());
+ String number = dialerPhoneNumberUtil.normalizeNumber(dialerPhoneNumber);
+ if (TextUtils.isEmpty(number)) {
return Cp2Info.getDefaultInstance();
}
- Optional<String> validE164 = TelecomCallUtil.getValidE164Number(appContext, call);
+ Optional<String> validE164 = dialerPhoneNumberUtil.formatToValidE164(dialerPhoneNumber);
Set<Cp2ContactInfo> cp2ContactInfos = new ArraySet<>();
// Note: It would make sense to use PHONE_LOOKUP for E164 numbers as well, but we use PHONE to
// ensure consistency when the batch methods are used to update data.
@@ -110,7 +110,7 @@ public final class Cp2LocalPhoneLookup implements PhoneLookup<Cp2Info> {
validE164.isPresent()
? queryPhoneTableBasedOnE164(
Cp2Projections.getProjectionForPhoneTable(), ImmutableSet.of(validE164.get()))
- : queryPhoneLookup(Cp2Projections.getProjectionForPhoneLookupTable(), rawNumber)) {
+ : queryPhoneLookup(Cp2Projections.getProjectionForPhoneLookupTable(), number)) {
if (cursor == null) {
LogUtil.w("Cp2LocalPhoneLookup.lookupInternal", "null cursor");
return Cp2Info.getDefaultInstance();
@@ -122,35 +122,6 @@ public final class Cp2LocalPhoneLookup implements PhoneLookup<Cp2Info> {
return Cp2Info.newBuilder().addAllCp2ContactInfo(cp2ContactInfos).build();
}
- /**
- * Queries ContactsContract.PhoneLookup for the {@link Cp2Info} associated with the provided
- * {@link DialerPhoneNumber}. Returns {@link Cp2Info#getDefaultInstance()} if there is no
- * information.
- */
- public ListenableFuture<Cp2Info> lookupByNumber(DialerPhoneNumber dialerPhoneNumber) {
- return backgroundExecutorService.submit(
- () -> {
- DialerPhoneNumberUtil dialerPhoneNumberUtil =
- new DialerPhoneNumberUtil(PhoneNumberUtil.getInstance());
- String rawNumber = dialerPhoneNumberUtil.normalizeNumber(dialerPhoneNumber);
- if (rawNumber.isEmpty()) {
- return Cp2Info.getDefaultInstance();
- }
- Set<Cp2ContactInfo> cp2ContactInfos = new ArraySet<>();
- try (Cursor cursor =
- queryPhoneLookup(Cp2Projections.getProjectionForPhoneLookupTable(), rawNumber)) {
- if (cursor == null) {
- LogUtil.w("Cp2LocalPhoneLookup.lookupByNumber", "null cursor");
- return Cp2Info.getDefaultInstance();
- }
- while (cursor.moveToNext()) {
- cp2ContactInfos.add(Cp2Projections.buildCp2ContactInfoFromCursor(appContext, cursor));
- }
- }
- return Cp2Info.newBuilder().addAllCp2ContactInfo(cp2ContactInfos).build();
- });
- }
-
@Override
public ListenableFuture<Boolean> isDirty(ImmutableSet<DialerPhoneNumber> phoneNumbers) {
PartitionedNumbers partitionedNumbers = new PartitionedNumbers(phoneNumbers);
diff --git a/java/com/android/dialer/phonelookup/cp2/Cp2RemotePhoneLookup.java b/java/com/android/dialer/phonelookup/cp2/Cp2RemotePhoneLookup.java
index 6a4682958..9596f15f7 100644
--- a/java/com/android/dialer/phonelookup/cp2/Cp2RemotePhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/cp2/Cp2RemotePhoneLookup.java
@@ -24,7 +24,6 @@ import android.os.Build.VERSION_CODES;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Directory;
import android.support.annotation.VisibleForTesting;
-import android.telecom.Call;
import com.android.dialer.DialerPhoneNumber;
import com.android.dialer.common.LogUtil;
import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
@@ -33,13 +32,14 @@ import com.android.dialer.inject.ApplicationContext;
import com.android.dialer.phonelookup.PhoneLookup;
import com.android.dialer.phonelookup.PhoneLookupInfo;
import com.android.dialer.phonelookup.PhoneLookupInfo.Cp2Info;
+import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil;
import com.android.dialer.phonenumberutil.PhoneNumberHelper;
-import com.android.dialer.telecom.TelecomCallUtil;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.i18n.phonenumbers.PhoneNumberUtil;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
@@ -62,15 +62,10 @@ public final class Cp2RemotePhoneLookup implements PhoneLookup<Cp2Info> {
}
@Override
- public ListenableFuture<Cp2Info> lookup(Call call) {
- String number = TelecomCallUtil.getNumber(call);
- if (number == null) {
- return Futures.immediateFuture(Cp2Info.getDefaultInstance());
- }
-
+ public ListenableFuture<Cp2Info> lookup(DialerPhoneNumber dialerPhoneNumber) {
return Futures.transformAsync(
queryCp2ForRemoteDirectoryIds(),
- remoteDirectoryIds -> queryCp2ForRemoteContact(number, remoteDirectoryIds),
+ remoteDirectoryIds -> queryCp2ForRemoteContact(dialerPhoneNumber, remoteDirectoryIds),
lightweightExecutorService);
}
@@ -114,11 +109,15 @@ public final class Cp2RemotePhoneLookup implements PhoneLookup<Cp2Info> {
}
private ListenableFuture<Cp2Info> queryCp2ForRemoteContact(
- String number, List<Long> remoteDirectoryIds) {
+ DialerPhoneNumber dialerPhoneNumber, List<Long> remoteDirectoryIds) {
if (remoteDirectoryIds.isEmpty()) {
return Futures.immediateFuture(Cp2Info.getDefaultInstance());
}
+ DialerPhoneNumberUtil dialerPhoneNumberUtil =
+ new DialerPhoneNumberUtil(PhoneNumberUtil.getInstance());
+ String number = dialerPhoneNumberUtil.normalizeNumber(dialerPhoneNumber);
+
List<ListenableFuture<Cp2Info>> cp2InfoFutures = new ArrayList<>();
for (long remoteDirectoryId : remoteDirectoryIds) {
cp2InfoFutures.add(queryCp2ForRemoteContact(number, remoteDirectoryId));
diff --git a/java/com/android/dialer/speeddial/res/layout/fragment_speed_dial.xml b/java/com/android/dialer/speeddial/res/layout/fragment_speed_dial.xml
index ecbb95193..9f2dec931 100644
--- a/java/com/android/dialer/speeddial/res/layout/fragment_speed_dial.xml
+++ b/java/com/android/dialer/speeddial/res/layout/fragment_speed_dial.xml
@@ -22,4 +22,5 @@
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:clipToPadding="false"
- android:background="@color/background_dialer_light"/>
+ android:background="@color/background_dialer_light"
+ android:paddingBottom="@dimen/floating_action_button_list_bottom_padding"/>
diff --git a/java/com/android/dialer/theme/res/values/dimens.xml b/java/com/android/dialer/theme/res/values/dimens.xml
index 2b5243ebd..88b8a0423 100644
--- a/java/com/android/dialer/theme/res/values/dimens.xml
+++ b/java/com/android/dialer/theme/res/values/dimens.xml
@@ -50,4 +50,8 @@
<!-- Minimum width for material compliant buttons. -->
<dimen name="dialer_button_min_width">72dp</dimen>
+
+ <!-- Padding to be applied to the bottom of lists to make space for the floating action
+ button -->
+ <dimen name="floating_action_button_list_bottom_padding">88dp</dimen>
</resources>
diff --git a/java/com/android/dialer/voicemail/listui/res/layout/new_voicemail_call_log_fragment.xml b/java/com/android/dialer/voicemail/listui/res/layout/new_voicemail_call_log_fragment.xml
index c3d885968..9daa3e114 100644
--- a/java/com/android/dialer/voicemail/listui/res/layout/new_voicemail_call_log_fragment.xml
+++ b/java/com/android/dialer/voicemail/listui/res/layout/new_voicemail_call_log_fragment.xml
@@ -17,7 +17,6 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:fab="http://schemas.android.com/apk/res-auto"
android:id="@+id/fragment_my_frame_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -29,6 +28,8 @@
android:id="@+id/new_voicemail_call_log_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@color/background_dialer_light"/>
+ android:background="@color/background_dialer_light"
+ android:paddingBottom="@dimen/floating_action_button_list_bottom_padding"
+ android:clipToPadding="false"/>
</FrameLayout>
diff --git a/java/com/android/incallui/PhoneLookupHistoryRecorder.java b/java/com/android/incallui/PhoneLookupHistoryRecorder.java
index 8517deb65..abbf934f0 100644
--- a/java/com/android/incallui/PhoneLookupHistoryRecorder.java
+++ b/java/com/android/incallui/PhoneLookupHistoryRecorder.java
@@ -19,18 +19,23 @@ import android.content.ContentValues;
import android.content.Context;
import android.support.annotation.Nullable;
import android.telecom.Call;
+import com.android.dialer.DialerPhoneNumber;
import com.android.dialer.buildtype.BuildType;
import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
-import com.android.dialer.common.concurrent.DialerExecutors;
+import com.android.dialer.common.concurrent.DialerExecutorComponent;
import com.android.dialer.phonelookup.PhoneLookupComponent;
import com.android.dialer.phonelookup.PhoneLookupInfo;
import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract.PhoneLookupHistory;
+import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil;
import com.android.dialer.telecom.TelecomCallUtil;
import com.google.common.base.Optional;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.i18n.phonenumbers.PhoneNumberUtil;
/**
* Fetches the current {@link PhoneLookupInfo} for the provided call and writes it to the
@@ -46,10 +51,29 @@ final class PhoneLookupHistoryRecorder {
if (!(BuildType.get() == BuildType.BUGFOOD || LogUtil.isDebugEnabled())) {
return;
}
- ListenableFuture<PhoneLookupInfo> future =
- PhoneLookupComponent.get(appContext).phoneLookup().lookup(call);
+
+ ListeningExecutorService backgroundExecutor =
+ DialerExecutorComponent.get(appContext).backgroundExecutor();
+
+ ListenableFuture<DialerPhoneNumber> numberFuture =
+ backgroundExecutor.submit(
+ () -> {
+ DialerPhoneNumberUtil dialerPhoneNumberUtil =
+ new DialerPhoneNumberUtil(PhoneNumberUtil.getInstance());
+ return dialerPhoneNumberUtil.parse(
+ TelecomCallUtil.getNumber(call),
+ TelecomCallUtil.getCountryCode(appContext, call).orNull());
+ });
+
+ ListenableFuture<PhoneLookupInfo> infoFuture =
+ Futures.transformAsync(
+ numberFuture,
+ dialerPhoneNumber ->
+ PhoneLookupComponent.get(appContext).phoneLookup().lookup(dialerPhoneNumber),
+ MoreExecutors.directExecutor());
+
Futures.addCallback(
- future,
+ infoFuture,
new FutureCallback<PhoneLookupInfo>() {
@Override
public void onSuccess(@Nullable PhoneLookupInfo result) {
@@ -79,6 +103,6 @@ final class PhoneLookupHistoryRecorder {
"PhoneLookupHistoryRecorder.onFailure", "could not write PhoneLookupHistory", t);
}
},
- DialerExecutors.getLowPriorityThreadPool(appContext));
+ backgroundExecutor);
}
}