Merge changes I31e744f3,Ie64d2c6f

* changes:
  Use SwitchPreferenceWithClickableSummary instead of SwitchPreference for VM Donation
  Removed appContext from CallLogDataSource and PhoneLookup.
diff --git a/java/com/android/dialer/calllog/CallLogFramework.java b/java/com/android/dialer/calllog/CallLogFramework.java
index 365e4d7..936f2bb 100644
--- a/java/com/android/dialer/calllog/CallLogFramework.java
+++ b/java/com/android/dialer/calllog/CallLogFramework.java
@@ -63,7 +63,7 @@
     // TODO(zachh): Find a way to access Main#isNewUiEnabled without creating a circular dependency.
     if (ConfigProviderBindings.get(appContext).getBoolean("is_nui_shortcut_enabled", false)) {
       for (CallLogDataSource dataSource : dataSources.getDataSourcesIncludingSystemCallLog()) {
-        dataSource.registerContentObservers(appContext);
+        dataSource.registerContentObservers();
       }
     } else {
       LogUtil.i("CallLogFramework.registerContentObservers", "not registering content observers");
@@ -80,7 +80,7 @@
     }
 
     for (CallLogDataSource dataSource : dataSources.getDataSourcesIncludingSystemCallLog()) {
-      dataSource.unregisterContentObservers(appContext);
+      dataSource.unregisterContentObservers();
     }
 
     // Clear data only after all content observers have been disabled.
