1 /*
2  * Copyright (C) 2014 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.systemui.statusbar.policy;
18 
19 import android.content.ComponentName;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.pm.ActivityInfo;
23 import android.content.pm.PackageManager;
24 import android.content.pm.ResolveInfo;
25 import android.os.Bundle;
26 import android.util.Log;
27 import android.view.LayoutInflater;
28 import android.view.View;
29 
30 import com.android.internal.widget.LockPatternUtils;
31 import com.android.keyguard.KeyguardUpdateMonitor;
32 import com.android.systemui.statusbar.phone.KeyguardPreviewContainer;
33 
34 import java.util.List;
35 
36 /**
37  * Utility class to inflate previews for phone and camera affordance.
38  */
39 public class PreviewInflater {
40 
41     private static final String TAG = "PreviewInflater";
42 
43     private static final String META_DATA_KEYGUARD_LAYOUT = "com.android.keyguard.layout";
44 
45     private Context mContext;
46     private LockPatternUtils mLockPatternUtils;
47 
PreviewInflater(Context context, LockPatternUtils lockPatternUtils)48     public PreviewInflater(Context context, LockPatternUtils lockPatternUtils) {
49         mContext = context;
50         mLockPatternUtils = lockPatternUtils;
51     }
52 
inflatePreview(Intent intent)53     public View inflatePreview(Intent intent) {
54         WidgetInfo info = getWidgetInfo(intent);
55         return inflatePreview(info);
56     }
57 
inflatePreviewFromService(ComponentName componentName)58     public View inflatePreviewFromService(ComponentName componentName) {
59         WidgetInfo info = getWidgetInfoFromService(componentName);
60         return inflatePreview(info);
61     }
62 
inflatePreview(WidgetInfo info)63     private KeyguardPreviewContainer inflatePreview(WidgetInfo info) {
64         if (info == null) {
65             return null;
66         }
67         View v = inflateWidgetView(info);
68         if (v == null) {
69             return null;
70         }
71         KeyguardPreviewContainer container = new KeyguardPreviewContainer(mContext, null);
72         container.addView(v);
73         return container;
74     }
75 
inflateWidgetView(WidgetInfo widgetInfo)76     private View inflateWidgetView(WidgetInfo widgetInfo) {
77         View widgetView = null;
78         try {
79             Context appContext = mContext.createPackageContext(
80                     widgetInfo.contextPackage, Context.CONTEXT_RESTRICTED);
81             LayoutInflater appInflater = (LayoutInflater)
82                     appContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
83             appInflater = appInflater.cloneInContext(appContext);
84             widgetView = appInflater.inflate(widgetInfo.layoutId, null, false);
85         } catch (PackageManager.NameNotFoundException|RuntimeException e) {
86             Log.w(TAG, "Error creating widget view", e);
87         }
88         return widgetView;
89     }
90 
getWidgetInfoFromService(ComponentName componentName)91     private WidgetInfo getWidgetInfoFromService(ComponentName componentName) {
92         PackageManager packageManager = mContext.getPackageManager();
93         // Look for the preview specified in the service meta-data
94         try {
95             Bundle metaData = packageManager.getServiceInfo(
96                     componentName, PackageManager.GET_META_DATA).metaData;
97             return getWidgetInfoFromMetaData(componentName.getPackageName(), metaData);
98         } catch (PackageManager.NameNotFoundException e) {
99             Log.w(TAG, "Failed to load preview; " + componentName.flattenToShortString()
100                     + " not found", e);
101         }
102         return null;
103     }
104 
getWidgetInfoFromMetaData(String contextPackage, Bundle metaData)105     private WidgetInfo getWidgetInfoFromMetaData(String contextPackage,
106             Bundle metaData) {
107         if (metaData == null) {
108             return null;
109         }
110         int layoutId = metaData.getInt(META_DATA_KEYGUARD_LAYOUT);
111         if (layoutId == 0) {
112             return null;
113         }
114         WidgetInfo info = new WidgetInfo();
115         info.contextPackage = contextPackage;
116         info.layoutId = layoutId;
117         return info;
118     }
119 
getWidgetInfo(Intent intent)120     private WidgetInfo getWidgetInfo(Intent intent) {
121         PackageManager packageManager = mContext.getPackageManager();
122         final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
123                 intent, PackageManager.MATCH_DEFAULT_ONLY, KeyguardUpdateMonitor.getCurrentUser());
124         if (appList.size() == 0) {
125             return null;
126         }
127         ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
128                 PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA,
129                 KeyguardUpdateMonitor.getCurrentUser());
130         if (wouldLaunchResolverActivity(resolved, appList)) {
131             return null;
132         }
133         if (resolved == null || resolved.activityInfo == null) {
134             return null;
135         }
136         return getWidgetInfoFromMetaData(resolved.activityInfo.packageName,
137                 resolved.activityInfo.metaData);
138     }
139 
wouldLaunchResolverActivity(Context ctx, Intent intent, int currentUserId)140     public static boolean wouldLaunchResolverActivity(Context ctx, Intent intent,
141             int currentUserId) {
142         return getTargetActivityInfo(ctx, intent, currentUserId) == null;
143     }
144 
145     /**
146      * @return the target activity info of the intent it resolves to a specific package or
147      *         {@code null} if it resolved to the resolver activity
148      */
getTargetActivityInfo(Context ctx, Intent intent, int currentUserId)149     public static ActivityInfo getTargetActivityInfo(Context ctx, Intent intent,
150             int currentUserId) {
151         PackageManager packageManager = ctx.getPackageManager();
152         final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
153                 intent, PackageManager.MATCH_DEFAULT_ONLY, currentUserId);
154         if (appList.size() == 0) {
155             return null;
156         }
157         ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
158                 PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA, currentUserId);
159         if (resolved == null || wouldLaunchResolverActivity(resolved, appList)) {
160             return null;
161         } else {
162             return resolved.activityInfo;
163         }
164     }
165 
wouldLaunchResolverActivity( ResolveInfo resolved, List<ResolveInfo> appList)166     private static boolean wouldLaunchResolverActivity(
167             ResolveInfo resolved, List<ResolveInfo> appList) {
168         // If the list contains the above resolved activity, then it can't be
169         // ResolverActivity itself.
170         for (int i = 0; i < appList.size(); i++) {
171             ResolveInfo tmp = appList.get(i);
172             if (tmp.activityInfo.name.equals(resolved.activityInfo.name)
173                     && tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) {
174                 return false;
175             }
176         }
177         return true;
178     }
179 
180     private static class WidgetInfo {
181         String contextPackage;
182         int layoutId;
183     }
184 }
185