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
This commit is contained in:
Sunny Goyal 2018-06-27 15:47:49 -07:00
parent 132bfb8a1f
commit 7f920b8d5e
19 changed files with 163 additions and 121 deletions

View File

@ -44,6 +44,28 @@
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<!--
Permissions required for read/write access to the workspace data. These permission name
should not conflict with that defined in other apps, as such an app should embed its package
name in the permissions. eq com.mypackage.permission.READ_SETTINGS
-->
<permission
android:name="${packageName}.permission.READ_SETTINGS"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="signatureOrSystem"
android:label="@string/permlab_read_settings"
android:description="@string/permdesc_read_settings"/>
<permission
android:name="${packageName}.permission.WRITE_SETTINGS"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="signatureOrSystem"
android:label="@string/permlab_write_settings"
android:description="@string/permdesc_write_settings"/>
<uses-permission android:name="${packageName}.permission.READ_SETTINGS" />
<uses-permission android:name="${packageName}.permission.WRITE_SETTINGS" />
<application
android:backupAgent="com.android.launcher3.LauncherBackupAgent"
android:fullBackupOnly="true"
@ -118,5 +140,31 @@
android:name="com.android.launcher3.launcher_dump_provider"
android:value="com.android.launcher3.LauncherProvider" />
<!--
The settings provider contains Home's data, like the workspace favorites. The permissions
should be changed to what is defined above. The authorities should also be changed to
represent the package name.
-->
<provider
android:name="com.android.launcher3.LauncherProvider"
android:authorities="${packageName}.settings"
android:exported="true"
android:writePermission="${packageName}.permission.WRITE_SETTINGS"
android:readPermission="${packageName}.permission.READ_SETTINGS" />
<!--
The settings activity. To extend point settings_fragment_name to appropriate fragment class
-->
<activity
android:name="com.android.launcher3.SettingsActivity"
android:label="@string/settings_button_text"
android:theme="@android:style/Theme.DeviceDefault.Settings"
android:autoRemoveFromRecents="true">
<intent-filter>
<action android:name="android.intent.action.APPLICATION_PREFERENCES" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -26,29 +26,6 @@
Refer comments around specific entries on how to extend individual components.
-->
<!--
Permissions required for read/write access to the workspace data. These permission name
should not conflict with that defined in other apps, as such an app should embed its package
name in the permissions. eq com.mypackage.permission.READ_SETTINGS
-->
<permission
android:name="com.android.launcher3.permission.READ_SETTINGS"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="signatureOrSystem"
android:label="@string/permlab_read_settings"
android:description="@string/permdesc_read_settings"/>
<permission
android:name="com.android.launcher3.permission.WRITE_SETTINGS"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="signatureOrSystem"
android:label="@string/permlab_write_settings"
android:description="@string/permdesc_write_settings"/>
<uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
<uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />
<uses-permission android:name="com.android.launcher3.permission.READ_SETTINGS" />
<uses-permission android:name="com.android.launcher3.permission.WRITE_SETTINGS" />
<application
android:backupAgent="com.android.launcher3.LauncherBackupAgent"
android:fullBackupOnly="true"
@ -86,31 +63,5 @@
</intent-filter>
</activity>
<!--
The settings activity. When extending keep the intent filter present
-->
<activity
android:name="com.android.launcher3.SettingsActivity"
android:label="@string/settings_button_text"
android:theme="@android:style/Theme.DeviceDefault.Settings"
android:autoRemoveFromRecents="true">
<intent-filter>
<action android:name="android.intent.action.APPLICATION_PREFERENCES" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<!--
The settings provider contains Home's data, like the workspace favorites. The permissions
should be changed to what is defined above. The authorities should also be changed to
represent the package name.
-->
<provider
android:name="com.android.launcher3.LauncherProvider"
android:authorities="com.android.launcher3.settings"
android:exported="true"
android:writePermission="com.android.launcher3.permission.WRITE_SETTINGS"
android:readPermission="com.android.launcher3.permission.READ_SETTINGS" />
</application>
</manifest>

View File

