165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane/*
265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Copyright (C) 2014 The Android Open Source Project
365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *
465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Licensed under the Apache License, Version 2.0 (the "License");
565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * you may not use this file except in compliance with the License.
665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * You may obtain a copy of the License at
765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *
865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *      http://www.apache.org/licenses/LICENSE-2.0
965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *
1065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Unless required by applicable law or agreed to in writing, software
1165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * distributed under the License is distributed on an "AS IS" BASIS,
1265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * See the License for the specific language governing permissions and
1465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * limitations under the License.
1565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */
1665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
1765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lanepackage com.android.tv.settings.device.display.daydream;
1865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
1965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport com.android.tv.settings.R;
2065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport com.android.tv.settings.dialog.old.Action;
2165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
2265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.ComponentName;
2365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.Context;
2465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.Intent;
2565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.pm.PackageManager;
2665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.pm.ResolveInfo;
2765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.pm.PackageManager.NameNotFoundException;
2865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.res.Resources;
2965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.res.TypedArray;
3065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.res.XmlResourceParser;
3165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.graphics.drawable.Drawable;
3265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.os.Parcel;
3365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.os.Parcelable;
3465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.service.dreams.DreamService;
3565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.util.AttributeSet;
3665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.util.Log;
3765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.util.Xml;
3865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
3965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport org.xmlpull.v1.XmlPullParser;
4065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport org.xmlpull.v1.XmlPullParserException;
4165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
4265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.io.IOException;
4365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.util.Comparator;
4465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
4565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lanepublic class DreamInfoAction extends Action {
4665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
4765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final int CHECK_SET_ID = 1;
4865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
4965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    static class DreamInfoActionComparator implements Comparator<DreamInfoAction> {
5065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private final ComponentName mDefaultDream;
5165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
5265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public DreamInfoActionComparator(ComponentName defaultDream) {
5365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mDefaultDream = defaultDream;
5465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
5565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
5665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        @Override
5765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public int compare(DreamInfoAction lhs, DreamInfoAction rhs) {
5865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return sortKey(lhs).compareTo(sortKey(rhs));
5965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
6065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
6165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private String sortKey(DreamInfoAction di) {
6265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            StringBuilder sb = new StringBuilder();
6365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            sb.append(di.mDreamComponentName.equals(mDefaultDream) ? '0' : '1');
6465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            sb.append(di.getTitle());
6565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return sb.toString();
6665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
6765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
6865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
6965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final String TAG = "DreamInfoAction";
7065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
7165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private final ResolveInfo mResolveInfo;
7265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private final ComponentName mDreamComponentName;
7365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private final ComponentName mSettingsComponentName;
7465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
7565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    DreamInfoAction(ResolveInfo resolveInfo, ComponentName activeDream, PackageManager pm) {
7665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        this(resolveInfo, activeDream, (String) resolveInfo.loadLabel(pm), getDreamComponentName(
7765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                resolveInfo), getSettingsComponentName(resolveInfo, pm));
7865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
7965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
8065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private DreamInfoAction(ResolveInfo resolveInfo, ComponentName activeDream, String title,
8165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            ComponentName dreamComponentName, ComponentName settingsComponentName) {
8265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        this(resolveInfo, dreamComponentName.equals(activeDream), title, dreamComponentName,
8365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                settingsComponentName);
8465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
8565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
8665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    protected DreamInfoAction(ResolveInfo resolveInfo, boolean checked, String title,
8765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            ComponentName dreamComponentName, ComponentName settingsComponentName) {
8865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        super(null, title, null, null, 0, checked, false, settingsComponentName != null, false,
8965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                null, CHECK_SET_ID);
9065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mResolveInfo = resolveInfo;
9165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mDreamComponentName = dreamComponentName;
9265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mSettingsComponentName = settingsComponentName;
9365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
9465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
9565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public Drawable getIndicator(Context context) {
9665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return mResolveInfo.loadIcon(context.getPackageManager());
9765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
9865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
9965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static Parcelable.Creator<DreamInfoAction> CREATOR =
10065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            new Parcelable.Creator<DreamInfoAction>() {
10165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
10265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                @Override
10365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                public DreamInfoAction createFromParcel(Parcel source) {
10465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    return new DreamInfoAction((ResolveInfo) source.readParcelable(null),
10565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                            source.readInt() == 1, source.readString(),
10665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                            (ComponentName) source.readParcelable(null),
10765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                            (ComponentName) source.readParcelable(null));
10865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
10965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
11065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                @Override
11165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                public DreamInfoAction[] newArray(int size) {
11265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    return new DreamInfoAction[size];
11365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
11465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            };
11565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
11665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    @Override
11765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public void writeToParcel(Parcel dest, int flags) {
11865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        dest.writeParcelable(mResolveInfo, flags);
11965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        dest.writeInt(isChecked() ? 1 : 0);
12065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        dest.writeString(getTitle());
12165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        dest.writeParcelable(mDreamComponentName, flags);
12265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        dest.writeParcelable(mSettingsComponentName, flags);
12365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
12465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
12565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public Intent getSettingsIntent() {
12665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (mSettingsComponentName != null) {
12765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return new Intent().setComponent(mSettingsComponentName);
12865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else {
12965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return null;
13065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
13165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
13265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
13365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public void setDream(DreamBackend dreamBackend) {
13465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (!dreamBackend.isEnabled()) {
13565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            dreamBackend.setEnabled(true);
13665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
13765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        dreamBackend.setActiveDream(mDreamComponentName);
13865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        dreamBackend.setActiveDreamInfoAction(this);
13965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
14065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
14165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static ComponentName getDreamComponentName(ResolveInfo resolveInfo) {
14265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (resolveInfo == null || resolveInfo.serviceInfo == null) {
14365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return null;
14465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
14565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return new ComponentName(resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name);
14665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
14765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
14865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static ComponentName getSettingsComponentName(ResolveInfo resolveInfo,
14965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            PackageManager pm) {
15065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (resolveInfo == null || resolveInfo.serviceInfo == null
15165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                || resolveInfo.serviceInfo.metaData == null) {
15265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return null;
15365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
15465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        String cn = null;
15565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        XmlResourceParser parser = null;
15665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        Exception caughtException = null;
15765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        try {
15865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            parser = resolveInfo.serviceInfo.loadXmlMetaData(pm, DreamService.DREAM_META_DATA);
15965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (parser == null) {
16065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                Log.w(TAG, "No " + DreamService.DREAM_META_DATA + " meta-data");
16165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return null;
16265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
16365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Resources res = pm.getResourcesForApplication(resolveInfo.serviceInfo.applicationInfo);
16465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            AttributeSet attrs = Xml.asAttributeSet(parser);
16565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            int type;
16665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
16765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    && type != XmlPullParser.START_TAG) {
16865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
16965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            String nodeName = parser.getName();
17065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (!"dream".equals(nodeName)) {
17165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                Log.w(TAG, "Meta-data does not start with dream tag");
17265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return null;
17365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
17465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.Dream);
17565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            cn = sa.getString(com.android.internal.R.styleable.Dream_settingsActivity);
17665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            sa.recycle();
17765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } catch (NameNotFoundException e) {
17865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            caughtException = e;
17965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } catch (IOException e) {
18065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            caughtException = e;
18165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } catch (XmlPullParserException e) {
18265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            caughtException = e;
18365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } finally {
18465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (parser != null) {
18565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                parser.close();
18665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
18765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
18865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (caughtException != null) {
18965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Log.w(TAG, "Error parsing : " + resolveInfo.serviceInfo.packageName, caughtException);
19065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return null;
19165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
19265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return cn == null ? null : ComponentName.unflattenFromString(cn);
19365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
19465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane}
195