Merge "Only register conditions receiver when needed." into oc-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 10c0bc2..d96e505 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -3033,39 +3033,6 @@
             android:exported="true"
             android:permission="android.permission.DUMP" />
 
-        <!-- Conditional receivers, only enabled during silenced state, default off-->
-        <receiver
-            android:name=".dashboard.conditional.HotspotCondition$Receiver"
-            android:enabled="false">
-            <intent-filter>
-                 <action android:name="android.net.wifi.WIFI_AP_STATE_CHANGED" />
-            </intent-filter>
-       </receiver>
-
-        <receiver
-            android:name=".dashboard.conditional.AirplaneModeCondition$Receiver"
-            android:enabled="false">
-            <intent-filter>
-                 <action android:name="android.intent.action.AIRPLANE_MODE" />
-            </intent-filter>
-       </receiver>
-
-        <receiver
-            android:name=".dashboard.conditional.DndCondition$Receiver"
-            android:enabled="false">
-            <intent-filter>
-                 <action android:name="android.app.action.INTERRUPTION_FILTER_CHANGED_INTERNAL" />
-            </intent-filter>
-       </receiver>
-
-        <receiver
-            android:name=".dashboard.conditional.CellularDataCondition$Receiver"
-            android:enabled="false">
-            <intent-filter>
-                 <action android:name="android.intent.action.ANY_DATA_STATE" />
-            </intent-filter>
-       </receiver>
-
         <!-- Quick Settings tiles for Developer Options -->
         <service
             android:name=".qstile.DevelopmentTiles$ShowLayout"
diff --git a/src/com/android/settings/dashboard/DashboardSummary.java b/src/com/android/settings/dashboard/DashboardSummary.java
index 4419f24..67eae1e 100644
--- a/src/com/android/settings/dashboard/DashboardSummary.java
+++ b/src/com/android/settings/dashboard/DashboardSummary.java
@@ -92,6 +92,7 @@
         mSummaryLoader = new SummaryLoader(activity, CategoryKey.CATEGORY_HOMEPAGE);
 
         mConditionManager = ConditionManager.get(activity, false);
+        getLifecycle().addObserver(mConditionManager);
         mSuggestionParser = new SuggestionParser(activity,
                 activity.getSharedPreferences(SUGGESTIONS, 0), R.xml.suggestion_ordering);
         mSuggestionsChecks = new SuggestionsChecks(getContext());
diff --git a/src/com/android/settings/dashboard/conditional/AirplaneModeCondition.java b/src/com/android/settings/dashboard/conditional/AirplaneModeCondition.java
index 2d6a14d..7fd9af8 100644
--- a/src/com/android/settings/dashboard/conditional/AirplaneModeCondition.java
+++ b/src/com/android/settings/dashboard/conditional/AirplaneModeCondition.java
@@ -18,6 +18,7 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.graphics.drawable.Icon;
 import android.net.ConnectivityManager;
 import android.util.Log;
@@ -29,8 +30,14 @@
 public class AirplaneModeCondition extends Condition {
     public static String TAG = "APM_Condition";
 
+    private final Receiver mReceiver;
+
+    private static final IntentFilter AIRPLANE_MODE_FILTER =
+        new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+
     public AirplaneModeCondition(ConditionManager conditionManager) {
         super(conditionManager);
+        mReceiver = new Receiver();
     }
 
     @Override
@@ -40,8 +47,13 @@
     }
 
     @Override
