Allow the EmergencyInfo app to be tweaked with an overlay.

This allows the EmergencyInfo app to be replaced on some devices with
another app with the same package name. The two apps will share much of
the same source. The FeatureFactory interface provides a form of
dependency injection, allowing distinct concrete implementations to
exist for the same interface in the different apps.

In this first commit, the fragment class for the Contacts tab can be
overridden. This is an interim state intended to demonstrate the use of
the overlay mechanism in this app, while causing no regressions.

Test: Ran existing unit tests. Manually verified app still works as
expected.

Bug: 35335027
Change-Id: Icad03cf3351e47378c9b6a4b304621546d4e40c2
diff --git a/res/values/config.xml b/res/values/config.xml
new file mode 100644
index 0000000..54792d2
--- /dev/null
+++ b/res/values/config.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+      <!-- Fully-qualified class name for the implementation of the FeatureFactory to be instantiated. -->
+    <string name="config_featureFactory" translatable="false">com.android.emergency.overlay.FeatureFactoryImpl</string>
+
+</resources>
diff --git a/src/com/android/emergency/edit/EditInfoActivity.java b/src/com/android/emergency/edit/EditInfoActivity.java
index 405e506..cda3642 100644
--- a/src/com/android/emergency/edit/EditInfoActivity.java
+++ b/src/com/android/emergency/edit/EditInfoActivity.java
@@ -35,6 +35,7 @@
 import com.android.emergency.EmergencyTabActivity;
 import com.android.emergency.PreferenceKeys;
 import com.android.emergency.R;
+import com.android.emergency.overlay.FeatureFactory;
 import com.android.emergency.view.ViewInfoActivity;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -104,12 +105,14 @@
 
     @Override
     protected ArrayList<Pair<String, Fragment>> setUpFragments() {
+        FeatureFactory featureFactory = FeatureFactory.getFactory(this);
+
         // Always return the two fragments in edit mode.
         ArrayList<Pair<String, Fragment>> fragments = new ArrayList<>(2);
         fragments.add(Pair.create(getResources().getString(R.string.tab_title_info),
                 EditEmergencyInfoFragment.newInstance()));
         fragments.add(Pair.create(getResources().getString(R.string.tab_title_contacts),
-                EditEmergencyContactsFragment.newInstance()));
+                featureFactory.getEmergencyContactsFeatureProvider().createEditContactsFragment()));
         return fragments;
     }
 
diff --git a/src/com/android/emergency/edit/EmergencyContactsFeatureProvider.java b/src/com/android/emergency/edit/EmergencyContactsFeatureProvider.java
new file mode 100644
index 0000000..3b2dcf5
--- /dev/null
+++ b/src/com/android/emergency/edit/EmergencyContactsFeatureProvider.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.emergency.edit;
+
+import android.app.Fragment;
+
+/** Feature provider for Emergency Contacts. */
+public interface EmergencyContactsFeatureProvider {
+    /** @return a fragment to edit emergency contacts. */
+    Fragment createEditContactsFragment();
+}
diff --git a/src/com/android/emergency/edit/EmergencyContactsFeatureProviderImpl.java b/src/com/android/emergency/edit/EmergencyContactsFeatureProviderImpl.java
new file mode 100644
index 0000000..b0c640c
--- /dev/null
+++ b/src/com/android/emergency/edit/EmergencyContactsFeatureProviderImpl.java
@@ -0,0 +1,26 @@
+/*
+ * 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.emergency.edit;
+
+import android.app.Fragment;
+
+/** AOSP implementation of Emergency Contacts data source. */
+public class EmergencyContactsFeatureProviderImpl implements EmergencyContactsFeatureProvider {
+    @Override
+    public Fragment createEditContactsFragment() {
+        return EditEmergencyContactsFragment.newInstance();
+    }
+}
diff --git a/src/com/android/emergency/overlay/FeatureFactory.java b/src/com/android/emergency/overlay/FeatureFactory.java
new file mode 100644
index 0000000..1e86d0d
--- /dev/null
+++ b/src/com/android/emergency/overlay/FeatureFactory.java
@@ -0,0 +1,69 @@
+/*
+ * 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.emergency.overlay;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.emergency.edit.EmergencyContactsFeatureProvider;
+import com.android.emergency.R;
+
+/**
+ * Abstract class for creating feature controllers. Allows OEM implementations to define their own
+ * factories with their own controllers containing whatever code is needed to implement the
+ * features. To provide a factory implementation, implementers should override
+ * {@link R.string#config_featureFactory} in their override.
+ */
+public abstract class FeatureFactory {
+    private static final String LOG_TAG = "FeatureFactory";
+    private static final boolean DEBUG = false;
+
+    protected static FeatureFactory sFactory;
+
+    /** @return a singleton factory instance. */
+    public static FeatureFactory getFactory(Context context) {
+        if (sFactory != null) {
+            return sFactory;
+        }
+
+        if (DEBUG) Log.d(LOG_TAG, "getFactory");
+        final String clsName = context.getString(R.string.config_featureFactory);
+        if (TextUtils.isEmpty(clsName)) {
+            throw new UnsupportedOperationException("No feature factory configured");
+        }
+        try {
+            sFactory = (FeatureFactory) context.getClassLoader().loadClass(clsName).newInstance();
+        } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
+            throw new FactoryNotFoundException(e);
+        }
+
+        if (DEBUG) Log.d(LOG_TAG, "started " + sFactory.getClass().getSimpleName());
+        return sFactory;
+    }
+
+    public abstract EmergencyContactsFeatureProvider getEmergencyContactsFeatureProvider();
+
+    /** Exception thrown when a factory can't be created. */
+    public static final class FactoryNotFoundException extends RuntimeException {
+        /**
+         * @param throwable The underlying cause for this exception.
+         */
+        public FactoryNotFoundException(Throwable throwable) {
+            super("Unable to create factory. Did you misconfigure Proguard?", throwable);
+        }
+    }
+}
diff --git a/src/com/android/emergency/overlay/FeatureFactoryImpl.java b/src/com/android/emergency/overlay/FeatureFactoryImpl.java
new file mode 100644
index 0000000..d25fd59
--- /dev/null
+++ b/src/com/android/emergency/overlay/FeatureFactoryImpl.java
@@ -0,0 +1,24 @@
+package com.android.emergency.overlay;
+
+import android.content.Context;
+import android.net.Uri;
+import android.support.annotation.Keep;
+
+import com.android.emergency.edit.EmergencyContactsFeatureProvider;
+import com.android.emergency.edit.EmergencyContactsFeatureProviderImpl;
+
+/**
+ * {@link FeatureFactory} implementation for AOSP Emergency Info.
+ */
+@Keep
+public class FeatureFactoryImpl extends FeatureFactory {
+    protected EmergencyContactsFeatureProvider mEmergencyContactsFeatureProvider;
+
+    @Override
+    public EmergencyContactsFeatureProvider getEmergencyContactsFeatureProvider() {
+        if (mEmergencyContactsFeatureProvider == null) {
+            mEmergencyContactsFeatureProvider = new EmergencyContactsFeatureProviderImpl();
+        }
+        return mEmergencyContactsFeatureProvider;
+    }
+}