Home | History | Annotate | Download | only in musicfx
      1 /*
      2  * Copyright (C) 2011 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.musicfx;
     18 
     19 import android.app.Activity;
     20 import android.app.IntentService;
     21 import android.content.BroadcastReceiver;
     22 import android.content.ComponentName;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.content.SharedPreferences;
     26 import android.content.SharedPreferences.Editor;
     27 import android.content.pm.PackageManager;
     28 import android.content.pm.ResolveInfo;
     29 import android.media.audiofx.AudioEffect;
     30 import android.net.Uri;
     31 import android.os.Bundle;
     32 import android.util.Log;
     33 
     34 import java.util.List;
     35 
     36 /**
     37  * Provide backwards compatibility for existing control panels.
     38  * There are two major parts to this:
     39  * - a BroadcastReceiver that listens for installed or removed packages, and
     40  *   enables or disables control panel receivers as needed to ensure that only
     41  *   one control panel package will receive the broadcasts that applications end
     42  * - a high priority control panel activity that redirects to the currently
     43  *   selected control panel activity
     44  *
     45  */
     46 public class Compatibility {
     47 
     48     private final static String TAG = "MusicFXCompat";
     49     // run "setprop log.tag.MusicFXCompat DEBUG" to turn on logging
     50     private final static boolean LOG = Log.isLoggable(TAG, Log.DEBUG);
     51 
     52 
     53     /**
     54      * This activity has an intent filter with the highest possible priority, so
     55      * it will always be chosen. It then looks up the correct control panel to
     56      * use and launches that.
     57      */
     58     public static class Redirector extends Activity {
     59 
     60         @Override
     61         public void onCreate(final Bundle savedInstanceState) {
     62             super.onCreate(savedInstanceState);
     63             log("Compatibility Activity called from " + getCallingPackage());
     64             Intent i = new Intent(getIntent());
     65             i.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
     66             SharedPreferences pref = getSharedPreferences("musicfx", MODE_PRIVATE);
     67             String defPackage = pref.getString("defaultpanelpackage", null);
     68             String defName = pref.getString("defaultpanelname", null);
     69             log("read " + defPackage + "/" + defName + " as default");
     70             if (defPackage == null || defName == null) {
     71                 Log.e(TAG, "no default set!");
     72                 // use the built-in panel
     73                 i.setComponent(new ComponentName(this, ActivityMusic.class));
     74                 // also save it as the default
     75                 Intent updateIntent = new Intent(this, Service.class);
     76                 updateIntent.putExtra("defPackage", getPackageName());
     77                 updateIntent.putExtra("defName", ActivityMusic.class.getName());
     78                 startService(updateIntent);
     79             } else {
     80                 i.setComponent(new ComponentName(defPackage, defName));
     81             }
     82             startActivity(i);
     83             finish();
     84         }
     85     }
     86 
     87     /**
     88      * This BroadcastReceiver responds to BOOT_COMPLETED, PACKAGE_ADDED,
     89      * PACKAGE_REPLACED and PACKAGE_REMOVED intents. When run, it checks
     90      * to see whether the active control panel needs to be updated:
     91      * - if there is no default, it picks one
     92      * - if a new control panel is installed, it becomes the default
     93      * It then enables the open/close receivers in the active control panel,
     94      * and disables them in the others.
     95      */
     96     public static class Receiver extends BroadcastReceiver {
     97 
     98         @Override
     99         public void onReceive(final Context context, final Intent intent) {
    100 
    101             log("received");
    102             Intent updateIntent = new Intent(context, Service.class);
    103             updateIntent.putExtra("reason", intent);
    104             context.startService(updateIntent);
    105         }
    106     }
    107 
    108     public static class Service extends IntentService {
    109 
    110         PackageManager mPackageManager;
    111 
    112         public Service() {
    113             super("CompatibilityService");
    114         }
    115 
    116         @Override
    117         protected void onHandleIntent(final Intent intent) {
    118             log("handleintent");
    119             if (mPackageManager == null) {
    120                 mPackageManager = getPackageManager();
    121             }
    122 
    123             String defPackage = intent.getStringExtra("defPackage");
    124             String defName = intent.getStringExtra("defName");
    125             if (defPackage != null && defName != null) {
    126                 setDefault(defPackage, defName);
    127                 return;
    128             }
    129 
    130             Intent packageIntent = intent.getParcelableExtra("reason");
    131             Bundle b = packageIntent.getExtras();
    132             if (b != null) b.size();
    133             log("intentservice saw: " + packageIntent + " " + b);
    134             // TODO, be smarter about package upgrades (which results in three
    135             // broadcasts: removed, added, replaced)
    136             Uri packageUri = packageIntent.getData();
    137             String updatedPackage = null;
    138             if (packageUri != null) {
    139                 updatedPackage = packageUri.toString().substring(8);
    140                 pickDefaultControlPanel(updatedPackage);
    141             }
    142         }
    143 
    144         private void pickDefaultControlPanel(String updatedPackage) {
    145 
    146             ResolveInfo defPanel = null;
    147             ResolveInfo otherPanel = null;
    148             ResolveInfo thisPanel = null;
    149             Intent i = new Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL);
    150             List<ResolveInfo> ris = mPackageManager.queryIntentActivities(i, PackageManager.GET_DISABLED_COMPONENTS);
    151             log("found: " + ris.size());
    152             SharedPreferences pref = getSharedPreferences("musicfx", MODE_PRIVATE);
    153             String savedDefPackage = pref.getString("defaultpanelpackage", null);
    154             String savedDefName = pref.getString("defaultpanelname", null);
    155             log("saved default: " + savedDefName);
    156             for (ResolveInfo foo: ris) {
    157                 if (foo.activityInfo.name.equals(Compatibility.Redirector.class.getName())) {
    158                     log("skipping " + foo);
    159                     continue;
    160                 }
    161                 log("considering " + foo);
    162                 if (foo.activityInfo.name.equals(savedDefName) &&
    163                         foo.activityInfo.packageName.equals(savedDefPackage) &&
    164                         foo.activityInfo.enabled) {
    165                     log("default: " + savedDefName);
    166                     defPanel = foo;
    167                     break;
    168                 } else if (foo.activityInfo.packageName.equals(updatedPackage)) {
    169                     log("choosing newly installed package " + updatedPackage);
    170                     otherPanel = foo;
    171                 } else if (otherPanel == null && !foo.activityInfo.packageName.equals(getPackageName())) {
    172                     otherPanel = foo;
    173                 } else {
    174                     thisPanel = foo;
    175                 }
    176             }
    177 
    178             if (defPanel == null) {
    179                 // pick a default control panel
    180                 if (otherPanel == null) {
    181                     if (thisPanel == null) {
    182                         Log.e(TAG, "No control panels found!");
    183                         return;
    184                     }
    185                     otherPanel = thisPanel;
    186                 }
    187                 defPanel = otherPanel;
    188             }
    189 
    190             // Now that we have selected a default control panel activity, ensure
    191             // that the broadcast receiver(s) in that same package are enabled,
    192             // and the ones in the other packages are disabled.
    193             String defPackage = defPanel.activityInfo.packageName;
    194             String defName = defPanel.activityInfo.name;
    195             setDefault(defPackage, defName);
    196         }
    197 
    198         private void setDefault(String defPackage, String defName) {
    199             Intent i = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION);
    200             List<ResolveInfo> ris = mPackageManager.queryBroadcastReceivers(i, PackageManager.GET_DISABLED_COMPONENTS);
    201             setupReceivers(ris, defPackage);
    202             // The open and close receivers are likely the same, but they may not be.
    203             i = new Intent(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION);
    204             ris = mPackageManager.queryBroadcastReceivers(i, PackageManager.GET_DISABLED_COMPONENTS);
    205             setupReceivers(ris, defPackage);
    206 
    207             // Write the selected default to the prefs so that the Redirector activity
    208             // knows which one to use.
    209             SharedPreferences pref = getSharedPreferences("musicfx", MODE_PRIVATE);
    210             Editor ed = pref.edit();
    211             ed.putString("defaultpanelpackage", defPackage);
    212             ed.putString("defaultpanelname", defName);
    213             ed.commit();
    214             log("wrote " + defPackage + "/" + defName + " as default");
    215         }
    216 
    217         private void setupReceivers(List<ResolveInfo> ris, String defPackage) {
    218             // TODO - we may need to keep track of active sessions and send "open session"
    219             // broadcast to newly enabled receivers, while sending "close session" to
    220             // receivers that are about to be disabled. We could also consider just
    221             // killing the process hosting the disabled components.
    222             for (ResolveInfo foo: ris) {
    223                 ComponentName comp = new ComponentName(foo.activityInfo.packageName, foo.activityInfo.name);
    224                 if (foo.activityInfo.packageName.equals(defPackage)) {
    225                     log("enabling receiver " + foo);
    226                     mPackageManager.setComponentEnabledSetting(comp,
    227                             PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
    228                             PackageManager.DONT_KILL_APP);
    229                 } else {
    230                     log("disabling receiver " + foo);
    231                     mPackageManager.setComponentEnabledSetting(comp,
    232                             PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
    233                             PackageManager.DONT_KILL_APP);
    234                 }
    235             }
    236         }
    237     }
    238 
    239     private static void log(String out) {
    240         if (LOG) {
    241             Log.d(TAG, out);
    242         }
    243     }
    244 }
    245