-    protected Class<?> getReceiverClass() {
-        return Receiver.class;
+    protected BroadcastReceiver getReceiver() {
+        return mReceiver;
+    }
+
+    @Override
+    protected IntentFilter getIntentFilter() {
+        return AIRPLANE_MODE_FILTER;
     }
 
     @Override
diff --git a/src/com/android/settings/dashboard/conditional/CellularDataCondition.java b/src/com/android/settings/dashboard/conditional/CellularDataCondition.java
index 6156e39..523bfc4 100644
--- a/src/com/android/settings/dashboard/conditional/CellularDataCondition.java
+++ b/src/com/android/settings/dashboard/conditional/CellularDataCondition.java
@@ -13,6 +13,7 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.graphics.drawable.Icon;
 import android.net.ConnectivityManager;
 import android.telephony.TelephonyManager;
@@ -23,8 +24,14 @@
 
 public class CellularDataCondition extends Condition {
 
+    private final Receiver mReceiver;
+
+    private static final IntentFilter DATA_CONNECTION_FILTER =
+        new IntentFilter(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
+
     public CellularDataCondition(ConditionManager manager) {
         super(manager);
+        mReceiver = new Receiver();
     }
 
     @Override
@@ -41,8 +48,13 @@
     }
 
     @Override
-    protected Class<?> getReceiverClass() {
-        return Receiver.class;
+    protected BroadcastReceiver getReceiver() {
+        return mReceiver;
+    }
+
+    @Override
+    protected IntentFilter getIntentFilter() {
+        return DATA_CONNECTION_FILTER;
     }
 
     @Override
diff --git a/src/com/android/settings/dashboard/conditional/Condition.java b/src/com/android/settings/dashboard/conditional/Condition.java
index b184004..769b521 100644
--- a/src/com/android/settings/dashboard/conditional/Condition.java
+++ b/src/com/android/settings/dashboard/conditional/Condition.java
@@ -16,18 +16,17 @@
 
 package com.android.settings.dashboard.conditional;
 
-import android.content.ComponentName;
+import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.pm.PackageManager;
+import android.content.IntentFilter;
 import android.graphics.drawable.Icon;
 import android.os.PersistableBundle;
 
+import android.support.annotation.VisibleForTesting;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.core.instrumentation.MetricsFeatureProvider;
 import com.android.settings.overlay.FeatureFactory;
 
-import static android.content.pm.PackageManager.DONT_KILL_APP;
-
 public abstract class Condition {
 
     private static final String KEY_SILENCE = "silence";
@@ -49,12 +48,6 @@
     Condition(ConditionManager manager, MetricsFeatureProvider metricsFeatureProvider) {
         mManager = manager;
         mMetricsFeatureProvider = metricsFeatureProvider;
-        Class<?> receiverClass = getReceiverClass();
-        if (receiverClass != null && shouldAlwaysListenToBroadcast()) {
-            PackageManager pm = mManager.getContext().getPackageManager();
-            pm.setComponentEnabledSetting(new ComponentName(mManager.getContext(), receiverClass),
-                    PackageManager.COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP);
-        }
     }
 
     void restoreState(PersistableBundle bundle) {
@@ -110,29 +103,25 @@
         }
     }
 
-    private void onSilenceChanged(boolean silenced) {
-        if (shouldAlwaysListenToBroadcast()) {
-            // Don't try to disable BroadcastReceiver if we want it always on.
+    @VisibleForTesting
+    void onSilenceChanged(boolean silenced) {
+        final BroadcastReceiver receiver = getReceiver();
+        if (receiver == null) {
             return;
         }
-        Class<?> clz = getReceiverClass();
-        if (clz == null) {
-            return;
+        if (silenced) {
+            mManager.getContext().registerReceiver(receiver, getIntentFilter());
+        } else {
+            mManager.getContext().unregisterReceiver(receiver);
         }
-        // Only need to listen for changes when its been silenced.
-        PackageManager pm = mManager.getContext().getPackageManager();
-        pm.setComponentEnabledSetting(new ComponentName(mManager.getContext(), clz),
-                silenced ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
-                        : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
-                DONT_KILL_APP);
     }
 
-    protected Class<?> getReceiverClass() {
+    protected BroadcastReceiver getReceiver() {
         return null;
     }
 
-    protected boolean shouldAlwaysListenToBroadcast() {
-        return false;
+    protected IntentFilter getIntentFilter() {
+        return null;
     }
 
     public boolean shouldShow() {
@@ -143,6 +132,12 @@
         return mLastStateChange;
     }
 
+    public void onResume() {
+    }
+
+    public void onPause() {
+    }
+
     // State.
     public abstract void refreshState();
 
diff --git a/src/com/android/settings/dashboard/conditional/ConditionManager.java b/src/com/android/settings/dashboard/conditional/ConditionManager.java
index 030c62d..c67fb32 100644
--- a/src/com/android/settings/dashboard/conditional/ConditionManager.java
+++ b/src/com/android/settings/dashboard/conditional/ConditionManager.java
@@ -21,6 +21,9 @@
 import android.util.Log;
 import android.util.Xml;
 
+import com.android.settings.core.lifecycle.LifecycleObserver;
+import com.android.settings.core.lifecycle.events.OnPause;
+import com.android.settings.core.lifecycle.events.OnResume;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
@@ -34,7 +37,7 @@
 import java.util.Comparator;
 import java.util.List;
 
-public class ConditionManager {
+public class ConditionManager implements LifecycleObserver, OnResume, OnPause {
 
     private static final String TAG = "ConditionManager";
 
@@ -228,6 +231,20 @@
         mListeners.remove(listener);
     }
 
+    @Override
+    public void onResume() {
+        for (int i = 0, size = mConditions.size(); i < size; i++) {
+            mConditions.get(i).onResume();
+        }
+    }
+
+    @Override
+    public void onPause() {
+        for (int i = 0, size = mConditions.size(); i < size; i++) {
+            mConditions.get(i).onPause();
+        }
+    }
+
     private class ConditionLoader extends AsyncTask<Void, Void, ArrayList<Condition>> {
         @Override
         protected ArrayList<Condition> doInBackground(Void... params) {
diff --git a/src/com/android/settings/dashboard/conditional/DndCondition.java b/src/com/android/settings/dashboard/conditional/DndCondition.java
index f6f64fa..a60c362 100644
--- a/src/com/android/settings/dashboard/conditional/DndCondition.java
+++ b/src/com/android/settings/dashboard/conditional/DndCondition.java
@@ -21,11 +21,13 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.graphics.drawable.Icon;
 import android.os.PersistableBundle;
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.service.notification.ZenModeConfig;
+import android.support.annotation.VisibleForTesting;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
@@ -35,11 +37,21 @@
     private static final String TAG = "DndCondition";
     private static final String KEY_STATE = "state";
 
+    private boolean mRegistered;
+
+    @VisibleForTesting
+    static final IntentFilter DND_FILTER =
+        new IntentFilter(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED_INTERNAL);
+
     private int mZen;
     private ZenModeConfig mConfig;
+    private final Receiver mReceiver;
 
     public DndCondition(ConditionManager manager) {
         super(manager);
+        mReceiver = new Receiver();
+        mManager.getContext().registerReceiver(mReceiver, DND_FILTER);
+        mRegistered = true;
     }
 
     @Override
@@ -68,11 +80,6 @@
         mZen = bundle.getInt(KEY_STATE, Global.ZEN_MODE_OFF);
     }
 
-    @Override
-    protected Class<?> getReceiverClass() {
-        return Receiver.class;
-    }
-
     private CharSequence getZenState() {
         switch (mZen) {
             case Settings.Global.ZEN_MODE_ALARMS:
@@ -149,7 +156,16 @@
     }
 
     @Override
-    protected boolean shouldAlwaysListenToBroadcast() {
-        return true;
+    public void onResume() {
+        if (!mRegistered) {
+           mManager.getContext().registerReceiver(mReceiver, DND_FILTER);
+           mRegistered = true;
+        }
+    }
+
+    @Override
+    public void onPause() {
+        mManager.getContext().unregisterReceiver(mReceiver);
+        mRegistered = false;
     }
 }
diff --git a/src/com/android/settings/dashboard/conditional/HotspotCondition.java b/src/com/android/settings/dashboard/conditional/HotspotCondition.java
index 19e0e85..4ddf47c 100644
--- a/src/com/android/settings/dashboard/conditional/HotspotCondition.java
+++ b/src/com/android/settings/dashboard/conditional/HotspotCondition.java
@@ -18,6 +18,7 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.graphics.drawable.Icon;
 import android.net.ConnectivityManager;
 import android.net.wifi.WifiConfiguration;
@@ -31,15 +32,19 @@
 import com.android.settings.Utils;
 import com.android.settingslib.RestrictedLockUtils;
 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
-import com.android.settingslib.TetherUtil;
 
 public class HotspotCondition extends Condition {
 
     private final WifiManager mWifiManager;
+    private final Receiver mReceiver;
+
+    private static final IntentFilter WIFI_AP_STATE_FILTER =
+        new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
 
     public HotspotCondition(ConditionManager manager) {
         super(manager);
         mWifiManager = mManager.getContext().getSystemService(WifiManager.class);
+        mReceiver = new Receiver();
     }
 
     @Override
@@ -49,8 +54,13 @@
     }
 
     @Override
-    protected Class<?> getReceiverClass() {
-        return Receiver.class;
+    protected BroadcastReceiver getReceiver() {
+        return mReceiver;
+    }
+
+    @Override
+    protected IntentFilter getIntentFilter() {
+        return WIFI_AP_STATE_FILTER;
     }
 
     @Override
diff --git a/tests/robotests/src/com/android/settings/dashboard/conditional/ConditionTest.java b/tests/robotests/src/com/android/settings/dashboard/conditional/ConditionTest.java
index c3c1d6d..a408096 100644
--- a/tests/robotests/src/com/android/settings/dashboard/conditional/ConditionTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/conditional/ConditionTest.java
@@ -15,7 +15,9 @@
  */
 package com.android.settings.dashboard.conditional;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.IntentFilter;
 import android.graphics.drawable.Icon;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.settings.SettingsRobolectricTestRunner;
@@ -31,7 +33,9 @@
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 @RunWith(SettingsRobolectricTestRunner.class)
 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
@@ -41,6 +45,8 @@
     private ConditionManager mConditionManager;
     @Mock
     private MetricsFeatureProvider mMetricsFeatureProvider;
+    @Mock
+    private Context mContext;
 
     private TestCondition mCondition;
 
@@ -48,6 +54,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mCondition = new TestCondition(mConditionManager, mMetricsFeatureProvider);
+        when(mConditionManager.getContext()).thenReturn(mContext);
     }
 
     @Test
@@ -66,9 +73,26 @@
                 eq(TestCondition.TEST_METRIC_CONSTANT));
     }
 
+    @Test
+    public void onSilenceChanged_silenced_shouldRegisterReceiver() {
+        mCondition.onSilenceChanged(true);
+
+        verify(mContext).registerReceiver(
+            TestCondition.mReceiver, TestCondition.TESTS_INTENT_FILTER);
+    }
+
+    @Test
+    public void onSilenceChanged_notSilenced_shouldUnregisterReceiver() {
+        mCondition.onSilenceChanged(false);
+
+        verify(mContext).unregisterReceiver(TestCondition.mReceiver);
+    }
+
     private static final class TestCondition extends Condition {
 
         private static final int TEST_METRIC_CONSTANT = 1234;
+        private static final IntentFilter TESTS_INTENT_FILTER = new IntentFilter("TestIntent");
+        private static final BroadcastReceiver mReceiver = mock(BroadcastReceiver.class);
 
         TestCondition(ConditionManager manager,
                 MetricsFeatureProvider metricsFeatureProvider) {
@@ -114,5 +138,16 @@
         public void onActionClick(int index) {
 
         }
+
+        @Override
+        public BroadcastReceiver getReceiver() {
+            return mReceiver;
+        }
+
+        @Override
+        public IntentFilter getIntentFilter() {
+            return TESTS_INTENT_FILTER;
+        }
+
     }
 }
diff --git a/tests/robotests/src/com/android/settings/dashboard/conditional/DndConditionTest.java b/tests/robotests/src/com/android/settings/dashboard/conditional/DndConditionTest.java
new file mode 100644
index 0000000..3344698
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/dashboard/conditional/DndConditionTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.settings.dashboard.conditional;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class DndConditionTest {
+
+    @Mock
+    private ConditionManager mConditionManager;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mConditionManager.getContext()).thenReturn(mContext);
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+    }
+
+    @Test
+    public void constructor_shouldNotDisableReceiver() {
+        DndCondition condition = new DndCondition(mConditionManager);
+        verify(mPackageManager, never()).setComponentEnabledSetting(any(ComponentName.class),
+            eq(PackageManager.COMPONENT_ENABLED_STATE_DISABLED), eq(PackageManager.DONT_KILL_APP));
+    }
+
+    @Test
+    public void constructor_shouldRegisterReceiver() {
+        DndCondition condition = new DndCondition(mConditionManager);
+        verify(mContext).registerReceiver(any(DndCondition.Receiver.class),
+            eq(DndCondition.DND_FILTER));
+    }
+
+    @Test
+    public void silence_shouldNotDisableReceiver() {
+        DndCondition condition = new DndCondition(mConditionManager);
+        condition.silence();
+
+        verify(mPackageManager, never()).setComponentEnabledSetting(any(ComponentName.class),
+            eq(PackageManager.COMPONENT_ENABLED_STATE_DISABLED), eq(PackageManager.DONT_KILL_APP));
+    }
+
+    @Test
+    public void onResume_shouldRegisterReceiver() {
+        DndCondition condition = new DndCondition(mConditionManager);
+        condition.onPause();
+        condition.onResume();
+
+        // one from constructor, one from onResume()
+        verify(mContext, times(2)).registerReceiver(any(DndCondition.Receiver.class),
+            eq(DndCondition.DND_FILTER));
+    }
+
+    @Test
+    public void onPause_shouldUnregisterReceiver() {
+        DndCondition condition = new DndCondition(mConditionManager);
+        condition.onPause();
+
+        verify(mContext).unregisterReceiver(any(DndCondition.Receiver.class));
+    }
+}