Home | History | Annotate | Download | only in customization
      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 
     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      */
     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 
     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      */
    153     public List<CustomAction> getCustomActions(String rowId) {
    154         return mRowIdToCustomActionsMap.get(rowId);
    155     }
    156 
    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      */
    178     public String getPartnerRowTitle() {
    179         return mPartnerRowTitle;
    180     }
    181 }
    182