diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2018-01-23 01:46:59 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2018-01-23 01:46:59 +0000 |
commit | c979f257060fbcb95cbd4871461dd5eb1ae04f1b (patch) | |
tree | 9ae99486deaf71b55a3f2a635ff95581d703c90b | |
parent | e5a20898faa4c66758a30d12e9245f15d6685a37 (diff) | |
parent | 0e00f578d641440c5ddc4da48ab86eabbc21d403 (diff) |
Merge changes I1360b7ee,I587f5f4d,I8cb26827,Ib8766a89,I952f4935
* changes:
Implement System blocked number
Don't let the FAB obscure the last item of speed dial/call log/voicemail.
Changed PhoneLookup#lookup to accept a DialerPhoneNumber.
For call log entry labels, remove the dot after abbreviated time units.
Implement blocking commands
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); } } |