summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AndroidManifest.xml24
-rw-r--r--res/drawable-hdpi/ic_blacklist_calls_off.pngbin0 -> 3311 bytes
-rw-r--r--res/drawable-hdpi/ic_blacklist_calls_on.pngbin0 -> 3236 bytes
-rw-r--r--res/drawable-hdpi/ic_blacklist_messages_off.pngbin0 -> 3169 bytes
-rw-r--r--res/drawable-hdpi/ic_blacklist_messages_on.pngbin0 -> 3129 bytes
-rw-r--r--res/drawable-hdpi/ic_see_contacts_holo_dark.pngbin0 -> 1482 bytes
-rw-r--r--res/drawable-mdpi/ic_blacklist_calls_off.pngbin0 -> 3134 bytes
-rw-r--r--res/drawable-mdpi/ic_blacklist_calls_on.pngbin0 -> 3081 bytes
-rw-r--r--res/drawable-mdpi/ic_blacklist_messages_off.pngbin0 -> 3063 bytes
-rw-r--r--res/drawable-mdpi/ic_blacklist_messages_on.pngbin0 -> 3030 bytes
-rw-r--r--res/drawable-mdpi/ic_see_contacts_holo_dark.pngbin0 -> 1053 bytes
-rw-r--r--res/drawable-xhdpi/ic_blacklist_calls_off.pngbin0 -> 3459 bytes
-rw-r--r--res/drawable-xhdpi/ic_blacklist_calls_on.pngbin0 -> 3358 bytes
-rw-r--r--res/drawable-xhdpi/ic_blacklist_messages_off.pngbin0 -> 3274 bytes
-rw-r--r--res/drawable-xhdpi/ic_blacklist_messages_on.pngbin0 -> 3219 bytes
-rw-r--r--res/drawable-xhdpi/ic_see_contacts_holo_dark.pngbin0 -> 2058 bytes
-rw-r--r--res/drawable-xxhdpi/ic_blacklist_calls_off.pngbin0 -> 3769 bytes
-rw-r--r--res/drawable-xxhdpi/ic_blacklist_calls_on.pngbin0 -> 3606 bytes
-rw-r--r--res/drawable-xxhdpi/ic_blacklist_messages_off.pngbin0 -> 3470 bytes
-rw-r--r--res/drawable-xxhdpi/ic_blacklist_messages_on.pngbin0 -> 3361 bytes
-rw-r--r--res/drawable/ic_blacklist_calls.xml21
-rw-r--r--res/drawable/ic_blacklist_messages.xml21
-rw-r--r--res/layout/blacklist_entry_row.xml64
-rw-r--r--res/layout/dialog_blacklist_edit_entry.xml71
-rw-r--r--res/menu/blacklist.xml28
-rw-r--r--res/values/cm_arrays.xml19
-rw-r--r--res/values/cm_strings.xml31
-rw-r--r--res/xml/blacklist_prefs.xml46
-rw-r--r--res/xml/security_settings_app_cyanogenmod.xml31
-rwxr-xr-xsrc/com/android/settings/SecuritySettings.java36
-rwxr-xr-x[-rw-r--r--]src/com/android/settings/Settings.java2
-rwxr-xr-x[-rw-r--r--]src/com/android/settings/SettingsActivity.java2
-rwxr-xr-x[-rw-r--r--]src/com/android/settings/SubSettings.java0
-rw-r--r--src/com/android/settings/blacklist/BlacklistSettings.java385
-rw-r--r--src/com/android/settings/blacklist/EntryEditDialogFragment.java298
-rw-r--r--src/com/android/settings/blacklist/PreferenceFragment.java144
-rw-r--r--src/com/android/settings/blacklist/ToggleImageView.java92
37 files changed, 1312 insertions, 3 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index e28c1d4..28489d6 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2161,12 +2161,12 @@
<category android:name="android.intent.category.VOICE_LAUNCH" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.SHORTCUT" />
- </intent-filter>
+ </intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.profiles.ProfilesSettings" />
<meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
android:resource="@id/profiles_settings" />
- </activity>
+ </activity>
<!-- Keep compatibility with old shortcuts. -->
<activity-alias android:name="ProfileSettings"
@@ -2219,10 +2219,28 @@
<meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
android:resource="@id/display_settings" />
</activity-alias>
- <!-- CyanogenMod activities End -->
+
<activity android:name=".cyanogenmod.SpamList" />
+ <!-- "Blacklist settings" UI, used only on voice-capable phone devices. -->
+ <activity android:name="Settings$BlacklistSettingsActivity"
+ android:theme="@style/Theme.SubSettingsDialogWhenLarge"
+ android:uiOptions="splitActionBarWhenNarrow"
+ android:label="@string/blacklist_title"
+ android:excludeFromRecents="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+ android:value="com.android.settings.blacklist.BlacklistSettings" />
+ <meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
+ android:resource="@id/security_settings" />
+ </activity>
+
+ <!-- CyanogenMod activities End -->
+
<!-- Pseudo-activity used to provide an intent-filter entry point to encryption settings -->
<activity android:name="Settings$CryptKeeperSettingsActivity"
android:label="@string/crypt_keeper_encrypt_title">
diff --git a/res/drawable-hdpi/ic_blacklist_calls_off.png b/res/drawable-hdpi/ic_blacklist_calls_off.png
new file mode 100644
index 0000000..22a05c2
--- /dev/null
+++ b/res/drawable-hdpi/ic_blacklist_calls_off.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_blacklist_calls_on.png b/res/drawable-hdpi/ic_blacklist_calls_on.png
new file mode 100644
index 0000000..5073d63
--- /dev/null
+++ b/res/drawable-hdpi/ic_blacklist_calls_on.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_blacklist_messages_off.png b/res/drawable-hdpi/ic_blacklist_messages_off.png
new file mode 100644
index 0000000..812aaa5
--- /dev/null
+++ b/res/drawable-hdpi/ic_blacklist_messages_off.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_blacklist_messages_on.png b/res/drawable-hdpi/ic_blacklist_messages_on.png
new file mode 100644
index 0000000..4d7c809
--- /dev/null
+++ b/res/drawable-hdpi/ic_blacklist_messages_on.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_see_contacts_holo_dark.png b/res/drawable-hdpi/ic_see_contacts_holo_dark.png
new file mode 100644
index 0000000..8e2182f
--- /dev/null
+++ b/res/drawable-hdpi/ic_see_contacts_holo_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_blacklist_calls_off.png b/res/drawable-mdpi/ic_blacklist_calls_off.png
new file mode 100644
index 0000000..7917f46
--- /dev/null
+++ b/res/drawable-mdpi/ic_blacklist_calls_off.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_blacklist_calls_on.png b/res/drawable-mdpi/ic_blacklist_calls_on.png
new file mode 100644
index 0000000..9ec572d
--- /dev/null
+++ b/res/drawable-mdpi/ic_blacklist_calls_on.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_blacklist_messages_off.png b/res/drawable-mdpi/ic_blacklist_messages_off.png
new file mode 100644
index 0000000..a2e53e3
--- /dev/null
+++ b/res/drawable-mdpi/ic_blacklist_messages_off.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_blacklist_messages_on.png b/res/drawable-mdpi/ic_blacklist_messages_on.png
new file mode 100644
index 0000000..01b4747
--- /dev/null
+++ b/res/drawable-mdpi/ic_blacklist_messages_on.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_see_contacts_holo_dark.png b/res/drawable-mdpi/ic_see_contacts_holo_dark.png
new file mode 100644
index 0000000..ef1d2df
--- /dev/null
+++ b/res/drawable-mdpi/ic_see_contacts_holo_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_blacklist_calls_off.png b/res/drawable-xhdpi/ic_blacklist_calls_off.png
new file mode 100644
index 0000000..6fd91de
--- /dev/null
+++ b/res/drawable-xhdpi/ic_blacklist_calls_off.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_blacklist_calls_on.png b/res/drawable-xhdpi/ic_blacklist_calls_on.png
new file mode 100644
index 0000000..cb67ee0
--- /dev/null
+++ b/res/drawable-xhdpi/ic_blacklist_calls_on.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_blacklist_messages_off.png b/res/drawable-xhdpi/ic_blacklist_messages_off.png
new file mode 100644
index 0000000..f32eab6
--- /dev/null
+++ b/res/drawable-xhdpi/ic_blacklist_messages_off.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_blacklist_messages_on.png b/res/drawable-xhdpi/ic_blacklist_messages_on.png
new file mode 100644
index 0000000..c12e04c
--- /dev/null
+++ b/res/drawable-xhdpi/ic_blacklist_messages_on.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_see_contacts_holo_dark.png b/res/drawable-xhdpi/ic_see_contacts_holo_dark.png
new file mode 100644
index 0000000..279fff9
--- /dev/null
+++ b/res/drawable-xhdpi/ic_see_contacts_holo_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_blacklist_calls_off.png b/res/drawable-xxhdpi/ic_blacklist_calls_off.png
new file mode 100644
index 0000000..0841442
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_blacklist_calls_off.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_blacklist_calls_on.png b/res/drawable-xxhdpi/ic_blacklist_calls_on.png
new file mode 100644
index 0000000..7f252d3
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_blacklist_calls_on.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_blacklist_messages_off.png b/res/drawable-xxhdpi/ic_blacklist_messages_off.png
new file mode 100644
index 0000000..10a07db
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_blacklist_messages_off.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_blacklist_messages_on.png b/res/drawable-xxhdpi/ic_blacklist_messages_on.png
new file mode 100644
index 0000000..50dd562
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_blacklist_messages_on.png
Binary files differ
diff --git a/res/drawable/ic_blacklist_calls.xml b/res/drawable/ic_blacklist_calls.xml
new file mode 100644
index 0000000..401fb4a
--- /dev/null
+++ b/res/drawable/ic_blacklist_calls.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_checked="true" android:drawable="@drawable/ic_blacklist_calls_on" />
+ <item android:drawable="@drawable/ic_blacklist_calls_off" />
+</selector>
+
diff --git a/res/drawable/ic_blacklist_messages.xml b/res/drawable/ic_blacklist_messages.xml
new file mode 100644
index 0000000..f0fafa1
--- /dev/null
+++ b/res/drawable/ic_blacklist_messages.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_checked="true" android:drawable="@drawable/ic_blacklist_messages_on" />
+ <item android:drawable="@drawable/ic_blacklist_messages_off" />
+</selector>
+
diff --git a/res/layout/blacklist_entry_row.xml b/res/layout/blacklist_entry_row.xml
new file mode 100644
index 0000000..16dc1d2
--- /dev/null
+++ b/res/layout/blacklist_entry_row.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:gravity="center_vertical"
+ android:padding="5dip"
+ android:orientation="horizontal"
+ android:descendantFocusability="blocksDescendants">
+
+ <LinearLayout
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="center_vertical"
+ android:orientation="vertical"
+ android:paddingStart="10dip"
+ android:paddingEnd="10dip">
+
+ <TextView android:id="@+id/number"
+ style="?android:attr/textAppearanceMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <TextView android:id="@+id/name"
+ style="?android:attr/textAppearanceSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <com.android.settings.blacklist.ToggleImageView
+ android:id="@+id/block_calls"
+ android:layout_width="@android:dimen/app_icon_size"
+ android:layout_height="@android:dimen/app_icon_size"
+ android:src="@drawable/ic_blacklist_calls"
+ android:scaleType="centerInside"
+ android:clickable="true"
+ android:contentDescription="@null" />
+
+ <com.android.settings.blacklist.ToggleImageView
+ android:id="@+id/block_messages"
+ android:layout_width="@android:dimen/app_icon_size"
+ android:layout_height="@android:dimen/app_icon_size"
+ android:src="@drawable/ic_blacklist_messages"
+ android:scaleType="centerInside"
+ android:clickable="true"
+ android:contentDescription="@null" />
+
+</LinearLayout>
diff --git a/res/layout/dialog_blacklist_edit_entry.xml b/res/layout/dialog_blacklist_edit_entry.xml
new file mode 100644
index 0000000..30b7c24
--- /dev/null
+++ b/res/layout/dialog_blacklist_edit_entry.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 The Android Open Source 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="5dip"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/number_field"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1.0"
+ android:addStatesFromChildren="true"
+ android:gravity="center_vertical"
+ android:baselineAligned="false"
+ android:paddingStart="10dip"
+ android:paddingEnd="10dip">
+
+ <EditText android:id="@+id/number_edit"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ android:layout_height="wrap_content"/>
+
+ <ImageButton android:id="@+id/select_contact"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="10dip"
+ android:src="@drawable/ic_see_contacts_holo_dark"
+ android:contentDescription="@string/select_contact" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/options_field"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1.0"
+ android:orientation="vertical"
+ android:baselineAligned="false"
+ android:padding="10dip" >
+
+ <CheckBox android:id="@+id/incoming_calls"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/blacklist_policy_block_calls"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ <CheckBox android:id="@+id/incoming_messages"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/blacklist_policy_block_messages"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/res/menu/blacklist.xml b/res/menu/blacklist.xml
new file mode 100644
index 0000000..a3d294e
--- /dev/null
+++ b/res/menu/blacklist.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/blacklist_add"
+ android:title="@string/add_blacklist_number"
+ android:icon="@drawable/ic_menu_add"
+ android:showAsAction="ifRoom|withText" />
+ <item
+ android:id="@+id/blacklist_prefs"
+ android:title="@string/blacklist_prefs"
+ android:icon="@drawable/ic_sysbar_quicksettings"
+ android:showAsAction="ifRoom|withText" />
+</menu>
diff --git a/res/values/cm_arrays.xml b/res/values/cm_arrays.xml
index 8aee1ff..7990443 100644
--- a/res/values/cm_arrays.xml
+++ b/res/values/cm_arrays.xml
@@ -406,4 +406,23 @@
<item>100</item>
<item>200</item>
</string-array>
+
+ <!-- Phone blacklist management -->
+ <string-array name="blacklist_policy_unknown_entries" translatable="false">
+ <item>@string/blacklist_policy_block_calls</item>
+ <item>@string/blacklist_policy_block_messages</item>
+ </string-array>
+
+ <string-array name="blacklist_policy_unknown_values" translatable="false">
+ <item>1</item>
+ <item>16</item>
+ </string-array>
+
+ <string-array name="blacklist_policy_private_entries" translatable="false">
+ <item>@string/blacklist_policy_block_calls</item>
+ </string-array>
+
+ <string-array name="blacklist_policy_private_values" translatable="false">
+ <item>1</item>
+ </string-array>
</resources>
diff --git a/res/values/cm_strings.xml b/res/values/cm_strings.xml
index 17ef49d..9bf77af 100644
--- a/res/values/cm_strings.xml
+++ b/res/values/cm_strings.xml
@@ -732,4 +732,35 @@
<string name="block_notifications_title">Filter notifications</string>
<string name="no_filters_title">No filters set</string>
+ <!-- Blacklist preferences -->
+ <string name="blacklist_title">Blacklist</string>
+ <string name="blacklist_edit_dialog_title">Edit blacklist entry</string>
+ <string name="blacklist_prefs">Settings</string>
+ <string name="blacklist_button_delete">Delete</string>
+ <string name="blacklist_empty_text">You don\'t have any blacklisted numbers. Add an entry by touching the Add (+) button.</string>
+ <string name="blacklist_disabled_empty_text">To blacklist calls and/or messages from certain numbers, enable the blacklist.</string>
+ <string name="blacklist_summary_disabled">Disabled</string>
+ <string name="blacklist_summary">Incoming calls and messages from phone numbers in the blacklist will be blocked</string>
+ <string name="blacklist_notify">Show notification</string>
+ <string name="blacklist_private_numbers_title">Private numbers</string>
+ <string name="blacklist_private_numbers_summary_disabled">Don\'t block calls from private numbers</string>
+ <string name="blacklist_private_numbers_summary">Block incoming <xliff:g id="type">%2$s</xliff:g> from private numbers</string>
+ <string name="blacklist_unknown_numbers_title">Unknown numbers</string>
+ <string name="blacklist_unknown_numbers_summary_disabled">Don\'t block calls or messages from numbers not in the contact list</string>
+ <string name="blacklist_unknown_numbers_summary">Block incoming <xliff:g id="type">%2$s</xliff:g> from numbers not in the contact list</string>
+ <string name="blacklist_summary_type_calls_only">calls</string>
+ <string name="blacklist_summary_type_messages_only">messages</string>
+ <string name="blacklist_summary_type_calls_and_messages">calls and messages</string>
+ <string name="blacklist_regex_title">Use wildcards</string>
+ <string name="blacklist_regex_summary">Use . as a wildcard and * for repetition. E.g. 123.* blocks numbers starting with 123 and .*123.* blocks numbers containing 123</string>
+ <string name="blacklist_policy_block_calls">Block incoming calls</string>
+ <string name="blacklist_policy_block_messages">Block incoming messages</string>
+ <string name="blacklist_bad_number_add">Unable to add invalid number to blacklist</string>
+
+ <!-- Blacklist management -->
+ <string name="add_blacklist_number">Add number</string>
+ <string name="remove_blacklist_number_title">Remove number</string>
+ <string name="remove_blacklist_entry">Do you want to remove this blacklist entry?</string>
+ <string name="select_contact">Select contact</string>
+
</resources>
diff --git a/res/xml/blacklist_prefs.xml b/res/xml/blacklist_prefs.xml
new file mode 100644
index 0000000..be63b7d
--- /dev/null
+++ b/res/xml/blacklist_prefs.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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"
+ android:title="@string/blacklist_title" >
+
+ <com.android.settings.cyanogenmod.SystemSettingCheckBoxPreference
+ android:key="phone_blacklist_notify_enabled"
+ android:title="@string/blacklist_notify"
+ android:defaultValue="true" />
+
+ <MultiSelectListPreference
+ android:key="button_blacklist_private_numbers"
+ android:title="@string/blacklist_private_numbers_title"
+ android:dialogTitle="@string/blacklist_private_numbers_title"
+ android:entries="@array/blacklist_policy_private_entries"
+ android:entryValues="@array/blacklist_policy_private_values"
+ android:persistent="false" />
+
+ <MultiSelectListPreference
+ android:key="button_blacklist_unknown_numbers"
+ android:title="@string/blacklist_unknown_numbers_title"
+ android:dialogTitle="@string/blacklist_unknown_numbers_title"
+ android:entries="@array/blacklist_policy_unknown_entries"
+ android:entryValues="@array/blacklist_policy_unknown_values"
+ android:persistent="false" />
+
+ <com.android.settings.cyanogenmod.SystemSettingCheckBoxPreference
+ android:key="phone_blacklist_regex_enabled"
+ android:title="@string/blacklist_regex_title"
+ android:summary="@string/blacklist_regex_summary" />
+
+</PreferenceScreen>
diff --git a/res/xml/security_settings_app_cyanogenmod.xml b/res/xml/security_settings_app_cyanogenmod.xml
new file mode 100644
index 0000000..a63fba6
--- /dev/null
+++ b/res/xml/security_settings_app_cyanogenmod.xml
@@ -0,0 +1,31 @@
+<?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">
+
+ <PreferenceCategory
+ android:key="app_security"
+ android:title="@string/app_security_title">
+
+ <PreferenceScreen
+ android:key="blacklist"
+ android:title="@string/blacklist_title"
+ android:fragment="com.android.settings.blacklist.BlacklistSettings" />
+
+ </PreferenceCategory>
+
+</PreferenceScreen>
diff --git a/src/com/android/settings/SecuritySettings.java b/src/com/android/settings/SecuritySettings.java
index 66af22b..fd785d3 100755
--- a/src/com/android/settings/SecuritySettings.java
+++ b/src/com/android/settings/SecuritySettings.java
@@ -52,6 +52,7 @@ import android.text.TextUtils;
import android.util.Log;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.telephony.util.BlacklistUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.TrustAgentUtils.TrustAgentComponentInfo;
import com.android.settings.fingerprint.FingerprintEnrollIntroduction;
@@ -60,6 +61,7 @@ import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Index;
import com.android.settings.search.Indexable;
import com.android.settings.search.SearchIndexableRaw;
+import com.android.settings.R;
import java.util.ArrayList;
import java.util.List;
@@ -119,6 +121,11 @@ public class SecuritySettings extends SettingsPreferenceFragment
private static final int MY_USER_ID = UserHandle.myUserId();
+ // CyanogenMod Additions
+ private static final String KEY_APP_SECURITY_CATEGORY = "app_security";
+ private static final String KEY_BLACKLIST = "blacklist";
+
+ private PackageManager mPM;
private DevicePolicyManager mDPM;
private SubscriptionManager mSubscriptionManager;
@@ -142,6 +149,7 @@ public class SecuritySettings extends SettingsPreferenceFragment
private boolean mIsPrimary;
private Intent mTrustAgentClickIntent;
+
private Preference mOwnerInfoPref;
@Override
@@ -149,6 +157,9 @@ public class SecuritySettings extends SettingsPreferenceFragment
return MetricsLogger.SECURITY;
}
+ // CyanogenMod Additions
+ private PreferenceScreen mBlacklist;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -169,6 +180,7 @@ public class SecuritySettings extends SettingsPreferenceFragment
private static int getResIdForLockUnlockScreen(Context context,
LockPatternUtils lockPatternUtils) {
+ // Add options for lock/unlock screen
int resid = 0;
if (!lockPatternUtils.isSecure(MY_USER_ID)) {
if (lockPatternUtils.isLockScreenDisabled(MY_USER_ID)) {
@@ -208,6 +220,8 @@ public class SecuritySettings extends SettingsPreferenceFragment
}
addPreferencesFromResource(R.xml.security_settings);
root = getPreferenceScreen();
+ // Add package manager to check if features are available
+ PackageManager pm = getActivity().getPackageManager();
// Add package manager to check if features are available
PackageManager pm = getPackageManager();
@@ -347,6 +361,18 @@ public class SecuritySettings extends SettingsPreferenceFragment
}
}
+ // App security settings
+ addPreferencesFromResource(R.xml.security_settings_app_cyanogenmod);
+ mBlacklist = (PreferenceScreen) root.findPreference(KEY_BLACKLIST);
+
+ // Determine options based on device telephony support
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ // No telephony, remove dependent options
+ PreferenceGroup appCategory = (PreferenceGroup)
+ root.findPreference(KEY_APP_SECURITY_CATEGORY);
+ appCategory.removePreference(mBlacklist);
+ }
+
// The above preferences come and go based on security state, so we need to update
// the index. This call is expected to be fairly cheap, but we may want to do something
// smarter in the future.
@@ -648,6 +674,7 @@ public class SecuritySettings extends SettingsPreferenceFragment
}
updateOwnerInfo();
+ updateBlacklistSummary();
}
public void updateOwnerInfo() {
@@ -901,4 +928,13 @@ public class SecuritySettings extends SettingsPreferenceFragment
}
}
+ private void updateBlacklistSummary() {
+ if (mBlacklist != null) {
+ if (BlacklistUtils.isBlacklistEnabled(getActivity())) {
+ mBlacklist.setSummary(R.string.blacklist_summary);
+ } else {
+ mBlacklist.setSummary(R.string.blacklist_summary_disabled);
+ }
+ }
+ }
}
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 101219b..06f0149 100644..100755
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -17,6 +17,7 @@
package com.android.settings;
import com.android.settings.applications.AppOpsSummary;
+import com.android.settings.blacklist.BlacklistSettings;
/**
* Top-level Settings activity
@@ -118,4 +119,5 @@ public class Settings extends SettingsActivity {
public static class AppDrawOverlaySettingsActivity extends SettingsActivity { /* empty */ }
public static class AppWriteSettingsActivity extends SettingsActivity { /* empty */ }
public static class LiveDisplayActivity extends SettingsActivity { /* empty */ }
+ public static class BlacklistSettingsActivity extends SettingsActivity { /* empty */ }
}
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 70694c8..74b8ff9 100644..100755
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -79,6 +79,7 @@ import com.android.settings.applications.ProcessStatsSummary;
import com.android.settings.applications.ProcessStatsUi;
import com.android.settings.applications.UsageAccessDetails;
import com.android.settings.applications.WriteSettingsDetails;
+import com.android.settings.blacklist.BlacklistSettings;
import com.android.settings.bluetooth.BluetoothSettings;
import com.android.settings.dashboard.DashboardCategory;
import com.android.settings.dashboard.DashboardSummary;
@@ -364,6 +365,7 @@ public class SettingsActivity extends Activity
WriteSettingsDetails.class.getName(),
LiveDisplay.class.getName(),
com.android.settings.cyanogenmod.PrivacySettings.class.getName()
+ BlacklistSettings.class.getName()
};
diff --git a/src/com/android/settings/SubSettings.java b/src/com/android/settings/SubSettings.java
index 13ead6e..13ead6e 100644..100755
--- a/src/com/android/settings/SubSettings.java
+++ b/src/com/android/settings/SubSettings.java
diff --git a/src/com/android/settings/blacklist/BlacklistSettings.java b/src/com/android/settings/blacklist/BlacklistSettings.java
new file mode 100644
index 0000000..c30d410
--- /dev/null
+++ b/src/com/android/settings/blacklist/BlacklistSettings.java
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2013 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.blacklist;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.FragmentTransaction;
+import android.app.ListFragment;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.location.CountryDetector;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.provider.ContactsContract.PhoneLookup;
+import android.provider.Settings;
+import android.provider.Telephony.Blacklist;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.util.SparseArray;
+import android.view.Gravity;
+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.CompoundButton;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.ResourceCursorAdapter;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import com.android.internal.telephony.util.BlacklistUtils;
+import com.android.settings.R;
+
+import java.util.HashMap;
+
+/**
+ * Blacklist settings UI for the Phone app.
+ */
+public class BlacklistSettings extends ListFragment
+ implements CompoundButton.OnCheckedChangeListener {
+
+ private static final String[] BLACKLIST_PROJECTION = {
+ Blacklist._ID,
+ Blacklist.NUMBER,
+ Blacklist.PHONE_MODE,
+ Blacklist.MESSAGE_MODE
+ };
+ private static final int COLUMN_ID = 0;
+ private static final int COLUMN_NUMBER = 1;
+ private static final int COLUMN_PHONE = 2;
+ private static final int COLUMN_MESSAGE = 3;
+
+ private Switch mEnabledSwitch;
+ private boolean mLastEnabledState;
+
+ private BlacklistAdapter mAdapter;
+ private Cursor mCursor;
+ private TextView mEmptyView;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater,
+ ViewGroup container, Bundle savedInstanceState) {
+ return inflater.inflate(com.android.internal.R.layout.preference_list_fragment,
+ container, false);
+ }
+
+ @Override
+ public void onActivityCreated(Bundle icicle) {
+ super.onActivityCreated(icicle);
+
+ setHasOptionsMenu(true);
+
+ final Activity activity = getActivity();
+ mEnabledSwitch = new Switch(activity);
+
+ final int padding = activity.getResources().getDimensionPixelSize(
+ R.dimen.action_bar_switch_padding);
+ mEnabledSwitch.setPaddingRelative(0, 0, padding, 0);
+ mEnabledSwitch.setOnCheckedChangeListener(this);
+
+ mCursor = getActivity().managedQuery(Blacklist.CONTENT_URI,
+ BLACKLIST_PROJECTION, null, null, null);
+ mAdapter = new BlacklistAdapter(getActivity(), null);
+
+ mEmptyView = (TextView) getView().findViewById(android.R.id.empty);
+
+ final ListView listView = getListView();
+ listView.setAdapter(mAdapter);
+ listView.setEmptyView(mEmptyView);
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ inflater.inflate(R.menu.blacklist, menu);
+ }
+
+ @Override
+ public void onPrepareOptionsMenu(Menu menu) {
+ super.onPrepareOptionsMenu(menu);
+ menu.findItem(R.id.blacklist_add).setEnabled(mLastEnabledState);
+ menu.findItem(R.id.blacklist_prefs).setEnabled(mLastEnabledState);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.blacklist_add:
+ showEntryEditDialog(-1);
+ return true;
+ case R.id.blacklist_prefs:
+ PreferenceFragment prefs = new PreferenceFragment();
+ FragmentTransaction ft = getFragmentManager().beginTransaction();
+ ft.replace(android.R.id.content, prefs);
+ ft.hide(this);
+ ft.addToBackStack(null);
+ ft.commitAllowingStateLoss();
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ final Activity activity = getActivity();
+ activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
+ ActionBar.DISPLAY_SHOW_CUSTOM);
+ activity.getActionBar().setCustomView(mEnabledSwitch, new ActionBar.LayoutParams(
+ ActionBar.LayoutParams.WRAP_CONTENT,
+ ActionBar.LayoutParams.WRAP_CONTENT,
+ Gravity.CENTER_VERTICAL | Gravity.END));
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ final Activity activity = getActivity();
+ activity.getActionBar().setDisplayOptions(0, ActionBar.DISPLAY_SHOW_CUSTOM);
+ activity.getActionBar().setCustomView(null);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ final Context context = getActivity();
+ mLastEnabledState = BlacklistUtils.isBlacklistEnabled(context);
+ mEnabledSwitch.setChecked(mLastEnabledState);
+ updateEnabledState();
+ }
+
+ @Override
+ public void onListItemClick(ListView l, View v, int position, long id) {
+ showEntryEditDialog(id);
+ }
+
+ private void showEntryEditDialog(long id) {
+ EntryEditDialogFragment fragment = EntryEditDialogFragment.newInstance(id);
+ fragment.show(getFragmentManager(), "blacklist_edit");
+ }
+
+ private void updateEnabledState() {
+ getListView().setEnabled(mLastEnabledState);
+ getActivity().invalidateOptionsMenu();
+
+ mEmptyView.setText(mLastEnabledState
+ ? R.string.blacklist_empty_text
+ : R.string.blacklist_disabled_empty_text);
+ mAdapter.swapCursor(mLastEnabledState ? mCursor : null);
+ }
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (buttonView == mEnabledSwitch && isChecked != mLastEnabledState) {
+ Settings.System.putInt(getActivity().getContentResolver(),
+ Settings.System.PHONE_BLACKLIST_ENABLED, isChecked ? 1 : 0);
+ mLastEnabledState = isChecked;
+ updateEnabledState();
+ }
+ }
+
+ private static class BlacklistAdapter extends ResourceCursorAdapter
+ implements ToggleImageView.OnCheckedChangeListener {
+ private Object mLock = new Object();
+ private ContentResolver mResolver;
+ private String mCurrentCountryIso;
+ private SparseArray<String> mRequestedLookups = new SparseArray<String>();
+ private HashMap<String, String> mContactNameCache = new HashMap<String, String>();
+
+ private Handler mMainHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ int lookupIndex = msg.arg1;
+ String name = (String) msg.obj;
+ mContactNameCache.put(mRequestedLookups.get(lookupIndex),
+ name == null ? "" : name);
+ mRequestedLookups.delete(lookupIndex);
+ notifyDataSetChanged();
+ }
+ };
+ private Handler mQueryHandler;
+
+ private class QueryHandler extends Handler {
+ public static final int MSG_LOOKUP = 1;
+ private static final int MSG_FINISH = 2;
+
+ public QueryHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_LOOKUP:
+ String name = lookupNameForNumber((String) msg.obj);
+ mMainHandler.obtainMessage(0, msg.arg1, 0, name).sendToTarget();
+ synchronized (mLock) {
+ if (mQueryHandler != null) {
+ Message finishMessage = mQueryHandler.obtainMessage(MSG_FINISH);
+ mQueryHandler.sendMessageDelayed(finishMessage, 3000);
+ }
+ }
+ break;
+ case MSG_FINISH:
+ synchronized (mLock) {
+ if (mQueryHandler != null) {
+ mQueryHandler.getLooper().quit();
+ mQueryHandler = null;
+ }
+ }
+ break;
+ }
+ }
+
+ private String lookupNameForNumber(String number) {
+ if (!TextUtils.isEmpty(mCurrentCountryIso)) {
+ // Normalise the number: this is needed because the PhoneLookup query
+ // below does not accept a country code as an input.
+ String numberE164 = PhoneNumberUtils.formatNumberToE164(number,
+ mCurrentCountryIso);
+ if (!TextUtils.isEmpty(numberE164)) {
+ // Only use it if the number could be formatted to E164.
+ number = numberE164;
+ }
+ }
+
+ String result = null;
+ final String[] projection = new String[] { PhoneLookup.DISPLAY_NAME };
+ Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
+ Cursor cursor = mResolver.query(uri, projection, null, null, null);
+ if (cursor != null) {
+ if (cursor.moveToFirst()) {
+ result = cursor.getString(0);
+ }
+ cursor.close();
+ }
+
+ return result;
+ }
+ }
+
+ public BlacklistAdapter(Context context, Cursor cursor) {
+ super(context, R.layout.blacklist_entry_row, cursor);
+
+ final CountryDetector detector =
+ (CountryDetector) context.getSystemService(Context.COUNTRY_DETECTOR);
+ mCurrentCountryIso = detector.detectCountry().getCountryIso();
+ mResolver = context.getContentResolver();
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ View view = super.newView(context, cursor, parent);
+
+ ViewHolder holder = new ViewHolder();
+ holder.mainText = (TextView) view.findViewById(R.id.number);
+ holder.subText = (TextView) view.findViewById(R.id.name);
+ holder.callStatus = (ToggleImageView) view.findViewById(R.id.block_calls);
+ holder.messageStatus = (ToggleImageView) view.findViewById(R.id.block_messages);
+
+ holder.callStatus.setTag(Blacklist.PHONE_MODE);
+ holder.callStatus.setOnCheckedChangeListener(this);
+
+ holder.messageStatus.setTag(Blacklist.MESSAGE_MODE);
+ holder.messageStatus.setOnCheckedChangeListener(this);
+
+ view.setTag(holder);
+
+ return view;
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ ViewHolder holder = (ViewHolder) view.getTag();
+ String number = cursor.getString(COLUMN_NUMBER);
+ String name = mContactNameCache.get(number);
+ String formattedNumber = PhoneNumberUtils.formatNumber(number,
+ null, mCurrentCountryIso);
+
+ if (TextUtils.isEmpty(name)) {
+ holder.mainText.setText(formattedNumber);
+ holder.subText.setVisibility(View.GONE);
+ } else {
+ holder.mainText.setText(name);
+ holder.subText.setText(formattedNumber);
+ holder.subText.setVisibility(View.VISIBLE);
+ }
+
+ if (name == null) {
+ int id = cursor.getInt(COLUMN_ID);
+ scheduleNameLookup(id, number);
+ }
+
+ holder.callStatus.setCheckedInternal(cursor.getInt(COLUMN_PHONE) != 0, false);
+ holder.messageStatus.setCheckedInternal(cursor.getInt(COLUMN_MESSAGE) != 0, false);
+ holder.position = cursor.getPosition();
+ }
+
+ @Override
+ public void onCheckedChanged(ToggleImageView view, boolean isChecked) {
+ View parent = (View) view.getParent();
+ ViewHolder holder = (ViewHolder) parent.getTag();
+ String column = (String) view.getTag();
+ long id = getItemId(holder.position);
+ Uri uri = ContentUris.withAppendedId(Blacklist.CONTENT_URI, id);
+ ContentValues cv = new ContentValues();
+
+ cv.put(column, view.isChecked() ? 1 : 0);
+ if (mResolver.update(uri, cv, null, null) <= 0) {
+ // something went wrong, force an update to the correct state
+ notifyDataSetChanged();
+ }
+ }
+
+ private void scheduleNameLookup(int id, String number) {
+ synchronized (mLock) {
+ if (mQueryHandler == null) {
+ HandlerThread thread = new HandlerThread("blacklist_contact_query",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ thread.start();
+ mQueryHandler = new QueryHandler(thread.getLooper());
+ }
+ }
+
+ mRequestedLookups.put(id, number);
+ Message msg = mQueryHandler.obtainMessage(QueryHandler.MSG_LOOKUP, id, 0, number);
+ msg.sendToTarget();
+ }
+
+ private static class ViewHolder {
+ TextView mainText;
+ TextView subText;
+ ToggleImageView callStatus;
+ ToggleImageView messageStatus;
+ int position;
+ }
+ }
+}
diff --git a/src/com/android/settings/blacklist/EntryEditDialogFragment.java b/src/com/android/settings/blacklist/EntryEditDialogFragment.java
new file mode 100644
index 0000000..3e29279
--- /dev/null
+++ b/src/com/android/settings/blacklist/EntryEditDialogFragment.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.blacklist;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract.CommonDataKinds;
+import android.provider.Telephony.Blacklist;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.text.method.ArrowKeyMovementMethod;
+import android.text.method.DialerKeyListener;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.ImageButton;
+
+import android.widget.Toast;
+import com.android.internal.telephony.util.BlacklistUtils;
+import com.android.settings.R;
+
+public class EntryEditDialogFragment extends DialogFragment
+ implements TextWatcher, DialogInterface.OnClickListener {
+
+ private EditText mEditText;
+ private ImageButton mContactPickButton;
+ private CheckBox mBlockCalls;
+ private CheckBox mBlockMessages;
+ private Button mOkButton;
+
+ private static final String[] BLACKLIST_PROJECTION = {
+ Blacklist.NUMBER, Blacklist.PHONE_MODE, Blacklist.MESSAGE_MODE
+ };
+ private static final String[] NUMBER_PROJECTION = {
+ CommonDataKinds.Phone.NUMBER
+ };
+ private static final int COLUMN_NUMBER = 0;
+ private static final int COLUMN_PHONE = 1;
+ private static final int COLUMN_MESSAGE = 2;
+
+ private static final int REQUEST_CODE_PICKER = 1;
+
+ private static final String DIALOG_STATE = "blacklist_edit_state";
+ private static final String STATE_NUMBER = "number";
+ private static final String STATE_PHONE = "phone";
+ private static final String STATE_MESSAGE = "message";
+
+ public static EntryEditDialogFragment newInstance(long id) {
+ Bundle args = new Bundle();
+ args.putLong("id", id);
+
+ EntryEditDialogFragment fragment = new EntryEditDialogFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ public EntryEditDialogFragment() {
+ super();
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ long id = getEntryId();
+ Bundle dialogState = savedInstanceState != null
+ ? savedInstanceState.getBundle(DIALOG_STATE) : null;
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.blacklist_edit_dialog_title)
+ .setPositiveButton(android.R.string.ok, this)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setView(createDialogView(id, dialogState));
+
+ if (id >= 0) {
+ builder.setNeutralButton(R.string.blacklist_button_delete, this);
+ }
+
+ return builder.create();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ AlertDialog dialog = (AlertDialog) getDialog();
+ Button neutralButton = dialog.getButton(DialogInterface.BUTTON_NEUTRAL);
+ neutralButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ EntryEditDialogFragment parent = EntryEditDialogFragment.this;
+ DialogFragment confirm = DeleteConfirmationFragment.newInstance(parent);
+ confirm.show(getFragmentManager(), "delete_confirm");
+ }
+ });
+
+ updateOkButtonState();
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ updateBlacklistEntry();
+ }
+ }
+
+ private void onDeleteConfirmResult(boolean confirmed) {
+ if (confirmed) {
+ Uri uri = ContentUris.withAppendedId(Blacklist.CONTENT_URI, getEntryId());
+ getActivity().getContentResolver().delete(uri, null, null);
+ dismiss();
+ }
+ }
+
+ private long getEntryId() {
+ return getArguments().getLong("id", -1);
+ }
+
+ private View createDialogView(long id, Bundle savedState) {
+ final Activity activity = getActivity();
+ final LayoutInflater inflater = (LayoutInflater)
+ activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ final View view = inflater.inflate(R.layout.dialog_blacklist_edit_entry, null);
+
+ mEditText = (EditText) view.findViewById(R.id.number_edit);
+ mEditText.setMovementMethod(ArrowKeyMovementMethod.getInstance());
+ mEditText.setKeyListener(DialerKeyListener.getInstance());
+ mEditText.addTextChangedListener(this);
+
+ mContactPickButton = (ImageButton) view.findViewById(R.id.select_contact);
+ mContactPickButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent contactListIntent = new Intent(Intent.ACTION_PICK);
+ contactListIntent.setType(CommonDataKinds.Phone.CONTENT_TYPE);
+
+ startActivityForResult(contactListIntent, REQUEST_CODE_PICKER, null);
+ }
+ });
+
+ mBlockCalls = (CheckBox) view.findViewById(R.id.incoming_calls);
+ mBlockMessages = (CheckBox) view.findViewById(R.id.incoming_messages);
+
+ if (savedState != null) {
+ mEditText.setText(savedState.getCharSequence(STATE_NUMBER));
+ mBlockCalls.setChecked(savedState.getBoolean(STATE_PHONE));
+ mBlockMessages.setChecked(savedState.getBoolean(STATE_MESSAGE));
+ } else if (id >= 0) {
+ Uri uri = ContentUris.withAppendedId(Blacklist.CONTENT_URI, id);
+ Cursor cursor = activity.getContentResolver().query(uri,
+ BLACKLIST_PROJECTION, null, null, null);
+ if (cursor != null && cursor.moveToFirst()) {
+ mEditText.setText(cursor.getString(COLUMN_NUMBER));
+ mBlockCalls.setChecked(cursor.getInt(COLUMN_PHONE) != 0);
+ mBlockMessages.setChecked(cursor.getInt(COLUMN_MESSAGE) != 0);
+ } else {
+ id = -1;
+ }
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ if (id < 0) {
+ // defaults
+ mEditText.setText("");
+ mBlockCalls.setChecked(true);
+ mBlockMessages.setChecked(true);
+ }
+
+ return view;
+ }
+
+ private void updateBlacklistEntry() {
+ String number = mEditText.getText().toString();
+ int flags = 0;
+ if (mBlockCalls.isChecked()) {
+ flags = flags | BlacklistUtils.BLOCK_CALLS;
+ }
+ if (mBlockMessages.isChecked()) {
+ flags = flags | BlacklistUtils.BLOCK_MESSAGES;
+ }
+ // Since BlacklistProvider enforces validity for a number to be added
+ // we should alert the user if and when it gets rejected
+ if (!BlacklistUtils.addOrUpdate(getActivity(), number, flags, flags)) {
+ Toast.makeText(getActivity(), getString(R.string.blacklist_bad_number_add),
+ Toast.LENGTH_LONG).show();
+ }
+ }
+
+ private void updateOkButtonState() {
+ if (mOkButton == null) {
+ AlertDialog dialog = (AlertDialog) getDialog();
+ if (dialog != null) {
+ mOkButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
+ }
+ }
+
+ if (mOkButton != null) {
+ mOkButton.setEnabled(mEditText.getText().length() != 0);
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle state) {
+ super.onSaveInstanceState(state);
+
+ Bundle dialogState = new Bundle();
+ dialogState.putCharSequence(STATE_NUMBER, mEditText.getText());
+ dialogState.putBoolean(STATE_PHONE, mBlockCalls.isChecked());
+ dialogState.putBoolean(STATE_MESSAGE, mBlockMessages.isChecked());
+ state.putBundle(DIALOG_STATE, dialogState);
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ updateOkButtonState();
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode != REQUEST_CODE_PICKER) {
+ super.onActivityResult(requestCode, resultCode, data);
+ return;
+ }
+
+ if (resultCode == Activity.RESULT_OK) {
+ Cursor cursor = getActivity().getContentResolver().query(data.getData(),
+ NUMBER_PROJECTION, null, null, null);
+ if (cursor != null) {
+ if (cursor.moveToFirst()) {
+ mEditText.setText(cursor.getString(COLUMN_NUMBER));
+ }
+ cursor.close();
+ }
+ }
+ }
+
+ private static class DeleteConfirmationFragment extends DialogFragment
+ implements DialogInterface.OnClickListener {
+ public static DialogFragment newInstance(EntryEditDialogFragment parent) {
+ DialogFragment fragment = new DeleteConfirmationFragment();
+ fragment.setTargetFragment(parent, 0);
+ return fragment;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog dialog = new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.remove_blacklist_number_title)
+ .setMessage(R.string.remove_blacklist_entry)
+ .setPositiveButton(R.string.yes, this)
+ .setNegativeButton(R.string.no, this)
+ .create();
+
+ return dialog;
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ EntryEditDialogFragment parent = (EntryEditDialogFragment) getTargetFragment();
+ parent.onDeleteConfirmResult(which == DialogInterface.BUTTON_POSITIVE);
+ }
+ }
+}
diff --git a/src/com/android/settings/blacklist/PreferenceFragment.java b/src/com/android/settings/blacklist/PreferenceFragment.java
new file mode 100644
index 0000000..b8ecddd
--- /dev/null
+++ b/src/com/android/settings/blacklist/PreferenceFragment.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2013 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.blacklist;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.MultiSelectListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+import android.provider.Settings;
+
+import com.android.internal.telephony.util.BlacklistUtils;
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class PreferenceFragment extends SettingsPreferenceFragment implements
+ Preference.OnPreferenceChangeListener {
+
+ private static final String BUTTON_BLACKLIST_PRIVATE = "button_blacklist_private_numbers";
+ private static final String BUTTON_BLACKLIST_UNKNOWN = "button_blacklist_unknown_numbers";
+
+ private MultiSelectListPreference mBlacklistPrivate;
+ private MultiSelectListPreference mBlacklistUnknown;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ addPreferencesFromResource(R.xml.blacklist_prefs);
+
+ PreferenceScreen prefSet = getPreferenceScreen();
+ mBlacklistPrivate =
+ (MultiSelectListPreference) prefSet.findPreference(BUTTON_BLACKLIST_PRIVATE);
+ mBlacklistPrivate.setOnPreferenceChangeListener(this);
+ mBlacklistUnknown =
+ (MultiSelectListPreference) prefSet.findPreference(BUTTON_BLACKLIST_UNKNOWN);
+ mBlacklistUnknown.setOnPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ final Context context = getActivity();
+ updateSelectListFromPolicy(mBlacklistPrivate,
+ Settings.System.PHONE_BLACKLIST_PRIVATE_NUMBER_MODE);
+ updateSelectListSummary(mBlacklistPrivate, mBlacklistPrivate.getValues(),
+ R.string.blacklist_private_numbers_summary,
+ R.string.blacklist_private_numbers_summary_disabled);
+ updateSelectListFromPolicy(mBlacklistUnknown,
+ Settings.System.PHONE_BLACKLIST_UNKNOWN_NUMBER_MODE);
+ updateSelectListSummary(mBlacklistUnknown, mBlacklistUnknown.getValues(),
+ R.string.blacklist_unknown_numbers_summary,
+ R.string.blacklist_unknown_numbers_summary_disabled);
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object objValue) {
+ if (preference == mBlacklistUnknown) {
+ Set<String> newValues = (Set<String>) objValue;
+ updatePolicyFromSelectList(newValues,
+ Settings.System.PHONE_BLACKLIST_UNKNOWN_NUMBER_MODE);
+ updateSelectListSummary(mBlacklistUnknown, newValues,
+ R.string.blacklist_unknown_numbers_summary,
+ R.string.blacklist_unknown_numbers_summary_disabled);
+ } else if (preference == mBlacklistPrivate) {
+ Set<String> newValues = (Set<String>) objValue;
+ updatePolicyFromSelectList(newValues,
+ Settings.System.PHONE_BLACKLIST_PRIVATE_NUMBER_MODE);
+ updateSelectListSummary(mBlacklistPrivate, newValues,
+ R.string.blacklist_private_numbers_summary,
+ R.string.blacklist_private_numbers_summary_disabled);
+ }
+
+ return true;
+ }
+
+ private void updateSelectListFromPolicy(MultiSelectListPreference pref, String setting) {
+ int mode = Settings.System.getInt(getContentResolver(), setting, 0);
+ Set<String> values = new HashSet<String>();
+
+ if ((mode & BlacklistUtils.BLOCK_CALLS) != 0) {
+ values.add(Integer.toString(BlacklistUtils.BLOCK_CALLS));
+ }
+ if ((mode & BlacklistUtils.BLOCK_MESSAGES) != 0) {
+ values.add(Integer.toString(BlacklistUtils.BLOCK_MESSAGES));
+ }
+ pref.setValues(values);
+ }
+
+ private int getPolicyFromSelectList(Set<String> values) {
+ int mode = 0;
+
+ for (String value : values) {
+ mode |= Integer.parseInt(value);
+ }
+
+ return mode;
+ }
+
+ private void updatePolicyFromSelectList(Set<String> values, String setting) {
+ int mode = getPolicyFromSelectList(values);
+ Settings.System.putInt(getContentResolver(), setting, mode);
+ }
+
+ private void updateSelectListSummary(MultiSelectListPreference pref,
+ Set<String> values, int summaryResId, int disabledSummaryResId) {
+ int mode = getPolicyFromSelectList(values);
+ int typeResId;
+
+ if (mode == 0) {
+ pref.setSummary(getString(disabledSummaryResId));
+ return;
+ }
+
+ if (mode == BlacklistUtils.BLOCK_CALLS) {
+ typeResId = R.string.blacklist_summary_type_calls_only;
+ } else if (mode == BlacklistUtils.BLOCK_MESSAGES) {
+ typeResId = R.string.blacklist_summary_type_messages_only;
+ } else {
+ typeResId = R.string.blacklist_summary_type_calls_and_messages;
+ }
+
+ pref.setSummary(getString(summaryResId, getString(typeResId)));
+ }
+}
diff --git a/src/com/android/settings/blacklist/ToggleImageView.java b/src/com/android/settings/blacklist/ToggleImageView.java
new file mode 100644
index 0000000..3091c52
--- /dev/null
+++ b/src/com/android/settings/blacklist/ToggleImageView.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2013 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.blacklist;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.widget.Checkable;
+import android.widget.CompoundButton;
+import android.widget.ImageView;
+
+public class ToggleImageView extends ImageView implements Checkable {
+ public static interface OnCheckedChangeListener {
+ void onCheckedChanged(ToggleImageView view, boolean isChecked);
+ }
+
+ private static final int[] CHECKED_STATE_SET = {
+ com.android.internal.R.attr.state_checked
+ };
+
+ private boolean mIsChecked = false;
+ private OnCheckedChangeListener mOnCheckedChangeListener;
+
+ public ToggleImageView(Context context) {
+ super(context);
+ }
+
+ public ToggleImageView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public ToggleImageView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.CompoundButton, defStyle, 0);
+ boolean checked = a.getBoolean(
+ com.android.internal.R.styleable.CompoundButton_checked, false);
+ setChecked(checked);
+ a.recycle();
+ }
+
+ @Override
+ public boolean performClick() {
+ /* When clicked, toggle the state */
+ toggle();
+ return super.performClick();
+ }
+
+ @Override
+ public void setChecked(boolean checked) {
+ setCheckedInternal(checked, true);
+ }
+
+ @Override
+ public boolean isChecked() {
+ return mIsChecked;
+ }
+
+ @Override
+ public void toggle() {
+ setChecked(!mIsChecked);
+ }
+
+ public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
+ mOnCheckedChangeListener = listener;
+ }
+
+ /* package */ void setCheckedInternal(boolean checked, boolean callListener) {
+ if (mIsChecked != checked) {
+ mIsChecked = checked;
+ setImageState(checked ? CHECKED_STATE_SET : null, true);
+ if (callListener && mOnCheckedChangeListener != null) {
+ mOnCheckedChangeListener.onCheckedChanged(this, mIsChecked);
+ }
+ }
+ }
+}