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
17package com.android.tv.settings.device.display.daydream;
18
19import com.android.tv.settings.R;
20import com.android.tv.settings.dialog.old.Action;
21
22import android.content.ComponentName;
23import android.content.Context;
24import android.content.Intent;
25import android.content.pm.PackageManager;
26import android.content.pm.ResolveInfo;
27import android.content.pm.PackageManager.NameNotFoundException;
28import android.content.res.Resources;
29import android.content.res.TypedArray;
30import android.content.res.XmlResourceParser;
31import android.graphics.drawable.Drawable;
32import android.os.Parcel;
33import android.os.Parcelable;
34import android.service.dreams.DreamService;
35import android.util.AttributeSet;
36import android.util.Log;
37import android.util.Xml;
38
39import org.xmlpull.v1.XmlPullParser;
40import org.xmlpull.v1.XmlPullParserException;
41
42import java.io.IOException;
43import java.util.Comparator;
44
45public class DreamInfoAction extends Action {
46
47    private static final int CHECK_SET_ID = 1;
48
49    static class DreamInfoActionComparator implements Comparator<DreamInfoAction> {
50        private final ComponentName mDefaultDream;
51
52        public DreamInfoActionComparator(ComponentName defaultDream) {
53            mDefaultDream = defaultDream;
54        }
55
56        @Override
57        public int compare(DreamInfoAction lhs, DreamInfoAction rhs) {
58            return sortKey(lhs).compareTo(sortKey(rhs));
59        }
60
61        private String sortKey(DreamInfoAction di) {
62            StringBuilder sb = new StringBuilder();
63            sb.append(di.mDreamComponentName.equals(mDefaultDream) ? '0' : '1');
64            sb.append(di.getTitle());
65            return sb.toString();
66        }
67    }
68
69    private static final String TAG = "DreamInfoAction";
70
71    private final ResolveInfo mResolveInfo;
72    private final ComponentName mDreamComponentName;
73    private final ComponentName mSettingsComponentName;
74
75    DreamInfoAction(ResolveInfo resolveInfo, ComponentName activeDream, PackageManager pm) {
76        this(resolveInfo, activeDream, (String) resolveInfo.loadLabel(pm), getDreamComponentName(
77                resolveInfo), getSettingsComponentName(resolveInfo, pm));
78    }
79
80    private DreamInfoAction(ResolveInfo resolveInfo, ComponentName activeDream, String title,
81            ComponentName dreamComponentName, ComponentName settingsComponentName) {
82        this(resolveInfo, dreamComponentName.equals(activeDream), title, dreamComponentName,
83                settingsComponentName);
84    }
85
86    protected DreamInfoAction(ResolveInfo resolveInfo, boolean checked, String title,
87            ComponentName dreamComponentName, ComponentName settingsComponentName) {
88        super(null, title, null, null, 0, checked, false, settingsComponentName != null, false,
89                null, CHECK_SET_ID);
90        mResolveInfo = resolveInfo;
91        mDreamComponentName = dreamComponentName;
92        mSettingsComponentName = settingsComponentName;
93    }
94
95    public Drawable getIndicator(Context context) {
96        return mResolveInfo.loadIcon(context.getPackageManager());
97    }
98
99    public static Parcelable.Creator<DreamInfoAction> CREATOR =
100            new Parcelable.Creator<DreamInfoAction>() {
101
102                @Override
103                public DreamInfoAction createFromParcel(Parcel source) {
104                    return new DreamInfoAction((ResolveInfo) source.readParcelable(null),
105                            source.readInt() == 1, source.readString(),
106                            (ComponentName) source.readParcelable(null),
107                            (ComponentName) source.readParcelable(null));
108                }
109
110                @Override
111                public DreamInfoAction[] newArray(int size) {
112                    return new DreamInfoAction[size];
113                }
114            };
115
116    @Override
117    public void writeToParcel(Parcel dest, int flags) {
118        dest.writeParcelable(mResolveInfo, flags);
119        dest.writeInt(isChecked() ? 1 : 0);
120        dest.writeString(getTitle());
121        dest.writeParcelable(mDreamComponentName, flags);
122        dest.writeParcelable(mSettingsComponentName, flags);
123    }
124
125    public Intent getSettingsIntent() {
126        if (mSettingsComponentName != null) {
127            return new Intent().setComponent(mSettingsComponentName);
128        } else {
129            return null;
130        }
131    }
132
133    public void setDream(DreamBackend dreamBackend) {
134        if (!dreamBackend.isEnabled()) {
135            dreamBackend.setEnabled(true);
136        }
137        dreamBackend.setActiveDream(mDreamComponentName);
138        dreamBackend.setActiveDreamInfoAction(this);
139    }
140
141    private static ComponentName getDreamComponentName(ResolveInfo resolveInfo) {
142        if (resolveInfo == null || resolveInfo.serviceInfo == null) {
143            return null;
144        }
145        return new ComponentName(resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name);
146    }
147
148    private static ComponentName getSettingsComponentName(ResolveInfo resolveInfo,
149            PackageManager pm) {
150        if (resolveInfo == null || resolveInfo.serviceInfo == null
151                || resolveInfo.serviceInfo.metaData == null) {
152            return null;
153        }
154        String cn = null;
155        XmlResourceParser parser = null;
156        Exception caughtException = null;
157        try {
158            parser = resolveInfo.serviceInfo.loadXmlMetaData(pm, DreamService.DREAM_META_DATA);
159            if (parser == null) {
160                Log.w(TAG, "No " + DreamService.DREAM_META_DATA + " meta-data");
161                return null;
162            }
163            Resources res = pm.getResourcesForApplication(resolveInfo.serviceInfo.applicationInfo);
164            AttributeSet attrs = Xml.asAttributeSet(parser);
165            int type;
166            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
167                    && type != XmlPullParser.START_TAG) {
168            }
169            String nodeName = parser.getName();
170            if (!"dream".equals(nodeName)) {
171                Log.w(TAG, "Meta-data does not start with dream tag");
172                return null;
173            }
174            TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.Dream);
175            cn = sa.getString(com.android.internal.R.styleable.Dream_settingsActivity);
176            sa.recycle();
177        } catch (NameNotFoundException e) {
178            caughtException = e;
179        } catch (IOException e) {
180            caughtException = e;
181        } catch (XmlPullParserException e) {
182            caughtException = e;
183        } finally {
184            if (parser != null) {
185                parser.close();
186            }
187        }
188        if (caughtException != null) {
189            Log.w(TAG, "Error parsing : " + resolveInfo.serviceInfo.packageName, caughtException);
190            return null;
191        }
192        return cn == null ? null : ComponentName.unflattenFromString(cn);
193    }
194}
195