1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.setupwizardlib.util; 18 19import android.content.Context; 20import android.content.Intent; 21import android.content.pm.ApplicationInfo; 22import android.content.pm.PackageManager; 23import android.content.pm.PackageManager.NameNotFoundException; 24import android.content.pm.ResolveInfo; 25import android.content.res.Resources; 26import android.graphics.drawable.Drawable; 27import android.util.Log; 28 29import com.android.setupwizardlib.annotations.VisibleForTesting; 30 31/** 32 * Utilities to discover and interact with partner customizations. There can only be one set of 33 * customizations on a device, and it must be bundled with the system. 34 * 35 * Derived from com.android.launcher3/Partner.java 36 */ 37public class Partner { 38 private static final String TAG = "(SUW) Partner"; 39 40 /** Marker action used to discover partner */ 41 private static final String 42 ACTION_PARTNER_CUSTOMIZATION = "com.android.setupwizard.action.PARTNER_CUSTOMIZATION"; 43 44 private static boolean sSearched = false; 45 private static Partner sPartner; 46 47 /** 48 * Convenience to get a drawable from partner overlay, or if not available, the drawable from 49 * the original context. 50 * 51 * @see #getResourceEntry(android.content.Context, int) 52 */ 53 public static Drawable getDrawable(Context context, int id) { 54 final ResourceEntry entry = getResourceEntry(context, id); 55 return entry.resources.getDrawable(entry.id); 56 } 57 58 /** 59 * Convenience to get a string from partner overlay, or if not available, the string from the 60 * original context. 61 * 62 * @see #getResourceEntry(android.content.Context, int) 63 */ 64 public static String getString(Context context, int id) { 65 final ResourceEntry entry = getResourceEntry(context, id); 66 return entry.resources.getString(entry.id); 67 } 68 69 /** 70 * Find an entry of resource in the overlay package provided by partners. It will first look for 71 * the resource in the overlay package, and if not available, will return the one in the 72 * original context. 73 * 74 * @return a ResourceEntry in the partner overlay's resources, if one is defined. Otherwise the 75 * resources from the original context is returned. Clients can then get the resource by 76 * {@code entry.resources.getString(entry.id)}, or other methods available in 77 * {@link android.content.res.Resources}. 78 */ 79 public static ResourceEntry getResourceEntry(Context context, int id) { 80 final Partner partner = Partner.get(context); 81 if (partner != null) { 82 final Resources ourResources = context.getResources(); 83 final String name = ourResources.getResourceEntryName(id); 84 final String type = ourResources.getResourceTypeName(id); 85 final int partnerId = partner.getIdentifier(name, type); 86 if (partnerId != 0) { 87 return new ResourceEntry(partner.mResources, partnerId, true); 88 } 89 } 90 return new ResourceEntry(context.getResources(), id, false); 91 } 92 93 public static class ResourceEntry { 94 public Resources resources; 95 public int id; 96 public boolean isOverlay; 97 98 ResourceEntry(Resources resources, int id, boolean isOverlay) { 99 this.resources = resources; 100 this.id = id; 101 this.isOverlay = isOverlay; 102 } 103 } 104 105 /** 106 * Find and return partner details, or {@code null} if none exists. A partner package is marked 107 * by a broadcast receiver declared in the manifest that handles the 108 * com.android.setupwizard.action.PARTNER_CUSTOMIZATION intent action. The overlay package must 109 * also be a system package. 110 */ 111 public static synchronized Partner get(Context context) { 112 if (!sSearched) { 113 PackageManager pm = context.getPackageManager(); 114 final Intent intent = new Intent(ACTION_PARTNER_CUSTOMIZATION); 115 for (ResolveInfo info : pm.queryBroadcastReceivers(intent, 0)) { 116 if (info.activityInfo == null) { 117 continue; 118 } 119 final ApplicationInfo appInfo = info.activityInfo.applicationInfo; 120 if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 121 try { 122 final Resources res = pm.getResourcesForApplication(appInfo); 123 sPartner = new Partner(appInfo.packageName, res); 124 break; 125 } catch (NameNotFoundException e) { 126 Log.w(TAG, "Failed to find resources for " + appInfo.packageName); 127 } 128 } 129 } 130 sSearched = true; 131 } 132 return sPartner; 133 } 134 135 @VisibleForTesting 136 public static synchronized void resetForTesting() { 137 sSearched = false; 138 sPartner = null; 139 } 140 141 private final String mPackageName; 142 private final Resources mResources; 143 144 private Partner(String packageName, Resources res) { 145 mPackageName = packageName; 146 mResources = res; 147 } 148 149 public String getPackageName() { 150 return mPackageName; 151 } 152 153 public Resources getResources() { 154 return mResources; 155 } 156 157 public int getIdentifier(String name, String defType) { 158 return mResources.getIdentifier(name, defType, mPackageName); 159 } 160} 161