diff --git a/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java b/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java
index c399b05..32c2788 100644
--- a/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java
+++ b/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java
@@ -154,7 +154,7 @@
   private ListenableFuture<Boolean> isDirty() {
     List<ListenableFuture<Boolean>> isDirtyFutures = new ArrayList<>();
     for (CallLogDataSource dataSource : dataSources.getDataSourcesIncludingSystemCallLog()) {
-      ListenableFuture<Boolean> dataSourceDirty = dataSource.isDirty(appContext);
+      ListenableFuture<Boolean> dataSourceDirty = dataSource.isDirty();
       isDirtyFutures.add(dataSourceDirty);
       String eventName =
           String.format(Metrics.IS_DIRTY_TEMPLATE, dataSource.getClass().getSimpleName());
@@ -172,7 +172,7 @@
 
     // Start by filling the data sources--the system call log data source must go first!
     CallLogDataSource systemCallLogDataSource = dataSources.getSystemCallLogDataSource();
-    ListenableFuture<Void> fillFuture = systemCallLogDataSource.fill(appContext, mutations);
+    ListenableFuture<Void> fillFuture = systemCallLogDataSource.fill(mutations);
     String systemEventName = eventNameForFill(systemCallLogDataSource, isBuilt);
     futureTimer.applyTiming(fillFuture, systemEventName);
 
@@ -184,7 +184,7 @@
           Futures.transformAsync(
               fillFuture,
               unused -> {
-                ListenableFuture<Void> dataSourceFuture = dataSource.fill(appContext, mutations);
+                ListenableFuture<Void> dataSourceFuture = dataSource.fill(mutations);
                 String eventName = eventNameForFill(dataSource, isBuilt);
                 futureTimer.applyTiming(dataSourceFuture, eventName);
                 return dataSourceFuture;
@@ -215,7 +215,7 @@
               List<ListenableFuture<Void>> onSuccessfulFillFutures = new ArrayList<>();
               for (CallLogDataSource dataSource :
                   dataSources.getDataSourcesIncludingSystemCallLog()) {
-                ListenableFuture<Void> dataSourceFuture = dataSource.onSuccessfulFill(appContext);
+                ListenableFuture<Void> dataSourceFuture = dataSource.onSuccessfulFill();
                 onSuccessfulFillFutures.add(dataSourceFuture);
                 String eventName = eventNameForOnSuccessfulFill(dataSource, isBuilt);
                 futureTimer.applyTiming(dataSourceFuture, eventName);
diff --git a/java/com/android/dialer/calllog/datasources/CallLogDataSource.java b/java/com/android/dialer/calllog/datasources/CallLogDataSource.java
index fa3b6a9..f6796c7 100644
--- a/java/com/android/dialer/calllog/datasources/CallLogDataSource.java
+++ b/java/com/android/dialer/calllog/datasources/CallLogDataSource.java
@@ -17,7 +17,6 @@
 package com.android.dialer.calllog.datasources;
 
 import android.content.ContentValues;
-import android.content.Context;
 import android.support.annotation.MainThread;
 import android.support.annotation.WorkerThread;
 import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract;
@@ -32,18 +31,17 @@
  * always invoked.
  *
  * <ol>
- *   <li>{@link #isDirty(Context)}: Invoked only if the framework doesn't yet know if a rebuild is
+ *   <li>{@link #isDirty()}: Invoked only if the framework doesn't yet know if a rebuild is
  *       necessary.
- *   <li>{@link #fill(Context, CallLogMutations)}: Invoked only if the framework determined a
- *       rebuild is necessary.
- *   <li>{@link #onSuccessfulFill(Context)}: Invoked if and only if fill was previously called and
- *       the mutations provided by the previous fill operation succeeded in being applied.
+ *   <li>{@link #fill(CallLogMutations)}: Invoked only if the framework determined a rebuild is
+ *       necessary.
+ *   <li>{@link #onSuccessfulFill()}: Invoked if and only if fill was previously called and the
+ *       mutations provided by the previous fill operation succeeded in being applied.
  * </ol>
  *
- * <p>Because {@link #isDirty(Context)} is not always invoked, {@link #fill(Context,
- * CallLogMutations)} shouldn't rely on any state saved during {@link #isDirty(Context)}. It
- * <em>is</em> safe to assume that {@link #onSuccessfulFill(Context)} refers to the previous fill
- * operation.
+ * <p>Because {@link #isDirty()} is not always invoked, {@link #fill(CallLogMutations)} shouldn't
+ * rely on any state saved during {@link #isDirty()}. It <em>is</em> safe to assume that {@link
+ * #onSuccessfulFill()} refers to the previous fill operation.
  *
  * <p>The same data source objects may be reused across multiple checkDirtyAndRebuild cycles, so
  * implementors should take care to clear any internal state at the start of a new cycle.
@@ -65,7 +63,7 @@
    *
    * @see CallLogDataSource class doc for complete lifecyle information
    */
-  ListenableFuture<Boolean> isDirty(Context appContext);
+  ListenableFuture<Boolean> isDirty();
 
   /**
    * Computes the set of mutations necessary to update the annotated call log with respect to this
@@ -76,7 +74,7 @@
    *     contain inserts from the system call log, and these inserts should be modified by each data
    *     source.
    */
-  ListenableFuture<Void> fill(Context appContext, CallLogMutations mutations);
+  ListenableFuture<Void> fill(CallLogMutations mutations);
 
   /**
    * Called after database mutations have been applied to all data sources. This is useful for
@@ -85,7 +83,7 @@
    *
    * @see CallLogDataSource class doc for complete lifecyle information
    */
-  ListenableFuture<Void> onSuccessfulFill(Context appContext);
+  ListenableFuture<Void> onSuccessfulFill();
 
   /**
    * Combines raw annotated call log rows into a single coalesced row.
@@ -104,10 +102,10 @@
   ContentValues coalesce(List<ContentValues> individualRowsSortedByTimestampDesc);
 
   @MainThread
-  void registerContentObservers(Context appContext);
+  void registerContentObservers();
 
   @MainThread
-  void unregisterContentObservers(Context appContext);
+  void unregisterContentObservers();
 
   /**
    * Clear any data written by this data source. This is called when the new call log framework has
diff --git a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
index 615a457..0de9873 100644
--- a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
+++ b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
@@ -37,6 +37,7 @@
 import com.android.dialer.common.LogUtil;
 import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
 import com.android.dialer.common.concurrent.Annotations.LightweightExecutor;
+import com.android.dialer.inject.ApplicationContext;
 import com.android.dialer.phonelookup.PhoneLookup;
 import com.android.dialer.phonelookup.PhoneLookupInfo;
 import com.android.dialer.phonelookup.composite.CompositePhoneLookup;
@@ -66,6 +67,7 @@
  */
 public final class PhoneLookupDataSource implements CallLogDataSource {
 
+  private final Context appContext;
   private final CompositePhoneLookup compositePhoneLookup;
   private final ListeningExecutorService backgroundExecutorService;
   private final ListeningExecutorService lightweightExecutorService;
@@ -73,8 +75,8 @@
   /**
    * Keyed by normalized number (the primary key for PhoneLookupHistory).
    *
-   * <p>This is state saved between the {@link #fill(Context, CallLogMutations)} and {@link
-   * #onSuccessfulFill(Context)} operations.
+   * <p>This is state saved between the {@link CallLogDataSource#fill(CallLogMutations)} and {@link
+   * CallLogDataSource#onSuccessfulFill()} operations.
    */
   private final Map<String, PhoneLookupInfo> phoneLookupHistoryRowsToUpdate = new ArrayMap<>();
 
@@ -82,8 +84,8 @@
    * Normalized numbers (the primary key for PhoneLookupHistory) which should be deleted from
    * PhoneLookupHistory.
    *
-   * <p>This is state saved between the {@link #fill(Context, CallLogMutations)} and {@link
-   * #onSuccessfulFill(Context)} operations.
+   * <p>This is state saved between the {@link CallLogDataSource#fill(CallLogMutations)} and {@link
+   * CallLogDataSource#onSuccessfulFill()} operations.
    */
   private final Set<String> phoneLookupHistoryRowsToDelete = new ArraySet<>();
 
@@ -91,10 +93,12 @@
 
   @Inject
   PhoneLookupDataSource(
+      @ApplicationContext Context appContext,
       CompositePhoneLookup compositePhoneLookup,
       @BackgroundExecutor ListeningExecutorService backgroundExecutorService,
       @LightweightExecutor ListeningExecutorService lightweightExecutorService,
       PhoneLookupHistoryDatabaseHelper phoneLookupHistoryDatabaseHelper) {
+    this.appContext = appContext;
     this.compositePhoneLookup = compositePhoneLookup;
     this.backgroundExecutorService = backgroundExecutorService;
     this.lightweightExecutorService = lightweightExecutorService;
@@ -102,7 +106,7 @@
   }
 
   @Override
-  public ListenableFuture<Boolean> isDirty(Context appContext) {
+  public ListenableFuture<Boolean> isDirty() {
     ListenableFuture<ImmutableSet<DialerPhoneNumber>> phoneNumbers =
         backgroundExecutorService.submit(
             () -> queryDistinctDialerPhoneNumbersFromAnnotatedCallLog(appContext));
@@ -139,7 +143,7 @@
    * </ul>
    */
   @Override
-  public ListenableFuture<Void> fill(Context appContext, CallLogMutations mutations) {
+  public ListenableFuture<Void> fill(CallLogMutations mutations) {
     LogUtil.v(
         "PhoneLookupDataSource.fill",
         "processing mutations (inserts: %d, updates: %d, deletes: %d)",
@@ -243,7 +247,7 @@
   }
 
   @Override
-  public ListenableFuture<Void> onSuccessfulFill(Context appContext) {
+  public ListenableFuture<Void> onSuccessfulFill() {
     // First update and/or delete the appropriate rows in PhoneLookupHistory.
     ListenableFuture<Void> writePhoneLookupHistory =
         backgroundExecutorService.submit(() -> writePhoneLookupHistory(appContext));
@@ -296,13 +300,13 @@
 
   @MainThread
   @Override
-  public void registerContentObservers(Context appContext) {
-    compositePhoneLookup.registerContentObservers(appContext);
+  public void registerContentObservers() {
+    compositePhoneLookup.registerContentObservers();
   }
 
   @Override
-  public void unregisterContentObservers(Context appContext) {
-    compositePhoneLookup.unregisterContentObservers(appContext);
+  public void unregisterContentObservers() {
+    compositePhoneLookup.unregisterContentObservers();
   }
 
   @Override
diff --git a/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java b/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java
index e079961..6fd19dd 100644
--- a/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java
+++ b/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java
@@ -52,6 +52,7 @@
 import com.android.dialer.common.LogUtil;
 import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
 import com.android.dialer.compat.android.provider.VoicemailCompat;
+import com.android.dialer.inject.ApplicationContext;
 import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil;
 import com.android.dialer.storage.Unencrypted;
 import com.android.dialer.telecom.TelecomUtil;
@@ -79,6 +80,7 @@
   @VisibleForTesting
   static final String PREF_LAST_TIMESTAMP_PROCESSED = "systemCallLogLastTimestampProcessed";
 
+  private final Context appContext;
   private final ListeningExecutorService backgroundExecutorService;
   private final MarkDirtyObserver markDirtyObserver;
   private final SharedPreferences sharedPreferences;
@@ -88,10 +90,12 @@
 
   @Inject
   SystemCallLogDataSource(
+      @ApplicationContext Context appContext,
       @BackgroundExecutor ListeningExecutorService backgroundExecutorService,
       MarkDirtyObserver markDirtyObserver,
       @Unencrypted SharedPreferences sharedPreferences,
       AnnotatedCallLogDatabaseHelper annotatedCallLogDatabaseHelper) {
+    this.appContext = appContext;
     this.backgroundExecutorService = backgroundExecutorService;
     this.markDirtyObserver = markDirtyObserver;
     this.sharedPreferences = sharedPreferences;
@@ -100,7 +104,7 @@
 
   @MainThread
   @Override
-  public void registerContentObservers(Context appContext) {
+  public void registerContentObservers() {
     Assert.isMainThread();
 
     LogUtil.enterBlock("SystemCallLogDataSource.registerContentObservers");
@@ -130,7 +134,7 @@
   }
 
   @Override
-  public void unregisterContentObservers(Context appContext) {
+  public void unregisterContentObservers() {
     appContext.getContentResolver().unregisterContentObserver(markDirtyObserver);
   }
 
@@ -151,17 +155,17 @@
   }
 
   @Override
-  public ListenableFuture<Boolean> isDirty(Context appContext) {
+  public ListenableFuture<Boolean> isDirty() {
     return backgroundExecutorService.submit(this::isDirtyInternal);
   }
 
   @Override
-  public ListenableFuture<Void> fill(Context appContext, CallLogMutations mutations) {
-    return backgroundExecutorService.submit(() -> fillInternal(appContext, mutations));
+  public ListenableFuture<Void> fill(CallLogMutations mutations) {
+    return backgroundExecutorService.submit(() -> fillInternal(mutations));
   }
 
   @Override
-  public ListenableFuture<Void> onSuccessfulFill(Context appContext) {
+  public ListenableFuture<Void> onSuccessfulFill() {
     return backgroundExecutorService.submit(this::onSuccessfulFillInternal);
   }
 
@@ -181,7 +185,7 @@
   }
 
   @WorkerThread
-  private Void fillInternal(Context appContext, CallLogMutations mutations) {
+  private Void fillInternal(CallLogMutations mutations) {
     Assert.isWorkerThread();
 
     lastTimestampProcessed = null;
diff --git a/java/com/android/dialer/calllog/datasources/voicemail/VoicemailDataSource.java b/java/com/android/dialer/calllog/datasources/voicemail/VoicemailDataSource.java
index 75c0513..ab9288a 100644
--- a/java/com/android/dialer/calllog/datasources/voicemail/VoicemailDataSource.java
+++ b/java/com/android/dialer/calllog/datasources/voicemail/VoicemailDataSource.java
@@ -28,6 +28,7 @@
 import com.android.dialer.calllog.datasources.util.RowCombiner;
 import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
 import com.android.dialer.compat.telephony.TelephonyManagerCompat;
+import com.android.dialer.inject.ApplicationContext;
 import com.android.dialer.telecom.TelecomUtil;
 import com.android.dialer.util.PermissionsUtil;
 import com.google.common.util.concurrent.Futures;
@@ -41,15 +42,19 @@
 /** Provide information for whether the call is a call to the voicemail inbox. */
 public class VoicemailDataSource implements CallLogDataSource {
 
+  private final Context appContext;
   private final ListeningExecutorService backgroundExecutor;
 
   @Inject
-  VoicemailDataSource(@BackgroundExecutor ListeningExecutorService backgroundExecutor) {
+  VoicemailDataSource(
+      @ApplicationContext Context appContext,
+      @BackgroundExecutor ListeningExecutorService backgroundExecutor) {
+    this.appContext = appContext;
     this.backgroundExecutor = backgroundExecutor;
   }
 
   @Override
-  public ListenableFuture<Boolean> isDirty(Context appContext) {
+  public ListenableFuture<Boolean> isDirty() {
     // The isVoicemail status is immutable and permanent. The call will always show as "Voicemail"
     // even if the SIM is swapped. Dialing the row will result in some unexpected number after a SIM
     // swap but this is deemed acceptable.
@@ -58,7 +63,7 @@
 
   @Override
   @SuppressWarnings("missingPermission")
-  public ListenableFuture<Void> fill(Context appContext, CallLogMutations mutations) {
+  public ListenableFuture<Void> fill(CallLogMutations mutations) {
     if (!PermissionsUtil.hasReadPhoneStatePermissions(appContext)) {
       return Futures.immediateFuture(null);
     }
@@ -94,7 +99,7 @@
   }
 
   @Override
-  public ListenableFuture<Void> onSuccessfulFill(Context appContext) {
+  public ListenableFuture<Void> onSuccessfulFill() {
     return Futures.immediateFuture(null);
   }
 
@@ -107,10 +112,10 @@
   }
 
   @Override
-  public void registerContentObservers(Context appContext) {}
+  public void registerContentObservers() {}
 
   @Override
-  public void unregisterContentObservers(Context appContext) {}
+  public void unregisterContentObservers() {}
 
   @Override
   public ListenableFuture<Void> clearData() {
diff --git a/java/com/android/dialer/common/preference/AndroidManifest.xml b/java/com/android/dialer/common/preference/AndroidManifest.xml
new file mode 100644
index 0000000..3e80622
--- /dev/null
+++ b/java/com/android/dialer/common/preference/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<!--
+  ~ 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
+  -->
+<manifest
+  package="com.android.dialer.common.preference">
+</manifest>
diff --git a/java/com/android/dialer/common/preference/SwitchPreferenceWithClickableSummary.java b/java/com/android/dialer/common/preference/SwitchPreferenceWithClickableSummary.java
new file mode 100644
index 0000000..7b3564d
--- /dev/null
+++ b/java/com/android/dialer/common/preference/SwitchPreferenceWithClickableSummary.java
@@ -0,0 +1,104 @@
+/*
+ * 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.common.preference;
+
+import static android.support.v4.content.ContextCompat.startActivity;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.TypedArray;
+import android.net.Uri;
+import android.preference.SwitchPreference;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import com.android.dialer.common.Assert;
+
+/**
+ * Utility to allow the summary of a {@link SwitchPreference} to be clicked and opened via a browser
+ * to the specified {@link urlToOpen} attribute while maintaining all other aspects of a {@link
+ * SwitchPreference}.
+ *
+ * <p>Example usage:
+ *
+ * <pre>
+ *   <com.android.dialer.common.preference.SwitchPreferenceWithClickableSummary
+ *          android:dependency="...."
+ *          android:key="...."
+ *          android:title="...."
+ *          app:urlToOpen="...."/>
+ * </pre>
+ */
+public class SwitchPreferenceWithClickableSummary extends SwitchPreference {
+  private final String urlToOpen;
+
+  public SwitchPreferenceWithClickableSummary(
+      Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+    super(context, attrs, defStyleAttr, defStyleRes);
+    TypedArray typedArray =
+        context.obtainStyledAttributes(attrs, R.styleable.SwitchPreferenceWithClickableSummary);
+    urlToOpen =
+        String.valueOf(
+            typedArray.getText(R.styleable.SwitchPreferenceWithClickableSummary_urlToOpen));
+  }
+
+  public SwitchPreferenceWithClickableSummary(
+      Context context, AttributeSet attrs, int defStyleAttr) {
+    super(context, attrs, defStyleAttr, defStyleAttr);
+    TypedArray typedArray =
+        context.obtainStyledAttributes(attrs, R.styleable.SwitchPreferenceWithClickableSummary);
+    urlToOpen =
+        String.valueOf(
+            typedArray.getText(R.styleable.SwitchPreferenceWithClickableSummary_urlToOpen));
+  }
+
+  public SwitchPreferenceWithClickableSummary(Context context, AttributeSet attrs) {
+    super(context, attrs);
+    TypedArray typedArray =
+        context.obtainStyledAttributes(attrs, R.styleable.SwitchPreferenceWithClickableSummary);
+    urlToOpen =
+        String.valueOf(
+            typedArray.getText(R.styleable.SwitchPreferenceWithClickableSummary_urlToOpen));
+  }
+
+  public SwitchPreferenceWithClickableSummary(Context context) {
+    this(context, null);
+  }
+
+  @Override
+  protected View onCreateView(ViewGroup parent) {
+    return super.onCreateView(parent);
+  }
+
+  @Override
+  protected void onBindView(View view) {
+    super.onBindView(view);
+    Assert.checkArgument(
+        urlToOpen != null,
+        "must have a urlToOpen attribute when using SwitchPreferenceWithClickableSummary");
+    view.findViewById(android.R.id.summary)
+        .setOnClickListener(
+            new OnClickListener() {
+              @Override
+              public void onClick(View v) {
+                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(urlToOpen));
+                startActivity(view.getContext(), intent, null);
+              }
+            });
+  }
+}
diff --git a/java/com/android/dialer/common/preference/res/values/attrs.xml b/java/com/android/dialer/common/preference/res/values/attrs.xml
new file mode 100644
index 0000000..c1d1de0
--- /dev/null
+++ b/java/com/android/dialer/common/preference/res/values/attrs.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<resources>
+  <declare-styleable name="SwitchPreferenceWithClickableSummary">
+    <attr name="urlToOpen" format="string"/>
+  </declare-styleable>
+</resources>
+
diff --git a/java/com/android/dialer/phonelookup/PhoneLookup.java b/java/com/android/dialer/phonelookup/PhoneLookup.java
index d4a1ddf..d11f023 100644
--- a/java/com/android/dialer/phonelookup/PhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/PhoneLookup.java
@@ -16,7 +16,6 @@
 
 package com.android.dialer.phonelookup;
 
-import android.content.Context;
 import android.support.annotation.MainThread;
 import com.android.dialer.DialerPhoneNumber;
 import com.google.common.collect.ImmutableMap;
@@ -84,10 +83,10 @@
   ListenableFuture<Void> onSuccessfulBulkUpdate();
 
   @MainThread
-  void registerContentObservers(Context appContext);
+  void registerContentObservers();
 
   @MainThread
-  void unregisterContentObservers(Context appContext);
+  void unregisterContentObservers();
 
   /**
    * Clear any data written by this lookup. This is called when the new call log framework has been
diff --git a/java/com/android/dialer/phonelookup/blockednumber/DialerBlockedNumberPhoneLookup.java b/java/com/android/dialer/phonelookup/blockednumber/DialerBlockedNumberPhoneLookup.java
index f5a249a..ce879a9 100644
--- a/java/com/android/dialer/phonelookup/blockednumber/DialerBlockedNumberPhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/blockednumber/DialerBlockedNumberPhoneLookup.java
@@ -169,7 +169,7 @@
   }
 
   @Override
-  public void registerContentObservers(Context appContext) {
+  public void registerContentObservers() {
     appContext
         .getContentResolver()
         .registerContentObserver(
@@ -179,7 +179,7 @@
   }
 
   @Override
-  public void unregisterContentObservers(Context appContext) {
+  public void unregisterContentObservers() {
     appContext.getContentResolver().unregisterContentObserver(markDirtyObserver);
   }
 
diff --git a/java/com/android/dialer/phonelookup/blockednumber/SystemBlockedNumberPhoneLookup.java b/java/com/android/dialer/phonelookup/blockednumber/SystemBlockedNumberPhoneLookup.java
index e2a90c2..a3d51a7 100644
--- a/java/com/android/dialer/phonelookup/blockednumber/SystemBlockedNumberPhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/blockednumber/SystemBlockedNumberPhoneLookup.java
@@ -170,7 +170,7 @@
   }
 
   @Override
-  public void registerContentObservers(Context appContext) {
+  public void registerContentObservers() {
     if (VERSION.SDK_INT < VERSION_CODES.N) {
       return;
     }
@@ -183,7 +183,7 @@
   }
 
   @Override
-  public void unregisterContentObservers(Context appContext) {
+  public void unregisterContentObservers() {
     appContext.getContentResolver().unregisterContentObserver(markDirtyObserver);
   }
 
diff --git a/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java b/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java
index 365b218..7cc7a6d 100644
--- a/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java
@@ -16,7 +16,6 @@
 
 package com.android.dialer.phonelookup.composite;
 
-import android.content.Context;
 import android.support.annotation.MainThread;
 import android.support.annotation.VisibleForTesting;
 import com.android.dialer.DialerPhoneNumber;
@@ -214,19 +213,19 @@
         MoreExecutors.directExecutor());
   }
 
-  /** Delegates to sub-lookups' {@link PhoneLookup#registerContentObservers(Context)}. */
+  /** Delegates to sub-lookups' {@link PhoneLookup#registerContentObservers()}. */
   @MainThread
-  public void registerContentObservers(Context appContext) {
+  public void registerContentObservers() {
     for (PhoneLookup phoneLookup : phoneLookups) {
-      phoneLookup.registerContentObservers(appContext);
+      phoneLookup.registerContentObservers();
     }
   }
 
-  /** Delegates to sub-lookups' {@link PhoneLookup#unregisterContentObservers(Context)}. */
+  /** Delegates to sub-lookups' {@link PhoneLookup#unregisterContentObservers()}. */
   @MainThread
-  public void unregisterContentObservers(Context appContext) {
+  public void unregisterContentObservers() {
     for (PhoneLookup phoneLookup : phoneLookups) {
-      phoneLookup.unregisterContentObservers(appContext);
+      phoneLookup.unregisterContentObservers();
     }
   }
 
diff --git a/java/com/android/dialer/phonelookup/cp2/Cp2DefaultDirectoryPhoneLookup.java b/java/com/android/dialer/phonelookup/cp2/Cp2DefaultDirectoryPhoneLookup.java
index a5b3b72..902a2fb 100644
--- a/java/com/android/dialer/phonelookup/cp2/Cp2DefaultDirectoryPhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/cp2/Cp2DefaultDirectoryPhoneLookup.java
@@ -623,12 +623,12 @@
   }
 
   @Override
-  public void registerContentObservers(Context appContext) {
+  public void registerContentObservers() {
     // Do nothing since CP2 changes are too noisy.
   }
 
   @Override
-  public void unregisterContentObservers(Context appContext) {}
+  public void unregisterContentObservers() {}
 
   @Override
   public ListenableFuture<Void> clearData() {
diff --git a/java/com/android/dialer/phonelookup/cp2/Cp2ExtendedDirectoryPhoneLookup.java b/java/com/android/dialer/phonelookup/cp2/Cp2ExtendedDirectoryPhoneLookup.java
index 9110aa7..e497989 100644
--- a/java/com/android/dialer/phonelookup/cp2/Cp2ExtendedDirectoryPhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/cp2/Cp2ExtendedDirectoryPhoneLookup.java
@@ -228,14 +228,14 @@
   }
 
   @Override
-  public void registerContentObservers(Context appContext) {
+  public void registerContentObservers() {
     // For contacts in remote directories, no content observer can be registered.
     // For contacts in local (but not default) directories (e.g., the local work directory), we
     // don't register a content observer for now.
   }
 
   @Override
-  public void unregisterContentObservers(Context appContext) {}
+  public void unregisterContentObservers() {}
 
   @Override
   public ListenableFuture<Void> clearData() {
diff --git a/java/com/android/dialer/phonelookup/spam/SpamPhoneLookup.java b/java/com/android/dialer/phonelookup/spam/SpamPhoneLookup.java
index 315f658..6b77036 100644
--- a/java/com/android/dialer/phonelookup/spam/SpamPhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/spam/SpamPhoneLookup.java
@@ -16,7 +16,6 @@
 
 package com.android.dialer.phonelookup.spam;
 
-import android.content.Context;
 import android.content.SharedPreferences;
 import android.support.annotation.Nullable;
 import android.support.annotation.VisibleForTesting;
@@ -152,13 +151,13 @@
   }
 
   @Override
-  public void registerContentObservers(Context appContext) {
+  public void registerContentObservers() {
     // No content observer can be registered as Spam is not based on a content provider.
     // Each Spam implementation should be responsible for notifying any data changes.
   }
 
   @Override
-  public void unregisterContentObservers(Context appContext) {}
+  public void unregisterContentObservers() {}
 
   @Override
   public ListenableFuture<Void> clearData() {
diff --git a/java/com/android/dialer/voicemail/settings/VoicemailSettingsFragment.java b/java/com/android/dialer/voicemail/settings/VoicemailSettingsFragment.java
index 53c14b7..2e76b70 100644
--- a/java/com/android/dialer/voicemail/settings/VoicemailSettingsFragment.java
+++ b/java/com/android/dialer/voicemail/settings/VoicemailSettingsFragment.java
@@ -25,6 +25,7 @@
 import android.preference.PreferenceScreen;
 import android.preference.SwitchPreference;
 import android.provider.Settings;
+import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
 import android.support.annotation.VisibleForTesting;
 import android.telecom.PhoneAccount;
@@ -34,14 +35,18 @@
 import android.telephony.TelephonyManager;
 import com.android.dialer.common.Assert;
 import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.preference.SwitchPreferenceWithClickableSummary;
 import com.android.dialer.logging.DialerImpression;
 import com.android.dialer.logging.Logger;
 import com.android.dialer.notification.NotificationChannelManager;
+import com.android.dialer.spannable.ContentWithLearnMoreSpanner;
 import com.android.dialer.telecom.TelecomUtil;
 import com.android.voicemail.VoicemailClient;
 import com.android.voicemail.VoicemailClient.ActivationStateListener;
 import com.android.voicemail.VoicemailComponent;
 import com.google.common.base.Optional;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * Fragment for voicemail settings. Requires {@link VoicemailClient#PARAM_PHONE_ACCOUNT_HANDLE} set
@@ -63,19 +68,16 @@
 
   private static final String TAG = "VmSettingsActivity";
   @Nullable private PhoneAccountHandle phoneAccountHandle;
-
   private VoicemailClient voicemailClient;
-
   // Settings that are independent of the carrier configurations
   private Preference voicemailNotificationPreference;
   private PreferenceScreen advancedSettingsPreference;
-
   // Settings that are supported by dialer only if the carrier configurations are valid.
   private SwitchPreference visualVoicemailPreference;
   private SwitchPreference voicemailAutoArchivePreference;
   private SwitchPreference transcribeVoicemailPreference;
   // Voicemail transcription analysis toggle
-  private SwitchPreference donateTranscribedVoicemailPreference;
+  private SwitchPreferenceWithClickableSummary donateTranscribedVoicemailPreference;
   private Preference voicemailChangePinPreference;
 
   @Override
@@ -98,7 +100,7 @@
 
     addPreferencesFromResource(R.xml.voicemail_settings);
 
-    initializePreferences();
+    initializeXmlPreferences();
 
     setupVisualVoicemailPreferences();
 
@@ -163,15 +165,28 @@
   }
 
   private void showTranscriptionDonationEnabledPreferences() {
-    donateTranscribedVoicemailPreference.setOnPreferenceChangeListener(this);
+    donateTranscribedVoicemailPreference.setEnabled(true);
     donateTranscribedVoicemailPreference.setChecked(
         voicemailClient.isVoicemailDonationEnabled(getContext(), phoneAccountHandle));
+    donateTranscribedVoicemailPreference.setOnPreferenceChangeListener(this);
     donateTranscribedVoicemailPreference.setSummary(
-        R.string.voicemail_donate_preference_summary_info);
-    donateTranscribedVoicemailPreference.setEnabled(true);
+        getVoicemailTranscriptionDonationInformationalText());
     getPreferenceScreen().addPreference(donateTranscribedVoicemailPreference);
   }
 
+  /**
+   * Builds a spannable string containing the voicemail donation informational text containing the
+   * appropriate "Learn More" urls.
+   *
+   * @return The voicemail donation information text.
+   */
+  private CharSequence getVoicemailTranscriptionDonationInformationalText() {
+    return new ContentWithLearnMoreSpanner(getContext())
+        .create(
+            getContext().getString(R.string.voicemail_donate_preference_summary_info),
+            getContext().getString(R.string.donation_learn_more_url));
+  }
+
   private void removeAllTranscriptionPreferences() {
     getPreferenceScreen().removePreference(transcribeVoicemailPreference);
     getPreferenceScreen().removePreference(donateTranscribedVoicemailPreference);
@@ -196,29 +211,38 @@
     updateVoicemailSummaryMessage();
   }
 
-  private void initializePreferences() {
+  /** The preferences that are present in the voicemail_settings.xml file are initialized here. */
+  private void initializeXmlPreferences() {
     voicemailNotificationPreference =
         findPreference(getString(R.string.voicemail_notifications_key));
+    voicemailNotificationPreference.setOrder(VMSettingOrdering.NOTIFICATIONS);
 
     advancedSettingsPreference =
         (PreferenceScreen) findPreference(getString(R.string.voicemail_advanced_settings_key));
+    advancedSettingsPreference.setOrder(VMSettingOrdering.ADVANCED_SETTING);
 
     visualVoicemailPreference =
         (SwitchPreference) findPreference(getString(R.string.voicemail_visual_voicemail_key));
+    visualVoicemailPreference.setOrder(VMSettingOrdering.VISUAL_VOICEMAIL);
 
     voicemailAutoArchivePreference =
         (SwitchPreference)
             findPreference(getString(R.string.voicemail_visual_voicemail_archive_key));
+    voicemailAutoArchivePreference.setOrder(VMSettingOrdering.VOICEMAIL_AUTO_ARCHIVE);
 
     transcribeVoicemailPreference =
         (SwitchPreference)
             findPreference(getString(R.string.voicemail_visual_voicemail_transcription_key));
+    transcribeVoicemailPreference.setOrder(VMSettingOrdering.VOICEMAIL_TRANSCRIPTION);
 
     donateTranscribedVoicemailPreference =
-        (SwitchPreference)
+        (SwitchPreferenceWithClickableSummary)
             findPreference(getString(R.string.voicemail_visual_voicemail_donation_key));
+    donateTranscribedVoicemailPreference.setOrder(
+        VMSettingOrdering.VOICEMAIL_TRANSCRIPTION_DONATION);
 
     voicemailChangePinPreference = findPreference(getString(R.string.voicemail_change_pin_key));
+    voicemailChangePinPreference.setOrder(VMSettingOrdering.VOICEMAIL_CHANGE_PIN);
   }
 
   /** Removes vvm settings since the carrier setup is not supported by Dialer */
@@ -462,4 +486,25 @@
     builder.setCancelable(true);
     builder.show();
   }
+
+  /** The ordering in which to show the voicemail settings */
+  @Retention(RetentionPolicy.SOURCE)
+  @IntDef({
+    VMSettingOrdering.NOTIFICATIONS,
+    VMSettingOrdering.VISUAL_VOICEMAIL,
+    VMSettingOrdering.VOICEMAIL_TRANSCRIPTION,
+    VMSettingOrdering.VOICEMAIL_TRANSCRIPTION_DONATION,
+    VMSettingOrdering.VOICEMAIL_CHANGE_PIN,
+    VMSettingOrdering.VOICEMAIL_AUTO_ARCHIVE,
+    VMSettingOrdering.ADVANCED_SETTING
+  })
+  private @interface VMSettingOrdering {
+    int NOTIFICATIONS = 1;
+    int VISUAL_VOICEMAIL = 2;
+    int VOICEMAIL_TRANSCRIPTION = 3;
+    int VOICEMAIL_TRANSCRIPTION_DONATION = 4;
+    int VOICEMAIL_CHANGE_PIN = 5;
+    int VOICEMAIL_AUTO_ARCHIVE = 6;
+    int ADVANCED_SETTING = 7;
+  }
 }
diff --git a/java/com/android/dialer/voicemail/settings/res/values/strings.xml b/java/com/android/dialer/voicemail/settings/res/values/strings.xml
index db63098..7df8a01 100644
--- a/java/com/android/dialer/voicemail/settings/res/values/strings.xml
+++ b/java/com/android/dialer/voicemail/settings/res/values/strings.xml
@@ -119,9 +119,9 @@
   <string name="voicemail_activating_summary_info">Activating voicemail</string>
 
   <!-- Summary information for visual voicemail transcription setting [CHAR LIMIT=NONE] -->
-  <string name="voicemail_transcription_preference_summary_info">Get transcripts of your voicemail using Google\'s transcription service</string>
+  <string name="voicemail_transcription_preference_summary_info">Get transcripts of your voicemail using Google\'s transcription service.</string>
   <!-- Summary information for visual voicemail donation setting [CHAR LIMIT=NONE] -->
-  <string name="voicemail_donate_preference_summary_info">Let Google review your voicemail messages to improve transcription accuracy</string>
+  <string name="voicemail_donate_preference_summary_info">Let Google review your voicemail messages to improve transcription accuracy. Your voicemail messages are stored anonymously. <xliff:g example="Learn more">%1$s</xliff:g></string>
 
   <!-- Title for disable visual voicemail confirmation dialog [CHAR LIMIT=40] -->
   <string name="confirm_disable_voicemail_dialog_title">Turn off visual voicemail</string>
diff --git a/java/com/android/dialer/voicemail/settings/res/xml/voicemail_settings.xml b/java/com/android/dialer/voicemail/settings/res/xml/voicemail_settings.xml
index 175a127..e5af813 100644
--- a/java/com/android/dialer/voicemail/settings/res/xml/voicemail_settings.xml
+++ b/java/com/android/dialer/voicemail/settings/res/xml/voicemail_settings.xml
@@ -15,37 +15,40 @@
 -->
 
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
-  android:title="@string/voicemail_settings_title">
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:title="@string/voicemail_settings_title">
 
   <Preference
       android:key="@string/voicemail_notifications_key"
       android:title="@string/voicemail_notifications_preference_title"/>
 
   <SwitchPreference
-    android:key="@string/voicemail_visual_voicemail_key"
-    android:title="@string/voicemail_visual_voicemail_switch_title"/>"
+      android:key="@string/voicemail_visual_voicemail_key"
+      android:title="@string/voicemail_visual_voicemail_switch_title"/>"
 
   <SwitchPreference
-    android:key="@string/voicemail_visual_voicemail_archive_key"
-    android:dependency="@string/voicemail_visual_voicemail_key"
-    android:title="@string/voicemail_visual_voicemail_auto_archive_switch_title"/>"
-
-  <SwitchPreference
-      android:key="@string/voicemail_visual_voicemail_transcription_key"
       android:dependency="@string/voicemail_visual_voicemail_key"
-      android:title="@string/voicemail_visual_voicemail_transcription_switch_title"/>"
+      android:key="@string/voicemail_visual_voicemail_archive_key"
+      android:title="@string/voicemail_visual_voicemail_auto_archive_switch_title"/>"
+
   <SwitchPreference
-    android:key="@string/voicemail_visual_voicemail_donation_key"
-    android:dependency="@string/voicemail_visual_voicemail_transcription_key"
-    android:title="@string/voicemail_visual_voicemail_donation_switch_title"/>"
+      android:dependency="@string/voicemail_visual_voicemail_key"
+      android:key="@string/voicemail_visual_voicemail_transcription_key"
+      android:title="@string/voicemail_visual_voicemail_transcription_switch_title"/>"
+
+  <com.android.dialer.common.preference.SwitchPreferenceWithClickableSummary
+      android:dependency="@string/voicemail_visual_voicemail_transcription_key"
+      android:key="@string/voicemail_visual_voicemail_donation_key"
+      android:title="@string/voicemail_visual_voicemail_donation_switch_title"
+      app:urlToOpen="@string/donation_learn_more_url"/>
 
   <Preference
-    android:key="@string/voicemail_change_pin_key"
-    android:title="@string/voicemail_change_pin_preference_title"/>
+      android:key="@string/voicemail_change_pin_key"
+      android:title="@string/voicemail_change_pin_preference_title"/>
 
   <PreferenceScreen
-    android:key="@string/voicemail_advanced_settings_key"
-    android:title="@string/voicemail_advanced_settings_title">
-    </PreferenceScreen>
+      android:key="@string/voicemail_advanced_settings_key"
+      android:title="@string/voicemail_advanced_settings_title">
+  </PreferenceScreen>
 
 </PreferenceScreen>
diff --git a/packages.mk b/packages.mk
index 5e69a15..4ae490c 100644
--- a/packages.mk
+++ b/packages.mk
@@ -27,6 +27,7 @@
 	com.android.dialer.clipboard \
 	com.android.dialer.commandline \
 	com.android.dialer.common \
+	com.android.dialer.common.preference \
 	com.android.dialer.configprovider \
 	com.android.dialer.contactphoto \
 	com.android.dialer.contactsfragment \