/* * Copyright (C) 2012 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.notificationlight; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.TreeMap; import android.app.AlertDialog; import android.app.Dialog; import android.app.Notification; import android.app.NotificationManager; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; import android.preference.CheckBoxPreference; import android.preference.Preference; import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; import android.provider.Settings; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; public class NotificationLightSettings extends SettingsPreferenceFragment implements Preference.OnPreferenceChangeListener, View.OnLongClickListener { private static final String TAG = "NotificationLightSettings"; private static final String NOTIFICATION_LIGHT_PULSE_DEFAULT_COLOR = "notification_light_pulse_default_color"; private static final String NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_ON = "notification_light_pulse_default_led_on"; private static final String NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_OFF = "notification_light_pulse_default_led_off"; private static final String NOTIFICATION_LIGHT_PULSE_CUSTOM_ENABLE = "notification_light_pulse_custom_enable"; private static final String NOTIFICATION_LIGHT_PULSE_CUSTOM_VALUES = "notification_light_pulse_custom_values"; private static final String NOTIFICATION_LIGHT_PULSE = "notification_light_pulse"; private static final String NOTIFICATION_LIGHT_PULSE_CALL_COLOR = "notification_light_pulse_call_color"; private static final String NOTIFICATION_LIGHT_PULSE_CALL_LED_ON = "notification_light_pulse_call_led_on"; private static final String NOTIFICATION_LIGHT_PULSE_CALL_LED_OFF = "notification_light_pulse_call_led_off"; private static final String NOTIFICATION_LIGHT_PULSE_VMAIL_COLOR = "notification_light_pulse_vmail_color"; private static final String NOTIFICATION_LIGHT_PULSE_VMAIL_LED_ON = "notification_light_pulse_vmail_led_on"; private static final String NOTIFICATION_LIGHT_PULSE_VMAIL_LED_OFF = "notification_light_pulse_vmail_led_off"; private static final String PULSE_PREF = "pulse_enabled"; private static final String DEFAULT_PREF = "default"; private static final String CUSTOM_PREF = "custom_enabled"; private static final String MISSED_CALL_PREF = "missed_call"; private static final String VOICEMAIL_PREF = "voicemail"; public static final int ACTION_TEST = 0; public static final int ACTION_DELETE = 1; private static final int MENU_ADD = 0; private static final int DIALOG_APPS = 0; private int mDefaultColor; private int mDefaultLedOn; private int mDefaultLedOff; private List<ResolveInfo> mInstalledApps; private PackageManager mPackageManager; private boolean mCustomEnabled; private boolean mLightEnabled; private boolean mVoiceCapable; private ApplicationLightPreference mDefaultPref; private ApplicationLightPreference mCallPref; private ApplicationLightPreference mVoicemailPref; private CheckBoxPreference mCustomEnabledPref; private Menu mMenu; AppAdapter mAppAdapter; private String mApplicationList; private Map<String, Application> mApplications; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.notification_light_settings); Resources resources = getResources(); mDefaultColor = resources.getColor( com.android.internal.R.color.config_defaultNotificationColor); mDefaultLedOn = resources.getInteger( com.android.internal.R.integer.config_defaultNotificationLedOn); mDefaultLedOff = resources.getInteger( com.android.internal.R.integer.config_defaultNotificationLedOff); // Get launch-able applications mPackageManager = getPackageManager(); final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); mInstalledApps = mPackageManager.queryIntentActivities(mainIntent, 0); mAppAdapter = new AppAdapter(mInstalledApps); mAppAdapter.update(); mApplications = new HashMap<String, Application>(); // Determine if the device has voice capabilities mVoiceCapable = (((TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE)).getPhoneType() != TelephonyManager.PHONE_TYPE_NONE); setHasOptionsMenu(true); } @Override public void onResume() { super.onResume(); refreshDefault(); refreshCustomApplications(); setCustomEnabled(); } private void refreshDefault() { ContentResolver resolver = getContentResolver(); int color = Settings.System.getInt(resolver, NOTIFICATION_LIGHT_PULSE_DEFAULT_COLOR, mDefaultColor); int timeOn = Settings.System.getInt(resolver, NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_ON, mDefaultLedOn); int timeOff = Settings.System.getInt(resolver, NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_OFF, mDefaultLedOff); mLightEnabled = Settings.System.getInt(resolver, NOTIFICATION_LIGHT_PULSE, 0) == 1; mCustomEnabled = Settings.System.getInt(resolver, NOTIFICATION_LIGHT_PULSE_CUSTOM_ENABLE, 0) == 1; // Get Missed call and Voicemail values int callColor = Settings.System.getInt(resolver, NOTIFICATION_LIGHT_PULSE_CALL_COLOR, mDefaultColor); int callTimeOn = Settings.System.getInt(resolver, NOTIFICATION_LIGHT_PULSE_CALL_LED_ON, mDefaultLedOn); int callTimeOff = Settings.System.getInt(resolver, NOTIFICATION_LIGHT_PULSE_CALL_LED_OFF, mDefaultLedOff); int vmailColor = Settings.System.getInt(resolver, NOTIFICATION_LIGHT_PULSE_VMAIL_COLOR, mDefaultColor); int vmailTimeOn = Settings.System.getInt(resolver, NOTIFICATION_LIGHT_PULSE_VMAIL_LED_ON, mDefaultLedOn); int vmailTimeOff = Settings.System.getInt(resolver, NOTIFICATION_LIGHT_PULSE_VMAIL_LED_OFF, mDefaultLedOff); PreferenceScreen prefSet = getPreferenceScreen(); PreferenceGroup generalPrefs = (PreferenceGroup) prefSet.findPreference("general_section"); if (generalPrefs != null) { // Pulse preference CheckBoxPreference cPref = (CheckBoxPreference) prefSet.findPreference(PULSE_PREF); cPref.setChecked(mLightEnabled); cPref.setOnPreferenceChangeListener(this); // Default preference mDefaultPref = (ApplicationLightPreference) prefSet.findPreference(DEFAULT_PREF); mDefaultPref.setAllValues(color, timeOn, timeOff); mDefaultPref.setEnabled(mLightEnabled); mDefaultPref.setOnPreferenceChangeListener(this); // Custom enabled preference mCustomEnabledPref = (CheckBoxPreference) prefSet.findPreference(CUSTOM_PREF); mCustomEnabledPref.setChecked(mCustomEnabled); mCustomEnabledPref.setEnabled(mLightEnabled); mCustomEnabledPref.setOnPreferenceChangeListener(this); } PreferenceGroup phonePrefs = (PreferenceGroup) prefSet.findPreference("phone_list"); if (phonePrefs != null) { // Missed call and Voicemail preferences // Should only show on devices with a voice capabilities if (!mVoiceCapable) { prefSet.removePreference(phonePrefs); } else { mCallPref = (ApplicationLightPreference) prefSet.findPreference(MISSED_CALL_PREF); mCallPref.setAllValues(callColor, callTimeOn, callTimeOff); mCallPref.setEnabled(mCustomEnabled); mCallPref.setOnPreferenceChangeListener(this); mVoicemailPref = (ApplicationLightPreference) prefSet.findPreference(VOICEMAIL_PREF); mVoicemailPref.setAllValues(vmailColor, vmailTimeOn, vmailTimeOff); mVoicemailPref.setEnabled(mCustomEnabled); mVoicemailPref.setOnPreferenceChangeListener(this); } } } private void refreshCustomApplications() { Context context = getActivity(); if (!parseApplicationList()) { return; } // Add the Application Preferences final PreferenceScreen prefSet = getPreferenceScreen(); final PackageManager pm = getPackageManager(); final PreferenceGroup appList = (PreferenceGroup) prefSet.findPreference("applications_list"); if (appList != null) { final Map<CharSequence, ApplicationLightPreference> prefs = new TreeMap<CharSequence, ApplicationLightPreference>(); appList.removeAll(); for (Application i : mApplications.values()) { try { PackageInfo info = pm.getPackageInfo(i.name, PackageManager.GET_META_DATA); ApplicationLightPreference pref = new ApplicationLightPreference(context, this, i.color, i.timeon, i.timeoff); final CharSequence label = info.applicationInfo.loadLabel(pm); pref.setKey(i.name); pref.setTitle(label); pref.setIcon(info.applicationInfo.loadIcon(pm)); // Does not fit on low res devices, we need it so we hide the view in the preference pref.setSummary(i.name); pref.setPersistent(false); pref.setOnPreferenceChangeListener(this); prefs.put(label, pref); } catch (NameNotFoundException e) { // Do nothing } } for (ApplicationLightPreference pref : prefs.values()) { appList.addPreference(pref); } } } private void setCustomEnabled() { Boolean enabled = mCustomEnabled && mLightEnabled; // Phone related preferences if (mVoiceCapable) { mCallPref.setEnabled(enabled); mVoicemailPref.setEnabled(enabled); } // Custom applications PreferenceScreen prefSet = getPreferenceScreen(); PreferenceGroup appList = (PreferenceGroup) prefSet.findPreference("applications_list"); if (appList != null) { appList.setEnabled(enabled); setHasOptionsMenu(enabled); } } private void addCustomApplication(String packageName) { Application app = mApplications.get(packageName); if (app == null) { app = new Application(packageName, mDefaultColor, mDefaultLedOn, mDefaultLedOff); mApplications.put(packageName, app); saveApplicationList(false); refreshCustomApplications(); } } private void removeCustomApplication(String packageName) { if (mApplications.remove(packageName) != null) { saveApplicationList(false); refreshCustomApplications(); } } private boolean parseApplicationList() { final String baseString = Settings.System.getString(getContentResolver(), Settings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_VALUES); if (TextUtils.equals(mApplicationList, baseString)) { return false; } mApplicationList = baseString; mApplications.clear(); if (baseString != null) { final String[] array = TextUtils.split(baseString, "\\|"); for (String item : array) { if (TextUtils.isEmpty(item)) { continue; } Application app = Application.fromString(item); if (app != null) { mApplications.put(app.name, app); } } } return true; } private void saveApplicationList(boolean preferencesUpdated) { List<String> settings = new ArrayList<String>(); for (Application app : mApplications.values()) { settings.add(app.toString()); } final String value = TextUtils.join("|", settings); if (preferencesUpdated) { mApplicationList = value; } Settings.System.putString(getContentResolver(), Settings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_VALUES, value); } /** * Updates the default or application specific notification settings. * * @param application Package name of application specific settings to update * @param color * @param timeon * @param timeoff */ protected void updateValues(String application, Integer color, Integer timeon, Integer timeoff) { ContentResolver resolver = getContentResolver(); if (application.equals(DEFAULT_PREF)) { Settings.System.putInt(resolver, NOTIFICATION_LIGHT_PULSE_DEFAULT_COLOR, color); Settings.System.putInt(resolver, NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_ON, timeon); Settings.System.putInt(resolver, NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_OFF, timeoff); refreshDefault(); return; } else if (application.equals(MISSED_CALL_PREF)) { Settings.System.putInt(resolver, NOTIFICATION_LIGHT_PULSE_CALL_COLOR, color); Settings.System.putInt(resolver, NOTIFICATION_LIGHT_PULSE_CALL_LED_ON, timeon); Settings.System.putInt(resolver, NOTIFICATION_LIGHT_PULSE_CALL_LED_OFF, timeoff); refreshDefault(); return; } else if (application.equals(VOICEMAIL_PREF)) { Settings.System.putInt(resolver, NOTIFICATION_LIGHT_PULSE_VMAIL_COLOR, color); Settings.System.putInt(resolver, NOTIFICATION_LIGHT_PULSE_VMAIL_LED_ON, timeon); Settings.System.putInt(resolver, NOTIFICATION_LIGHT_PULSE_VMAIL_LED_OFF, timeoff); refreshDefault(); return; } // Find the custom app and sets its new values Application app = mApplications.get(application); if (app != null) { app.color = color; app.timeon = timeon; app.timeoff = timeoff; saveApplicationList(true); } } public boolean onLongClick(View v) { final TextView tView; AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); if ((v != null) && ((tView = (TextView) v.findViewById(android.R.id.summary)) != null)) { builder.setTitle(R.string.dialog_delete_title); builder.setMessage(R.string.dialog_delete_message); builder.setIconAttribute(android.R.attr.alertDialogIcon); builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { removeCustomApplication(tView.getText().toString()); } }); builder.setNegativeButton(android.R.string.cancel, null); builder.create().show(); return true; } return false; } public boolean onPreferenceChange(Preference preference, Object objValue) { String key = preference.getKey(); if (PULSE_PREF.equals(key)) { mLightEnabled = (Boolean) objValue; Settings.System.putInt(getContentResolver(), Settings.System.NOTIFICATION_LIGHT_PULSE, mLightEnabled ? 1 : 0); mDefaultPref.setEnabled(mLightEnabled); mCustomEnabledPref.setEnabled(mLightEnabled); setCustomEnabled(); } else if (CUSTOM_PREF.equals(key)) { mCustomEnabled = (Boolean) objValue; Settings.System.putInt(getContentResolver(), NOTIFICATION_LIGHT_PULSE_CUSTOM_ENABLE, mCustomEnabled ? 1 : 0); setCustomEnabled(); } else { ApplicationLightPreference tPref = (ApplicationLightPreference) preference; updateValues(key, tPref.getColor(), tPref.getOnValue(), tPref.getOffValue()); } return true; } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { mMenu = menu; mMenu.add(0, MENU_ADD, 0, R.string.profiles_add) .setIcon(R.drawable.ic_menu_add) .setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case MENU_ADD: showDialog(DIALOG_APPS); return true; } return false; } /** * Utility classes and supporting methods */ @Override public Dialog onCreateDialog(int id) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); final Dialog dialog; switch (id) { case DIALOG_APPS: final ListView list = new ListView(getActivity()); list.setAdapter(mAppAdapter); builder.setTitle(R.string.profile_choose_app); builder.setView(list); dialog = builder.create(); list.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // Add empty application definition, the user will be able to edit it later AppItem info = (AppItem) parent.getItemAtPosition(position); addCustomApplication(info.packageName); dialog.cancel(); } }); break; default: dialog = null; } return dialog; } /** * Application class */ private static class Application { public String name; public Integer color; public Integer timeon; public Integer timeoff; /** * Stores all the application values in one call * @param name * @param color * @param timeon * @param timeoff */ public Application(String name, Integer color, Integer timeon, Integer timeoff) { this.name = name; this.color = color; this.timeon = timeon; this.timeoff = timeoff; } public Application() { } public String toString() { StringBuilder builder = new StringBuilder(); builder.append(name); builder.append("="); builder.append(color); builder.append(";"); builder.append(timeon); builder.append(";"); builder.append(timeoff); return builder.toString(); } public static Application fromString(String value) { if (TextUtils.isEmpty(value)) { return null; } String[] app = value.split("=", -1); if (app.length != 2) return null; String[] values = app[1].split(";", -1); if (values.length != 3) return null; try { Application item = new Application(app[0], Integer.parseInt(values[0]), Integer .parseInt(values[1]), Integer.parseInt(values[2])); return item; } catch (NumberFormatException e) { return null; } } }; /** * AppItem class */ class AppItem implements Comparable<AppItem> { CharSequence title; String packageName; Drawable icon; @Override public int compareTo(AppItem another) { return this.title.toString().compareTo(another.title.toString()); } } /** * AppAdapter class */ class AppAdapter extends BaseAdapter { protected List<ResolveInfo> mInstalledAppInfo; protected List<AppItem> mInstalledApps = new LinkedList<AppItem>(); private void reloadList() { final Handler handler = new Handler(); new Thread(new Runnable() { @Override public void run() { synchronized (mInstalledApps) { mInstalledApps.clear(); for (ResolveInfo info : mInstalledAppInfo) { final AppItem item = new AppItem(); item.title = info.loadLabel(mPackageManager); item.icon = info.loadIcon(mPackageManager); item.packageName = info.activityInfo.packageName; handler.post(new Runnable() { @Override public void run() { int index = Collections.binarySearch(mInstalledApps, item); if (index < 0) { index = -index - 1; mInstalledApps.add(index, item); } notifyDataSetChanged(); } }); } } } }).start(); } public AppAdapter(List<ResolveInfo> installedAppsInfo) { mInstalledAppInfo = installedAppsInfo; } public void update() { reloadList(); } @Override public int getCount() { return mInstalledApps.size(); } @Override public AppItem getItem(int position) { return mInstalledApps.get(position); } @Override public long getItemId(int position) { return mInstalledApps.get(position).packageName.hashCode(); } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView != null) { holder = (ViewHolder) convertView.getTag(); } else { final LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = layoutInflater.inflate(R.layout.preference_icon, null, false); holder = new ViewHolder(); convertView.setTag(holder); holder.title = (TextView) convertView.findViewById(com.android.internal.R.id.title); holder.summary = (TextView) convertView.findViewById(com.android.internal.R.id.summary); holder.icon = (ImageView) convertView.findViewById(R.id.icon); } AppItem applicationInfo = getItem(position); if (holder.title != null) { holder.title.setText(applicationInfo.title); } if (holder.summary != null) { holder.summary.setVisibility(View.GONE); } if (holder.icon != null) { Drawable loadIcon = applicationInfo.icon; holder.icon.setImageDrawable(loadIcon); } return convertView; } } static class ViewHolder { TextView title; TextView summary; ImageView icon; } }