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 17 package com.android.tv.customization; 18 19 import android.content.Context; 20 import android.content.Intent; 21 import android.content.pm.ApplicationInfo; 22 import android.content.pm.PackageManager; 23 import android.content.pm.PackageManager.NameNotFoundException; 24 import android.content.pm.ResolveInfo; 25 import android.content.res.Resources; 26 import android.graphics.drawable.Drawable; 27 import android.text.TextUtils; 28 import android.util.Log; 29 30 import java.util.ArrayList; 31 import java.util.Collections; 32 import java.util.HashMap; 33 import java.util.List; 34 import java.util.Map; 35 36 public class TvCustomizationManager { 37 private static final String TAG = "TvCustomizationManager"; 38 private static final boolean DEBUG = false; 39 40 private static final String CATEGORY_TV_CUSTOMIZATION = 41 "com.android.tv.category"; 42 43 /** 44 * Row IDs to share customized actions. 45 * Only rows listed below can have customized action. 46 */ 47 public static final String ID_OPTIONS_ROW = "options_row"; 48 public static final String ID_PARTNER_ROW = "partner_row"; 49 50 private static final HashMap<String, String> INTENT_CATEGORY_TO_ROW_ID; 51 static { 52 INTENT_CATEGORY_TO_ROW_ID = new HashMap<>(); 53 INTENT_CATEGORY_TO_ROW_ID.put(CATEGORY_TV_CUSTOMIZATION + ".OPTIONS_ROW", ID_OPTIONS_ROW); 54 INTENT_CATEGORY_TO_ROW_ID.put(CATEGORY_TV_CUSTOMIZATION + ".PARTNER_ROW", ID_PARTNER_ROW); 55 } 56 57 private static final String RES_ID_PARTNER_ROW_TITLE = "partner_row_title"; 58 59 private static final String RES_TYPE_STRING = "string"; 60 61 private final Context mContext; 62 private boolean mInitialized; 63 private String mCustomizationPackage; 64 65 private String mPartnerRowTitle; 66 private final Map<String, List<CustomAction>> mRowIdToCustomActionsMap = new HashMap<>(); 67 TvCustomizationManager(Context context)68 public TvCustomizationManager(Context context) { 69 mContext = context; 70 mInitialized = false; 71 } 72 73 /** 74 * Initialize TV customization options. 75 * Run this API only on the main thread. 76 */ initialize()77 public void initialize() { 78 if (mInitialized) { 79 return; 80 } 81 mInitialized = true; 82 buildCustomActions(); 83 if (!TextUtils.isEmpty(mCustomizationPackage)) { 84 buildPartnerRow(); 85 } 86 } 87 buildCustomActions()88 private void buildCustomActions() { 89 mCustomizationPackage = null; 90 mRowIdToCustomActionsMap.clear(); 91 PackageManager pm = mContext.getPackageManager(); 92 for (String intentCategory : INTENT_CATEGORY_TO_ROW_ID.keySet()) { 93 Intent customOptionIntent = new Intent(Intent.ACTION_MAIN); 94 customOptionIntent.addCategory(intentCategory); 95 96 List<ResolveInfo> activities = pm.queryIntentActivities(customOptionIntent, 97 PackageManager.GET_RECEIVERS | PackageManager.GET_RESOLVED_FILTER 98 | PackageManager.GET_META_DATA); 99 for (ResolveInfo info : activities) { 100 String packageName = info.activityInfo.packageName; 101 if (TextUtils.isEmpty(mCustomizationPackage)) { 102 if (DEBUG) Log.d(TAG, "Found TV customization package " + packageName); 103 if ((info.activityInfo.applicationInfo.flags 104 & ApplicationInfo.FLAG_SYSTEM) == 0) { 105 Log.w(TAG, "Only system app can customize TV. Ignoring " + packageName); 106 continue; 107 } 108 mCustomizationPackage = packageName; 109 } else if (!packageName.equals(mCustomizationPackage)) { 110 Log.w(TAG, "A customization package " + mCustomizationPackage 111 + " already exist. Ignoring " + packageName); 112 continue; 113 } 114 115 int position = info.filter.getPriority(); 116 String title = info.loadLabel(pm).toString(); 117 Drawable drawable = info.loadIcon(pm); 118 Intent intent = new Intent(Intent.ACTION_MAIN); 119 intent.addCategory(intentCategory); 120 intent.setClassName(mCustomizationPackage, info.activityInfo.name); 121 122 String rowId = INTENT_CATEGORY_TO_ROW_ID.get(intentCategory); 123 List<CustomAction> actions = mRowIdToCustomActionsMap.get(rowId); 124 if (actions == null) { 125 actions = new ArrayList<>(); 126 mRowIdToCustomActionsMap.put(rowId, actions); 127 } 128 actions.add(new CustomAction(position, title, drawable, intent)); 129 } 130 } 131 // Sort items by position 132 for (List<CustomAction> actions : mRowIdToCustomActionsMap.values()) { 133 Collections.sort(actions); 134 } 135 136 if (DEBUG) { 137 Log.d(TAG, "Dumping custom actions"); 138 for (String id : mRowIdToCustomActionsMap.keySet()) { 139 for (CustomAction action : mRowIdToCustomActionsMap.get(id)) { 140 Log.d(TAG, "Custom row rowId=" + id + " title=" + action.getTitle() 141 + " class=" + action.getIntent()); 142 } 143 } 144 Log.d(TAG, "Dumping custom actions - end of dump"); 145 } 146 } 147 148 /** 149 * Returns custom actions for given row id. 150 * 151 * Row ID is one of ID_OPTIONS_ROW or ID_PARTNER_ROW. 152 */ getCustomActions(String rowId)153 public List<CustomAction> getCustomActions(String rowId) { 154 return mRowIdToCustomActionsMap.get(rowId); 155 } 156 buildPartnerRow()157 private void buildPartnerRow() { 158 mPartnerRowTitle = null; 159 Resources res; 160 try { 161 res = mContext.getPackageManager() 162 .getResourcesForApplication(mCustomizationPackage); 163 } catch (NameNotFoundException e) { 164 Log.w(TAG, "Could not get resources for package " + mCustomizationPackage); 165 return; 166 } 167 int resId = res.getIdentifier( 168 RES_ID_PARTNER_ROW_TITLE, RES_TYPE_STRING, mCustomizationPackage); 169 if (resId != 0) { 170 mPartnerRowTitle = res.getString(resId); 171 } 172 if (DEBUG) Log.d(TAG, "Partner row title [" + mPartnerRowTitle + "]"); 173 } 174 175 /** 176 * Returns partner row title. 177 */ getPartnerRowTitle()178 public String getPartnerRowTitle() { 179 return mPartnerRowTitle; 180 } 181 } 182