1b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi/*
2b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi * Copyright (C) 2014 The Android Open Source Project
3b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi *
4b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi * Licensed under the Apache License, Version 2.0 (the "License");
5b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi * you may not use this file except in compliance with the License.
6b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi * You may obtain a copy of the License at
7b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi *
8b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi *      http://www.apache.org/licenses/LICENSE-2.0
9b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi *
10b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi * Unless required by applicable law or agreed to in writing, software
11b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi * distributed under the License is distributed on an "AS IS" BASIS,
12b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi * See the License for the specific language governing permissions and
14b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi * limitations under the License
15b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi */
16b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi
17b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggipackage com.android.systemui.statusbar.policy;
18b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi
19a86790bf23a98ede5fc0c29b996a5229e08181cdJorim Jaggiimport android.content.ComponentName;
20b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggiimport android.content.Context;
21b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggiimport android.content.Intent;
22d944986fbdb3d45fab9ae4120af76ca4f6b0909cJorim Jaggiimport android.content.pm.ActivityInfo;
23b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggiimport android.content.pm.PackageManager;
24b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggiimport android.content.pm.ResolveInfo;
25e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinekimport android.os.Bundle;
26b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggiimport android.util.Log;
27b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggiimport android.view.LayoutInflater;
28b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggiimport android.view.View;
29b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi
30b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggiimport com.android.internal.widget.LockPatternUtils;
31d6aa6cb0cc8c70a30434048e25e14643b1645755Adrian Roosimport com.android.keyguard.KeyguardUpdateMonitor;
32b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggiimport com.android.systemui.statusbar.phone.KeyguardPreviewContainer;
33b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi
34b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggiimport java.util.List;
35b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi
36b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi/**
37b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi * Utility class to inflate previews for phone and camera affordance.
38b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi */
39b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggipublic class PreviewInflater {
40b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi
41b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    private static final String TAG = "PreviewInflater";
42b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi
43b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    private static final String META_DATA_KEYGUARD_LAYOUT = "com.android.keyguard.layout";
44b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi
45b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    private Context mContext;
46b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    private LockPatternUtils mLockPatternUtils;
47b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi
48b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    public PreviewInflater(Context context, LockPatternUtils lockPatternUtils) {
49b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        mContext = context;
50b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        mLockPatternUtils = lockPatternUtils;
51b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    }
52b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi
53b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    public View inflatePreview(Intent intent) {
54b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        WidgetInfo info = getWidgetInfo(intent);
55e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek        return inflatePreview(info);
56e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek    }
57e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek
58e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek    public View inflatePreviewFromService(ComponentName componentName) {
59e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek        WidgetInfo info = getWidgetInfoFromService(componentName);
60e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek        return inflatePreview(info);
61e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek    }
62e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek
63e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek    private KeyguardPreviewContainer inflatePreview(WidgetInfo info) {
64b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        if (info == null) {
65b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            return null;
66b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        }
67b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        View v = inflateWidgetView(info);
68b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        if (v == null) {
69b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            return null;
70b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        }
71b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        KeyguardPreviewContainer container = new KeyguardPreviewContainer(mContext, null);
72b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        container.addView(v);
73b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        return container;
74b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    }
75b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi
76b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    private View inflateWidgetView(WidgetInfo widgetInfo) {
77b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        View widgetView = null;
78b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        try {
79b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            Context appContext = mContext.createPackageContext(
80b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                    widgetInfo.contextPackage, Context.CONTEXT_RESTRICTED);
81b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            LayoutInflater appInflater = (LayoutInflater)
82b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                    appContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
83b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            appInflater = appInflater.cloneInContext(appContext);
84b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            widgetView = appInflater.inflate(widgetInfo.layoutId, null, false);
85b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        } catch (PackageManager.NameNotFoundException|RuntimeException e) {
86b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            Log.w(TAG, "Error creating widget view", e);
87b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        }
88b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        return widgetView;
89b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    }
90b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi
91e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek    private WidgetInfo getWidgetInfoFromService(ComponentName componentName) {
92e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek        PackageManager packageManager = mContext.getPackageManager();
93e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek        // Look for the preview specified in the service meta-data
94e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek        try {
95e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek            Bundle metaData = packageManager.getServiceInfo(
96e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek                    componentName, PackageManager.GET_META_DATA).metaData;
97e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek            return getWidgetInfoFromMetaData(componentName.getPackageName(), metaData);
98e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek        } catch (PackageManager.NameNotFoundException e) {
99e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek            Log.w(TAG, "Failed to load preview; " + componentName.flattenToShortString()
100e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek                    + " not found", e);
101e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek        }
102e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek        return null;
103e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek    }
104e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek
105e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek    private WidgetInfo getWidgetInfoFromMetaData(String contextPackage,
106e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek            Bundle metaData) {
107e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek        if (metaData == null) {
108e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek            return null;
109e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek        }
110e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek        int layoutId = metaData.getInt(META_DATA_KEYGUARD_LAYOUT);
111e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek        if (layoutId == 0) {
112e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek            return null;
113e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek        }
114b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        WidgetInfo info = new WidgetInfo();
115e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek        info.contextPackage = contextPackage;
116e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek        info.layoutId = layoutId;
117e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek        return info;
118e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek    }
119e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek
120e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek    private WidgetInfo getWidgetInfo(Intent intent) {
121b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        PackageManager packageManager = mContext.getPackageManager();
1225f1450229bb8a827c4e6d4ed6dbc2de6e31aabffSelim Cinek        int flags = PackageManager.MATCH_DEFAULT_ONLY
1235f1450229bb8a827c4e6d4ed6dbc2de6e31aabffSelim Cinek                | PackageManager.MATCH_DIRECT_BOOT_AWARE
1245f1450229bb8a827c4e6d4ed6dbc2de6e31aabffSelim Cinek                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
125b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
1265f1450229bb8a827c4e6d4ed6dbc2de6e31aabffSelim Cinek                intent, flags, KeyguardUpdateMonitor.getCurrentUser());
127b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        if (appList.size() == 0) {
128b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            return null;
129b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        }
130b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
1315f1450229bb8a827c4e6d4ed6dbc2de6e31aabffSelim Cinek                flags | PackageManager.GET_META_DATA,
132d6aa6cb0cc8c70a30434048e25e14643b1645755Adrian Roos                KeyguardUpdateMonitor.getCurrentUser());
133b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        if (wouldLaunchResolverActivity(resolved, appList)) {
134b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            return null;
135b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        }
136b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        if (resolved == null || resolved.activityInfo == null) {
137b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            return null;
138b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        }
139e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek        return getWidgetInfoFromMetaData(resolved.activityInfo.packageName,
140e70d6535237d2e6f03adcd0bdc11e45ea714dc97Selim Cinek                resolved.activityInfo.metaData);
141b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    }
142b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi
14385dc23cc696d4c5de4619da317c16c5a2b49a62aJorim Jaggi    public static boolean wouldLaunchResolverActivity(Context ctx, Intent intent,
14485dc23cc696d4c5de4619da317c16c5a2b49a62aJorim Jaggi            int currentUserId) {
1455f1450229bb8a827c4e6d4ed6dbc2de6e31aabffSelim Cinek        return getTargetActivityInfo(ctx, intent, currentUserId, false /* onlyDirectBootAware */)
1465f1450229bb8a827c4e6d4ed6dbc2de6e31aabffSelim Cinek                == null;
147a86790bf23a98ede5fc0c29b996a5229e08181cdJorim Jaggi    }
148a86790bf23a98ede5fc0c29b996a5229e08181cdJorim Jaggi
149a86790bf23a98ede5fc0c29b996a5229e08181cdJorim Jaggi    /**
1505f1450229bb8a827c4e6d4ed6dbc2de6e31aabffSelim Cinek     * @param onlyDirectBootAware a boolean indicating whether the matched activity packages must
1515f1450229bb8a827c4e6d4ed6dbc2de6e31aabffSelim Cinek     *                            be direct boot aware when in direct boot mode if false, all
1525f1450229bb8a827c4e6d4ed6dbc2de6e31aabffSelim Cinek     *                            packages are considered a match even if they are not aware.
153d944986fbdb3d45fab9ae4120af76ca4f6b0909cJorim Jaggi     * @return the target activity info of the intent it resolves to a specific package or
154d944986fbdb3d45fab9ae4120af76ca4f6b0909cJorim Jaggi     *         {@code null} if it resolved to the resolver activity
155a86790bf23a98ede5fc0c29b996a5229e08181cdJorim Jaggi     */
156d944986fbdb3d45fab9ae4120af76ca4f6b0909cJorim Jaggi    public static ActivityInfo getTargetActivityInfo(Context ctx, Intent intent,
1575f1450229bb8a827c4e6d4ed6dbc2de6e31aabffSelim Cinek            int currentUserId, boolean onlyDirectBootAware) {
15885dc23cc696d4c5de4619da317c16c5a2b49a62aJorim Jaggi        PackageManager packageManager = ctx.getPackageManager();
1595f1450229bb8a827c4e6d4ed6dbc2de6e31aabffSelim Cinek        int flags = PackageManager.MATCH_DEFAULT_ONLY;
1605f1450229bb8a827c4e6d4ed6dbc2de6e31aabffSelim Cinek        if (!onlyDirectBootAware) {
1615f1450229bb8a827c4e6d4ed6dbc2de6e31aabffSelim Cinek            flags |=  PackageManager.MATCH_DIRECT_BOOT_AWARE
1625f1450229bb8a827c4e6d4ed6dbc2de6e31aabffSelim Cinek                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
1635f1450229bb8a827c4e6d4ed6dbc2de6e31aabffSelim Cinek        }
164781c29c9a866dbf0a01d76838e912a5fec9247feJorim Jaggi        final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
1655f1450229bb8a827c4e6d4ed6dbc2de6e31aabffSelim Cinek                intent, flags, currentUserId);
166781c29c9a866dbf0a01d76838e912a5fec9247feJorim Jaggi        if (appList.size() == 0) {
167a86790bf23a98ede5fc0c29b996a5229e08181cdJorim Jaggi            return null;
168781c29c9a866dbf0a01d76838e912a5fec9247feJorim Jaggi        }
169781c29c9a866dbf0a01d76838e912a5fec9247feJorim Jaggi        ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
1705f1450229bb8a827c4e6d4ed6dbc2de6e31aabffSelim Cinek                flags | PackageManager.GET_META_DATA, currentUserId);
171a86790bf23a98ede5fc0c29b996a5229e08181cdJorim Jaggi        if (resolved == null || wouldLaunchResolverActivity(resolved, appList)) {
172a86790bf23a98ede5fc0c29b996a5229e08181cdJorim Jaggi            return null;
173a86790bf23a98ede5fc0c29b996a5229e08181cdJorim Jaggi        } else {
174d944986fbdb3d45fab9ae4120af76ca4f6b0909cJorim Jaggi            return resolved.activityInfo;
175a86790bf23a98ede5fc0c29b996a5229e08181cdJorim Jaggi        }
176781c29c9a866dbf0a01d76838e912a5fec9247feJorim Jaggi    }
177781c29c9a866dbf0a01d76838e912a5fec9247feJorim Jaggi
17885dc23cc696d4c5de4619da317c16c5a2b49a62aJorim Jaggi    private static boolean wouldLaunchResolverActivity(
17985dc23cc696d4c5de4619da317c16c5a2b49a62aJorim Jaggi            ResolveInfo resolved, List<ResolveInfo> appList) {
180b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        // If the list contains the above resolved activity, then it can't be
181b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        // ResolverActivity itself.
182b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        for (int i = 0; i < appList.size(); i++) {
183b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            ResolveInfo tmp = appList.get(i);
184b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            if (tmp.activityInfo.name.equals(resolved.activityInfo.name)
185b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                    && tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) {
186b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi                return false;
187b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi            }
188b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        }
189b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        return true;
190b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    }
191b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi
192b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    private static class WidgetInfo {
193b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        String contextPackage;
194b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi        int layoutId;
195b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi    }
196b6cdcbc66b4b862f83afde85b8e7109b6450b15eJorim Jaggi}
197