Merge "[SetupWizardLib] Implement Partner" into ub-setupwizard-alatar
This commit is contained in:
commit
16a7a7a87c
50
library/src/com/android/annotations/VisibleForTesting.java
Normal file
50
library/src/com/android/annotations/VisibleForTesting.java
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (C) 2011 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.annotations;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Denotes that the class, method or field has its visibility relaxed so
|
||||
* that unit tests can access it.
|
||||
* <p/>
|
||||
* The <code>visibility</code> argument can be used to specific what the original
|
||||
* visibility should have been if it had not been made public or package-private for testing.
|
||||
* The default is to consider the element private.
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface VisibleForTesting {
|
||||
/**
|
||||
* Intended visibility if the element had not been made public or package-private for
|
||||
* testing.
|
||||
*/
|
||||
enum Visibility {
|
||||
/** The element should be considered protected. */
|
||||
PROTECTED,
|
||||
/** The element should be considered package-private. */
|
||||
PACKAGE,
|
||||
/** The element should be considered private. */
|
||||
PRIVATE
|
||||
}
|
||||
|
||||
/**
|
||||
* Intended visibility if the element had not been made public or package-private for testing.
|
||||
* If not specified, one should assume the element originally intended to be private.
|
||||
*/
|
||||
Visibility visibility() default Visibility.PRIVATE;
|
||||
}
|
157
library/src/com/android/setupwizardlib/util/Partner.java
Normal file
157
library/src/com/android/setupwizardlib/util/Partner.java
Normal file
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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.setupwizardlib.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.annotations.VisibleForTesting;
|
||||
|
||||
/**
|
||||
* Utilities to discover and interact with partner customizations. There can only be one set of
|
||||
* customizations on a device, and it must be bundled with the system.
|
||||
*
|
||||
* Derived from com.android.launcher3/Partner.java
|
||||
*/
|
||||
public class Partner {
|
||||
private static final String TAG = "(SUW) Partner";
|
||||
|
||||
/** Marker action used to discover partner */
|
||||
private static final String
|
||||
ACTION_PARTNER_CUSTOMIZATION = "com.android.setupwizard.action.PARTNER_CUSTOMIZATION";
|
||||
|
||||
private static boolean sSearched = false;
|
||||
private static Partner sPartner;
|
||||
|
||||
/**
|
||||
* Convenience to get a drawable from partner overlay, or if not available, the drawable from
|
||||
* the original context.
|
||||
*
|
||||
* @see #getResourceEntry(android.content.Context, int)
|
||||
*/
|
||||
public static Drawable getDrawable(Context context, int id) {
|
||||
final ResourceEntry entry = getResourceEntry(context, id);
|
||||
return entry.resources.getDrawable(entry.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience to get a string from partner overlay, or if not available, the string from the
|
||||
* original context.
|
||||
*
|
||||
* @see #getResourceEntry(android.content.Context, int)
|
||||
*/
|
||||
public static String getString(Context context, int id) {
|
||||
final ResourceEntry entry = getResourceEntry(context, id);
|
||||
return entry.resources.getString(entry.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an entry of resource in the overlay package provided by partners. It will first look for
|
||||
* the resource in the overlay package, and if not available, will return the one in the
|
||||
* original context.
|
||||
*
|
||||
* @return a ResourceEntry in the partner overlay's resources, if one is defined. Otherwise the
|
||||
* resources from the original context is returned. Clients can then get the resource by
|
||||
* {@code entry.resources.getString(entry.id)}, or other methods available in
|
||||
* {@link android.content.res.Resources}.
|
||||
*/
|
||||
public static ResourceEntry getResourceEntry(Context context, int id) {
|
||||
final Partner partner = Partner.get(context);
|
||||
if (partner == null) {
|
||||
return new ResourceEntry(context.getResources(), id);
|
||||
} else {
|
||||
final Resources ourResources = context.getResources();
|
||||
final String name = ourResources.getResourceEntryName(id);
|
||||
final String type = ourResources.getResourceTypeName(id);
|
||||
final int partnerId = partner.getIdentifier(name, type);
|
||||
return new ResourceEntry(partner.mResources, partnerId);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ResourceEntry {
|
||||
public Resources resources;
|
||||
public int id;
|
||||
|
||||
ResourceEntry(Resources resources, int id) {
|
||||
this.resources = resources;
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and return partner details, or {@code null} if none exists. A partner package is marked
|
||||
* by a broadcast receiver declared in the manifest that handles the
|
||||
* com.android.setupwizard.action.PARTNER_CUSTOMIZATION intent action. The overlay package must
|
||||
* also be a system package.
|
||||
*/
|
||||
public static synchronized Partner get(Context context) {
|
||||
if (!sSearched) {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
final Intent intent = new Intent(ACTION_PARTNER_CUSTOMIZATION);
|
||||
for (ResolveInfo info : pm.queryBroadcastReceivers(intent, 0)) {
|
||||
if (info.activityInfo == null) {
|
||||
continue;
|
||||
}
|
||||
final ApplicationInfo appInfo = info.activityInfo.applicationInfo;
|
||||
if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
|
||||
try {
|
||||
final Resources res = pm.getResourcesForApplication(appInfo);
|
||||
sPartner = new Partner(appInfo.packageName, res);
|
||||
break;
|
||||
} catch (NameNotFoundException e) {
|
||||
Log.w(TAG, "Failed to find resources for " + appInfo.packageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
sSearched = true;
|
||||
}
|
||||
return sPartner;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static synchronized void resetForTesting() {
|
||||
sSearched = false;
|
||||
sPartner = null;
|
||||
}
|
||||
|
||||
private final String mPackageName;
|
||||
private final Resources mResources;
|
||||
|
||||
private Partner(String packageName, Resources res) {
|
||||
mPackageName = packageName;
|
||||
mResources = res;
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
return mPackageName;
|
||||
}
|
||||
|
||||
public Resources getResources() {
|
||||
return mResources;
|
||||
}
|
||||
|
||||
public int getIdentifier(String name, String defType) {
|
||||
return mResources.getIdentifier(name, defType, mPackageName);
|
||||
}
|
||||
}
|
23
library/test/res/values/config.xml
Normal file
23
library/test/res/values/config.xml
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2015 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>
|
||||
|
||||
<!-- Overlay the transition duration for testing partner overlays -->
|
||||
<integer name="suwTransitionDuration">5000</integer>
|
||||
|
||||
</resources>
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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.setupwizardlib.test;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.test.InstrumentationTestCase;
|
||||
import android.test.mock.MockPackageManager;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
|
||||
import com.android.setupwizardlib.util.Partner;
|
||||
import com.android.setupwizardlib.util.Partner.ResourceEntry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class PartnerTest extends InstrumentationTestCase {
|
||||
|
||||
private TestContext mTestContext;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
mTestContext = new TestContext(getInstrumentation().getTargetContext());
|
||||
Partner.resetForTesting();
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testLoadPartner() {
|
||||
mTestContext.partnerList = Arrays.asList(
|
||||
createResolveInfo("hocus.pocus", false),
|
||||
createResolveInfo("com.android.setupwizardlib.test", true)
|
||||
);
|
||||
|
||||
Partner partner = Partner.get(mTestContext);
|
||||
assertNotNull("Partner should not be null", partner);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testLoadNoPartner() {
|
||||
mTestContext.partnerList = new ArrayList<>();
|
||||
|
||||
Partner partner = Partner.get(mTestContext);
|
||||
assertNull("Partner should be null", partner);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
public void testLoadNonSystemPartner() {
|
||||
mTestContext.partnerList = Arrays.asList(
|
||||
createResolveInfo("hocus.pocus", false),
|
||||
createResolveInfo("com.android.setupwizardlib.test", false)
|
||||
);
|
||||
|
||||
Partner partner = Partner.get(mTestContext);
|
||||
assertNull("Partner should be null", partner);
|
||||
}
|
||||
|
||||
public void testLoadPartnerValue() {
|
||||
mTestContext.partnerList = Arrays.asList(
|
||||
createResolveInfo("hocus.pocus", false),
|
||||
createResolveInfo("com.android.setupwizardlib.test", true)
|
||||
);
|
||||
|
||||
ResourceEntry entry =
|
||||
Partner.getResourceEntry(mTestContext, R.integer.suwTransitionDuration);
|
||||
int partnerValue = entry.resources.getInteger(entry.id);
|
||||
assertEquals("Partner value should be overlaid to 5000", 5000, partnerValue);
|
||||
}
|
||||
|
||||
public void testLoadDefaultValue() {
|
||||
mTestContext.partnerList = Arrays.asList(
|
||||
createResolveInfo("hocus.pocus", false),
|
||||
createResolveInfo("com.android.setupwizardlib.test", true)
|
||||
);
|
||||
|
||||
ResourceEntry entry =
|
||||
Partner.getResourceEntry(mTestContext, R.color.suw_navbar_text_dark);
|
||||
int partnerValue = entry.resources.getColor(entry.id);
|
||||
assertEquals("Partner value should default to 0xdeffffff", 0xdeffffff, partnerValue);
|
||||
}
|
||||
|
||||
private ResolveInfo createResolveInfo(String packageName, boolean isSystem) {
|
||||
ResolveInfo info = new ResolveInfo();
|
||||
info.resolvePackageName = packageName;
|
||||
ActivityInfo activityInfo = new ActivityInfo();
|
||||
ApplicationInfo appInfo = new ApplicationInfo();
|
||||
appInfo.flags = isSystem ? ApplicationInfo.FLAG_SYSTEM : 0;
|
||||
appInfo.packageName = packageName;
|
||||
activityInfo.applicationInfo = appInfo;
|
||||
activityInfo.packageName = packageName;
|
||||
activityInfo.name = packageName;
|
||||
info.activityInfo = activityInfo;
|
||||
return info;
|
||||
}
|
||||
|
||||
private static class TestPackageManager extends MockPackageManager {
|
||||
|
||||
private Context mTestContext;
|
||||
|
||||
public TestPackageManager(Context testContext) {
|
||||
mTestContext = testContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resources getResourcesForApplication(ApplicationInfo app) {
|
||||
if (app != null && "com.android.setupwizardlib.test".equals(app.packageName)) {
|
||||
return mTestContext.getResources();
|
||||
} else {
|
||||
return super.getResourcesForApplication(app);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags) {
|
||||
if ("com.android.setupwizard.action.PARTNER_CUSTOMIZATION".equals(intent.getAction())) {
|
||||
return ((TestContext) mTestContext).partnerList;
|
||||
} else {
|
||||
return super.queryBroadcastReceivers(intent, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestContext extends ContextWrapper {
|
||||
|
||||
public List<ResolveInfo> partnerList;
|
||||
|
||||
public TestContext(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageManager getPackageManager() {
|
||||
return new TestPackageManager(this);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue