Merge "Status bar brightness control (Settings portion)" into android-4.3
diff --git a/res/layout/dialog_light_settings.xml b/res/layout/dialog_light_settings.xml
new file mode 100644
index 0000000..6e7e01f
--- /dev/null
+++ b/res/layout/dialog_light_settings.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2010 Daniel Nilsson
+ 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. -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <org.omnirom.omnigears.notificationlight.ColorPickerView
+ android:id="@+id/color_picker_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_marginStart="10dp"
+ android:layout_marginEnd="10dp" />
+
+ <LinearLayout
+ android:id="@+id/color_panel_view"
+ android:layout_width="match_parent"
+ android:layout_height="40dp"
+ android:layout_alignStart="@id/color_picker_view"
+ android:layout_alignEnd="@id/color_picker_view"
+ android:layout_below="@id/color_picker_view"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="4dp"
+ android:orientation="horizontal" >
+
+ <EditText
+ android:id="@+id/hex_color_input"
+ android:layout_width="0px"
+ android:maxLength="6"
+ android:digits="0123456789ABCDEFabcdef"
+ android:inputType="textNoSuggestions"
+ android:layout_height="match_parent"
+ android:layout_weight="0.5" />
+
+ <org.omnirom.omnigears.notificationlight.ColorPanelView
+ android:id="@+id/color_panel"
+ android:layout_width="0px"
+ android:layout_height="match_parent"
+ android:layout_weight="0.5" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/speed_title_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/color_panel_view"
+ android:layout_marginStart="10dp"
+ android:layout_marginEnd="10dp"
+ android:layout_marginTop="4dp"
+ android:orientation="vertical" >
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="2dp"
+ android:background="@android:drawable/divider_horizontal_dark" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
+ android:text="@string/pulse_speed_title"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingBottom="4dip" >
+
+ <Spinner
+ android:id="@+id/on_spinner"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
+
+ <View
+ android:layout_width="8dip"
+ android:layout_height="match_parent" />
+
+ <Spinner
+ android:id="@+id/off_spinner"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
+ </LinearLayout>
+ </LinearLayout>
+
+</RelativeLayout>
diff --git a/res/layout/preference_application_light.xml b/res/layout/preference_application_light.xml
new file mode 100644
index 0000000..f88038c
--- /dev/null
+++ b/res/layout/preference_application_light.xml
@@ -0,0 +1,69 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/app_light_pref"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:gravity="center_vertical"
+ android:background="?android:attr/selectableItemBackground" >
+
+ <ImageView
+ android:id="@+android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="4dip"
+ android:maxWidth="36dip"
+ android:maxHeight="36dip"
+ android:adjustViewBounds="true"
+ android:layout_gravity="center" />
+
+ <RelativeLayout
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_margin="4dip"
+ android:layout_weight="1">
+
+ <TextView android:id="@+android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal" />
+
+ <TextView android:id="@+android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="1" />
+ </RelativeLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@+id/textViewTimeOnValue"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ <TextView
+ android:id="@+id/textViewTimeOffValue"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/light_color"
+ android:layout_width="32dip"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center" />
+
+</LinearLayout>
diff --git a/res/layout/pulse_time_item.xml b/res/layout/pulse_time_item.xml
new file mode 100644
index 0000000..9052caf
--- /dev/null
+++ b/res/layout/pulse_time_item.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/textViewName"
+ android:paddingStart="4dp"
+ android:paddingEnd="4dp"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium" >
+
+</TextView>
diff --git a/res/values/custom_arrays.xml b/res/values/custom_arrays.xml
index 933ad47..c4c7858 100644
--- a/res/values/custom_arrays.xml
+++ b/res/values/custom_arrays.xml
@@ -85,4 +85,39 @@
<item>@*android:drawable/ic_lockscreen_google_normal</item>
<item>@*android:drawable/ic_lockscreen_twitter_normal</item>
</string-array>
+
+ <!-- Values for the notification light pulse spinners -->
+ <string-array name="notification_pulse_length_entries" translatable="false">
+ <item>@string/pulse_length_always_on</item>
+ <item>@string/pulse_length_very_short</item>
+ <item>@string/pulse_length_short</item>
+ <item>@string/pulse_length_normal</item>
+ <item>@string/pulse_length_long</item>
+ <item>@string/pulse_length_very_long</item>
+ </string-array>
+
+ <string-array name="notification_pulse_length_values" translatable="false">
+ <item>1</item>
+ <item>250</item>
+ <item>500</item>
+ <item>1000</item>
+ <item>2500</item>
+ <item>5000</item>
+ </string-array>
+
+ <string-array name="notification_pulse_speed_entries" translatable="false">
+ <item>@string/pulse_speed_very_fast</item>
+ <item>@string/pulse_speed_fast</item>
+ <item>@string/pulse_speed_normal</item>
+ <item>@string/pulse_speed_slow</item>
+ <item>@string/pulse_speed_very_slow</item>
+ </string-array>
+
+ <string-array name="notification_pulse_speed_values" translatable="false">
+ <item>250</item>
+ <item>500</item>
+ <item>1000</item>
+ <item>2500</item>
+ <item>5000</item>
+ </string-array>
</resources>
diff --git a/res/values/custom_strings.xml b/res/values/custom_strings.xml
index 50262e3..c8293c9 100644
--- a/res/values/custom_strings.xml
+++ b/res/values/custom_strings.xml
@@ -5,7 +5,7 @@
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 2 of the License, or
+ the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
@@ -90,5 +90,55 @@
<string name="icon_picker_pack_title">Icon pack</string>
<string name="profile_applist_title">Apps</string>
+ <string name="profile_choose_app">Choose app</string>
+ <string name="profiles_add">Add</string>
+
+ <!-- Notification light dialogs -->
+ <string name="edit_light_settings">Edit light settings</string>
+ <string name="pulse_speed_title">Pulse length and speed</string>
+ <string name="default_time">Normal</string>
+ <string name="custom_time">Custom</string>
+ <string name="dialog_test">Test</string>
+ <string name="dialog_delete_title">Delete</string>
+ <string name="dialog_delete_message">Remove selected item?</string>
+ <string name="dialog_test_message">Turn the screen off to see the selected notification in action or dismiss this dialog to stop the test</string>
+ <string name="dialog_test_button">Dismiss</string>
+
+ <!-- Lights settings screen, notification light settings -->
+ <string name="display_lights_settings_title">Display & lights</string>
+ <string name="lights_category_title">Lights</string>
+ <string name="notification_pulse_title">Notification light</string>
+ <string name="notification_light_general_title">General</string>
+ <string name="notification_light_applist_title">Applications</string>
+ <string name="notification_light_phonelist_title">Phone</string>
+ <string name="notification_light_use_custom">Use custom values</string>
+ <string name="notification_light_enabled">Enabled</string>
+ <string name="notification_light_disabled">Disabled</string>
+ <string name="notification_light_default_value">Default</string>
+ <string name="notification_light_missed_call_title">Missed call</string>
+ <string name="notification_light_voicemail_title">Voicemail</string>
+
+ <!-- Values for the notification light pulse spinners -->
+ <string name="pulse_length_always_on">Always on</string>
+ <string name="pulse_length_very_short">Very short</string>
+ <string name="pulse_length_short">Short</string>
+ <string name="pulse_length_normal">Normal</string>
+ <string name="pulse_length_long">Long</string>
+ <string name="pulse_length_very_long">Very long</string>
+ <string name="pulse_speed_very_fast">Very fast</string>
+ <string name="pulse_speed_fast">Fast</string>
+ <string name="pulse_speed_normal">Normal</string>
+ <string name="pulse_speed_slow">Slow</string>
+ <string name="pulse_speed_very_slow">Very slow</string>
+
+ <!-- Battery light settings -->
+ <string name="battery_light_title">Battery light</string>
+ <string name="battery_low_pulse_title">Pulse if battery low</string>
+ <string name="battery_light_list_title">Colors</string>
+ <string name="battery_light_low_color_title">Battery low</string>
+ <string name="battery_light_medium_color_title">Charging</string>
+ <string name="battery_light_full_color_title">Fully charged</string>
+
</resources>
+
diff --git a/res/xml/battery_light_settings.xml b/res/xml/battery_light_settings.xml
new file mode 100644
index 0000000..d5fda1e
--- /dev/null
+++ b/res/xml/battery_light_settings.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res/com.android.settings">
+
+ <PreferenceCategory
+ android:key="general_section"
+ android:title="@string/notification_light_general_title">
+
+ <CheckBoxPreference
+ android:key="battery_light_enabled"
+ android:title="@string/battery_light_title"
+ android:defaultValue="true" />
+
+ <CheckBoxPreference
+ android:key="battery_light_pulse"
+ android:title="@string/battery_low_pulse_title"
+ android:defaultValue="true"
+ android:dependency="battery_light_enabled" />
+
+ </PreferenceCategory>
+
+ <PreferenceCategory
+ android:key="colors_list"
+ android:title="@string/battery_light_list_title"
+ android:dependency="battery_light_enabled" >
+
+ <org.omnirom.omnigears.notificationlight.ApplicationLightPreference
+ android:key="low_color"
+ android:title="@string/battery_light_low_color_title"
+ android:persistent="false" />
+
+ <org.omnirom.omnigears.notificationlight.ApplicationLightPreference
+ android:key="medium_color"
+ android:title="@string/battery_light_medium_color_title"
+ android:persistent="false" />
+
+ <org.omnirom.omnigears.notificationlight.ApplicationLightPreference
+ android:key="full_color"
+ android:title="@string/battery_light_full_color_title"
+ android:persistent="false" />
+
+ </PreferenceCategory>
+
+</PreferenceScreen>
diff --git a/res/xml/notification_light_settings.xml b/res/xml/notification_light_settings.xml
new file mode 100644
index 0000000..e40c970
--- /dev/null
+++ b/res/xml/notification_light_settings.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res/com.android.settings">
+
+ <PreferenceCategory
+ android:key="general_section"
+ android:title="@string/notification_light_general_title">
+
+ <CheckBoxPreference
+ android:key="notification_light_pulse"
+ android:title="@string/notification_pulse_title" />
+
+ <org.omnirom.omnigears.notificationlight.ApplicationLightPreference
+ android:key="default"
+ android:title="@string/notification_light_default_value"
+ android:persistent="false"
+ android:dependency="notification_light_pulse" />
+
+ <CheckBoxPreference
+ android:key="notification_light_pulse_custom_enable"
+ android:title="@string/notification_light_use_custom"
+ android:dependency="notification_light_pulse" />
+
+ </PreferenceCategory>
+
+ <PreferenceCategory
+ android:key="phone_list"
+ android:title="@string/notification_light_phonelist_title" >
+
+ <org.omnirom.omnigears.notificationlight.ApplicationLightPreference
+ android:key="missed_call"
+ android:title="@string/notification_light_missed_call_title"
+ android:persistent="false"
+ android:dependency="notification_light_pulse" />
+
+ <org.omnirom.omnigears.notificationlight.ApplicationLightPreference
+ android:key="voicemail"
+ android:title="@string/notification_light_voicemail_title"
+ android:persistent="false"
+ android:dependency="notification_light_pulse" />
+
+ </PreferenceCategory>
+
+ <PreferenceCategory
+ android:key="applications_list"
+ android:title="@string/notification_light_applist_title"
+ android:dependency="notification_light_pulse_custom_enable" >
+ </PreferenceCategory>
+
+</PreferenceScreen>
diff --git a/src/org/omnirom/omnigears/notificationlight/AlphaPatternDrawable.java b/src/org/omnirom/omnigears/notificationlight/AlphaPatternDrawable.java
new file mode 100644
index 0000000..a527151
--- /dev/null
+++ b/src/org/omnirom/omnigears/notificationlight/AlphaPatternDrawable.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2010 Daniel Nilsson
+ * 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 org.omnirom.omnigears.notificationlight;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Bitmap.Config;
+import android.graphics.drawable.Drawable;
+
+/**
+ * This drawable that draws a simple white and gray chess board pattern. It's
+ * pattern you will often see as a background behind a partly transparent image
+ * in many applications.
+ *
+ * @author Daniel Nilsson
+ */
+public class AlphaPatternDrawable extends Drawable {
+
+ private int mRectangleSize = 10;
+
+ private Paint mPaint = new Paint();
+ private Paint mPaintWhite = new Paint();
+ private Paint mPaintGray = new Paint();
+
+ private int numRectanglesHorizontal;
+ private int numRectanglesVertical;
+
+ /**
+ * Bitmap in which the pattern will be cached.
+ */
+ private Bitmap mBitmap;
+
+ public AlphaPatternDrawable(int rectangleSize) {
+ mRectangleSize = rectangleSize;
+ mPaintWhite.setColor(0xffffffff);
+ mPaintGray.setColor(0xffcbcbcb);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ if (mBitmap != null) {
+ canvas.drawBitmap(mBitmap, null, getBounds(), mPaint);
+ }
+ }
+
+ @Override
+ public int getOpacity() {
+ return 0;
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ throw new UnsupportedOperationException("Alpha is not supported by this drawwable.");
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ throw new UnsupportedOperationException("ColorFilter is not supported by this drawwable.");
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ super.onBoundsChange(bounds);
+
+ int height = bounds.height();
+ int width = bounds.width();
+
+ numRectanglesHorizontal = (int) Math.ceil((width / mRectangleSize));
+ numRectanglesVertical = (int) Math.ceil(height / mRectangleSize);
+
+ generatePatternBitmap();
+ }
+
+ /**
+ * This will generate a bitmap with the pattern as big as the rectangle we
+ * were allow to draw on. We do this to cache the bitmap so we don't need
+ * to recreate it each time draw() is called since it takes a few
+ * milliseconds.
+ */
+ private void generatePatternBitmap() {
+
+ if (getBounds().width() <= 0 || getBounds().height() <= 0) {
+ return;
+ }
+
+ mBitmap = Bitmap.createBitmap(getBounds().width(), getBounds().height(), Config.ARGB_8888);
+ Canvas canvas = new Canvas(mBitmap);
+
+ Rect r = new Rect();
+ boolean verticalStartWhite = true;
+ for (int i = 0; i <= numRectanglesVertical; i++) {
+ boolean isWhite = verticalStartWhite;
+ for (int j = 0; j <= numRectanglesHorizontal; j++) {
+ r.top = i * mRectangleSize;
+ r.left = j * mRectangleSize;
+ r.bottom = r.top + mRectangleSize;
+ r.right = r.left + mRectangleSize;
+
+ canvas.drawRect(r, isWhite ? mPaintWhite : mPaintGray);
+
+ isWhite = !isWhite;
+ }
+
+ verticalStartWhite = !verticalStartWhite;
+ }
+ }
+}
diff --git a/src/org/omnirom/omnigears/notificationlight/ApplicationLightPreference.java b/src/org/omnirom/omnigears/notificationlight/ApplicationLightPreference.java
new file mode 100644
index 0000000..2756be7
--- /dev/null
+++ b/src/org/omnirom/omnigears/notificationlight/ApplicationLightPreference.java
@@ -0,0 +1,360 @@
+/*
+ * 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 org.omnirom.omnigears.notificationlight;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.RectShape;
+import android.os.Bundle;
+import android.preference.DialogPreference;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.settings.R;
+
+public class ApplicationLightPreference extends DialogPreference {
+
+ private static String TAG = "AppLightPreference";
+ public static final int DEFAULT_TIME = 1000;
+ public static final int DEFAULT_COLOR = 0xFFFFFF; //White
+
+ private ImageView mLightColorView;
+ private TextView mOnValueView;
+ private TextView mOffValueView;
+
+ private int mColorValue;
+ private int mOnValue;
+ private int mOffValue;
+ private boolean mOnOffChangeable;
+
+ private Resources mResources;
+ private ScreenReceiver mReceiver = null;
+ private AlertDialog mTestDialog;
+
+ /**
+ * @param context
+ * @param attrs
+ */
+ public ApplicationLightPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mColorValue = DEFAULT_COLOR;
+ mOnValue = DEFAULT_TIME;
+ mOffValue = DEFAULT_TIME;
+ mOnOffChangeable = true;
+ init();
+ }
+
+ /**
+ * @param context
+ * @param color
+ * @param onValue
+ * @param offValue
+ */
+ public ApplicationLightPreference(Context context, int color, int onValue, int offValue) {
+ super(context, null);
+ mColorValue = color;
+ mOnValue = onValue;
+ mOffValue = offValue;
+ mOnOffChangeable = true;
+ init();
+ }
+
+ /**
+ * @param context
+ * @param onLongClickListener
+ * @param color
+ * @param onValue
+ * @param offValue
+ */
+ public ApplicationLightPreference(Context context, int color, int onValue, int offValue, boolean onOffChangeable) {
+ super(context, null);
+ mColorValue = color;
+ mOnValue = onValue;
+ mOffValue = offValue;
+ mOnOffChangeable = onOffChangeable;
+ init();
+ }
+
+ private void init() {
+ setLayoutResource(R.layout.preference_application_light);
+ mResources = getContext().getResources();
+ }
+
+ @Override
+ protected void onBindView(View view) {
+ super.onBindView(view);
+
+ mLightColorView = (ImageView) view.findViewById(R.id.light_color);
+ mOnValueView = (TextView) view.findViewById(R.id.textViewTimeOnValue);
+ mOffValueView = (TextView) view.findViewById(R.id.textViewTimeOffValue);
+
+ // Hide the summary text - it takes up too much space on a low res device
+ // We use it for storing the package name for the longClickListener
+ TextView tView = (TextView) view.findViewById(android.R.id.summary);
+ tView.setVisibility(View.GONE);
+
+ updatePreferenceViews();
+ }
+
+ private void updatePreferenceViews() {
+ final int width = (int) mResources.getDimension(R.dimen.device_memory_usage_button_width);
+ final int height = (int) mResources.getDimension(R.dimen.device_memory_usage_button_height);
+
+ if (mLightColorView != null) {
+ mLightColorView.setEnabled(true);
+ mLightColorView.setImageDrawable(createRectShape(width, height, 0xFF000000 + mColorValue));
+ }
+ if (mOnValueView != null) {
+ mOnValueView.setText(mapLengthValue(mOnValue));
+ }
+ if (mOffValueView != null) {
+ if (mOnValue == 1) {
+ mOffValueView.setVisibility(View.GONE);
+ } else {
+ mOffValueView.setVisibility(View.VISIBLE);
+ }
+ mOffValueView.setText(mapSpeedValue(mOffValue));
+ }
+ }
+
+ @Override
+ protected void showDialog(Bundle state) {
+ super.showDialog(state);
+
+ if (getDialog() instanceof LightSettingsDialog) {
+ final LightSettingsDialog d = (LightSettingsDialog) getDialog();
+
+ // Intercept the click on the middle button to show the test dialog and prevent the onDismiss
+ d.findViewById(android.R.id.button3).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ int onTime = d.getPulseSpeedOn();
+ int offTime = d.getPulseSpeedOff();
+
+ showTestDialog(d.getColor() - 0xFF000000, onTime, offTime);
+ }
+ });
+ }
+ }
+
+ @Override
+ protected Dialog createDialog() {
+ android.util.Log.e("XPLOD", "XPLOD/ LOLWTF");
+ final LightSettingsDialog d = new LightSettingsDialog(getContext(),
+ 0xFF000000 + mColorValue, mOnValue, mOffValue, mOnOffChangeable);
+ d.setAlphaSliderVisible(false);
+
+ d.setButton(AlertDialog.BUTTON_POSITIVE, mResources.getString(R.string.ok),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ mColorValue = d.getColor() - 0xFF000000; // strip alpha, led does not support it
+ mOnValue = d.getPulseSpeedOn();
+ mOffValue = d.getPulseSpeedOff();
+ updatePreferenceViews();
+ callChangeListener(this);
+ }
+ });
+ d.setButton(AlertDialog.BUTTON_NEUTRAL, mResources.getString(R.string.dialog_test),
+ (DialogInterface.OnClickListener) null);
+ d.setButton(AlertDialog.BUTTON_NEGATIVE, mResources.getString(R.string.cancel),
+ (DialogInterface.OnClickListener) null);
+
+ return d;
+ }
+
+ private void showTestDialog(int color, int speedOn, int speedOff) {
+ final Context context = getContext();
+
+ if (mReceiver != null) {
+ context.unregisterReceiver(mReceiver);
+ }
+ if (mTestDialog != null) {
+ mTestDialog.dismiss();
+ }
+
+ mReceiver = new ScreenReceiver(color, speedOn, speedOff);
+
+ IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
+ filter.addAction(Intent.ACTION_SCREEN_ON);
+ context.registerReceiver(mReceiver, filter);
+
+ mTestDialog = new AlertDialog.Builder(context)
+ .setTitle(R.string.dialog_test)
+ .setMessage(R.string.dialog_test_message)
+ .setPositiveButton(R.string.dialog_test_button, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (mReceiver != null) {
+ context.unregisterReceiver(mReceiver);
+ mReceiver = null;
+ }
+ }
+ })
+ .create();
+
+ mTestDialog.show();
+ }
+
+ /**
+ * Getters and Setters
+ */
+
+ public int getColor() {
+ return mColorValue;
+ }
+
+ public void setColor(int color) {
+ mColorValue = color;
+ updatePreferenceViews();
+ }
+
+ public void setOnValue(int value) {
+ mOnValue = value;
+ updatePreferenceViews();
+ }
+
+ public int getOnValue() {
+ return mOnValue;
+ }
+
+ public void setOffValue(int value) {
+ mOffValue = value;
+ updatePreferenceViews();
+ }
+
+ public int getOffValue() {
+ return mOffValue;
+ }
+
+ public void setAllValues(int color, int onValue, int offValue) {
+ mColorValue = color;
+ mOnValue = onValue;
+ mOffValue = offValue;
+ mOnOffChangeable = true;
+ updatePreferenceViews();
+ }
+
+ public void setAllValues(int color, int onValue, int offValue, boolean onOffChangeable) {
+ mColorValue = color;
+ mOnValue = onValue;
+ mOffValue = offValue;
+ mOnOffChangeable = onOffChangeable;
+ updatePreferenceViews();
+ }
+
+ public void setOnOffValue(int onValue, int offValue) {
+ mOnValue = onValue;
+ mOffValue = offValue;
+ updatePreferenceViews();
+ }
+
+ public void setOnOffChangeable(boolean value) {
+ mOnOffChangeable = value;
+ }
+
+ /**
+ * Utility methods
+ */
+ public class ScreenReceiver extends BroadcastReceiver {
+ protected int timeon;
+ protected int timeoff;
+ protected int color;
+
+ public ScreenReceiver(int color, int timeon, int timeoff) {
+ this.timeon = timeon;
+ this.timeoff = timeoff;
+ this.color = color;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final NotificationManager nm =
+ (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+
+ if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
+ Notification.Builder builder = new Notification.Builder(context);
+ builder.setAutoCancel(true);
+ builder.setLights(color, timeon, timeoff);
+ Notification n = builder.getNotification();
+ n.flags |= Notification.FLAG_SHOW_LIGHTS;
+ nm.notify(1, n);
+ } else if(intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
+ nm.cancel(1);
+ context.unregisterReceiver(mReceiver);
+ mReceiver = null;
+ mTestDialog.dismiss();
+ mTestDialog = null;
+ }
+ }
+ }
+
+ private static ShapeDrawable createRectShape(int width, int height, int color) {
+ ShapeDrawable shape = new ShapeDrawable(new RectShape());
+ shape.setIntrinsicHeight(height);
+ shape.setIntrinsicWidth(width);
+ shape.getPaint().setColor(color);
+ return shape;
+ }
+
+ private String mapLengthValue(Integer time) {
+ if (time == DEFAULT_TIME) {
+ return getContext().getString(R.string.default_time);
+ }
+
+ String[] timeNames = mResources.getStringArray(R.array.notification_pulse_length_entries);
+ String[] timeValues = mResources.getStringArray(R.array.notification_pulse_length_values);
+
+ for (int i = 0; i < timeValues.length; i++) {
+ if (Integer.decode(timeValues[i]).equals(time)) {
+ return timeNames[i];
+ }
+ }
+
+ return getContext().getString(R.string.custom_time);
+ }
+
+ private String mapSpeedValue(Integer time) {
+ if (time == DEFAULT_TIME) {
+ return getContext().getString(R.string.default_time);
+ }
+
+ String[] timeNames = mResources.getStringArray(R.array.notification_pulse_speed_entries);
+ String[] timeValues = mResources.getStringArray(R.array.notification_pulse_speed_values);
+
+ for (int i = 0; i < timeValues.length; i++) {
+ if (Integer.decode(timeValues[i]).equals(time)) {
+ return timeNames[i];
+ }
+ }
+
+ return getContext().getString(R.string.custom_time);
+ }
+}
diff --git a/src/org/omnirom/omnigears/notificationlight/BatteryLightSettings.java b/src/org/omnirom/omnigears/notificationlight/BatteryLightSettings.java
new file mode 100644
index 0000000..63b1da1
--- /dev/null
+++ b/src/org/omnirom/omnigears/notificationlight/BatteryLightSettings.java
@@ -0,0 +1,161 @@
+/*
+ * 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 org.omnirom.omnigears.notificationlight;
+
+import android.content.ContentResolver;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.Preference;
+import android.preference.PreferenceGroup;
+import android.preference.PreferenceScreen;
+import android.provider.Settings;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+
+public class BatteryLightSettings extends SettingsPreferenceFragment implements
+ Preference.OnPreferenceChangeListener {
+ private static final String TAG = "BatteryLightSettings";
+
+ private static final String LOW_COLOR_PREF = "low_color";
+ private static final String MEDIUM_COLOR_PREF = "medium_color";
+ private static final String FULL_COLOR_PREF = "full_color";
+
+ private boolean mMultiColorLed;
+ private CheckBoxPreference mEnabledPref;
+ private PreferenceGroup mColorPrefs;
+ private ApplicationLightPreference mLowColorPref;
+ private ApplicationLightPreference mMediumColorPref;
+ private ApplicationLightPreference mFullColorPref;
+ private static final int MENU_RESET = Menu.FIRST;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.battery_light_settings);
+
+ PreferenceScreen prefSet = getPreferenceScreen();
+
+ // Does the Device support changing battery LED colors?
+ if (getResources().getBoolean(com.android.internal.R.bool.config_multiColorBatteryLed)) {
+ setHasOptionsMenu(true);
+
+ // Low, Medium and full color preferences
+ mLowColorPref = (ApplicationLightPreference) prefSet.findPreference(LOW_COLOR_PREF);
+ mLowColorPref.setOnPreferenceChangeListener(this);
+
+ mMediumColorPref = (ApplicationLightPreference) prefSet.findPreference(MEDIUM_COLOR_PREF);
+ mMediumColorPref.setOnPreferenceChangeListener(this);
+
+ mFullColorPref = (ApplicationLightPreference) prefSet.findPreference(FULL_COLOR_PREF);
+ mFullColorPref.setOnPreferenceChangeListener(this);
+ } else {
+ prefSet.removePreference(prefSet.findPreference("colors_list"));
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ refreshDefault();
+ }
+
+ private void refreshDefault() {
+ ContentResolver resolver = getContentResolver();
+ Resources res = getResources();
+
+ if (mLowColorPref != null) {
+ int lowColor = Settings.System.getInt(resolver, Settings.System.BATTERY_LIGHT_LOW_COLOR,
+ res.getInteger(com.android.internal.R.integer.config_notificationsBatteryLowARGB));
+ mLowColorPref.setAllValues(lowColor, 0, 0, false);
+ }
+
+ if (mMediumColorPref != null) {
+ int mediumColor = Settings.System.getInt(resolver, Settings.System.BATTERY_LIGHT_MEDIUM_COLOR,
+ res.getInteger(com.android.internal.R.integer.config_notificationsBatteryMediumARGB));
+ mMediumColorPref.setAllValues(mediumColor, 0, 0, false);
+ }
+
+ if (mFullColorPref != null) {
+ int fullColor = Settings.System.getInt(resolver, Settings.System.BATTERY_LIGHT_FULL_COLOR,
+ res.getInteger(com.android.internal.R.integer.config_notificationsBatteryFullARGB));
+ mFullColorPref.setAllValues(fullColor, 0, 0, false);
+ }
+ }
+
+ /**
+ * Updates the default or application specific notification settings.
+ *
+ * @param key of the specific setting to update
+ * @param color
+ */
+ protected void updateValues(String key, Integer color) {
+ ContentResolver resolver = getContentResolver();
+
+ if (key.equals(LOW_COLOR_PREF)) {
+ Settings.System.putInt(resolver, Settings.System.BATTERY_LIGHT_LOW_COLOR, color);
+ } else if (key.equals(MEDIUM_COLOR_PREF)) {
+ Settings.System.putInt(resolver, Settings.System.BATTERY_LIGHT_MEDIUM_COLOR, color);
+ } else if (key.equals(FULL_COLOR_PREF)) {
+ Settings.System.putInt(resolver, Settings.System.BATTERY_LIGHT_FULL_COLOR, color);
+ }
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ menu.add(0, MENU_RESET, 0, R.string.reset)
+ .setIcon(R.drawable.ic_settings_backup) // use the backup icon
+ .setAlphabeticShortcut('r')
+ .setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case MENU_RESET:
+ resetColors();
+ return true;
+ }
+ return false;
+ }
+
+ protected void resetColors() {
+ ContentResolver resolver = getContentResolver();
+ Resources res = getResources();
+
+ // Reset to the framework default colors
+ Settings.System.putInt(resolver, Settings.System.BATTERY_LIGHT_LOW_COLOR,
+ res.getInteger(com.android.internal.R.integer.config_notificationsBatteryLowARGB));
+ Settings.System.putInt(resolver, Settings.System.BATTERY_LIGHT_MEDIUM_COLOR,
+ res.getInteger(com.android.internal.R.integer.config_notificationsBatteryMediumARGB));
+ Settings.System.putInt(resolver, Settings.System.BATTERY_LIGHT_FULL_COLOR,
+ res.getInteger(com.android.internal.R.integer.config_notificationsBatteryFullARGB));
+ refreshDefault();
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object objValue) {
+ ApplicationLightPreference lightPref = (ApplicationLightPreference) preference;
+ updateValues(lightPref.getKey(), lightPref.getColor());
+
+ return true;
+ }
+}
diff --git a/src/org/omnirom/omnigears/notificationlight/ColorPanelView.java b/src/org/omnirom/omnigears/notificationlight/ColorPanelView.java
new file mode 100644
index 0000000..59eb56d
--- /dev/null
+++ b/src/org/omnirom/omnigears/notificationlight/ColorPanelView.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2010 Daniel Nilsson
+ * 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 org.omnirom.omnigears.notificationlight;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * This class draws a panel which which will be filled with a color which can be
+ * set. It can be used to show the currently selected color which you will get
+ * from the {@link ColorPickerView}.
+ *
+ * @author Daniel Nilsson
+ */
+public class ColorPanelView extends View {
+
+ /**
+ * The width in pixels of the border surrounding the color panel.
+ */
+ private final static float BORDER_WIDTH_PX = 1;
+
+ private static float mDensity = 1f;
+
+ private int mBorderColor = 0xff6E6E6E;
+ private int mColor = 0xff000000;
+
+ private Paint mBorderPaint;
+ private Paint mColorPaint;
+
+ private RectF mDrawingRect;
+ private RectF mColorRect;
+
+ private AlphaPatternDrawable mAlphaPattern;
+
+ public ColorPanelView(Context context) {
+ this(context, null);
+ }
+
+ public ColorPanelView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ColorPanelView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ init();
+ }
+
+ private void init() {
+ mBorderPaint = new Paint();
+ mColorPaint = new Paint();
+ mDensity = getContext().getResources().getDisplayMetrics().density;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+
+ final RectF rect = mColorRect;
+
+ if (BORDER_WIDTH_PX > 0) {
+ mBorderPaint.setColor(mBorderColor);
+ canvas.drawRect(mDrawingRect, mBorderPaint);
+ }
+
+ if (mAlphaPattern != null) {
+ mAlphaPattern.draw(canvas);
+ }
+
+ mColorPaint.setColor(mColor);
+
+ canvas.drawRect(rect, mColorPaint);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ int height = MeasureSpec.getSize(heightMeasureSpec);
+
+ setMeasuredDimension(width, height);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+
+ mDrawingRect = new RectF();
+ mDrawingRect.left = getPaddingLeft();
+ mDrawingRect.right = w - getPaddingRight();
+ mDrawingRect.top = getPaddingTop();
+ mDrawingRect.bottom = h - getPaddingBottom();
+
+ setUpColorRect();
+
+ }
+
+ private void setUpColorRect() {
+ final RectF dRect = mDrawingRect;
+
+ float left = dRect.left + BORDER_WIDTH_PX;
+ float top = dRect.top + BORDER_WIDTH_PX;
+ float bottom = dRect.bottom - BORDER_WIDTH_PX;
+ float right = dRect.right - BORDER_WIDTH_PX;
+
+ mColorRect = new RectF(left, top, right, bottom);
+
+ mAlphaPattern = new AlphaPatternDrawable((int) (5 * mDensity));
+
+ mAlphaPattern.setBounds(Math.round(mColorRect.left),
+ Math.round(mColorRect.top),
+ Math.round(mColorRect.right),
+ Math.round(mColorRect.bottom));
+
+ }
+
+ /**
+ * Set the color that should be shown by this view.
+ *
+ * @param color
+ */
+ public void setColor(int color) {
+ mColor = color;
+ invalidate();
+ }
+
+ /**
+ * Get the color currently show by this view.
+ *
+ * @return
+ */
+ public int getColor() {
+ return mColor;
+ }
+
+ /**
+ * Set the color of the border surrounding the panel.
+ *
+ * @param color
+ */
+ public void setBorderColor(int color) {
+ mBorderColor = color;
+ invalidate();
+ }
+
+ /**
+ * Get the color of the border surrounding the panel.
+ */
+ public int getBorderColor() {
+ return mBorderColor;
+ }
+
+}
diff --git a/src/org/omnirom/omnigears/notificationlight/ColorPickerView.java b/src/org/omnirom/omnigears/notificationlight/ColorPickerView.java
new file mode 100644
index 0000000..ec2e2c7
--- /dev/null
+++ b/src/org/omnirom/omnigears/notificationlight/ColorPickerView.java
@@ -0,0 +1,841 @@
+/*
+ * Copyright (C) 2010 Daniel Nilsson
+ * 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 org.omnirom.omnigears.notificationlight;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ComposeShader;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.graphics.Paint.Align;
+import android.graphics.Paint.Style;
+import android.graphics.Shader.TileMode;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+/**
+ * Displays a color picker to the user and allow them to select a color. A
+ * slider for the alpha channel is also available. Enable it by setting
+ * setAlphaSliderVisible(boolean) to true.
+ *
+ * @author Daniel Nilsson
+ */
+public class ColorPickerView extends View {
+
+ public interface OnColorChangedListener {
+ public void onColorChanged(int color);
+ }
+
+ private final static int PANEL_SAT_VAL = 0;
+ private final static int PANEL_HUE = 1;
+ private final static int PANEL_ALPHA = 2;
+
+ /**
+ * The width in pixels of the border surrounding all color panels.
+ */
+ private final static float BORDER_WIDTH_PX = 1;
+
+ /**
+ * The width in dp of the hue panel.
+ */
+ private float HUE_PANEL_WIDTH = 30f;
+ /**
+ * The height in dp of the alpha panel
+ */
+ private float ALPHA_PANEL_HEIGHT = 20f;
+ /**
+ * The distance in dp between the different color panels.
+ */
+ private float PANEL_SPACING = 10f;
+ /**
+ * The radius in dp of the color palette tracker circle.
+ */
+ private float PALETTE_CIRCLE_TRACKER_RADIUS = 5f;
+ /**
+ * The dp which the tracker of the hue or alpha panel will extend outside of
+ * its bounds.
+ */
+ private float RECTANGLE_TRACKER_OFFSET = 2f;
+
+ private static float mDensity = 1f;
+
+ private OnColorChangedListener mListener;
+
+ private Paint mSatValPaint;
+ private Paint mSatValTrackerPaint;
+
+ private Paint mHuePaint;
+ private Paint mHueTrackerPaint;
+
+ private Paint mAlphaPaint;
+ private Paint mAlphaTextPaint;
+
+ private Paint mBorderPaint;
+
+ private Shader mValShader;
+ private Shader mSatShader;
+ private Shader mHueShader;
+ private Shader mAlphaShader;
+
+ private int mAlpha = 0xff;
+ private float mHue = 360f;
+ private float mSat = 0f;
+ private float mVal = 0f;
+
+ private String mAlphaSliderText = "Alpha";
+ private int mSliderTrackerColor = 0xff1c1c1c;
+ private int mBorderColor = 0xff6E6E6E;
+ private boolean mShowAlphaPanel = false;
+
+ /*
+ * To remember which panel that has the "focus" when processing hardware
+ * button data.
+ */
+ private int mLastTouchedPanel = PANEL_SAT_VAL;
+
+ /**
+ * Offset from the edge we must have or else the finger tracker will get
+ * clipped when it is drawn outside of the view.
+ */
+ private float mDrawingOffset;
+
+ /*
+ * Distance form the edges of the view of where we are allowed to draw.
+ */
+ private RectF mDrawingRect;
+
+ private RectF mSatValRect;
+ private RectF mHueRect;
+ private RectF mAlphaRect;
+
+ private AlphaPatternDrawable mAlphaPattern;
+
+ private Point mStartTouchPoint = null;
+
+ public ColorPickerView(Context context) {
+ this(context, null);
+ }
+
+ public ColorPickerView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ColorPickerView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init();
+ }
+
+ private void init() {
+ mDensity = getContext().getResources().getDisplayMetrics().density;
+ PALETTE_CIRCLE_TRACKER_RADIUS *= mDensity;
+ RECTANGLE_TRACKER_OFFSET *= mDensity;
+ HUE_PANEL_WIDTH *= mDensity;
+ ALPHA_PANEL_HEIGHT *= mDensity;
+ PANEL_SPACING = PANEL_SPACING * mDensity;
+
+ mDrawingOffset = calculateRequiredOffset();
+ initPaintTools();
+
+ // Needed for receiving track ball motion events.
+ setFocusableInTouchMode(true);
+ setFocusable(true);
+ setClickable(true);
+ }
+
+ private void initPaintTools() {
+ mSatValPaint = new Paint();
+ mSatValTrackerPaint = new Paint();
+ mHuePaint = new Paint();
+ mHueTrackerPaint = new Paint();
+ mAlphaPaint = new Paint();
+ mAlphaTextPaint = new Paint();
+ mBorderPaint = new Paint();
+
+ mSatValTrackerPaint.setStyle(Style.STROKE);
+ mSatValTrackerPaint.setStrokeWidth(2f * mDensity);
+ mSatValTrackerPaint.setAntiAlias(true);
+
+ mHueTrackerPaint.setColor(mSliderTrackerColor);
+ mHueTrackerPaint.setStyle(Style.STROKE);
+ mHueTrackerPaint.setStrokeWidth(2f * mDensity);
+ mHueTrackerPaint.setAntiAlias(true);
+
+ mAlphaTextPaint.setColor(0xff1c1c1c);
+ mAlphaTextPaint.setTextSize(14f * mDensity);
+ mAlphaTextPaint.setAntiAlias(true);
+ mAlphaTextPaint.setTextAlign(Align.CENTER);
+ mAlphaTextPaint.setFakeBoldText(true);
+ }
+
+ private float calculateRequiredOffset() {
+ float offset = Math.max(PALETTE_CIRCLE_TRACKER_RADIUS, RECTANGLE_TRACKER_OFFSET);
+ offset = Math.max(offset, BORDER_WIDTH_PX * mDensity);
+
+ return offset * 1.5f;
+ }
+
+ private int[] buildHueColorArray() {
+ int[] hue = new int[361];
+
+ int count = 0;
+ for (int i = hue.length - 1; i >= 0; i--, count++) {
+ hue[count] = Color.HSVToColor(new float[] {
+ i, 1f, 1f
+ });
+ }
+ return hue;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mDrawingRect.width() <= 0 || mDrawingRect.height() <= 0) {
+ return;
+ }
+ drawSatValPanel(canvas);
+ drawHuePanel(canvas);
+ drawAlphaPanel(canvas);
+ }
+
+ private void drawSatValPanel(Canvas canvas) {
+ final RectF rect = mSatValRect;
+ int rgb = Color.HSVToColor(new float[] {
+ mHue, 1f, 1f
+ });
+
+ if (BORDER_WIDTH_PX > 0) {
+ mBorderPaint.setColor(mBorderColor);
+ canvas.drawRect(mDrawingRect.left, mDrawingRect.top, rect.right + BORDER_WIDTH_PX,
+ rect.bottom + BORDER_WIDTH_PX, mBorderPaint);
+ }
+
+ // On Honeycomb+ we need to use software rendering to create the shader properly
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ }
+
+ // Get the overlaying gradients ready and create the ComposeShader
+ if (mValShader == null) {
+ mValShader = new LinearGradient(rect.left, rect.top, rect.left, rect.bottom,
+ 0xffffffff, 0xff000000, TileMode.CLAMP);
+ }
+ mSatShader = new LinearGradient(rect.left, rect.top, rect.right, rect.top,
+ 0xffffffff, rgb, TileMode.CLAMP);
+ ComposeShader mShader = new ComposeShader(mValShader, mSatShader, Mode.MULTIPLY);
+ mSatValPaint.setShader(mShader);
+ canvas.drawRect(rect, mSatValPaint);
+
+ Point p = satValToPoint(mSat, mVal);
+ mSatValTrackerPaint.setColor(0xff000000);
+ canvas.drawCircle(p.x, p.y, PALETTE_CIRCLE_TRACKER_RADIUS - 1f * mDensity,
+ mSatValTrackerPaint);
+
+ mSatValTrackerPaint.setColor(0xffdddddd);
+ canvas.drawCircle(p.x, p.y, PALETTE_CIRCLE_TRACKER_RADIUS, mSatValTrackerPaint);
+ }
+
+ private void drawHuePanel(Canvas canvas) {
+ final RectF rect = mHueRect;
+
+ if (BORDER_WIDTH_PX > 0) {
+ mBorderPaint.setColor(mBorderColor);
+ canvas.drawRect(rect.left - BORDER_WIDTH_PX,
+ rect.top - BORDER_WIDTH_PX,
+ rect.right + BORDER_WIDTH_PX,
+ rect.bottom + BORDER_WIDTH_PX,
+ mBorderPaint);
+ }
+
+ if (mHueShader == null) {
+ mHueShader = new LinearGradient(rect.left, rect.top, rect.left, rect.bottom,
+ buildHueColorArray(), null, TileMode.CLAMP);
+ mHuePaint.setShader(mHueShader);
+ }
+
+ canvas.drawRect(rect, mHuePaint);
+
+ float rectHeight = 4 * mDensity / 2;
+
+ Point p = hueToPoint(mHue);
+
+ RectF r = new RectF();
+ r.left = rect.left - RECTANGLE_TRACKER_OFFSET;
+ r.right = rect.right + RECTANGLE_TRACKER_OFFSET;
+ r.top = p.y - rectHeight;
+ r.bottom = p.y + rectHeight;
+
+ canvas.drawRoundRect(r, 2, 2, mHueTrackerPaint);
+
+ }
+
+ private void drawAlphaPanel(Canvas canvas) {
+ if (!mShowAlphaPanel || mAlphaRect == null || mAlphaPattern == null) {
+ return;
+ }
+
+ final RectF rect = mAlphaRect;
+
+ if (BORDER_WIDTH_PX > 0) {
+ mBorderPaint.setColor(mBorderColor);
+ canvas.drawRect(rect.left - BORDER_WIDTH_PX,
+ rect.top - BORDER_WIDTH_PX,
+ rect.right + BORDER_WIDTH_PX,
+ rect.bottom + BORDER_WIDTH_PX,
+ mBorderPaint);
+ }
+
+ mAlphaPattern.draw(canvas);
+
+ float[] hsv = new float[] {
+ mHue, mSat, mVal
+ };
+ int color = Color.HSVToColor(hsv);
+ int acolor = Color.HSVToColor(0, hsv);
+
+ mAlphaShader = new LinearGradient(rect.left, rect.top, rect.right, rect.top,
+ color, acolor, TileMode.CLAMP);
+
+ mAlphaPaint.setShader(mAlphaShader);
+
+ canvas.drawRect(rect, mAlphaPaint);
+
+ if (mAlphaSliderText != null && mAlphaSliderText != "") {
+ canvas.drawText(mAlphaSliderText, rect.centerX(), rect.centerY() + 4 * mDensity,
+ mAlphaTextPaint);
+ }
+
+ float rectWidth = 4 * mDensity / 2;
+ Point p = alphaToPoint(mAlpha);
+
+ RectF r = new RectF();
+ r.left = p.x - rectWidth;
+ r.right = p.x + rectWidth;
+ r.top = rect.top - RECTANGLE_TRACKER_OFFSET;
+ r.bottom = rect.bottom + RECTANGLE_TRACKER_OFFSET;
+
+ canvas.drawRoundRect(r, 2, 2, mHueTrackerPaint);
+ }
+
+ private Point hueToPoint(float hue) {
+ final RectF rect = mHueRect;
+ final float height = rect.height();
+
+ Point p = new Point();
+ p.y = (int) (height - (hue * height / 360f) + rect.top);
+ p.x = (int) rect.left;
+ return p;
+ }
+
+ private Point satValToPoint(float sat, float val) {
+
+ final RectF rect = mSatValRect;
+ final float height = rect.height();
+ final float width = rect.width();
+
+ Point p = new Point();
+
+ p.x = (int) (sat * width + rect.left);
+ p.y = (int) ((1f - val) * height + rect.top);
+
+ return p;
+ }
+
+ private Point alphaToPoint(int alpha) {
+ final RectF rect = mAlphaRect;
+ final float width = rect.width();
+
+ Point p = new Point();
+ p.x = (int) (width - (alpha * width / 0xff) + rect.left);
+ p.y = (int) rect.top;
+ return p;
+ }
+
+ private float[] pointToSatVal(float x, float y) {
+ final RectF rect = mSatValRect;
+ float[] result = new float[2];
+ float width = rect.width();
+ float height = rect.height();
+
+ if (x < rect.left) {
+ x = 0f;
+ } else if (x > rect.right) {
+ x = width;
+ } else {
+ x = x - rect.left;
+ }
+
+ if (y < rect.top) {
+ y = 0f;
+ } else if (y > rect.bottom) {
+ y = height;
+ } else {
+ y = y - rect.top;
+ }
+
+ result[0] = 1.f / width * x;
+ result[1] = 1.f - (1.f / height * y);
+ return result;
+ }
+
+ private float pointToHue(float y) {
+ final RectF rect = mHueRect;
+ float height = rect.height();
+
+ if (y < rect.top) {
+ y = 0f;
+ } else if (y > rect.bottom) {
+ y = height;
+ } else {
+ y = y - rect.top;
+ }
+ return 360f - (y * 360f / height);
+ }
+
+ private int pointToAlpha(int x) {
+ final RectF rect = mAlphaRect;
+ final int width = (int) rect.width();
+
+ if (x < rect.left) {
+ x = 0;
+ } else if (x > rect.right) {
+ x = width;
+ } else {
+ x = x - (int) rect.left;
+ }
+ return 0xff - (x * 0xff / width);
+ }
+
+ @Override
+ public boolean onTrackballEvent(MotionEvent event) {
+ float x = event.getX();
+ float y = event.getY();
+ boolean update = false;
+
+ if (event.getAction() == MotionEvent.ACTION_MOVE) {
+ switch (mLastTouchedPanel) {
+ case PANEL_SAT_VAL:
+ float sat,
+ val;
+ sat = mSat + x / 50f;
+ val = mVal - y / 50f;
+ if (sat < 0f) {
+ sat = 0f;
+ } else if (sat > 1f) {
+ sat = 1f;
+ }
+
+ if (val < 0f) {
+ val = 0f;
+ } else if (val > 1f) {
+ val = 1f;
+ }
+ mSat = sat;
+ mVal = val;
+ update = true;
+ break;
+ case PANEL_HUE:
+ float hue = mHue - y * 10f;
+ if (hue < 0f) {
+ hue = 0f;
+ } else if (hue > 360f) {
+ hue = 360f;
+ }
+ mHue = hue;
+ update = true;
+ break;
+ case PANEL_ALPHA:
+ if (!mShowAlphaPanel || mAlphaRect == null) {
+ update = false;
+ } else {
+ int alpha = (int) (mAlpha - x * 10);
+ if (alpha < 0) {
+ alpha = 0;
+ } else if (alpha > 0xff) {
+ alpha = 0xff;
+ }
+ mAlpha = alpha;
+ update = true;
+ }
+ break;
+ }
+ }
+
+ if (update) {
+ if (mListener != null) {
+ mListener.onColorChanged(Color.HSVToColor(mAlpha, new float[] {
+ mHue, mSat, mVal
+ }));
+ }
+ invalidate();
+ return true;
+ }
+ return super.onTrackballEvent(event);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ boolean update = false;
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mStartTouchPoint = new Point((int) event.getX(), (int) event.getY());
+ update = moveTrackersIfNeeded(event);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ update = moveTrackersIfNeeded(event);
+ break;
+ case MotionEvent.ACTION_UP:
+ mStartTouchPoint = null;
+ update = moveTrackersIfNeeded(event);
+ break;
+ }
+
+ if (update) {
+ requestFocus();
+ if (mListener != null) {
+ mListener.onColorChanged(Color.HSVToColor(mAlpha, new float[] {
+ mHue, mSat, mVal
+ }));
+ }
+ invalidate();
+ return true;
+ }
+
+ return super.onTouchEvent(event);
+ }
+
+ private boolean moveTrackersIfNeeded(MotionEvent event) {
+
+ if (mStartTouchPoint == null)
+ return false;
+
+ boolean update = false;
+ int startX = mStartTouchPoint.x;
+ int startY = mStartTouchPoint.y;
+
+ if (mHueRect.contains(startX, startY)) {
+ mLastTouchedPanel = PANEL_HUE;
+ mHue = pointToHue(event.getY());
+ update = true;
+ } else if (mSatValRect.contains(startX, startY)) {
+ mLastTouchedPanel = PANEL_SAT_VAL;
+ float[] result = pointToSatVal(event.getX(), event.getY());
+ mSat = result[0];
+ mVal = result[1];
+ update = true;
+ } else if (mAlphaRect != null && mAlphaRect.contains(startX, startY)) {
+ mLastTouchedPanel = PANEL_ALPHA;
+ mAlpha = pointToAlpha((int) event.getX());
+ update = true;
+ }
+
+ return update;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int width = 0;
+ int height = 0;
+
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+
+ int widthAllowed = MeasureSpec.getSize(widthMeasureSpec);
+ int heightAllowed = MeasureSpec.getSize(heightMeasureSpec);
+
+ widthAllowed = chooseWidth(widthMode, widthAllowed);
+ heightAllowed = chooseHeight(heightMode, heightAllowed);
+
+ if (!mShowAlphaPanel) {
+ height = (int) (widthAllowed - PANEL_SPACING - HUE_PANEL_WIDTH);
+
+ // If calculated height (based on the width) is more than the
+ // allowed height.
+ if (height > heightAllowed && heightMode != MeasureSpec.UNSPECIFIED) {
+ height = heightAllowed;
+ width = (int) (height + PANEL_SPACING + HUE_PANEL_WIDTH);
+ } else {
+ width = widthAllowed;
+ }
+ } else {
+
+ width = (int) (heightAllowed - ALPHA_PANEL_HEIGHT + HUE_PANEL_WIDTH);
+
+ if (width > widthAllowed && widthMode != MeasureSpec.UNSPECIFIED) {
+ width = widthAllowed;
+ height = (int) (widthAllowed - HUE_PANEL_WIDTH + ALPHA_PANEL_HEIGHT);
+ } else {
+ height = heightAllowed;
+ }
+ }
+ setMeasuredDimension(width, height);
+ }
+
+ private int chooseWidth(int mode, int size) {
+ if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) {
+ return size;
+ } else { // (mode == MeasureSpec.UNSPECIFIED)
+ return getPrefferedWidth();
+ }
+ }
+
+ private int chooseHeight(int mode, int size) {
+ if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) {
+ return size;
+ } else { // (mode == MeasureSpec.UNSPECIFIED)
+ return getPrefferedHeight();
+ }
+ }
+
+ private int getPrefferedWidth() {
+ int width = getPrefferedHeight();
+ if (mShowAlphaPanel) {
+ width -= (PANEL_SPACING + ALPHA_PANEL_HEIGHT);
+ }
+ return (int) (width + HUE_PANEL_WIDTH + PANEL_SPACING);
+ }
+
+ private int getPrefferedHeight() {
+ int height = (int) (200 * mDensity);
+ if (mShowAlphaPanel) {
+ height += PANEL_SPACING + ALPHA_PANEL_HEIGHT;
+ }
+ return height;
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+
+ mDrawingRect = new RectF();
+ mDrawingRect.left = mDrawingOffset + getPaddingLeft();
+ mDrawingRect.right = w - mDrawingOffset - getPaddingRight();
+ mDrawingRect.top = mDrawingOffset + getPaddingTop();
+ mDrawingRect.bottom = h - mDrawingOffset - getPaddingBottom();
+
+ setUpSatValRect();
+ setUpHueRect();
+ setUpAlphaRect();
+ }
+
+ private void setUpSatValRect() {
+ final RectF dRect = mDrawingRect;
+ float panelSide = dRect.height() - BORDER_WIDTH_PX * 2;
+
+ if (mShowAlphaPanel) {
+ panelSide -= PANEL_SPACING + ALPHA_PANEL_HEIGHT;
+ }
+
+ float left = dRect.left + BORDER_WIDTH_PX;
+ float top = dRect.top + BORDER_WIDTH_PX;
+ float bottom = top + panelSide;
+ float right = left + panelSide;
+ mSatValRect = new RectF(left, top, right, bottom);
+ }
+
+ private void setUpHueRect() {
+ final RectF dRect = mDrawingRect;
+
+ float left = dRect.right - HUE_PANEL_WIDTH + BORDER_WIDTH_PX;
+ float top = dRect.top + BORDER_WIDTH_PX;
+ float bottom = dRect.bottom - BORDER_WIDTH_PX
+ - (mShowAlphaPanel ? (PANEL_SPACING + ALPHA_PANEL_HEIGHT) : 0);
+ float right = dRect.right - BORDER_WIDTH_PX;
+
+ mHueRect = new RectF(left, top, right, bottom);
+ }
+
+ private void setUpAlphaRect() {
+ if (!mShowAlphaPanel) {
+ return;
+ }
+
+ final RectF dRect = mDrawingRect;
+ float left = dRect.left + BORDER_WIDTH_PX;
+ float top = dRect.bottom - ALPHA_PANEL_HEIGHT + BORDER_WIDTH_PX;
+ float bottom = dRect.bottom - BORDER_WIDTH_PX;
+ float right = dRect.right - BORDER_WIDTH_PX;
+
+ mAlphaRect = new RectF(left, top, right, bottom);
+ mAlphaPattern = new AlphaPatternDrawable((int) (5 * mDensity));
+ mAlphaPattern.setBounds(Math.round(mAlphaRect.left), Math
+ .round(mAlphaRect.top), Math.round(mAlphaRect.right), Math
+ .round(mAlphaRect.bottom));
+ }
+
+ /**
+ * Set a OnColorChangedListener to get notified when the color selected by
+ * the user has changed.
+ *
+ * @param listener
+ */
+ public void setOnColorChangedListener(OnColorChangedListener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Set the color of the border surrounding all panels.
+ *
+ * @param color
+ */
+ public void setBorderColor(int color) {
+ mBorderColor = color;
+ invalidate();
+ }
+
+ /**
+ * Get the color of the border surrounding all panels.
+ */
+ public int getBorderColor() {
+ return mBorderColor;
+ }
+
+ /**
+ * Get the current color this view is showing.
+ *
+ * @return the current color.
+ */
+ public int getColor() {
+ return Color.HSVToColor(mAlpha, new float[] {
+ mHue, mSat, mVal
+ });
+ }
+
+ /**
+ * Set the color the view should show.
+ *
+ * @param color The color that should be selected.
+ */
+ public void setColor(int color) {
+ setColor(color, false);
+ }
+
+ /**
+ * Set the color this view should show.
+ *
+ * @param color The color that should be selected.
+ * @param callback If you want to get a callback to your
+ * OnColorChangedListener.
+ */
+ public void setColor(int color, boolean callback) {
+ int alpha = Color.alpha(color);
+ int red = Color.red(color);
+ int blue = Color.blue(color);
+ int green = Color.green(color);
+ float[] hsv = new float[3];
+
+ Color.RGBToHSV(red, green, blue, hsv);
+ mAlpha = alpha;
+ mHue = hsv[0];
+ mSat = hsv[1];
+ mVal = hsv[2];
+
+ if (callback && mListener != null) {
+ mListener.onColorChanged(Color.HSVToColor(mAlpha, new float[] {
+ mHue, mSat, mVal
+ }));
+ }
+ invalidate();
+ }
+
+ /**
+ * Get the drawing offset of the color picker view. The drawing offset is
+ * the distance from the side of a panel to the side of the view minus the
+ * padding. Useful if you want to have your own panel below showing the
+ * currently selected color and want to align it perfectly.
+ *
+ * @return The offset in pixels.
+ */
+ public float getDrawingOffset() {
+ return mDrawingOffset;
+ }
+
+ /**
+ * Set if the user is allowed to adjust the alpha panel. Default is false.
+ * If it is set to false no alpha will be set.
+ *
+ * @param visible
+ */
+ public void setAlphaSliderVisible(boolean visible) {
+ if (mShowAlphaPanel != visible) {
+ mShowAlphaPanel = visible;
+
+ /*
+ * Reset all shader to force a recreation. Otherwise they will not
+ * look right after the size of the view has changed.
+ */
+ mValShader = null;
+ mSatShader = null;
+ mHueShader = null;
+ mAlphaShader = null;
+ requestLayout();
+ }
+
+ }
+
+ public boolean isAlphaSliderVisible() {
+ return mShowAlphaPanel;
+ }
+
+ public void setSliderTrackerColor(int color) {
+ mSliderTrackerColor = color;
+ mHueTrackerPaint.setColor(mSliderTrackerColor);
+ invalidate();
+ }
+
+ public int getSliderTrackerColor() {
+ return mSliderTrackerColor;
+ }
+
+ /**
+ * Set the text that should be shown in the alpha slider. Set to null to
+ * disable text.
+ *
+ * @param res string resource id.
+ */
+ public void setAlphaSliderText(int res) {
+ String text = getContext().getString(res);
+ setAlphaSliderText(text);
+ }
+
+ /**
+ * Set the text that should be shown in the alpha slider. Set to null to
+ * disable text.
+ *
+ * @param text Text that should be shown.
+ */
+ public void setAlphaSliderText(String text) {
+ mAlphaSliderText = text;
+ invalidate();
+ }
+
+ /**
+ * Get the current value of the text that will be shown in the alpha slider.
+ *
+ * @return
+ */
+ public String getAlphaSliderText() {
+ return mAlphaSliderText;
+ }
+}
diff --git a/src/org/omnirom/omnigears/notificationlight/LightSettingsDialog.java b/src/org/omnirom/omnigears/notificationlight/LightSettingsDialog.java
new file mode 100644
index 0000000..bda612b
--- /dev/null
+++ b/src/org/omnirom/omnigears/notificationlight/LightSettingsDialog.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2010 Daniel Nilsson
+ * 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 org.omnirom.omnigears.notificationlight;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.text.InputFilter;
+import android.text.InputFilter.LengthFilter;
+import android.util.Pair;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.Spinner;
+import android.widget.SpinnerAdapter;
+import android.widget.TextView;
+
+import com.android.settings.R;
+import org.omnirom.omnigears.notificationlight.ColorPickerView.OnColorChangedListener;
+
+import java.util.ArrayList;
+import java.util.IllegalFormatException;
+import java.util.Locale;
+
+public class LightSettingsDialog extends AlertDialog implements
+ ColorPickerView.OnColorChangedListener, TextWatcher, OnFocusChangeListener {
+
+ private final static String STATE_KEY_COLOR = "LightSettingsDialog:color";
+
+ private ColorPickerView mColorPicker;
+
+ private EditText mHexColorInput;
+ private ColorPanelView mNewColor;
+ private Spinner mPulseSpeedOn;
+ private Spinner mPulseSpeedOff;
+ private LayoutInflater mInflater;
+
+ private OnColorChangedListener mListener;
+
+ /**
+ * @param context
+ * @param initialColor
+ * @param initialSpeedOn
+ * @param initialSpeedOff
+ */
+ protected LightSettingsDialog(Context context, int initialColor, int initialSpeedOn,
+ int initialSpeedOff) {
+ super(context);
+
+ init(initialColor, initialSpeedOn, initialSpeedOff, true);
+ }
+
+ /**
+ * @param context
+ * @param initialColor
+ * @param initialSpeedOn
+ * @param initialSpeedOff
+ * @param onOffChangeable
+ */
+ protected LightSettingsDialog(Context context, int initialColor, int initialSpeedOn,
+ int initialSpeedOff, boolean onOffChangeable) {
+ super(context);
+
+ init(initialColor, initialSpeedOn, initialSpeedOff, onOffChangeable);
+ }
+
+ private void init(int color, int speedOn, int speedOff, boolean onOffChangeable) {
+ // To fight color banding.
+ getWindow().setFormat(PixelFormat.RGBA_8888);
+ setUp(color, speedOn, speedOff, onOffChangeable);
+ }
+
+ /**
+ * This function sets up the dialog with the proper values. If the speedOff parameters
+ * has a -1 value disable both spinners
+ *
+ * @param color - the color to set
+ * @param speedOn - the flash time in ms
+ * @param speedOff - the flash length in ms
+ */
+ private void setUp(int color, int speedOn, int speedOff, boolean onOffChangeable) {
+ mInflater = (LayoutInflater) getContext()
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View layout = mInflater.inflate(R.layout.dialog_light_settings, null);
+
+ mColorPicker = (ColorPickerView) layout.findViewById(R.id.color_picker_view);
+ mHexColorInput = (EditText) layout.findViewById(R.id.hex_color_input);
+ mNewColor = (ColorPanelView) layout.findViewById(R.id.color_panel);
+
+ mColorPicker.setOnColorChangedListener(this);
+ mColorPicker.setColor(color, true);
+
+ mHexColorInput.setOnFocusChangeListener(this);
+ mPulseSpeedOn = (Spinner) layout.findViewById(R.id.on_spinner);
+ PulseSpeedAdapter pulseSpeedAdapter = new PulseSpeedAdapter(
+ R.array.notification_pulse_length_entries,
+ R.array.notification_pulse_length_values,
+ speedOn);
+ mPulseSpeedOn.setAdapter(pulseSpeedAdapter);
+ mPulseSpeedOn.setSelection(pulseSpeedAdapter.getTimePosition(speedOn));
+ mPulseSpeedOn.setOnItemSelectedListener(mSelectionListener);
+
+ mPulseSpeedOff = (Spinner) layout.findViewById(R.id.off_spinner);
+ pulseSpeedAdapter = new PulseSpeedAdapter(R.array.notification_pulse_speed_entries,
+ R.array.notification_pulse_speed_values,
+ speedOff);
+ mPulseSpeedOff.setAdapter(pulseSpeedAdapter);
+ mPulseSpeedOff.setSelection(pulseSpeedAdapter.getTimePosition(speedOff));
+
+ mPulseSpeedOn.setEnabled(onOffChangeable);
+ mPulseSpeedOff.setEnabled((speedOn != 1) && onOffChangeable);
+
+ setView(layout);
+ setTitle(R.string.edit_light_settings);
+ }
+
+ private AdapterView.OnItemSelectedListener mSelectionListener = new AdapterView.OnItemSelectedListener() {
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ mPulseSpeedOff.setEnabled(getPulseSpeedOn() != 1);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ };
+
+ @Override
+ public Bundle onSaveInstanceState() {
+ Bundle state = super.onSaveInstanceState();
+ state.putInt(STATE_KEY_COLOR, getColor());
+ return state;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Bundle state) {
+ super.onRestoreInstanceState(state);
+ mColorPicker.setColor(state.getInt(STATE_KEY_COLOR), true);
+ }
+
+ @Override
+ public void onColorChanged(int color) {
+ final boolean hasAlpha = mColorPicker.isAlphaSliderVisible();
+ final String format = hasAlpha ? "%08x" : "%06x";
+ final int mask = hasAlpha ? 0xFFFFFFFF : 0x00FFFFFF;
+
+ mNewColor.setColor(color);
+ mHexColorInput.setText(String.format(Locale.US, format, color & mask));
+
+ if (mListener != null) {
+ mListener.onColorChanged(color);
+ }
+ }
+
+ public void setAlphaSliderVisible(boolean visible) {
+ mHexColorInput.setFilters(new InputFilter[] { new InputFilter.LengthFilter(visible ? 8 : 6) } );
+ mColorPicker.setAlphaSliderVisible(visible);
+ }
+
+ public int getColor() {
+ return mColorPicker.getColor();
+ }
+
+ @SuppressWarnings("unchecked")
+ public int getPulseSpeedOn() {
+ return ((Pair<String, Integer>) mPulseSpeedOn.getSelectedItem()).second;
+ }
+
+ @SuppressWarnings("unchecked")
+ public int getPulseSpeedOff() {
+ // return 0 if 'Always on' is selected
+ return getPulseSpeedOn() == 1 ? 0 : ((Pair<String, Integer>) mPulseSpeedOff.getSelectedItem()).second;
+ }
+
+ class PulseSpeedAdapter extends BaseAdapter implements SpinnerAdapter {
+ private ArrayList<Pair<String, Integer>> times;
+
+ public PulseSpeedAdapter(int timeNamesResource, int timeValuesResource) {
+ times = new ArrayList<Pair<String, Integer>>();
+
+ String[] time_names = getContext().getResources().getStringArray(timeNamesResource);
+ String[] time_values = getContext().getResources().getStringArray(timeValuesResource);
+
+ for(int i = 0; i < time_values.length; ++i) {
+ times.add(new Pair<String, Integer>(time_names[i], Integer.decode(time_values[i])));
+ }
+
+ }
+
+ /**
+ * This constructor apart from taking a usual time entry array takes the
+ * currently configured time value which might cause the addition of a
+ * "Custom" time entry in the spinner in case this time value does not
+ * match any of the predefined ones in the array.
+ *
+ * @param timeNamesResource The time entry names array
+ * @param timeValuesResource The time entry values array
+ * @param customTime Current time value that might be one of the
+ * predefined values or a totally custom value
+ */
+ public PulseSpeedAdapter(int timeNamesResource, int timeValuesResource, Integer customTime) {
+ this(timeNamesResource, timeValuesResource);
+
+ // Check if we also need to add the custom value entry
+ if (getTimePosition(customTime) == -1) {
+ times.add(new Pair<String, Integer>(getContext().getResources()
+ .getString(R.string.custom_time), customTime));
+ }
+ }
+
+ /**
+ * Will return the position of the spinner entry with the specified
+ * time. Returns -1 if there is no such entry.
+ *
+ * @param time Time in ms
+ * @return Position of entry with given time or -1 if not found.
+ */
+ public int getTimePosition(Integer time) {
+ for (int position = 0; position < getCount(); ++position) {
+ if (getItem(position).second.equals(time)) {
+ return position;
+ }
+ }
+
+ return -1;
+ }
+
+ @Override
+ public int getCount() {
+ return times.size();
+ }
+
+ @Override
+ public Pair<String, Integer> getItem(int position) {
+ return times.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View view, ViewGroup parent) {
+ if (view == null) {
+ view = mInflater.inflate(R.layout.pulse_time_item, null);
+ }
+
+ Pair<String, Integer> entry = getItem(position);
+ ((TextView) view.findViewById(R.id.textViewName)).setText(entry.first);
+
+ return view;
+ }
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ String hexColor = mHexColorInput.getText().toString();
+ if (!hexColor.isEmpty()) {
+ try {
+ int color = Color.parseColor('#' + hexColor);
+ if (!mColorPicker.isAlphaSliderVisible()) {
+ color |= 0xFF000000; // set opaque
+ }
+ mColorPicker.setColor(color);
+ mNewColor.setColor(color);
+ if (mListener != null) {
+ mListener.onColorChanged(color);
+ }
+ } catch (IllegalArgumentException ex) {
+ // Number format is incorrect, ignore
+ }
+ }
+ }
+
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (!hasFocus) {
+ mHexColorInput.removeTextChangedListener(this);
+ InputMethodManager inputMethodManager = (InputMethodManager) getContext()
+ .getSystemService(Activity.INPUT_METHOD_SERVICE);
+ inputMethodManager.hideSoftInputFromWindow(v.getWindowToken(), 0);
+ } else {
+ mHexColorInput.addTextChangedListener(this);
+ }
+ }
+}
diff --git a/src/org/omnirom/omnigears/notificationlight/NotificationLightSettings.java b/src/org/omnirom/omnigears/notificationlight/NotificationLightSettings.java
new file mode 100644
index 0000000..0cbeacd
--- /dev/null
+++ b/src/org/omnirom/omnigears/notificationlight/NotificationLightSettings.java
@@ -0,0 +1,610 @@
+/*
+ * 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 org.omnirom.omnigears.notificationlight;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+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.util.Log;
+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 com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+
+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.TreeSet;
+
+public class NotificationLightSettings extends SettingsPreferenceFragment implements
+ Preference.OnPreferenceChangeListener, AdapterView.OnItemLongClickListener {
+ 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 = "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 PackageManager mPackageManager;
+ private PreferenceGroup mApplicationPrefList;
+ private CheckBoxPreference mEnabledPref;
+ private CheckBoxPreference mCustomEnabledPref;
+ private ApplicationLightPreference mDefaultPref;
+ private ApplicationLightPreference mCallPref;
+ private ApplicationLightPreference mVoicemailPref;
+ private Menu mMenu;
+ private PackageAdapter mPackageAdapter;
+ private String mPackageList;
+ private Map<String, Package> mPackages;
+
+ @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);
+
+ mEnabledPref = (CheckBoxPreference)
+ findPreference(Settings.System.NOTIFICATION_LIGHT_PULSE);
+ mEnabledPref.setOnPreferenceChangeListener(this);
+ mCustomEnabledPref = (CheckBoxPreference)
+ findPreference(Settings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_ENABLE);
+ mCustomEnabledPref.setOnPreferenceChangeListener(this);
+
+ mDefaultPref = (ApplicationLightPreference) findPreference(DEFAULT_PREF);
+ mDefaultPref.setOnPreferenceChangeListener(this);
+
+ // Missed call and Voicemail preferences should only show on devices with a voice capabilities
+ TelephonyManager tm = (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);
+ if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_NONE) {
+ removePreference("phone_list");
+ } else {
+ mCallPref = (ApplicationLightPreference) findPreference(MISSED_CALL_PREF);
+ mCallPref.setOnPreferenceChangeListener(this);
+
+ mVoicemailPref = (ApplicationLightPreference) findPreference(VOICEMAIL_PREF);
+ mVoicemailPref.setOnPreferenceChangeListener(this);
+ }
+
+ mApplicationPrefList = (PreferenceGroup) findPreference("applications_list");
+ mApplicationPrefList.setOrderingAsAdded(false);
+
+ // Get launch-able applications
+ mPackageManager = getPackageManager();
+ mPackageAdapter = new PackageAdapter();
+
+ mPackages = new HashMap<String, Package>();
+
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ refreshDefault();
+ refreshCustomApplicationPrefs();
+ getListView().setOnItemLongClickListener(this);
+ getActivity().invalidateOptionsMenu();
+ }
+
+ 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);
+
+ mDefaultPref.setAllValues(color, timeOn, timeOff);
+
+ // Get Missed call and Voicemail values
+ if (mCallPref != null) {
+ 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);
+
+ mCallPref.setAllValues(callColor, callTimeOn, callTimeOff);
+ }
+
+ if (mVoicemailPref != null) {
+ 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);
+
+ mVoicemailPref.setAllValues(vmailColor, vmailTimeOn, vmailTimeOff);
+ }
+
+ mApplicationPrefList = (PreferenceGroup) findPreference("applications_list");
+ mApplicationPrefList.setOrderingAsAdded(false);
+ }
+
+ private void refreshCustomApplicationPrefs() {
+ Context context = getActivity();
+
+ if (!parsePackageList()) {
+ return;
+ }
+
+ // Add the Application Preferences
+ if (mApplicationPrefList != null) {
+ mApplicationPrefList.removeAll();
+
+ for (Package pkg : mPackages.values()) {
+ try {
+ PackageInfo info = mPackageManager.getPackageInfo(pkg.name,
+ PackageManager.GET_META_DATA);
+ ApplicationLightPreference pref =
+ new ApplicationLightPreference(context, pkg.color, pkg.timeon, pkg.timeoff);
+
+ pref.setKey(pkg.name);
+ pref.setTitle(info.applicationInfo.loadLabel(mPackageManager));
+ pref.setIcon(info.applicationInfo.loadIcon(mPackageManager));
+ pref.setPersistent(false);
+ pref.setOnPreferenceChangeListener(this);
+
+ mApplicationPrefList.addPreference(pref);
+ } catch (NameNotFoundException e) {
+ // Do nothing
+ }
+ }
+ }
+ }
+
+ private void addCustomApplicationPref(String packageName) {
+ Package pkg = mPackages.get(packageName);
+ if (pkg == null) {
+ pkg = new Package(packageName, mDefaultColor, mDefaultLedOn, mDefaultLedOff);
+ mPackages.put(packageName, pkg);
+ savePackageList(false);
+ refreshCustomApplicationPrefs();
+ }
+ }
+
+ private void removeCustomApplicationPref(String packageName) {
+ if (mPackages.remove(packageName) != null) {
+ savePackageList(false);
+ refreshCustomApplicationPrefs();
+ }
+ }
+
+ private boolean parsePackageList() {
+ final String baseString = Settings.System.getString(getContentResolver(),
+ Settings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_VALUES);
+
+ if (TextUtils.equals(mPackageList, baseString)) {
+ return false;
+ }
+
+ mPackageList = baseString;
+ mPackages.clear();
+
+ if (baseString != null) {
+ final String[] array = TextUtils.split(baseString, "\\|");
+ for (String item : array) {
+ if (TextUtils.isEmpty(item)) {
+ continue;
+ }
+ Package pkg = Package.fromString(item);
+ if (pkg != null) {
+ mPackages.put(pkg.name, pkg);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private void savePackageList(boolean preferencesUpdated) {
+ List<String> settings = new ArrayList<String>();
+ for (Package app : mPackages.values()) {
+ settings.add(app.toString());
+ }
+ final String value = TextUtils.join("|", settings);
+ if (preferencesUpdated) {
+ mPackageList = value;
+ }
+ Settings.System.putString(getContentResolver(),
+ Settings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_VALUES, value);
+ }
+
+ /**
+ * Updates the default or package specific notification settings.
+ *
+ * @param packageName Package name of application specific settings to update
+ * @param color
+ * @param timeon
+ * @param timeoff
+ */
+ protected void updateValues(String packageName, Integer color, Integer timeon, Integer timeoff) {
+ ContentResolver resolver = getContentResolver();
+
+ if (packageName.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 (packageName.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 (packageName.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 package and sets its new values
+ Package app = mPackages.get(packageName);
+ if (app != null) {
+ app.color = color;
+ app.timeon = timeon;
+ app.timeoff = timeoff;
+ savePackageList(true);
+ }
+ }
+
+ public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
+ final Preference pref = (Preference) getPreferenceScreen().getRootAdapter().getItem(position);
+
+ if (mApplicationPrefList.findPreference(pref.getKey()) != pref) {
+ return false;
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.dialog_delete_title)
+ .setMessage(R.string.dialog_delete_message)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ removeCustomApplicationPref(pref.getKey());
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, null);
+
+ builder.show();
+ return true;
+ }
+
+ public boolean onPreferenceChange(Preference preference, Object objValue) {
+ if (preference == mEnabledPref || preference == mCustomEnabledPref) {
+ getActivity().invalidateOptionsMenu();
+ } else {
+ ApplicationLightPreference lightPref = (ApplicationLightPreference) preference;
+ updateValues(lightPref.getKey(), lightPref.getColor(),
+ lightPref.getOnValue(), lightPref.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 void onPrepareOptionsMenu(Menu menu) {
+ boolean enableAddButton = mEnabledPref.isChecked() && mCustomEnabledPref.isChecked();
+ menu.findItem(MENU_ADD).setVisible(enableAddButton);
+ }
+
+ @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(mPackageAdapter);
+
+ 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
+ PackageItem info = (PackageItem) parent.getItemAtPosition(position);
+ addCustomApplicationPref(info.packageName);
+ dialog.cancel();
+ }
+ });
+ break;
+ default:
+ dialog = null;
+ }
+ return dialog;
+ }
+
+ /**
+ * Application class
+ */
+ private static class Package {
+ 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 Package(String name, Integer color, Integer timeon, Integer timeoff) {
+ this.name = name;
+ this.color = color;
+ this.timeon = timeon;
+ this.timeoff = timeoff;
+ }
+
+ 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 Package 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 {
+ Package item = new Package(app[0], Integer.parseInt(values[0]), Integer
+ .parseInt(values[1]), Integer.parseInt(values[2]));
+ return item;
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ }
+
+ };
+
+ /**
+ * AppItem class
+ */
+ private static class PackageItem implements Comparable<PackageItem> {
+ CharSequence title;
+ TreeSet<CharSequence> activityTitles = new TreeSet<CharSequence>();
+ String packageName;
+ Drawable icon;
+
+ @Override
+ public int compareTo(PackageItem another) {
+ int result = title.toString().compareToIgnoreCase(another.title.toString());
+ return result != 0 ? result : packageName.compareTo(another.packageName);
+ }
+ }
+
+ /**
+ * AppAdapter class
+ */
+ private class PackageAdapter extends BaseAdapter {
+ private List<PackageItem> mInstalledPackages = new LinkedList<PackageItem>();
+
+ private void reloadList() {
+ final Handler handler = new Handler();
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mInstalledPackages) {
+ mInstalledPackages.clear();
+ }
+
+ final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+ mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ List<ResolveInfo> installedAppsInfo =
+ mPackageManager.queryIntentActivities(mainIntent, 0);
+
+ for (ResolveInfo info : installedAppsInfo) {
+ ApplicationInfo appInfo = info.activityInfo.applicationInfo;
+
+ final PackageItem item = new PackageItem();
+ item.title = appInfo.loadLabel(mPackageManager);
+ item.activityTitles.add(info.loadLabel(mPackageManager));
+ item.icon = appInfo.loadIcon(mPackageManager);
+ item.packageName = appInfo.packageName;
+
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ // NO synchronize here: We know that mInstalledApps.clear()
+ // was called and will never be called again.
+ // At this point the only thread modifying mInstalledApp is main
+ int index = Collections.binarySearch(mInstalledPackages, item);
+ if (index < 0) {
+ mInstalledPackages.add(-index - 1, item);
+ } else {
+ mInstalledPackages.get(index).activityTitles.addAll(item.activityTitles);
+ }
+ notifyDataSetChanged();
+ }
+ });
+ }
+ }
+ }).start();
+ }
+
+ public PackageAdapter() {
+ reloadList();
+ }
+
+ @Override
+ public int getCount() {
+ synchronized (mInstalledPackages) {
+ return mInstalledPackages.size();
+ }
+ }
+
+ @Override
+ public PackageItem getItem(int position) {
+ synchronized (mInstalledPackages) {
+ return mInstalledPackages.get(position);
+ }
+ }
+
+ @Override
+ public long getItemId(int position) {
+ synchronized (mInstalledPackages) {
+ // packageName is guaranteed to be unique in mInstalledPackages
+ return mInstalledPackages.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);
+ }
+ PackageItem applicationInfo = getItem(position);
+
+ holder.title.setText(applicationInfo.title);
+ holder.icon.setImageDrawable(applicationInfo.icon);
+
+ boolean needSummary = applicationInfo.activityTitles.size() > 0;
+ if (applicationInfo.activityTitles.size() == 1) {
+ if (TextUtils.equals(applicationInfo.title, applicationInfo.activityTitles.first())) {
+ needSummary = false;
+ }
+ }
+
+ if (needSummary) {
+ holder.summary.setText(TextUtils.join(", ", applicationInfo.activityTitles));
+ holder.summary.setVisibility(View.VISIBLE);
+ } else {
+ holder.summary.setVisibility(View.GONE);
+ }
+
+ return convertView;
+ }
+ }
+
+ static class ViewHolder {
+ TextView title;
+ TextView summary;
+ ImageView icon;
+ }
+}