Cleaning up build rules to simplify customizing derivative projects

> Using {packageName} instead of hardcoding com.android.launcher3 in AndroidManifest.xml
  for strings which are dependent on packageName
> Adding proguard rule to prevent obfuscating any overridable class
> Making it easier to extend SettingsActivity by overriding the fragment class

Change-Id: I5668c3f33b4cf20ad01d7f54b3d79cc0d268d391
diff --git a/src/com/android/launcher3/AppFilter.java b/src/com/android/launcher3/AppFilter.java
index 923835a..9b6166f 100644
--- a/src/com/android/launcher3/AppFilter.java
+++ b/src/com/android/launcher3/AppFilter.java
@@ -3,10 +3,12 @@
 import android.content.ComponentName;
 import android.content.Context;
 
-public class AppFilter {
+import com.android.launcher3.util.ResourceBasedOverride;
+
+public class AppFilter implements ResourceBasedOverride {
 
     public static AppFilter newInstance(Context context) {
-        return Utilities.getOverrideObject(AppFilter.class, context, R.string.app_filter_class);
+        return Overrides.getObject(AppFilter.class, context, R.string.app_filter_class);
     }
 
     public boolean shouldShowApp(ComponentName app) {
diff --git a/src/com/android/launcher3/IconProvider.java b/src/com/android/launcher3/IconProvider.java
index b469a8f..ed8d03c 100644
--- a/src/com/android/launcher3/IconProvider.java
+++ b/src/com/android/launcher3/IconProvider.java
@@ -5,14 +5,16 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 
+import com.android.launcher3.util.ResourceBasedOverride;
+
 import java.util.Locale;
 
-public class IconProvider {
+public class IconProvider implements ResourceBasedOverride {
 
     protected String mSystemState;
 
     public static IconProvider newInstance(Context context) {
-        IconProvider provider = Utilities.getOverrideObject(
+        IconProvider provider = Overrides.getObject(
                 IconProvider.class, context, R.string.icon_provider_class);
         provider.updateSystemStateString(context);
         return provider;
diff --git a/src/com/android/launcher3/LauncherAppTransitionManager.java b/src/com/android/launcher3/LauncherAppTransitionManager.java
index 4037a23..970e558 100644
--- a/src/com/android/launcher3/LauncherAppTransitionManager.java
+++ b/src/com/android/launcher3/LauncherAppTransitionManager.java
@@ -23,13 +23,15 @@
 import android.graphics.drawable.Drawable;
 import android.view.View;
 
+import com.android.launcher3.util.ResourceBasedOverride;
+
 /**
  * Manages the opening and closing app transitions from Launcher.
  */
-public class LauncherAppTransitionManager {
+public class LauncherAppTransitionManager implements ResourceBasedOverride {
 
     public static LauncherAppTransitionManager newInstance(Context context) {
-        return Utilities.getOverrideObject(LauncherAppTransitionManager.class,
+        return Overrides.getObject(LauncherAppTransitionManager.class,
                 context, R.string.app_transition_manager_class);
     }
 
diff --git a/src/com/android/launcher3/MainProcessInitializer.java b/src/com/android/launcher3/MainProcessInitializer.java
index 462eadb..0028f97 100644
--- a/src/com/android/launcher3/MainProcessInitializer.java
+++ b/src/com/android/launcher3/MainProcessInitializer.java
@@ -20,14 +20,15 @@
 
 import com.android.launcher3.graphics.IconShapeOverride;
 import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.util.ResourceBasedOverride;
 
 /**
  * Utility class to handle one time initializations of the main process
  */
-public class MainProcessInitializer {
+public class MainProcessInitializer implements ResourceBasedOverride {
 
     public static void initialize(Context context) {
-        Utilities.getOverrideObject(
+        Overrides.getObject(
                 MainProcessInitializer.class, context, R.string.main_process_initializer_class)
                 .init(context);
     }
diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java
index 32c198a..8589b7e 100644
--- a/src/com/android/launcher3/SettingsActivity.java
+++ b/src/com/android/launcher3/SettingsActivity.java
@@ -24,6 +24,7 @@
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.DialogFragment;
+import android.app.Fragment;
 import android.app.FragmentManager;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -53,7 +54,8 @@
 /**
  * Settings activity for Launcher. Currently implements the following setting: Allow rotation
  */
-public class SettingsActivity extends Activity {
+public class SettingsActivity extends Activity
+        implements PreferenceFragment.OnPreferenceStartFragmentCallback {
 
     private static final String ICON_BADGING_PREFERENCE_KEY = "pref_icon_badging";
     /** Hidden field Settings.Secure.NOTIFICATION_BADGING */
@@ -71,9 +73,10 @@
         super.onCreate(savedInstanceState);
 
         if (savedInstanceState == null) {
+            Fragment f = Fragment.instantiate(this, getString(R.string.settings_fragment_name));
             // Display the fragment as the main content.
             getFragmentManager().beginTransaction()
-                    .replace(android.R.id.content, getNewFragment())
+                    .replace(android.R.id.content, f)
                     .commit();
         }
     }
@@ -82,6 +85,22 @@
         return new LauncherSettingsFragment();
     }
 
+    @Override
+    public boolean onPreferenceStartFragment(
+            PreferenceFragment preferenceFragment, Preference pref) {
+        Fragment f = Fragment.instantiate(this, pref.getFragment(), pref.getExtras());
+        if (f instanceof DialogFragment) {
+            ((DialogFragment) f).show(getFragmentManager(), pref.getKey());
+        } else {
+            getFragmentManager()
+                    .beginTransaction()
+                    .replace(android.R.id.content, f)
+                    .addToBackStack(pref.getKey())
+                    .commit();
+        }
+        return true;
+    }
+
     /**
      * This fragment shows the launcher preferences.
      */
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 7fe8d35..8683b21 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -55,7 +55,6 @@
 import java.io.ByteArrayOutputStream;
 import java.io.Closeable;
 import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.Collection;
 import java.util.HashSet;
@@ -581,25 +580,6 @@
                 || e.getCause() instanceof DeadObjectException;
     }
 
-    public static <T> T getOverrideObject(Class<T> clazz, Context context, int resId) {
-        String className = context.getString(resId);
-        if (!TextUtils.isEmpty(className)) {
-            try {
-                Class<?> cls = Class.forName(className);
-                return (T) cls.getDeclaredConstructor(Context.class).newInstance(context);
-            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
-                    | ClassCastException | NoSuchMethodException | InvocationTargetException e) {
-                Log.e(TAG, "Bad overriden class", e);
-            }
-        }
-
-        try {
-            return clazz.newInstance();
-        } catch (InstantiationException|IllegalAccessException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
     /**
      * Returns a HashSet with a single element. We use this instead of Collections.singleton()
      * because HashSet ensures all operations, such as remove, are supported.
diff --git a/src/com/android/launcher3/graphics/DrawableFactory.java b/src/com/android/launcher3/graphics/DrawableFactory.java
index 34a4e2d..bbc013d 100644
--- a/src/com/android/launcher3/graphics/DrawableFactory.java
+++ b/src/com/android/launcher3/graphics/DrawableFactory.java
@@ -36,11 +36,12 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.AllAppsBackgroundDrawable;
+import com.android.launcher3.util.ResourceBasedOverride;
 
 /**
  * Factory for creating new drawables.
  */
-public class DrawableFactory {
+public class DrawableFactory implements ResourceBasedOverride {
 
     private static final String TAG = "DrawableFactory";
 
@@ -52,7 +53,7 @@
     public static DrawableFactory get(Context context) {
         synchronized (LOCK) {
             if (sInstance == null) {
-                sInstance = Utilities.getOverrideObject(DrawableFactory.class,
+                sInstance = Overrides.getObject(DrawableFactory.class,
                         context.getApplicationContext(), R.string.drawable_factory_class);
             }
             return sInstance;
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index d1e1051..d9d3f68 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -51,6 +51,7 @@
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.InstantAppResolver;
 import com.android.launcher3.util.LogConfig;
+import com.android.launcher3.util.ResourceBasedOverride;
 
 import java.util.Locale;
 import java.util.UUID;
@@ -61,7 +62,7 @@
  *
  * $ adb shell setprop log.tag.UserEvent VERBOSE
  */
-public class UserEventDispatcher {
+public class UserEventDispatcher implements ResourceBasedOverride {
 
     private final static int MAXIMUM_VIEW_HIERARCHY_LEVEL = 5;
 
@@ -78,7 +79,7 @@
             uuidStr = UUID.randomUUID().toString();
             sharedPrefs.edit().putString(UUID_STORAGE, uuidStr).apply();
         }
-        UserEventDispatcher ued = Utilities.getOverrideObject(UserEventDispatcher.class,
+        UserEventDispatcher ued = Overrides.getObject(UserEventDispatcher.class,
                 context.getApplicationContext(), R.string.user_event_dispatcher_class);
         ued.mDelegate = delegate;
         ued.mIsInLandscapeMode = dp.isVerticalBarLayout();
diff --git a/src/com/android/launcher3/util/InstantAppResolver.java b/src/com/android/launcher3/util/InstantAppResolver.java
index 4485427..5dc7af8 100644
--- a/src/com/android/launcher3/util/InstantAppResolver.java
+++ b/src/com/android/launcher3/util/InstantAppResolver.java
@@ -23,7 +23,6 @@
 
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
 
 import java.util.Collections;
 import java.util.List;
@@ -31,10 +30,10 @@
 /**
  * A wrapper class to access instant app related APIs.
  */
-public class InstantAppResolver {
+public class InstantAppResolver implements ResourceBasedOverride {
 
     public static InstantAppResolver newInstance(Context context) {
-        return Utilities.getOverrideObject(
+        return Overrides.getObject(
                 InstantAppResolver.class, context, R.string.instant_app_resolver_class);
     }
 
diff --git a/src/com/android/launcher3/util/ResourceBasedOverride.java b/src/com/android/launcher3/util/ResourceBasedOverride.java
new file mode 100644
index 0000000..e2c4992
--- /dev/null
+++ b/src/com/android/launcher3/util/ResourceBasedOverride.java
@@ -0,0 +1,54 @@
+/*
+ * 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.launcher3.util;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * An interface to indicate that a class is dynamically loaded using resource overlay, hence its
+ * class name and constructor should be preserved by proguard
+ */
+public interface ResourceBasedOverride {
+
+    class Overrides {
+
+        private static final String TAG = "Overrides";
+
+        public static <T extends ResourceBasedOverride> T getObject(
+                Class<T> clazz, Context context, int resId) {
+            String className = context.getString(resId);
+            if (!TextUtils.isEmpty(className)) {
+                try {
+                    Class<?> cls = Class.forName(className);
+                    return (T) cls.getDeclaredConstructor(Context.class).newInstance(context);
+                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
+                        | ClassCastException | NoSuchMethodException | InvocationTargetException e) {
+                    Log.e(TAG, "Bad overriden class", e);
+                }
+            }
+
+            try {
+                return clazz.newInstance();
+            } catch (InstantiationException|IllegalAccessException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+}