Home | History | Annotate | Download | only in dream
      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.settingslib.dream;
     18 
     19 import android.annotation.IntDef;
     20 import android.content.ComponentName;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.pm.PackageManager;
     24 import android.content.pm.ResolveInfo;
     25 import android.content.pm.ServiceInfo;
     26 import android.content.res.Resources;
     27 import android.content.res.TypedArray;
     28 import android.content.res.XmlResourceParser;
     29 import android.graphics.drawable.Drawable;
     30 import android.os.RemoteException;
     31 import android.os.ServiceManager;
     32 import android.provider.Settings;
     33 import android.service.dreams.DreamService;
     34 import android.service.dreams.IDreamManager;
     35 import android.util.AttributeSet;
     36 import android.util.Log;
     37 import android.util.Xml;
     38 
     39 import java.lang.annotation.Retention;
     40 import java.lang.annotation.RetentionPolicy;
     41 import org.xmlpull.v1.XmlPullParser;
     42 import org.xmlpull.v1.XmlPullParserException;
     43 
     44 import java.io.IOException;
     45 import java.util.ArrayList;
     46 import java.util.Collections;
     47 import java.util.Comparator;
     48 import java.util.List;
     49 
     50 public class DreamBackend {
     51     private static final String TAG = "DreamBackend";
     52     private static final boolean DEBUG = false;
     53 
     54     public static class DreamInfo {
     55         public CharSequence caption;
     56         public Drawable icon;
     57         public boolean isActive;
     58         public ComponentName componentName;
     59         public ComponentName settingsComponentName;
     60 
     61         @Override
     62         public String toString() {
     63             StringBuilder sb = new StringBuilder(DreamInfo.class.getSimpleName());
     64             sb.append('[').append(caption);
     65             if (isActive)
     66                 sb.append(",active");
     67             sb.append(',').append(componentName);
     68             if (settingsComponentName != null)
     69                 sb.append("settings=").append(settingsComponentName);
     70             return sb.append(']').toString();
     71         }
     72     }
     73 
     74     @Retention(RetentionPolicy.SOURCE)
     75     @IntDef({WHILE_CHARGING, WHILE_DOCKED, EITHER, NEVER})
     76     public @interface WhenToDream{}
     77 
     78     public static final int WHILE_CHARGING = 0;
     79     public static final int WHILE_DOCKED = 1;
     80     public static final int EITHER = 2;
     81     public static final int NEVER = 3;
     82 
     83     private final Context mContext;
     84     private final IDreamManager mDreamManager;
     85     private final DreamInfoComparator mComparator;
     86     private final boolean mDreamsEnabledByDefault;
     87     private final boolean mDreamsActivatedOnSleepByDefault;
     88     private final boolean mDreamsActivatedOnDockByDefault;
     89 
     90     private static DreamBackend sInstance;
     91 
     92     public static DreamBackend getInstance(Context context) {
     93         if (sInstance == null) {
     94             sInstance = new DreamBackend(context);
     95         }
     96         return sInstance;
     97     }
     98 
     99     public DreamBackend(Context context) {
    100         mContext = context;
    101         mDreamManager = IDreamManager.Stub.asInterface(
    102                 ServiceManager.getService(DreamService.DREAM_SERVICE));
    103         mComparator = new DreamInfoComparator(getDefaultDream());
    104         mDreamsEnabledByDefault = context.getResources()
    105                 .getBoolean(com.android.internal.R.bool.config_dreamsEnabledByDefault);
    106         mDreamsActivatedOnSleepByDefault = context.getResources()
    107                 .getBoolean(com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault);
    108         mDreamsActivatedOnDockByDefault = context.getResources()
    109                 .getBoolean(com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault);
    110     }
    111 
    112     public List<DreamInfo> getDreamInfos() {
    113         logd("getDreamInfos()");
    114         ComponentName activeDream = getActiveDream();
    115         PackageManager pm = mContext.getPackageManager();
    116         Intent dreamIntent = new Intent(DreamService.SERVICE_INTERFACE);
    117         List<ResolveInfo> resolveInfos = pm.queryIntentServices(dreamIntent,
    118                 PackageManager.GET_META_DATA);
    119         List<DreamInfo> dreamInfos = new ArrayList<>(resolveInfos.size());
    120         for (ResolveInfo resolveInfo : resolveInfos) {
    121             if (resolveInfo.serviceInfo == null)
    122                 continue;
    123             DreamInfo dreamInfo = new DreamInfo();
    124             dreamInfo.caption = resolveInfo.loadLabel(pm);
    125             dreamInfo.icon = resolveInfo.loadIcon(pm);
    126             dreamInfo.componentName = getDreamComponentName(resolveInfo);
    127             dreamInfo.isActive = dreamInfo.componentName.equals(activeDream);
    128             dreamInfo.settingsComponentName = getSettingsComponentName(pm, resolveInfo);
    129             dreamInfos.add(dreamInfo);
    130         }
    131         Collections.sort(dreamInfos, mComparator);
    132         return dreamInfos;
    133     }
    134 
    135     public ComponentName getDefaultDream() {
    136         if (mDreamManager == null)
    137             return null;
    138         try {
    139             return mDreamManager.getDefaultDreamComponent();
    140         } catch (RemoteException e) {
    141             Log.w(TAG, "Failed to get default dream", e);
    142             return null;
    143         }
    144     }
    145 
    146     public CharSequence getActiveDreamName() {
    147         ComponentName cn = getActiveDream();
    148         if (cn != null) {
    149             PackageManager pm = mContext.getPackageManager();
    150             try {
    151                 ServiceInfo ri = pm.getServiceInfo(cn, 0);
    152                 if (ri != null) {
    153                     return ri.loadLabel(pm);
    154                 }
    155             } catch (PackageManager.NameNotFoundException exc) {
    156                 return null; // uninstalled?
    157             }
    158         }
    159         return null;
    160     }
    161 
    162     public @WhenToDream int getWhenToDreamSetting() {
    163         if (!isEnabled()) {
    164             return NEVER;
    165         }
    166         return isActivatedOnDock() && isActivatedOnSleep() ? EITHER
    167                 : isActivatedOnDock() ? WHILE_DOCKED
    168                 : isActivatedOnSleep() ? WHILE_CHARGING
    169                 : NEVER;
    170     }
    171 
    172     public void setWhenToDream(@WhenToDream int whenToDream) {
    173         setEnabled(whenToDream != NEVER);
    174 
    175         switch (whenToDream) {
    176             case WHILE_CHARGING:
    177                 setActivatedOnDock(false);
    178                 setActivatedOnSleep(true);
    179                 break;
    180 
    181             case WHILE_DOCKED:
    182                 setActivatedOnDock(true);
    183                 setActivatedOnSleep(false);
    184                 break;
    185 
    186             case EITHER:
    187                 setActivatedOnDock(true);
    188                 setActivatedOnSleep(true);
    189                 break;
    190 
    191             case NEVER:
    192             default:
    193                 break;
    194         }
    195 
    196     }
    197 
    198     public boolean isEnabled() {
    199         return getBoolean(Settings.Secure.SCREENSAVER_ENABLED, mDreamsEnabledByDefault);
    200     }
    201 
    202     public void setEnabled(boolean value) {
    203         logd("setEnabled(%s)", value);
    204         setBoolean(Settings.Secure.SCREENSAVER_ENABLED, value);
    205     }
    206 
    207     public boolean isActivatedOnDock() {
    208         return getBoolean(Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
    209                 mDreamsActivatedOnDockByDefault);
    210     }
    211 
    212     public void setActivatedOnDock(boolean value) {
    213         logd("setActivatedOnDock(%s)", value);
    214         setBoolean(Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK, value);
    215     }
    216 
    217     public boolean isActivatedOnSleep() {
    218         return getBoolean(Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
    219                 mDreamsActivatedOnSleepByDefault);
    220     }
    221 
    222     public void setActivatedOnSleep(boolean value) {
    223         logd("setActivatedOnSleep(%s)", value);
    224         setBoolean(Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, value);
    225     }
    226 
    227     private boolean getBoolean(String key, boolean def) {
    228         return Settings.Secure.getInt(mContext.getContentResolver(), key, def ? 1 : 0) == 1;
    229     }
    230 
    231     private void setBoolean(String key, boolean value) {
    232         Settings.Secure.putInt(mContext.getContentResolver(), key, value ? 1 : 0);
    233     }
    234 
    235     public void setActiveDream(ComponentName dream) {
    236         logd("setActiveDream(%s)", dream);
    237         if (mDreamManager == null)
    238             return;
    239         try {
    240             ComponentName[] dreams = { dream };
    241             mDreamManager.setDreamComponents(dream == null ? null : dreams);
    242         } catch (RemoteException e) {
    243             Log.w(TAG, "Failed to set active dream to " + dream, e);
    244         }
    245     }
    246 
    247     public ComponentName getActiveDream() {
    248         if (mDreamManager == null)
    249             return null;
    250         try {
    251             ComponentName[] dreams = mDreamManager.getDreamComponents();
    252             return dreams != null && dreams.length > 0 ? dreams[0] : null;
    253         } catch (RemoteException e) {
    254             Log.w(TAG, "Failed to get active dream", e);
    255             return null;
    256         }
    257     }
    258 
    259     public void launchSettings(DreamInfo dreamInfo) {
    260         logd("launchSettings(%s)", dreamInfo);
    261         if (dreamInfo == null || dreamInfo.settingsComponentName == null)
    262             return;
    263         mContext.startActivity(new Intent().setComponent(dreamInfo.settingsComponentName));
    264     }
    265 
    266     public void preview(DreamInfo dreamInfo) {
    267         logd("preview(%s)", dreamInfo);
    268         if (mDreamManager == null || dreamInfo == null || dreamInfo.componentName == null)
    269             return;
    270         try {
    271             mDreamManager.testDream(dreamInfo.componentName);
    272         } catch (RemoteException e) {
    273             Log.w(TAG, "Failed to preview " + dreamInfo, e);
    274         }
    275     }
    276 
    277     public void startDreaming() {
    278         logd("startDreaming()");
    279         if (mDreamManager == null)
    280             return;
    281         try {
    282             mDreamManager.dream();
    283         } catch (RemoteException e) {
    284             Log.w(TAG, "Failed to dream", e);
    285         }
    286     }
    287 
    288     private static ComponentName getDreamComponentName(ResolveInfo resolveInfo) {
    289         if (resolveInfo == null || resolveInfo.serviceInfo == null)
    290             return null;
    291         return new ComponentName(resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name);
    292     }
    293 
    294     private static ComponentName getSettingsComponentName(PackageManager pm, ResolveInfo resolveInfo) {
    295         if (resolveInfo == null
    296                 || resolveInfo.serviceInfo == null
    297                 || resolveInfo.serviceInfo.metaData == null)
    298             return null;
    299         String cn = null;
    300         XmlResourceParser parser = null;
    301         Exception caughtException = null;
    302         try {
    303             parser = resolveInfo.serviceInfo.loadXmlMetaData(pm, DreamService.DREAM_META_DATA);
    304             if (parser == null) {
    305                 Log.w(TAG, "No " + DreamService.DREAM_META_DATA + " meta-data");
    306                 return null;
    307             }
    308             Resources res = pm.getResourcesForApplication(resolveInfo.serviceInfo.applicationInfo);
    309             AttributeSet attrs = Xml.asAttributeSet(parser);
    310             int type;
    311             while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
    312                     && type != XmlPullParser.START_TAG) {
    313             }
    314             String nodeName = parser.getName();
    315             if (!"dream".equals(nodeName)) {
    316                 Log.w(TAG, "Meta-data does not start with dream tag");
    317                 return null;
    318             }
    319             TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.Dream);
    320             cn = sa.getString(com.android.internal.R.styleable.Dream_settingsActivity);
    321             sa.recycle();
    322         } catch (PackageManager.NameNotFoundException|IOException|XmlPullParserException e) {
    323             caughtException = e;
    324         } finally {
    325             if (parser != null) parser.close();
    326         }
    327         if (caughtException != null) {
    328             Log.w(TAG, "Error parsing : " + resolveInfo.serviceInfo.packageName, caughtException);
    329             return null;
    330         }
    331         if (cn != null && cn.indexOf('/') < 0) {
    332             cn = resolveInfo.serviceInfo.packageName + "/" + cn;
    333         }
    334         return cn == null ? null : ComponentName.unflattenFromString(cn);
    335     }
    336 
    337     private static void logd(String msg, Object... args) {
    338         if (DEBUG)
    339             Log.d(TAG, args == null || args.length == 0 ? msg : String.format(msg, args));
    340     }
    341 
    342     private static class DreamInfoComparator implements Comparator<DreamInfo> {
    343         private final ComponentName mDefaultDream;
    344 
    345         public DreamInfoComparator(ComponentName defaultDream) {
    346             mDefaultDream = defaultDream;
    347         }
    348 
    349         @Override
    350         public int compare(DreamInfo lhs, DreamInfo rhs) {
    351             return sortKey(lhs).compareTo(sortKey(rhs));
    352         }
    353 
    354         private String sortKey(DreamInfo di) {
    355             StringBuilder sb = new StringBuilder();
    356             sb.append(di.componentName.equals(mDefaultDream) ? '0' : '1');
    357             sb.append(di.caption);
    358             return sb.toString();
    359         }
    360     }
    361 }
    362