diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2017-12-22 18:44:31 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2017-12-22 18:44:31 +0000 |
commit | 88518503c1351ba86f503ebd8742d6b292928ddd (patch) | |
tree | 97c6b6429cd3a0276ece3bba0ce022e4c6b92866 /java/com/android/dialer | |
parent | e2da6ef8a56bd9f7eedfa1c6d89e968590e0c09b (diff) | |
parent | 1e8275f3e6f57f285900ce35946064e0550b174a (diff) |
Merge changes I66342431,I5e579f11,I927711aa,Idd4cc115,If8cdbdfa, ...
* changes:
Automated rollback of changelist 179615699
Automated rollback of changelist 179847039
Differentiate read/unread voicemails in the NUI Voicemail by bolding.
Use CHAR_TO_KEY_MAPS in DialpadCharMappings in SmartDialMaps.
Reorganize classes related to smart dial.
Use the orientation obtained in onFinishInflate as the truth in DialpadView.
Added context menu for favorite contacts in new speed dial.
Update assisted dialing extras in preparation for platform implementation.
Quick fix for bugs on simulator voice call.
Fixed crash in UiListener when launching activity with screen off.
Handle contacts which have been disassociated with a number in Cp2PhoneLookup.
Fixed crash in PinnedShortcuts.
Bug: 70402588
Added copySubMessage method to PhoneLookup interface.
Bubble v2 changes.
Always have hardware acceration enabled for InCallActivity
Include inserted calls for consideration in PhoneLookupDataSource.
Don't use phone number in spam call notifications
Register content observer when voicemail table changes.
Add SmartDialMaps for the Bulgarian alphabet and the Ukrainian alphabet.
Remove voicemail from UI only after delete request.
Bubble v2 RTL language fixes.
Implemented SpeedDialEntry Room Database.
Diffstat (limited to 'java/com/android/dialer')
37 files changed, 492 insertions, 219 deletions
diff --git a/java/com/android/dialer/app/DialtactsActivity.java b/java/com/android/dialer/app/DialtactsActivity.java index eb95a4ee4..bbb45943a 100644 --- a/java/com/android/dialer/app/DialtactsActivity.java +++ b/java/com/android/dialer/app/DialtactsActivity.java @@ -133,8 +133,8 @@ import com.android.dialer.searchfragment.list.NewSearchFragment; import com.android.dialer.searchfragment.list.NewSearchFragment.SearchFragmentListener; import com.android.dialer.simulator.Simulator; import com.android.dialer.simulator.SimulatorComponent; -import com.android.dialer.smartdial.SmartDialNameMatcher; -import com.android.dialer.smartdial.SmartDialPrefix; +import com.android.dialer.smartdial.util.SmartDialNameMatcher; +import com.android.dialer.smartdial.util.SmartDialPrefix; import com.android.dialer.storage.StorageComponent; import com.android.dialer.telecom.TelecomUtil; import com.android.dialer.util.DialerUtils; diff --git a/java/com/android/dialer/app/list/SmartDialNumberListAdapter.java b/java/com/android/dialer/app/list/SmartDialNumberListAdapter.java index 5b48ccfd0..1d2cda3ea 100644 --- a/java/com/android/dialer/app/list/SmartDialNumberListAdapter.java +++ b/java/com/android/dialer/app/list/SmartDialNumberListAdapter.java @@ -22,9 +22,9 @@ import android.telephony.PhoneNumberUtils; import android.text.TextUtils; import com.android.contacts.common.list.ContactListItemView; import com.android.dialer.common.LogUtil; -import com.android.dialer.dialpadview.SmartDialCursorLoader; -import com.android.dialer.smartdial.SmartDialMatchPosition; -import com.android.dialer.smartdial.SmartDialNameMatcher; +import com.android.dialer.smartdial.SmartDialCursorLoader; +import com.android.dialer.smartdial.util.SmartDialMatchPosition; +import com.android.dialer.smartdial.util.SmartDialNameMatcher; import com.android.dialer.util.CallUtil; import java.util.ArrayList; diff --git a/java/com/android/dialer/app/list/SmartDialSearchFragment.java b/java/com/android/dialer/app/list/SmartDialSearchFragment.java index e97a16c19..1a7f19515 100644 --- a/java/com/android/dialer/app/list/SmartDialSearchFragment.java +++ b/java/com/android/dialer/app/list/SmartDialSearchFragment.java @@ -31,7 +31,7 @@ import com.android.dialer.app.R; import com.android.dialer.callintent.CallInitiationType; import com.android.dialer.common.LogUtil; import com.android.dialer.database.DialerDatabaseHelper; -import com.android.dialer.dialpadview.SmartDialCursorLoader; +import com.android.dialer.smartdial.SmartDialCursorLoader; import com.android.dialer.util.PermissionsUtil; import com.android.dialer.widget.EmptyContentView; import java.util.Arrays; diff --git a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java index fbb58312a..042ff30a2 100644 --- a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java +++ b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java @@ -110,7 +110,9 @@ public final class PhoneLookupDataSource implements CallLogDataSource { * <p>This method uses the following algorithm: * * <ul> - * <li>Selects the distinct DialerPhoneNumbers from the AnnotatedCallLog + * <li>Finds the phone numbers of interest by taking the union of the distinct + * DialerPhoneNumbers from the AnnotatedCallLog and the pending inserts provided in {@code + * mutations} * <li>Uses them to fetch the current information from PhoneLookupHistory, in order to construct * a map from DialerPhoneNumber to PhoneLookupInfo * <ul> @@ -137,9 +139,10 @@ public final class PhoneLookupDataSource implements CallLogDataSource { phoneLookupHistoryRowsToUpdate.clear(); phoneLookupHistoryRowsToDelete.clear(); - // First query information from annotated call log. + // First query information from annotated call log (and include pending inserts). ListenableFuture<Map<DialerPhoneNumber, Set<Long>>> annotatedCallLogIdsByNumberFuture = - backgroundExecutorService.submit(() -> queryIdAndNumberFromAnnotatedCallLog(appContext)); + backgroundExecutorService.submit( + () -> collectIdAndNumberFromAnnotatedCallLogAndPendingInserts(appContext, mutations)); // Use it to create the original info map. ListenableFuture<ImmutableMap<DialerPhoneNumber, PhoneLookupInfo>> originalInfoMapFuture = @@ -317,9 +320,28 @@ public final class PhoneLookupDataSource implements CallLogDataSource { return numbers.build(); } - private Map<DialerPhoneNumber, Set<Long>> queryIdAndNumberFromAnnotatedCallLog( - Context appContext) { + private Map<DialerPhoneNumber, Set<Long>> collectIdAndNumberFromAnnotatedCallLogAndPendingInserts( + Context appContext, CallLogMutations mutations) { Map<DialerPhoneNumber, Set<Long>> idsByNumber = new ArrayMap<>(); + // First add any pending inserts to the map. + for (Entry<Long, ContentValues> entry : mutations.getInserts().entrySet()) { + long id = entry.getKey(); + ContentValues insertedContentValues = entry.getValue(); + DialerPhoneNumber dialerPhoneNumber; + try { + dialerPhoneNumber = + DialerPhoneNumber.parseFrom( + insertedContentValues.getAsByteArray(AnnotatedCallLog.NUMBER)); + } catch (InvalidProtocolBufferException e) { + throw new IllegalStateException(e); + } + Set<Long> ids = idsByNumber.get(dialerPhoneNumber); + if (ids == null) { + ids = new ArraySet<>(); + idsByNumber.put(dialerPhoneNumber, ids); + } + ids.add(id); + } try (Cursor cursor = appContext @@ -332,7 +354,9 @@ public final class PhoneLookupDataSource implements CallLogDataSource { null)) { if (cursor == null) { - LogUtil.e("PhoneLookupDataSource.queryIdAndNumberFromAnnotatedCallLog", "null cursor"); + LogUtil.e( + "PhoneLookupDataSource.collectIdAndNumberFromAnnotatedCallLogAndPendingInserts", + "null cursor"); return ImmutableMap.of(); } diff --git a/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java b/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java index 0ed185966..95fbf9d04 100644 --- a/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java +++ b/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java @@ -27,6 +27,7 @@ import android.os.Build; import android.os.Handler; import android.provider.CallLog; import android.provider.CallLog.Calls; +import android.provider.VoicemailContract; import android.support.annotation.ColorInt; import android.support.annotation.MainThread; import android.support.annotation.Nullable; @@ -92,13 +93,21 @@ public class SystemCallLogDataSource implements CallLogDataSource { } // TODO(zachh): Need to somehow register observers if user enables permission after launch? + CallLogObserver callLogObserver = + new CallLogObserver(ThreadUtil.getUiThreadHandler(), appContext, contentObserverCallbacks); + appContext .getContentResolver() - .registerContentObserver( - CallLog.Calls.CONTENT_URI_WITH_VOICEMAIL, - true, - new CallLogObserver( - ThreadUtil.getUiThreadHandler(), appContext, contentObserverCallbacks)); + .registerContentObserver(CallLog.Calls.CONTENT_URI_WITH_VOICEMAIL, true, callLogObserver); + + if (!PermissionsUtil.hasAddVoicemailPermissions(appContext)) { + LogUtil.i("SystemCallLogDataSource.registerContentObservers", "no add voicemail permissions"); + return; + } + // TODO(uabdullah): Need to somehow register observers if user enables permission after launch? + appContext + .getContentResolver() + .registerContentObserver(VoicemailContract.Status.CONTENT_URI, true, callLogObserver); } @Override @@ -462,7 +471,11 @@ public class SystemCallLogDataSource implements CallLogDataSource { @Override public void onChange(boolean selfChange, Uri uri) { Assert.isMainThread(); - LogUtil.enterBlock("SystemCallLogDataSource.CallLogObserver.onChange"); + LogUtil.i( + "SystemCallLogDataSource.CallLogObserver.onChange", + "Uri:%s, SelfChange:%b", + String.valueOf(uri), + selfChange); super.onChange(selfChange, uri); /* diff --git a/java/com/android/dialer/calllog/ui/NewCallLogFragment.java b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java index c10b52123..719878cec 100644 --- a/java/com/android/dialer/calllog/ui/NewCallLogFragment.java +++ b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java @@ -58,10 +58,10 @@ public final class NewCallLogFragment extends Fragment } @Override - public void onCreate(Bundle state) { - super.onCreate(state); + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); - LogUtil.enterBlock("NewCallLogFragment.onCreate"); + LogUtil.enterBlock("NewCallLogFragment.onActivityCreated"); CallLogComponent component = CallLogComponent.get(getContext()); CallLogFramework callLogFramework = component.callLogFramework(); diff --git a/java/com/android/dialer/common/concurrent/UiListener.java b/java/com/android/dialer/common/concurrent/UiListener.java index 9541dbc0c..df791301f 100644 --- a/java/com/android/dialer/common/concurrent/UiListener.java +++ b/java/com/android/dialer/common/concurrent/UiListener.java @@ -71,7 +71,10 @@ public class UiListener<OutputT> extends Fragment { if (uiListener == null) { LogUtil.i("UiListener.create", "creating new UiListener for " + taskId); uiListener = new UiListener<>(); - fragmentManager.beginTransaction().add(uiListener, taskId).commit(); + // When launching an activity with the screen off, its onSaveInstanceState() is called before + // its fragments are created, which means we can't use commit() and need to use + // commitAllowingStateLoss(). This is not a problem for UiListener which saves no state. + fragmentManager.beginTransaction().add(uiListener, taskId).commitAllowingStateLoss(); } return uiListener; } @@ -130,6 +133,9 @@ public class UiListener<OutputT> extends Fragment { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); + // Note: We use commitAllowingStateLoss when attaching the fragment so it may not be safe to + // read savedInstanceState in all situations. (But it's not anticipated that this fragment + // should need to rely on saved state.) } @Override diff --git a/java/com/android/dialer/compat/telephony/TelephonyManagerCompat.java b/java/com/android/dialer/compat/telephony/TelephonyManagerCompat.java index 6bed818da..b01689da4 100644 --- a/java/com/android/dialer/compat/telephony/TelephonyManagerCompat.java +++ b/java/com/android/dialer/compat/telephony/TelephonyManagerCompat.java @@ -57,22 +57,16 @@ public class TelephonyManagerCompat { * * <p>This signals to the telephony platform that an outgoing call qualifies for assisted dialing. */ - public static final String ALLOW_ASSISTED_DIAL = "android.telecom.extra.ALLOW_ASSISTED_DIAL"; - - // TODO(erfanian): a bug Replace with the platform/telecom constant when available. - /** - * Indicates that an outgoing call has undergone assisted dialing. - * - * <p>Unlike {@link ALLOW_ASSISTED_DIAL}, the presence of this key further indicates that a call - * has undergone Assisted Dialing -- not just that it qualified for Assisted Dialing. - */ - public static final String IS_ASSISTED_DIALED = "android.telecom.extra.IS_ASSISTED_DIALED"; + public static final String USE_ASSISTED_DIALING = "android.telecom.extra.USE_ASSISTED_DIALING"; // TODO(erfanian): a bug Replace with the platform/telecom API when available. /** Additional information relating to the assisted dialing transformation. */ public static final String ASSISTED_DIALING_EXTRAS = "android.telecom.extra.ASSISTED_DIALING_EXTRAS"; + /** Indicates the Connection/Call used assisted dialing. */ + public static final int PROPERTY_ASSISTED_DIALING_USED = 0x00000200; + public static final String EXTRA_IS_REFRESH = BuildCompat.isAtLeastOMR1() ? "android.telephony.extra.IS_REFRESH" : "is_refresh"; diff --git a/java/com/android/dialer/database/DialerDatabaseHelper.java b/java/com/android/dialer/database/DialerDatabaseHelper.java index b0bd62a34..3fb87304b 100644 --- a/java/com/android/dialer/database/DialerDatabaseHelper.java +++ b/java/com/android/dialer/database/DialerDatabaseHelper.java @@ -42,8 +42,8 @@ import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.DialerExecutor.Worker; import com.android.dialer.common.concurrent.DialerExecutorComponent; import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns; -import com.android.dialer.smartdial.SmartDialNameMatcher; -import com.android.dialer.smartdial.SmartDialPrefix; +import com.android.dialer.smartdial.util.SmartDialNameMatcher; +import com.android.dialer.smartdial.util.SmartDialPrefix; import com.android.dialer.util.PermissionsUtil; import java.util.ArrayList; import java.util.HashSet; diff --git a/java/com/android/dialer/dialpadview/DialpadCharMappings.java b/java/com/android/dialer/dialpadview/DialpadCharMappings.java index 03bc2e728..0bb28ae0d 100644 --- a/java/com/android/dialer/dialpadview/DialpadCharMappings.java +++ b/java/com/android/dialer/dialpadview/DialpadCharMappings.java @@ -147,6 +147,23 @@ public class DialpadCharMappings { : null; } + /** + * Returns the character-key map of the provided ISO 639-2 language code. + * + * <p>Note: this method is for implementations of {@link + * com.android.dialer.smartdial.map.SmartDialMap} only. {@link #getCharToKeyMap(Context)} should + * be used for all other purposes. + * + * <p>It is the caller's responsibility to ensure the language code is valid and a character + * mapping is defined for that language. Otherwise, an exception will be thrown. + */ + public static SimpleArrayMap<Character, Character> getCharToKeyMap(String languageCode) { + SimpleArrayMap<Character, Character> charToKeyMap = CHAR_TO_KEY_MAPS.get(languageCode); + + return Assert.isNotNull( + charToKeyMap, "No character mappings can be found for language code '%s'", languageCode); + } + /** Returns the default character-key map (the one that uses the Latin alphabet). */ public static SimpleArrayMap<Character, Character> getDefaultCharToKeyMap() { return Latin.CHAR_TO_KEY; diff --git a/java/com/android/dialer/dialpadview/DialpadView.java b/java/com/android/dialer/dialpadview/DialpadView.java index 2f494e49d..7b95ba7ea 100644 --- a/java/com/android/dialer/dialpadview/DialpadView.java +++ b/java/com/android/dialer/dialpadview/DialpadView.java @@ -87,6 +87,7 @@ public class DialpadView extends LinearLayout { private ViewGroup mRateContainer; private TextView mIldCountry; private TextView mIldRate; + private boolean mIsLandscapeMode; public DialpadView(Context context) { this(context, null); @@ -125,6 +126,12 @@ public class DialpadView extends LinearLayout { protected void onFinishInflate() { super.onFinishInflate(); + // The orientation obtained at this point should be used as the only truth for DialpadView as we + // observed inconsistency between configurations obtained here and in + // OnPreDrawListenerForKeyLayoutAdjust under rare circumstances. + mIsLandscapeMode = + (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE); + setupKeypad(); mDigits = (EditText) findViewById(R.id.digits); mDelete = (ImageButton) findViewById(R.id.deleteButton); @@ -281,7 +288,7 @@ public class DialpadView extends LinearLayout { final DialpadKeyButton dialpadKey = (DialpadKeyButton) findViewById(BUTTON_IDS[i]); ViewPropertyAnimator animator = dialpadKey.animate(); - if (isLandscapeMode()) { + if (mIsLandscapeMode) { // Landscape orientation requires translation along the X axis. // For RTL locales, ensure we translate negative on the X axis. dialpadKey.setTranslationX((mIsRtl ? -1 : 1) * mTranslateDistance); @@ -320,7 +327,7 @@ public class DialpadView extends LinearLayout { * @return The animation delay. */ private int getKeyButtonAnimationDelay(int buttonId) { - if (isLandscapeMode()) { + if (mIsLandscapeMode) { if (mIsRtl) { if (buttonId == R.id.three) { return KEY_FRAME_DURATION * 1; @@ -408,7 +415,7 @@ public class DialpadView extends LinearLayout { * @return The animation duration. */ private int getKeyButtonAnimationDuration(int buttonId) { - if (isLandscapeMode()) { + if (mIsLandscapeMode) { if (mIsRtl) { if (buttonId == R.id.one || buttonId == R.id.four @@ -463,10 +470,6 @@ public class DialpadView extends LinearLayout { return 0; } - private boolean isLandscapeMode() { - return getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; - } - /** * An {@link OnPreDrawListener} that adjusts the height/width of each key layout so that they can * be properly aligned. @@ -525,7 +528,7 @@ public class DialpadView extends LinearLayout { } private boolean shouldAdjustKeySizes() { - return isLandscapeMode() ? shouldAdjustKeyWidths() : shouldAdjustDigitKeyHeights(); + return mIsLandscapeMode ? shouldAdjustKeyWidths() : shouldAdjustDigitKeyHeights(); } /** @@ -533,7 +536,7 @@ public class DialpadView extends LinearLayout { * device is in landscape mode. */ private boolean shouldAdjustKeyWidths() { - Assert.checkState(isLandscapeMode()); + Assert.checkState(mIsLandscapeMode); DialpadKeyButton dialpadKeyButton = (DialpadKeyButton) findViewById(BUTTON_IDS[0]); LinearLayout keyLayout = @@ -556,7 +559,7 @@ public class DialpadView extends LinearLayout { * called when the device is in portrait mode. */ private boolean shouldAdjustDigitKeyHeights() { - Assert.checkState(!isLandscapeMode()); + Assert.checkState(!mIsLandscapeMode); DialpadKeyButton dialpadKey = (DialpadKeyButton) findViewById(BUTTON_IDS[0]); LinearLayout keyLayout = (LinearLayout) dialpadKey.findViewById(R.id.dialpad_key_layout); @@ -576,7 +579,7 @@ public class DialpadView extends LinearLayout { } private void adjustKeySizes() { - if (isLandscapeMode()) { + if (mIsLandscapeMode) { adjustKeyWidths(); } else { adjustDigitKeyHeights(); @@ -594,7 +597,7 @@ public class DialpadView extends LinearLayout { * LinearLayout#setLayoutParams(ViewGroup.LayoutParams)}. */ private void adjustDigitKeyHeights() { - Assert.checkState(!isLandscapeMode()); + Assert.checkState(!mIsLandscapeMode); int maxHeight = 0; @@ -638,7 +641,7 @@ public class DialpadView extends LinearLayout { * View#setLayoutParams(ViewGroup.LayoutParams)}. */ private void adjustKeyWidths() { - Assert.checkState(isLandscapeMode()); + Assert.checkState(mIsLandscapeMode); int maxWidth = 0; for (int buttonId : BUTTON_IDS) { diff --git a/java/com/android/dialer/function/BiConsumer.java b/java/com/android/dialer/function/BiConsumer.java new file mode 100644 index 000000000..50052210e --- /dev/null +++ b/java/com/android/dialer/function/BiConsumer.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.dialer.function; + +/** Functional interface for consuming two generic values. */ +public interface BiConsumer<T, U> { + + /** Consumes a value. */ + void accept(T t, U u); +} diff --git a/java/com/android/dialer/main/impl/MainPagerAdapter.java b/java/com/android/dialer/main/impl/MainPagerAdapter.java index 2d224f69c..d294640ee 100644 --- a/java/com/android/dialer/main/impl/MainPagerAdapter.java +++ b/java/com/android/dialer/main/impl/MainPagerAdapter.java @@ -20,7 +20,7 @@ import android.content.Context; import android.support.annotation.IntDef; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentStatePagerAdapter; +import android.support.v4.app.FragmentPagerAdapter; import com.android.dialer.calllog.ui.NewCallLogFragment; import com.android.dialer.common.Assert; import com.android.dialer.voicemail.listui.NewVoicemailFragment; @@ -28,7 +28,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** Adapter for {@link MainActivity} ViewPager. */ -final class MainPagerAdapter extends FragmentStatePagerAdapter { +final class MainPagerAdapter extends FragmentPagerAdapter { @Retention(RetentionPolicy.SOURCE) @IntDef({ diff --git a/java/com/android/dialer/phonelookup/PhoneLookup.java b/java/com/android/dialer/phonelookup/PhoneLookup.java index eeab4dadd..bb14c1ff6 100644 --- a/java/com/android/dialer/phonelookup/PhoneLookup.java +++ b/java/com/android/dialer/phonelookup/PhoneLookup.java @@ -62,6 +62,12 @@ public interface PhoneLookup { ImmutableMap<DialerPhoneNumber, PhoneLookupInfo> existingInfoMap); /** + * Populates the sub-message that this {@link PhoneLookup} is responsible for by copying the + * sub-message value from {@code source} to {@code destination} + */ + void copySubMessage(PhoneLookupInfo.Builder destination, PhoneLookupInfo source); + + /** * Called when the results of the {@link #getMostRecentPhoneLookupInfo(ImmutableMap)} have been * applied by the caller. * diff --git a/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java b/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java index ee2244615..bb7856fff 100644 --- a/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java +++ b/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java @@ -112,14 +112,15 @@ public final class CompositePhoneLookup implements PhoneLookup { ImmutableMap.builder(); for (DialerPhoneNumber dialerPhoneNumber : existingInfoMap.keySet()) { PhoneLookupInfo.Builder combinedInfo = PhoneLookupInfo.newBuilder(); - for (ImmutableMap<DialerPhoneNumber, PhoneLookupInfo> map : allMaps) { + for (int i = 0; i < allMaps.size(); i++) { + ImmutableMap<DialerPhoneNumber, PhoneLookupInfo> map = allMaps.get(i); PhoneLookupInfo subInfo = map.get(dialerPhoneNumber); if (subInfo == null) { throw new IllegalStateException( "A sublookup didn't return an info for number: " + LogUtil.sanitizePhoneNumber(dialerPhoneNumber.getRawInput().getNumber())); } - combinedInfo.mergeFrom(subInfo); + phoneLookups.get(i).copySubMessage(combinedInfo, subInfo); } combinedMap.put(dialerPhoneNumber, combinedInfo.build()); } @@ -129,6 +130,11 @@ public final class CompositePhoneLookup implements PhoneLookup { } @Override + public void copySubMessage(PhoneLookupInfo.Builder destination, PhoneLookupInfo source) { + throw new UnsupportedOperationException(); + } + + @Override public ListenableFuture<Void> onSuccessfulBulkUpdate() { List<ListenableFuture<Void>> futures = new ArrayList<>(); for (PhoneLookup phoneLookup : phoneLookups) { diff --git a/java/com/android/dialer/phonelookup/cp2/Cp2PhoneLookup.java b/java/com/android/dialer/phonelookup/cp2/Cp2PhoneLookup.java index b31d0e72e..a477e035c 100644 --- a/java/com/android/dialer/phonelookup/cp2/Cp2PhoneLookup.java +++ b/java/com/android/dialer/phonelookup/cp2/Cp2PhoneLookup.java @@ -36,6 +36,7 @@ import com.android.dialer.phonelookup.PhoneLookup; import com.android.dialer.phonelookup.PhoneLookupInfo; import com.android.dialer.phonelookup.PhoneLookupInfo.Cp2Info; import com.android.dialer.phonelookup.PhoneLookupInfo.Cp2Info.Cp2ContactInfo; +import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract.PhoneLookupHistory; import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil; import com.android.dialer.storage.Unencrypted; import com.android.dialer.telecom.TelecomCallUtil; @@ -45,6 +46,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.i18n.phonenumbers.PhoneNumberUtil; +import com.google.protobuf.InvalidProtocolBufferException; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -131,8 +133,25 @@ public final class Cp2PhoneLookup implements PhoneLookup { private boolean isDirtyInternal(ImmutableSet<DialerPhoneNumber> phoneNumbers) { long lastModified = sharedPreferences.getLong(PREF_LAST_TIMESTAMP_PROCESSED, 0L); - return contactsUpdated(queryPhoneTableForContactIds(phoneNumbers), lastModified) - || contactsDeleted(lastModified); + // We are always going to need to do this check and it is pretty cheap so do it first. + if (anyContactsDeletedSince(lastModified)) { + return true; + } + // Hopefully the most common case is there are no contacts updated; we can detect this cheaply. + if (noContactsModifiedSince(lastModified)) { + return false; + } + // This method is more expensive but is probably the most likely scenario; we are looking for + // changes to contacts which have been called. + if (contactsUpdated(queryPhoneTableForContactIds(phoneNumbers), lastModified)) { + return true; + } + // This is the most expensive method so do it last; the scenario is that a contact which has + // been called got disassociated with a number and we need to clear their information. + if (contactsUpdated(queryPhoneLookupHistoryForContactIds(), lastModified)) { + return true; + } + return false; } /** @@ -155,6 +174,46 @@ public final class Cp2PhoneLookup implements PhoneLookup { return contactIds; } + /** Gets all of the contact ids from PhoneLookupHistory. */ + private Set<Long> queryPhoneLookupHistoryForContactIds() { + Set<Long> contactIds = new ArraySet<>(); + try (Cursor cursor = + appContext + .getContentResolver() + .query( + PhoneLookupHistory.CONTENT_URI, + new String[] { + PhoneLookupHistory.PHONE_LOOKUP_INFO, + }, + null, + null, + null)) { + + if (cursor == null) { + LogUtil.w("Cp2PhoneLookup.queryPhoneLookupHistoryForContactIds", "null cursor"); + return contactIds; + } + + if (cursor.moveToFirst()) { + int phoneLookupInfoColumn = + cursor.getColumnIndexOrThrow(PhoneLookupHistory.PHONE_LOOKUP_INFO); + do { + PhoneLookupInfo phoneLookupInfo; + try { + phoneLookupInfo = PhoneLookupInfo.parseFrom(cursor.getBlob(phoneLookupInfoColumn)); + } catch (InvalidProtocolBufferException e) { + throw new IllegalStateException(e); + } + for (Cp2ContactInfo info : phoneLookupInfo.getCp2Info().getCp2ContactInfoList()) { + contactIds.add(info.getContactId()); + } + } while (cursor.moveToNext()); + } + } + + return contactIds; + } + private Set<Long> queryPhoneTableForContactIdsBasedOnE164(Set<String> validE164Numbers) { Set<Long> contactIds = new ArraySet<>(); if (validE164Numbers.isEmpty()) { @@ -226,8 +285,26 @@ public final class Cp2PhoneLookup implements PhoneLookup { null); } + private boolean noContactsModifiedSince(long lastModified) { + try (Cursor cursor = + appContext + .getContentResolver() + .query( + Contacts.CONTENT_URI, + new String[] {Contacts._ID}, + Contacts.CONTACT_LAST_UPDATED_TIMESTAMP + " > ?", + new String[] {Long.toString(lastModified)}, + Contacts._ID + " limit 1")) { + if (cursor == null) { + LogUtil.w("Cp2PhoneLookup.noContactsModifiedSince", "null cursor"); + return false; + } + return cursor.getCount() == 0; + } + } + /** Returns true if any contacts were deleted after {@code lastModified}. */ - private boolean contactsDeleted(long lastModified) { + private boolean anyContactsDeletedSince(long lastModified) { try (Cursor cursor = appContext .getContentResolver() @@ -236,9 +313,9 @@ public final class Cp2PhoneLookup implements PhoneLookup { new String[] {DeletedContacts.CONTACT_DELETED_TIMESTAMP}, DeletedContacts.CONTACT_DELETED_TIMESTAMP + " > ?", new String[] {Long.toString(lastModified)}, - null)) { + DeletedContacts.CONTACT_DELETED_TIMESTAMP + " limit 1")) { if (cursor == null) { - LogUtil.w("Cp2PhoneLookup.contactsDeleted", "null cursor"); + LogUtil.w("Cp2PhoneLookup.anyContactsDeletedSince", "null cursor"); return false; } return cursor.getCount() > 0; @@ -253,6 +330,11 @@ public final class Cp2PhoneLookup implements PhoneLookup { () -> getMostRecentPhoneLookupInfoInternal(existingInfoMap)); } + @Override + public void copySubMessage(PhoneLookupInfo.Builder destination, PhoneLookupInfo source) { + destination.setCp2Info(source.getCp2Info()); + } + private ImmutableMap<DialerPhoneNumber, PhoneLookupInfo> getMostRecentPhoneLookupInfoInternal( ImmutableMap<DialerPhoneNumber, PhoneLookupInfo> existingInfoMap) { currentLastTimestampProcessed = null; @@ -407,6 +489,11 @@ public final class Cp2PhoneLookup implements PhoneLookup { partitionedNumbers.dialerPhoneNumbersForE164(e164Number); Cp2ContactInfo info = buildCp2ContactInfoFromPhoneCursor(appContext, cursor); addInfo(map, dialerPhoneNumbers, info); + + // We are going to remove the numbers that we've handled so that we later can detect + // numbers that weren't handled and therefore need to have their contact information + // removed. + updatedNumbers.removeAll(dialerPhoneNumbers); } } } @@ -424,10 +511,20 @@ public final class Cp2PhoneLookup implements PhoneLookup { partitionedNumbers.dialerPhoneNumbersForUnformattable(unformattableNumber); Cp2ContactInfo info = buildCp2ContactInfoFromPhoneCursor(appContext, cursor); addInfo(map, dialerPhoneNumbers, info); + + // We are going to remove the numbers that we've handled so that we later can detect + // numbers that weren't handled and therefore need to have their contact information + // removed. + updatedNumbers.removeAll(dialerPhoneNumbers); } } } } + // The leftovers in updatedNumbers that weren't removed are numbers that were previously + // associated with contacts, but are no longer. Remove the contact information for them. + for (DialerPhoneNumber dialerPhoneNumber : updatedNumbers) { + map.put(dialerPhoneNumber, ImmutableSet.of()); + } return map; } diff --git a/java/com/android/dialer/precall/impl/AssistedDialAction.java b/java/com/android/dialer/precall/impl/AssistedDialAction.java index c4f61d2dd..dc2510960 100644 --- a/java/com/android/dialer/precall/impl/AssistedDialAction.java +++ b/java/com/android/dialer/precall/impl/AssistedDialAction.java @@ -51,10 +51,12 @@ public class AssistedDialAction implements PreCallAction { AssistedDialingMediator assistedDialingMediator = ConcreteCreator.createNewAssistedDialingMediator( context.getSystemService(TelephonyManager.class), context); + if (Build.VERSION.SDK_INT > ConcreteCreator.BUILD_CODE_CEILING) { + builder.getOutgoingCallExtras().putBoolean(TelephonyManagerCompat.USE_ASSISTED_DIALING, true); + } if (!assistedDialingMediator.isPlatformEligible()) { return; } - builder.getOutgoingCallExtras().putBoolean(TelephonyManagerCompat.ALLOW_ASSISTED_DIAL, true); String phoneNumber = builder.getUri().getScheme().equals(PhoneAccount.SCHEME_TEL) ? builder.getUri().getSchemeSpecificPart() @@ -62,8 +64,8 @@ public class AssistedDialAction implements PreCallAction { Optional<TransformationInfo> transformedNumber = assistedDialingMediator.attemptAssistedDial(phoneNumber); if (transformedNumber.isPresent()) { + builder.getOutgoingCallExtras().putBoolean(TelephonyManagerCompat.USE_ASSISTED_DIALING, true); Bundle assistedDialingExtras = transformedNumber.get().toBundle(); - builder.getOutgoingCallExtras().putBoolean(TelephonyManagerCompat.IS_ASSISTED_DIALED, true); builder .getOutgoingCallExtras() .putBundle(TelephonyManagerCompat.ASSISTED_DIALING_EXTRAS, assistedDialingExtras); diff --git a/java/com/android/dialer/searchfragment/cp2/SearchContactsCursorLoader.java b/java/com/android/dialer/searchfragment/cp2/SearchContactsCursorLoader.java index 23e3f9d88..23f368f54 100644 --- a/java/com/android/dialer/searchfragment/cp2/SearchContactsCursorLoader.java +++ b/java/com/android/dialer/searchfragment/cp2/SearchContactsCursorLoader.java @@ -28,9 +28,9 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextUtils; import com.android.contacts.common.preference.ContactsPreferences; -import com.android.dialer.dialpadview.SmartDialCursorLoader; import com.android.dialer.searchfragment.common.Projections; import com.android.dialer.searchfragment.common.SearchCursor; +import com.android.dialer.smartdial.SmartDialCursorLoader; /** Cursor Loader for CP2 contacts. */ public final class SearchContactsCursorLoader extends CursorLoader { diff --git a/java/com/android/dialer/shortcuts/PinnedShortcuts.java b/java/com/android/dialer/shortcuts/PinnedShortcuts.java index bfcc3df81..6e23a5c21 100644 --- a/java/com/android/dialer/shortcuts/PinnedShortcuts.java +++ b/java/com/android/dialer/shortcuts/PinnedShortcuts.java @@ -46,7 +46,6 @@ import java.util.Map; * <p>When refreshing pinned shortcuts, we check to make sure that pinned contact information is * still up to date (e.g. photo and name). We also check to see if the contact has been deleted from * the user's contacts, and if so, we disable the pinned shortcut. - * */ @TargetApi(VERSION_CODES.N_MR1) // Shortcuts introduced in N MR1 final class PinnedShortcuts { @@ -107,6 +106,14 @@ final class PinnedShortcuts { // setRank is nonsensical for pinned shortcuts and therefore could not be calculated. continue; } + // Exclude shortcuts like the "Phone NUI" shortcut. + String action = null; + if (shortcutInfo.getIntent() != null) { + action = shortcutInfo.getIntent().getAction(); + } + if (action == null || !action.equals("com.android.dialer.shortcuts.CALL_CONTACT")) { + continue; + } String lookupKey = DialerShortcut.getLookupKeyFromShortcutInfo(shortcutInfo); Uri lookupUri = DialerShortcut.getLookupUriFromShortcutInfo(shortcutInfo); diff --git a/java/com/android/dialer/simulator/impl/SimulatorDialogFragment.java b/java/com/android/dialer/simulator/impl/SimulatorDialogFragment.java index f8403c7fe..96ea62739 100644 --- a/java/com/android/dialer/simulator/impl/SimulatorDialogFragment.java +++ b/java/com/android/dialer/simulator/impl/SimulatorDialogFragment.java @@ -29,7 +29,7 @@ public final class SimulatorDialogFragment extends DialogFragment { private final String[] callerIdPresentationItems = { "ALLOWED", "PAYPHONE", "RESTRICTED", "UNKNOWN" }; - private int callerIdPresentationChoice; + private int callerIdPresentationChoice = 1; private DialogCallback dialogCallback; @@ -47,6 +47,7 @@ public final class SimulatorDialogFragment extends DialogFragment { public Dialog onCreateDialog(Bundle bundle) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); final EditText input = new EditText(getActivity()); + input.setHint("Please input phone number"); builder .setTitle("Phone Number:") .setView(input) diff --git a/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java b/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java index 89c5d2f14..ff00dd87e 100644 --- a/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java +++ b/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java @@ -120,7 +120,8 @@ final class SimulatorVoiceCall private void addSpamIncomingCall() { String callerId = "+1-661-778-3020"; /* Blacklisted custom spam number */ - SimulatorSimCallManager.addNewIncomingCall(context, callerId, false /* isVideo */); + connectionTag = + SimulatorSimCallManager.addNewIncomingCall(context, callerId, false /* isVideo */); } private void addNewEmergencyCallBack() { diff --git a/java/com/android/dialer/smartdial/RussianSmartDialMap.java b/java/com/android/dialer/smartdial/RussianSmartDialMap.java deleted file mode 100644 index ada9182e1..000000000 --- a/java/com/android/dialer/smartdial/RussianSmartDialMap.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.dialer.smartdial; - -import android.support.v4.util.SimpleArrayMap; -import com.google.common.base.Optional; - -/** A {@link SmartDialMap} for the Russian alphabet. */ -@SuppressWarnings("Guava") -final class RussianSmartDialMap extends SmartDialMap { - private static final SimpleArrayMap<Character, Character> CHAR_TO_KEY_MAP = - new SimpleArrayMap<>(); - - // Reference: https://en.wikipedia.org/wiki/Russian_alphabet - static { - CHAR_TO_KEY_MAP.put('а', '2'); - CHAR_TO_KEY_MAP.put('б', '2'); - CHAR_TO_KEY_MAP.put('в', '2'); - CHAR_TO_KEY_MAP.put('г', '2'); - - CHAR_TO_KEY_MAP.put('д', '3'); - CHAR_TO_KEY_MAP.put('е', '3'); - CHAR_TO_KEY_MAP.put('ё', '3'); - CHAR_TO_KEY_MAP.put('ж', '3'); - CHAR_TO_KEY_MAP.put('з', '3'); - - CHAR_TO_KEY_MAP.put('и', '4'); - CHAR_TO_KEY_MAP.put('й', '4'); - CHAR_TO_KEY_MAP.put('к', '4'); - CHAR_TO_KEY_MAP.put('л', '4'); - - CHAR_TO_KEY_MAP.put('м', '5'); - CHAR_TO_KEY_MAP.put('н', '5'); - CHAR_TO_KEY_MAP.put('о', '5'); - CHAR_TO_KEY_MAP.put('п', '5'); - - CHAR_TO_KEY_MAP.put('р', '6'); - CHAR_TO_KEY_MAP.put('с', '6'); - CHAR_TO_KEY_MAP.put('т', '6'); - CHAR_TO_KEY_MAP.put('у', '6'); - - CHAR_TO_KEY_MAP.put('ф', '7'); - CHAR_TO_KEY_MAP.put('х', '7'); - CHAR_TO_KEY_MAP.put('ц', '7'); - CHAR_TO_KEY_MAP.put('ч', '7'); - - CHAR_TO_KEY_MAP.put('ш', '8'); - CHAR_TO_KEY_MAP.put('щ', '8'); - CHAR_TO_KEY_MAP.put('ъ', '8'); - CHAR_TO_KEY_MAP.put('ы', '8'); - - CHAR_TO_KEY_MAP.put('ь', '9'); - CHAR_TO_KEY_MAP.put('э', '9'); - CHAR_TO_KEY_MAP.put('ю', '9'); - CHAR_TO_KEY_MAP.put('я', '9'); - } - - private static RussianSmartDialMap instance; - - static RussianSmartDialMap getInstance() { - if (instance == null) { - instance = new RussianSmartDialMap(); - } - - return instance; - } - - private RussianSmartDialMap() {} - - @Override - Optional<Character> normalizeCharacter(char ch) { - ch = Character.toLowerCase(ch); - return isValidDialpadAlphabeticChar(ch) ? Optional.of(ch) : Optional.absent(); - } - - @Override - SimpleArrayMap<Character, Character> getCharToKeyMap() { - return CHAR_TO_KEY_MAP; - } -} diff --git a/java/com/android/dialer/dialpadview/SmartDialCursorLoader.java b/java/com/android/dialer/smartdial/SmartDialCursorLoader.java index d085b55bd..f6bc9325a 100644 --- a/java/com/android/dialer/dialpadview/SmartDialCursorLoader.java +++ b/java/com/android/dialer/smartdial/SmartDialCursorLoader.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.dialer.dialpadview; +package com.android.dialer.smartdial; import android.content.AsyncTaskLoader; import android.content.Context; @@ -25,7 +25,7 @@ import com.android.dialer.common.LogUtil; import com.android.dialer.database.Database; import com.android.dialer.database.DialerDatabaseHelper; import com.android.dialer.database.DialerDatabaseHelper.ContactNumber; -import com.android.dialer.smartdial.SmartDialNameMatcher; +import com.android.dialer.smartdial.util.SmartDialNameMatcher; import com.android.dialer.util.PermissionsUtil; import java.util.ArrayList; diff --git a/java/com/android/dialer/smartdial/map/BulgarianSmartDialMap.java b/java/com/android/dialer/smartdial/map/BulgarianSmartDialMap.java new file mode 100644 index 000000000..5be9761a1 --- /dev/null +++ b/java/com/android/dialer/smartdial/map/BulgarianSmartDialMap.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.dialer.smartdial.map; + +import android.support.v4.util.SimpleArrayMap; +import com.android.dialer.dialpadview.DialpadCharMappings; +import com.google.common.base.Optional; + +/** A {@link SmartDialMap} for the Bulgarian alphabet. */ +@SuppressWarnings("Guava") +final class BulgarianSmartDialMap extends SmartDialMap { + + private static BulgarianSmartDialMap instance; + + static BulgarianSmartDialMap getInstance() { + if (instance == null) { + instance = new BulgarianSmartDialMap(); + } + + return instance; + } + + private BulgarianSmartDialMap() {} + + @Override + Optional<Character> normalizeCharacter(char ch) { + ch = Character.toLowerCase(ch); + return isValidDialpadAlphabeticChar(ch) ? Optional.of(ch) : Optional.absent(); + } + + @Override + SimpleArrayMap<Character, Character> getCharToKeyMap() { + return DialpadCharMappings.getCharToKeyMap("bul"); + } +} diff --git a/java/com/android/dialer/smartdial/CompositeSmartDialMap.java b/java/com/android/dialer/smartdial/map/CompositeSmartDialMap.java index d51e46f76..df32d4ce7 100644 --- a/java/com/android/dialer/smartdial/CompositeSmartDialMap.java +++ b/java/com/android/dialer/smartdial/map/CompositeSmartDialMap.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.dialer.smartdial; +package com.android.dialer.smartdial.map; import android.content.Context; import android.support.annotation.VisibleForTesting; @@ -45,7 +45,9 @@ public class CompositeSmartDialMap { private static final SimpleArrayMap<String, SmartDialMap> EXTRA_MAPS = new SimpleArrayMap<>(); static { + EXTRA_MAPS.put("bul", BulgarianSmartDialMap.getInstance()); EXTRA_MAPS.put("rus", RussianSmartDialMap.getInstance()); + EXTRA_MAPS.put("ukr", UkrainianSmartDialMap.getInstance()); } private CompositeSmartDialMap() {} @@ -56,7 +58,7 @@ public class CompositeSmartDialMap { * <p>The provided character is expected to be a normalized character. See {@link * SmartDialMap#normalizeCharacter(char)} for details. */ - static boolean isValidDialpadCharacter(Context context, char ch) { + public static boolean isValidDialpadCharacter(Context context, char ch) { if (DEFAULT_MAP.isValidDialpadCharacter(ch)) { return true; } @@ -71,7 +73,7 @@ public class CompositeSmartDialMap { * <p>The provided character is expected to be a normalized character. See {@link * SmartDialMap#normalizeCharacter(char)} for details. */ - static boolean isValidDialpadAlphabeticChar(Context context, char ch) { + public static boolean isValidDialpadAlphabeticChar(Context context, char ch) { if (DEFAULT_MAP.isValidDialpadAlphabeticChar(ch)) { return true; } @@ -83,7 +85,7 @@ public class CompositeSmartDialMap { /** * Returns true if the provided character is a digit, and can be mapped to a key on the dialpad. */ - static boolean isValidDialpadNumericChar(Context context, char ch) { + public static boolean isValidDialpadNumericChar(Context context, char ch) { if (DEFAULT_MAP.isValidDialpadNumericChar(ch)) { return true; } @@ -100,7 +102,7 @@ public class CompositeSmartDialMap { * * <p>If the provided character can't be mapped to a key on the dialpad, return -1. */ - static byte getDialpadIndex(Context context, char ch) { + public static byte getDialpadIndex(Context context, char ch) { Optional<Byte> dialpadIndex = DEFAULT_MAP.getDialpadIndex(ch); if (dialpadIndex.isPresent()) { return dialpadIndex.get(); @@ -122,7 +124,7 @@ public class CompositeSmartDialMap { * * <p>If the provided character can't be mapped to a key on the dialpad, return the character. */ - static char getDialpadNumericCharacter(Context context, char ch) { + public static char getDialpadNumericCharacter(Context context, char ch) { Optional<Character> dialpadNumericChar = DEFAULT_MAP.getDialpadNumericCharacter(ch); if (dialpadNumericChar.isPresent()) { return dialpadNumericChar.get(); @@ -142,7 +144,7 @@ public class CompositeSmartDialMap { * * <p>If the provided character can't be mapped to a key on the dialpad, return the character. */ - static char normalizeCharacter(Context context, char ch) { + public static char normalizeCharacter(Context context, char ch) { Optional<Character> normalizedChar = DEFAULT_MAP.normalizeCharacter(ch); if (normalizedChar.isPresent()) { return normalizedChar.get(); diff --git a/java/com/android/dialer/smartdial/LatinSmartDialMap.java b/java/com/android/dialer/smartdial/map/LatinSmartDialMap.java index b67901bbe..b8ef951c5 100644 --- a/java/com/android/dialer/smartdial/LatinSmartDialMap.java +++ b/java/com/android/dialer/smartdial/map/LatinSmartDialMap.java @@ -14,52 +14,15 @@ * limitations under the License. */ -package com.android.dialer.smartdial; +package com.android.dialer.smartdial.map; import android.support.v4.util.SimpleArrayMap; +import com.android.dialer.dialpadview.DialpadCharMappings; import com.google.common.base.Optional; /** A {@link SmartDialMap} for the Latin alphabet, which is for T9 dialpad searching. */ @SuppressWarnings("Guava") final class LatinSmartDialMap extends SmartDialMap { - private static final SimpleArrayMap<Character, Character> CHAR_TO_KEY_MAP = - new SimpleArrayMap<>(); - - static { - CHAR_TO_KEY_MAP.put('a', '2'); - CHAR_TO_KEY_MAP.put('b', '2'); - CHAR_TO_KEY_MAP.put('c', '2'); - - CHAR_TO_KEY_MAP.put('d', '3'); - CHAR_TO_KEY_MAP.put('e', '3'); - CHAR_TO_KEY_MAP.put('f', '3'); - - CHAR_TO_KEY_MAP.put('g', '4'); - CHAR_TO_KEY_MAP.put('h', '4'); - CHAR_TO_KEY_MAP.put('i', '4'); - - CHAR_TO_KEY_MAP.put('j', '5'); - CHAR_TO_KEY_MAP.put('k', '5'); - CHAR_TO_KEY_MAP.put('l', '5'); - - CHAR_TO_KEY_MAP.put('m', '6'); - CHAR_TO_KEY_MAP.put('n', '6'); - CHAR_TO_KEY_MAP.put('o', '6'); - - CHAR_TO_KEY_MAP.put('p', '7'); - CHAR_TO_KEY_MAP.put('q', '7'); - CHAR_TO_KEY_MAP.put('r', '7'); - CHAR_TO_KEY_MAP.put('s', '7'); - - CHAR_TO_KEY_MAP.put('t', '8'); - CHAR_TO_KEY_MAP.put('u', '8'); - CHAR_TO_KEY_MAP.put('v', '8'); - - CHAR_TO_KEY_MAP.put('w', '9'); - CHAR_TO_KEY_MAP.put('x', '9'); - CHAR_TO_KEY_MAP.put('y', '9'); - CHAR_TO_KEY_MAP.put('z', '9'); - } private static LatinSmartDialMap instance; @@ -780,6 +743,6 @@ final class LatinSmartDialMap extends SmartDialMap { @Override SimpleArrayMap<Character, Character> getCharToKeyMap() { - return CHAR_TO_KEY_MAP; + return DialpadCharMappings.getDefaultCharToKeyMap(); } } diff --git a/java/com/android/dialer/smartdial/map/RussianSmartDialMap.java b/java/com/android/dialer/smartdial/map/RussianSmartDialMap.java new file mode 100644 index 000000000..c10bbb0ce --- /dev/null +++ b/java/com/android/dialer/smartdial/map/RussianSmartDialMap.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.dialer.smartdial.map; + +import android.support.v4.util.SimpleArrayMap; +import com.android.dialer.dialpadview.DialpadCharMappings; +import com.google.common.base.Optional; + +/** A {@link SmartDialMap} for the Russian alphabet. */ +@SuppressWarnings("Guava") +final class RussianSmartDialMap extends SmartDialMap { + + private static RussianSmartDialMap instance; + + static RussianSmartDialMap getInstance() { + if (instance == null) { + instance = new RussianSmartDialMap(); + } + + return instance; + } + + private RussianSmartDialMap() {} + + @Override + Optional<Character> normalizeCharacter(char ch) { + ch = Character.toLowerCase(ch); + return isValidDialpadAlphabeticChar(ch) ? Optional.of(ch) : Optional.absent(); + } + + @Override + SimpleArrayMap<Character, Character> getCharToKeyMap() { + return DialpadCharMappings.getCharToKeyMap("rus"); + } +} diff --git a/java/com/android/dialer/smartdial/SmartDialMap.java b/java/com/android/dialer/smartdial/map/SmartDialMap.java index bc5c9ea72..c74dd2893 100644 --- a/java/com/android/dialer/smartdial/SmartDialMap.java +++ b/java/com/android/dialer/smartdial/map/SmartDialMap.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.dialer.smartdial; +package com.android.dialer.smartdial.map; import android.support.v4.util.SimpleArrayMap; import com.google.common.base.Optional; diff --git a/java/com/android/dialer/smartdial/map/UkrainianSmartDialMap.java b/java/com/android/dialer/smartdial/map/UkrainianSmartDialMap.java new file mode 100644 index 000000000..844732c32 --- /dev/null +++ b/java/com/android/dialer/smartdial/map/UkrainianSmartDialMap.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.dialer.smartdial.map; + +import android.support.v4.util.SimpleArrayMap; +import com.android.dialer.dialpadview.DialpadCharMappings; +import com.google.common.base.Optional; + +/** A {@link SmartDialMap} for the Ukrainian alphabet. */ +final class UkrainianSmartDialMap extends SmartDialMap { + + private static UkrainianSmartDialMap instance; + + static UkrainianSmartDialMap getInstance() { + if (instance == null) { + instance = new UkrainianSmartDialMap(); + } + + return instance; + } + + private UkrainianSmartDialMap() {} + + @Override + Optional<Character> normalizeCharacter(char ch) { + ch = Character.toLowerCase(ch); + return isValidDialpadAlphabeticChar(ch) ? Optional.of(ch) : Optional.absent(); + } + + @Override + SimpleArrayMap<Character, Character> getCharToKeyMap() { + return DialpadCharMappings.getCharToKeyMap("ukr"); + } +} diff --git a/java/com/android/dialer/smartdial/SmartDialMatchPosition.java b/java/com/android/dialer/smartdial/util/SmartDialMatchPosition.java index 8056ad723..db317ae6b 100644 --- a/java/com/android/dialer/smartdial/SmartDialMatchPosition.java +++ b/java/com/android/dialer/smartdial/util/SmartDialMatchPosition.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.dialer.smartdial; +package com.android.dialer.smartdial.util; -import android.util.Log; +import com.android.dialer.common.LogUtil; import java.util.ArrayList; /** @@ -59,7 +59,7 @@ public class SmartDialMatchPosition { public static void print(ArrayList<SmartDialMatchPosition> list) { for (int i = 0; i < list.size(); i++) { SmartDialMatchPosition m = list.get(i); - Log.d(TAG, "[" + m.start + "," + m.end + "]"); + LogUtil.d(TAG, "[" + m.start + "," + m.end + "]"); } } diff --git a/java/com/android/dialer/smartdial/SmartDialNameMatcher.java b/java/com/android/dialer/smartdial/util/SmartDialNameMatcher.java index 4e3e0cc3f..725c88c57 100644 --- a/java/com/android/dialer/smartdial/SmartDialNameMatcher.java +++ b/java/com/android/dialer/smartdial/util/SmartDialNameMatcher.java @@ -14,12 +14,13 @@ * limitations under the License. */ -package com.android.dialer.smartdial; +package com.android.dialer.smartdial.util; import android.content.Context; import android.support.annotation.Nullable; import android.text.TextUtils; -import com.android.dialer.smartdial.SmartDialPrefix.PhoneNumberTokens; +import com.android.dialer.smartdial.map.CompositeSmartDialMap; +import com.android.dialer.smartdial.util.SmartDialPrefix.PhoneNumberTokens; import java.util.ArrayList; /** diff --git a/java/com/android/dialer/smartdial/SmartDialPrefix.java b/java/com/android/dialer/smartdial/util/SmartDialPrefix.java index b9c1f8c11..9af411913 100644 --- a/java/com/android/dialer/smartdial/SmartDialPrefix.java +++ b/java/com/android/dialer/smartdial/util/SmartDialPrefix.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.dialer.smartdial; +package com.android.dialer.smartdial.util; import android.content.Context; import android.content.SharedPreferences; @@ -22,6 +22,7 @@ import android.preference.PreferenceManager; import android.support.annotation.VisibleForTesting; import android.telephony.TelephonyManager; import android.text.TextUtils; +import com.android.dialer.smartdial.map.CompositeSmartDialMap; import java.util.ArrayList; import java.util.HashSet; import java.util.Set; diff --git a/java/com/android/dialer/telecom/TelecomUtil.java b/java/com/android/dialer/telecom/TelecomUtil.java index 22f3727e6..c64a50231 100644 --- a/java/com/android/dialer/telecom/TelecomUtil.java +++ b/java/com/android/dialer/telecom/TelecomUtil.java @@ -178,6 +178,10 @@ public abstract class TelecomUtil { * are not included. */ public static boolean isInManagedCall(Context context) { + return instance.isInManagedCall(context); + } + + public static boolean isInCall(Context context) { return instance.isInCall(context); } @@ -289,7 +293,7 @@ public abstract class TelecomUtil { @VisibleForTesting() public static class TelecomUtilImpl { - public boolean isInCall(Context context) { + public boolean isInManagedCall(Context context) { if (hasReadPhoneStatePermission(context)) { // The TelecomManager#isInCall method returns true anytime the user is in a call. // Starting in O, the APIs include support for self-managed ConnectionServices so that other @@ -308,6 +312,10 @@ public abstract class TelecomUtil { return false; } + public boolean isInCall(Context context) { + return hasReadPhoneStatePermission(context) && getTelecomManager(context).isInCall(); + } + public boolean hasPermission(Context context, String permission) { return ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED; diff --git a/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java b/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java index 315bf1cf0..93d5cda3e 100644 --- a/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java +++ b/java/com/android/dialer/voicemail/listui/NewVoicemailAdapter.java @@ -67,9 +67,9 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter<ViewHolder> private final Clock clock; /** {@link Integer#MAX_VALUE} when the "Today" header should not be displayed. */ - private final int todayHeaderPosition; + private int todayHeaderPosition = Integer.MAX_VALUE; /** {@link Integer#MAX_VALUE} when the "Older" header should not be displayed. */ - private final int olderHeaderPosition; + private int olderHeaderPosition = Integer.MAX_VALUE; private final FragmentManager fragmentManager; /** A valid id for {@link VoicemailEntry} is greater than 0 */ @@ -107,7 +107,15 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter<ViewHolder> this.clock = clock; this.fragmentManager = fragmentManager; initializeMediaPlayerListeners(); + updateHeaderPositions(); + } + private void updateHeaderPositions() { + LogUtil.i( + "NewVoicemailAdapter.updateHeaderPositions", + "before updating todayPos:%d, olderPos:%d", + todayHeaderPosition, + olderHeaderPosition); // Calculate header adapter positions by reading cursor. long currentTimeMillis = clock.currentTimeMillis(); if (cursor.moveToNext()) { @@ -134,6 +142,11 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter<ViewHolder> this.todayHeaderPosition = Integer.MAX_VALUE; this.olderHeaderPosition = Integer.MAX_VALUE; } + LogUtil.i( + "NewVoicemailAdapter.updateHeaderPositions", + "after updating todayPos:%d, olderPos:%d", + todayHeaderPosition, + olderHeaderPosition); } private void initializeMediaPlayerListeners() { @@ -143,8 +156,10 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter<ViewHolder> } public void updateCursor(Cursor updatedCursor) { + LogUtil.enterBlock("NewVoicemailAdapter.updateCursor"); deletedVoicemailPosition.clear(); this.cursor = updatedCursor; + updateHeaderPositions(); notifyDataSetChanged(); } @@ -509,8 +524,6 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter<ViewHolder> Assert.checkArgument(expandedViewHolder.getViewHolderVoicemailUri().equals(voicemailUri)); - notifyItemRemoved(expandedViewHolder.getAdapterPosition()); - Assert.checkArgument(currentlyExpandedViewHolderId == expandedViewHolder.getViewHolderId()); collapseExpandedViewHolder(expandedViewHolder); @@ -524,6 +537,8 @@ final class NewVoicemailAdapter extends RecyclerView.Adapter<ViewHolder> .onSuccess(deleteVoicemailCallBack) .build() .executeSerial(new Pair<>(context, voicemailUri)); + + notifyItemRemoved(expandedViewHolder.getAdapterPosition()); } private void onVoicemailDeleted(Integer integer) { diff --git a/java/com/android/dialer/voicemail/listui/NewVoicemailViewHolder.java b/java/com/android/dialer/voicemail/listui/NewVoicemailViewHolder.java index 24bed0f04..dac4ebafc 100644 --- a/java/com/android/dialer/voicemail/listui/NewVoicemailViewHolder.java +++ b/java/com/android/dialer/voicemail/listui/NewVoicemailViewHolder.java @@ -21,6 +21,7 @@ import static android.view.View.VISIBLE; import android.app.FragmentManager; import android.content.Context; import android.database.Cursor; +import android.graphics.Typeface; import android.net.Uri; import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; @@ -124,6 +125,9 @@ final class NewVoicemailViewHolder extends RecyclerView.ViewHolder implements On transcriptionTextView.setText(voicemailTranscription); } + // Bold if voicemail is unread + boldViewHolderIfUnread(); + itemView.setOnClickListener(this); menuButton.setOnClickListener( NewVoicemailMenu.createOnClickListener(context, voicemailEntryOfViewHolder)); @@ -173,6 +177,20 @@ final class NewVoicemailViewHolder extends RecyclerView.ViewHolder implements On mediaPlayerView.getVisibility() == VISIBLE); } + private void boldViewHolderIfUnread() { + LogUtil.v( + "NewVoicemailViewHolder.boldViewHolderIfUnread", + "id:%d, isRead:%d", + voicemailEntryOfViewHolder.id(), + voicemailEntryOfViewHolder.isRead()); + + if (voicemailEntryOfViewHolder.isRead() == 0) { + primaryTextView.setTypeface(null, Typeface.BOLD); + secondaryTextView.setTypeface(null, Typeface.BOLD); + transcriptionTextView.setTypeface(null, Typeface.BOLD); + } + } + // TODO(uabdullah): Consider/Implement TYPE (e.g Spam, TYPE_VOICEMAIL) private void setPhoto(VoicemailEntry voicemailEntry) { ContactPhotoManager.getInstance(context) @@ -214,6 +232,10 @@ final class NewVoicemailViewHolder extends RecyclerView.ViewHolder implements On isViewHolderExpanded = false; viewHolderVoicemailUri = null; + primaryTextView.setTypeface(null, Typeface.NORMAL); + secondaryTextView.setTypeface(null, Typeface.NORMAL); + transcriptionTextView.setTypeface(null, Typeface.NORMAL); + mediaPlayerView.reset(); LogUtil.i( diff --git a/java/com/android/dialer/voicemail/listui/VoicemailCursorLoader.java b/java/com/android/dialer/voicemail/listui/VoicemailCursorLoader.java index 6a55483a4..55d36b364 100644 --- a/java/com/android/dialer/voicemail/listui/VoicemailCursorLoader.java +++ b/java/com/android/dialer/voicemail/listui/VoicemailCursorLoader.java @@ -43,7 +43,8 @@ final class VoicemailCursorLoader extends CursorLoader { AnnotatedCallLog.GEOCODED_LOCATION, AnnotatedCallLog.CALL_TYPE, AnnotatedCallLog.TRANSCRIPTION, - AnnotatedCallLog.VOICEMAIL_URI + AnnotatedCallLog.VOICEMAIL_URI, + AnnotatedCallLog.IS_READ }; // Indexes for VOICEMAIL_COLUMNS @@ -60,6 +61,7 @@ final class VoicemailCursorLoader extends CursorLoader { private static final int CALL_TYPE = 10; private static final int TRANSCRIPTION = 11; private static final int VOICEMAIL_URI = 12; + private static final int IS_READ = 13; // TODO(zachh): Optimize indexes VoicemailCursorLoader(Context context) { @@ -95,6 +97,7 @@ final class VoicemailCursorLoader extends CursorLoader { .setVoicemailUri(cursor.getString(VOICEMAIL_URI)) .setGeocodedLocation(cursor.getString(GEOCODED_LOCATION)) .setCallType(cursor.getInt(CALL_TYPE)) + .setIsRead(cursor.getInt(IS_READ)) .build(); } diff --git a/java/com/android/dialer/voicemail/model/VoicemailEntry.java b/java/com/android/dialer/voicemail/model/VoicemailEntry.java index df30dee9c..702f52d17 100644 --- a/java/com/android/dialer/voicemail/model/VoicemailEntry.java +++ b/java/com/android/dialer/voicemail/model/VoicemailEntry.java @@ -32,7 +32,8 @@ public abstract class VoicemailEntry { .setNumber(DialerPhoneNumber.getDefaultInstance()) .setPhotoId(0) .setDuration(0) - .setCallType(0); + .setCallType(0) + .setIsRead(0); } public abstract int id(); @@ -69,6 +70,8 @@ public abstract class VoicemailEntry { public abstract int callType(); + public abstract int isRead(); + /** Builder for {@link VoicemailEntry}. */ @AutoValue.Builder public abstract static class Builder { @@ -99,6 +102,8 @@ public abstract class VoicemailEntry { public abstract Builder setCallType(int callType); + public abstract Builder setIsRead(int isRead); + public abstract VoicemailEntry build(); } } |