Merge "[SetupWizardLib] Implement Partner" into ub-setupwizard-alatar

This commit is contained in:
Maurice Lam 2015-03-12 23:11:38 +00:00 committed by Android (Google) Code Review
commit 16a7a7a87c
4 changed files with 387 additions and 0 deletions

View 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;
}

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

View 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>

View 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.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);
}
}
}