@ -97,30 +97,19 @@
# support jar.
-keep class android.support.v7.widget.RecyclerView { *; }
# LauncherAppTransitionManager
-keep class com.android.launcher3.LauncherAppTransitionManagerImpl {
# Preference fragments
-keep class ** extends android.preference.PreferenceFragment {
public <init>(...);
}
# InstantAppResolver
-keep class com.android.quickstep.InstantAppResolverImpl {
public <init>(...);
}
# MainProcessInitializer
-keep class com.android.quickstep.QuickstepProcessInitializer {
public <init>(...);
}
# UserEventDispatcherExtension
-keep class com.android.quickstep.logging.UserEventDispatcherExtension {
## Prevent obfuscating various overridable objects
-keep class ** implements com.android.launcher3.util.ResourceBasedOverride {
public <init>(...);
}
-keep interface com.android.launcher3.userevent.nano.LauncherLogProto.** {
*;
}
-keep interface com.android.launcher3.model.nano.LauncherDumpProto.** {
*;
}

View File

@ -24,6 +24,7 @@
<uses-sdk android:targetSdkVersion="28" android:minSdkVersion="28"/>
<uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
<application
android:backupAgent="com.android.launcher3.LauncherBackupAgent"
android:fullBackupOnly="true"
@ -59,10 +60,10 @@
android:resumeWhilePausing="true"
android:taskAffinity="" />
<!-- Content provider to settings search -->
<!-- Content provider to settings search. The autority should be same as the packageName -->
<provider
android:name="com.android.quickstep.LauncherSearchIndexablesProvider"
android:authorities="com.android.launcher3"
android:authorities="${packageName}"
android:grantUriPermissions="true"
android:multiprocess="true"
android:permission="android.permission.READ_SEARCH_INDEXABLES"
@ -72,7 +73,6 @@
</intent-filter>
</provider>
<service
android:name="com.android.launcher3.uioverrides.dynamicui.WallpaperManagerCompatVL$ColorExtractionService"
tools:node="remove" />

View File

@ -18,20 +18,20 @@ package com.android.quickstep;
import android.content.Context;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.ResourceBasedOverride;
/**
* Callbacks related to overview/quicksteps.
*/
public class OverviewCallbacks {
public class OverviewCallbacks implements ResourceBasedOverride {
private static OverviewCallbacks sInstance;
public static OverviewCallbacks get(Context context) {
Preconditions.assertUIThread();
if (sInstance == null) {
sInstance = Utilities.getOverrideObject(OverviewCallbacks.class,
sInstance = Overrides.getObject(OverviewCallbacks.class,
context.getApplicationContext(), R.string.overview_callbacks_class);
}
return sInstance;

View File

@ -22,22 +22,22 @@ import android.support.annotation.AnyThread;
import android.view.View;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
/**
* Factory class to create and add an overlays on the TaskView
*/
public class TaskOverlayFactory {
public class TaskOverlayFactory implements ResourceBasedOverride {
private static TaskOverlayFactory sInstance;
public static TaskOverlayFactory get(Context context) {
Preconditions.assertUIThread();
if (sInstance == null) {
sInstance = Utilities.getOverrideObject(TaskOverlayFactory.class,
sInstance = Overrides.getObject(TaskOverlayFactory.class,
context.getApplicationContext(), R.string.task_overlay_factory_class);
}
return sInstance;

View File

@ -18,6 +18,9 @@
<!-- String representing the intent to delete a package.-->
<string name="delete_package_intent" translatable="false">#Intent;action=android.intent.action.DELETE;launchFlags=0x10800000;end</string>
<!-- String representing the fragment class for settings activity.-->
<string name="settings_fragment_name" translatable="false">com.android.launcher3.SettingsActivity$LauncherSettingsFragment</string>
<!-- Values for icon shape overrides. These should correspond to entries defined
in icon_shape_override_paths_names -->
<string-array translatable="false" name="icon_shape_override_paths_values">
@ -92,9 +95,6 @@
<!-- Name of an app transition manager class. -->
<string name="app_transition_manager_class" translatable="false"></string>
<!-- Name of a color extraction implementation class. -->
<string name="color_extraction_impl_class" translatable="false"></string>
<!-- Name of a subclass of com.android.launcher3.util.InstantAppResolver. Can be empty. -->
<string name="instant_app_resolver_class" translatable="false"></string>

View File

@ -3,10 +3,12 @@ package com.android.launcher3;
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) {

View File

@ -5,14 +5,16 @@ import android.content.pm.LauncherActivityInfo;
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;

View File

@ -23,13 +23,15 @@ import android.graphics.Rect;
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);
}

View File

@ -20,14 +20,15 @@ import android.content.Context;
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);
}

View File

@ -24,6 +24,7 @@ import android.app.Activity;
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 @@ import java.util.Objects;
/**
* 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 @@ public class SettingsActivity extends Activity {
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 @@ public class SettingsActivity extends Activity {
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.
*/

View File

@ -55,7 +55,6 @@ import com.android.launcher3.config.FeatureFlags;
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 @@ public final class Utilities {
|| 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.

View File

@ -36,11 +36,12 @@ import com.android.launcher3.ItemInfoWithIcon;
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 class DrawableFactory {
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;

View File

@ -51,6 +51,7 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
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 @@ import java.util.UUID;
*
* $ 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 @@ public class UserEventDispatcher {
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();

View File

@ -23,7 +23,6 @@ import android.util.Log;
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 @@ import java.util.List;
/**
* 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);
}

View File

@ -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);
}
}
}
}

View File

@ -56,7 +56,7 @@ public class WallpaperColorInfo implements WallpaperManagerCompat.OnColorsChange
private WallpaperColorInfo(Context context) {
mWallpaperManager = WallpaperManagerCompat.getInstance(context);
mWallpaperManager.addOnColorsChangedListener(this);
mExtractionType = ColorExtractionAlgorithm.newInstance(context);
mExtractionType = new ColorExtractionAlgorithm();
update(mWallpaperManager.getWallpaperColors(FLAG_SYSTEM));
}

View File

@ -16,7 +16,6 @@
package com.android.launcher3.uioverrides.dynamicui;
import android.content.Context;
import android.graphics.Color;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@ -25,7 +24,6 @@ import android.util.Log;
import android.util.Pair;
import android.util.Range;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import java.util.Arrays;
@ -37,11 +35,6 @@ import java.util.List;
**/
public class ColorExtractionAlgorithm {
public static ColorExtractionAlgorithm newInstance(Context context) {
return Utilities.getOverrideObject(ColorExtractionAlgorithm.class,
context.getApplicationContext(), R.string.color_extraction_impl_class);
}
private static final String TAG = "Tonal";
// Used for tonal palette fitting