Voicemail provider sample source code.
The app demostrates the how to use voicemail provider APIs to insert new voicemail records using a simple UI. Change-Id: I22610e06a8b80518b9f0d811f72afe245a4148c6
This commit is contained in:
@@ -180,6 +180,7 @@ development/samples/TicTacToeMain samples/${PLATFORM_NAME}/TicTacTo
|
|||||||
development/samples/USB/MissileLauncher samples/${PLATFORM_NAME}/USB/MissileLauncher
|
development/samples/USB/MissileLauncher samples/${PLATFORM_NAME}/USB/MissileLauncher
|
||||||
development/samples/USB/AdbTest samples/${PLATFORM_NAME}/USB/AdbTest
|
development/samples/USB/AdbTest samples/${PLATFORM_NAME}/USB/AdbTest
|
||||||
development/samples/VoiceRecognitionService samples/${PLATFORM_NAME}/VoiceRecognitionService
|
development/samples/VoiceRecognitionService samples/${PLATFORM_NAME}/VoiceRecognitionService
|
||||||
|
development/samples/VoicemailProviderDemo samples/${PLATFORM_NAME}/VoicemailProviderDemo
|
||||||
development/samples/WeatherListWidget samples/${PLATFORM_NAME}/WeatherListWidget
|
development/samples/WeatherListWidget samples/${PLATFORM_NAME}/WeatherListWidget
|
||||||
development/apps/WidgetPreview samples/${PLATFORM_NAME}/WidgetPreview
|
development/apps/WidgetPreview samples/${PLATFORM_NAME}/WidgetPreview
|
||||||
development/samples/Wiktionary samples/${PLATFORM_NAME}/Wiktionary
|
development/samples/Wiktionary samples/${PLATFORM_NAME}/Wiktionary
|
||||||
|
|||||||
32
samples/VoicemailProviderDemo/Android.mk
Normal file
32
samples/VoicemailProviderDemo/Android.mk
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2011 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
LOCAL_PATH:= $(call my-dir)
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_MODULE_TAGS := samples
|
||||||
|
|
||||||
|
# Only compile source java files in this apk.
|
||||||
|
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||||
|
|
||||||
|
LOCAL_PACKAGE_NAME := VoicemailProviderDemo
|
||||||
|
|
||||||
|
LOCAL_SDK_VERSION := current
|
||||||
|
|
||||||
|
include $(BUILD_PACKAGE)
|
||||||
|
|
||||||
|
# Use the following include to make our test apk.
|
||||||
|
include $(call all-makefiles-under,$(LOCAL_PATH))
|
||||||
41
samples/VoicemailProviderDemo/AndroidManifest.xml
Normal file
41
samples/VoicemailProviderDemo/AndroidManifest.xml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2011 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- Declare the contents of this Android application. The namespace
|
||||||
|
attribute brings in the Android platform namespace, and the package
|
||||||
|
supplies a unique name for the application. When writing your
|
||||||
|
own application, the package name must be changed from "com.example.*"
|
||||||
|
to come from a domain that you own or have control over. -->
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:versionCode="1" android:versionName="1.0" package="com.example.android.voicemail">
|
||||||
|
|
||||||
|
<uses-sdk android:minSdkVersion="9" />
|
||||||
|
<uses-sdk android:targetSdkVersion="9" />
|
||||||
|
|
||||||
|
<uses-permission android:name="com.android.providers.voicemail.permission.READ_WRITE_OWN_VOICEMAIL" />
|
||||||
|
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||||
|
|
||||||
|
<application android:icon="@drawable/icon" android:label="@string/app_name">
|
||||||
|
<activity android:name=".AddVoicemailActivity" android:label="@string/app_name">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
190
samples/VoicemailProviderDemo/NOTICE
Normal file
190
samples/VoicemailProviderDemo/NOTICE
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
|
||||||
|
Copyright (c) 2011, 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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
47
samples/VoicemailProviderDemo/_index.html
Normal file
47
samples/VoicemailProviderDemo/_index.html
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<p>
|
||||||
|
This is a simple sample application that demonstrates how to use voicemail
|
||||||
|
content provider APIs to insert new voicemail records.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The application includes
|
||||||
|
<a href="src/com/example/android/voicemail/AddVoicemailActivity.html">
|
||||||
|
<code>AddVoicemailActivity</code>
|
||||||
|
</a>,
|
||||||
|
an activity that lets the user enter voicemail details and record voicemail audio,
|
||||||
|
which can then be stored with the voicemail content provider by tapping the "Send"
|
||||||
|
button.
|
||||||
|
<p>
|
||||||
|
<img alt="Add voicemail" src="../images/VoicemailProviderDemo.png"
|
||||||
|
width=250px/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
In the real world, a similar application could download voicemails from a
|
||||||
|
remote voicemail server and store them locally with the voicemail content
|
||||||
|
provider. The platform would then take care of notification and rendering of the
|
||||||
|
voicemails.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Following interfaces are of particular interest:
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="src/com/example/android/voicemail/common/core/VoicemailProviderHelper.html">
|
||||||
|
<code>VoicemailProviderHelper</code>
|
||||||
|
</a> and its implementation in
|
||||||
|
<a href="src/com/example/android/voicemail/common/core/VoicemailProviderHelpers.html">
|
||||||
|
<code>VoicemailProviderHelpers</code>
|
||||||
|
</a>.
|
||||||
|
This interface provides a good demonstration of various fields exposed by voicemail
|
||||||
|
content provider and their usage.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="src/com/example/android/voicemail/common/core/Voicemail.html">
|
||||||
|
<code>Voicemail</code>
|
||||||
|
</a> and its implementation in
|
||||||
|
<a href="src/com/example/android/voicemail/common/core/VoicemailImpl.html">
|
||||||
|
<code>VoicemailImpl</code> </a>.
|
||||||
|
This interface provides a structured view of most the important fields in
|
||||||
|
voicemail content provider.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
BIN
samples/VoicemailProviderDemo/res/drawable-hdpi/icon.png
Normal file
BIN
samples/VoicemailProviderDemo/res/drawable-hdpi/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
BIN
samples/VoicemailProviderDemo/res/drawable-ldpi/icon.png
Normal file
BIN
samples/VoicemailProviderDemo/res/drawable-ldpi/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
BIN
samples/VoicemailProviderDemo/res/drawable-mdpi/icon.png
Normal file
BIN
samples/VoicemailProviderDemo/res/drawable-mdpi/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.5 KiB |
61
samples/VoicemailProviderDemo/res/layout/add_voicemail.xml
Normal file
61
samples/VoicemailProviderDemo/res/layout/add_voicemail.xml
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2011 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="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/add_voicemail"/>
|
||||||
|
|
||||||
|
<EditText android:id = "@+id/sender_number"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/sender_number"
|
||||||
|
android:inputType="phone"/>
|
||||||
|
<EditText android:id = "@+id/time"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/time"/>
|
||||||
|
<EditText android:id = "@+id/duration"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/duration"
|
||||||
|
android:inputType="number"/>
|
||||||
|
<EditText android:id = "@+id/mime_type"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/mime_type"/>
|
||||||
|
<EditText android:id = "@+id/provider_package"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
<!-- Recording button -->
|
||||||
|
<Button android:id = "@+id/start_recording_btn"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/record_voice"/>
|
||||||
|
|
||||||
|
<!-- Save button -->
|
||||||
|
<Button android:id = "@+id/save_btn"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/send"/>
|
||||||
|
</LinearLayout>
|
||||||
29
samples/VoicemailProviderDemo/res/values/strings.xml
Normal file
29
samples/VoicemailProviderDemo/res/values/strings.xml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2011 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">VoicemaiProviderDemo</string>
|
||||||
|
<!-- AddVoicemailActivity -->
|
||||||
|
<string name="add_voicemail">Add voicemail</string>
|
||||||
|
<string name="sender_number">Sender phone number</string>
|
||||||
|
<string name="time">Time in DD/MM/YYYY hh:mm</string>
|
||||||
|
<string name="duration">Duration in seconds</string>
|
||||||
|
<string name="mime_type">MIME type</string>
|
||||||
|
<string name="voicemail_store_error">Error in storing voicemail!</string>
|
||||||
|
<string name="send">Send</string>
|
||||||
|
<string name="record_voice">Record voice</string>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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.providers.voicemail.api;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the constants needed to access and interact with the voicemail content provider.
|
||||||
|
*/
|
||||||
|
public class VoicemailProvider {
|
||||||
|
/** The authority used by the voicemail provider. */
|
||||||
|
public static final String AUTHORITY =
|
||||||
|
"com.android.providers.voicemail";
|
||||||
|
|
||||||
|
/** The main URI exposed by the service. */
|
||||||
|
public static final Uri CONTENT_URI =
|
||||||
|
Uri.parse("content://" + AUTHORITY + "/voicemail");
|
||||||
|
/** The URI to fetch an individual voicemail. */
|
||||||
|
public static final Uri CONTENT_URI_ID_QUERY =
|
||||||
|
Uri.parse("content://" + AUTHORITY + "/voicemail/");
|
||||||
|
/** The URI to fetch all voicemails from a given provider. */
|
||||||
|
public static final Uri CONTENT_URI_PROVIDER_QUERY =
|
||||||
|
Uri.parse("content://" + AUTHORITY + "/voicemail/provider/");
|
||||||
|
/** The URI to fetch an individual voicemail from a given provider. */
|
||||||
|
public static final Uri CONTENT_URI_PROVIDER_ID_QUERY =
|
||||||
|
Uri.parse("content://" + AUTHORITY + "/voicemail/provider/");
|
||||||
|
|
||||||
|
/** Broadcast intent when a new voicemail record is inserted. */
|
||||||
|
public static final String ACTION_NEW_VOICEMAIL = "android.intent.action.NEW_VOICEMAIL";
|
||||||
|
/**
|
||||||
|
* Extra included in {@value Intent#ACTION_PROVIDER_CHANGED} and {@value #ACTION_NEW_VOICEMAIL}
|
||||||
|
* broadcast intents to indicate the package that caused the change in content provider.
|
||||||
|
* <p>
|
||||||
|
* Receivers of the broadcast can use this field to determine if this is a self change.
|
||||||
|
*/
|
||||||
|
public static final String EXTRA_CHANGED_BY =
|
||||||
|
"com.android.providers.voicemail.changed_by";
|
||||||
|
|
||||||
|
/** The different tables defined by the content provider. */
|
||||||
|
public static final class Tables {
|
||||||
|
/** The table containing voicemail information. */
|
||||||
|
public static final class Voicemails {
|
||||||
|
public static final String NAME = "voicemails";
|
||||||
|
|
||||||
|
/** The mime type for a collection of voicemails. */
|
||||||
|
public static final String DIR_TYPE =
|
||||||
|
"vnd.android.cursor.dir/voicemails";
|
||||||
|
|
||||||
|
/** The different columns contained within the voicemail table. */
|
||||||
|
public static final class Columns {
|
||||||
|
public static final String _ID = "_id";
|
||||||
|
public static final String _DATA = "_data";
|
||||||
|
public static final String _DATA_FILE_EXISTS = "_data_file_exists";
|
||||||
|
public static final String NUMBER = "number";
|
||||||
|
public static final String DATE = "date";
|
||||||
|
public static final String DURATION = "duration";
|
||||||
|
public static final String PROVIDER = "provider";
|
||||||
|
public static final String PROVIDER_DATA = "provider_data";
|
||||||
|
public static final String DATA_MIME_TYPE = "data_mime_type";
|
||||||
|
public static final String READ_STATUS = "read_status";
|
||||||
|
/**
|
||||||
|
* Current mailbox state of the message.
|
||||||
|
* <p>
|
||||||
|
* Legal values: 0(Inbox)/1(Deleted)/2(Undeleted).
|
||||||
|
*/
|
||||||
|
public static final String STATE = "state";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,240 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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.example.android.voicemail;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.util.Pair;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.example.android.voicemail.common.core.Voicemail;
|
||||||
|
import com.example.android.voicemail.common.core.VoicemailImpl;
|
||||||
|
import com.example.android.voicemail.common.core.VoicemailProviderHelper;
|
||||||
|
import com.example.android.voicemail.common.core.VoicemailProviderHelpers;
|
||||||
|
import com.example.android.voicemail.common.inject.InjectView;
|
||||||
|
import com.example.android.voicemail.common.inject.Injector;
|
||||||
|
import com.example.android.voicemail.common.logging.Logger;
|
||||||
|
import com.example.android.voicemail.common.ui.DialogHelperImpl;
|
||||||
|
import com.example.android.voicemail.common.utils.CloseUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple activity that stores user entered voicemail data into voicemail content provider. To be
|
||||||
|
* used as a test voicemail source.
|
||||||
|
*/
|
||||||
|
public class AddVoicemailActivity extends Activity {
|
||||||
|
private static final Logger logger = Logger.getLogger(AddVoicemailActivity.class);
|
||||||
|
|
||||||
|
private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("dd/MM/yyyy h:mm");
|
||||||
|
|
||||||
|
private static final int REQUEST_CODE_RECORDING = 100;
|
||||||
|
|
||||||
|
private final DialogHelperImpl mDialogHelper = new DialogHelperImpl(this);
|
||||||
|
/**
|
||||||
|
* This is created in {@link #onCreate(Bundle)}, and needs to be released in
|
||||||
|
* {@link #onDestroy()}.
|
||||||
|
*/
|
||||||
|
private VoicemailProviderHelper mVoicemailProviderHelper;
|
||||||
|
private Uri mRecordingUri;
|
||||||
|
|
||||||
|
// Mark the views as injectable. These objects are instantiated automatically during
|
||||||
|
// onCreate() by finding the appropriate view that matches the specified resource_id.
|
||||||
|
@InjectView(R.id.start_recording_btn)
|
||||||
|
private Button mStartRec;
|
||||||
|
@InjectView(R.id.save_btn)
|
||||||
|
private Button mSaveButton;
|
||||||
|
@InjectView(R.id.time)
|
||||||
|
private TextView mTime;
|
||||||
|
@InjectView(R.id.provider_package)
|
||||||
|
private TextView mProviderPackage;
|
||||||
|
@InjectView(R.id.mime_type)
|
||||||
|
private TextView mMimeType;
|
||||||
|
@InjectView(R.id.sender_number)
|
||||||
|
private TextView mSenderNumber;
|
||||||
|
@InjectView(R.id.duration)
|
||||||
|
private TextView mDuration;
|
||||||
|
|
||||||
|
/** Called when the activity is first created. */
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.add_voicemail);
|
||||||
|
// Inject all objects that are marked by @InjectView annotation.
|
||||||
|
Injector.get(this).inject();
|
||||||
|
mVoicemailProviderHelper = VoicemailProviderHelpers.createPackageScopedVoicemailProvider(this);
|
||||||
|
|
||||||
|
setDefaultValues();
|
||||||
|
|
||||||
|
// Record voice button.
|
||||||
|
mStartRec.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
startRecording();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Save voicemail button.
|
||||||
|
mSaveButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
storeVoicemail();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void storeVoicemail() {
|
||||||
|
try {
|
||||||
|
Pair<Voicemail, Uri> newVoicemail = new Pair<Voicemail, Uri>(
|
||||||
|
buildVoicemailObjectFromUiElements(), mRecordingUri);
|
||||||
|
new InsertVoicemailTask().execute(newVoicemail);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
handleError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Voicemail buildVoicemailObjectFromUiElements() throws ParseException {
|
||||||
|
String sender = mSenderNumber.getText().toString();
|
||||||
|
String dateStr = mTime.getText().toString();
|
||||||
|
String durationStr = mDuration.getText().toString();
|
||||||
|
String mimeType = mMimeType.getText().toString();
|
||||||
|
String sourcePackageName = mProviderPackage.getText().toString();
|
||||||
|
long time = DATE_FORMATTER.parse(dateStr.trim()).getTime();
|
||||||
|
long duration = durationStr.length() != 0 ? Long.parseLong(durationStr) : 0;
|
||||||
|
return VoicemailImpl.createForInsertion(time, sender)
|
||||||
|
.setDuration(duration)
|
||||||
|
.setSource(sourcePackageName)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
switch (requestCode) {
|
||||||
|
case REQUEST_CODE_RECORDING:
|
||||||
|
handleRecordingResult(resultCode, data);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logger.e("onActivityResult: Unexpected requestCode: " + requestCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Dialog onCreateDialog(int id, Bundle bundle) {
|
||||||
|
return mDialogHelper.handleOnCreateDialog(id, bundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set default values in the display */
|
||||||
|
private void setDefaultValues() {
|
||||||
|
// Set time to current time.
|
||||||
|
mTime.setText(DATE_FORMATTER.format(new Date()));
|
||||||
|
|
||||||
|
// Set provider package to this app's package.
|
||||||
|
mProviderPackage.setText(getPackageName());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startRecording() {
|
||||||
|
Intent recordingIntent = new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
|
||||||
|
startActivityForResult(recordingIntent, REQUEST_CODE_RECORDING);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleRecordingResult(int resultCode, Intent data) {
|
||||||
|
if (resultCode != RESULT_OK) {
|
||||||
|
handleError(new Exception("Failed to do recording. Error Code: " + resultCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
Uri uri = data.getData();
|
||||||
|
logger.d("Received recording URI: " + uri);
|
||||||
|
if (uri != null) {
|
||||||
|
mRecordingUri = uri;
|
||||||
|
mMimeType.setText(getContentResolver().getType(uri));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleError(Exception e) {
|
||||||
|
mDialogHelper.showErrorMessageDialog(R.string.voicemail_store_error, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An async task that inserts a new voicemail record using a background thread.
|
||||||
|
* The tasks accepts a pair of voicemail object and the recording Uri as the param.
|
||||||
|
* The result returned is the error exception, if any, encountered during the operation.
|
||||||
|
*/
|
||||||
|
private class InsertVoicemailTask extends AsyncTask<Pair<Voicemail, Uri>, Void, Exception> {
|
||||||
|
@Override
|
||||||
|
protected Exception doInBackground(Pair<Voicemail, Uri>... params) {
|
||||||
|
if (params.length > 0) {
|
||||||
|
try {
|
||||||
|
insertVoicemail(params[0].first, params[0].second);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insertVoicemail(Voicemail voicemail, Uri recordingUri) throws IOException {
|
||||||
|
InputStream inputAudioStream = recordingUri == null ? null :
|
||||||
|
getContentResolver().openInputStream(recordingUri);
|
||||||
|
Uri newVoicemailUri = mVoicemailProviderHelper.insert(voicemail);
|
||||||
|
logger.i("Inserted new voicemail URI: " + newVoicemailUri);
|
||||||
|
if (inputAudioStream != null) {
|
||||||
|
OutputStream outputStream = null;
|
||||||
|
try {
|
||||||
|
outputStream = mVoicemailProviderHelper.setVoicemailContent(
|
||||||
|
newVoicemailUri, getContentResolver().getType(recordingUri));
|
||||||
|
copyStreamData(inputAudioStream, outputStream);
|
||||||
|
} finally {
|
||||||
|
CloseUtils.closeQuietly(outputStream);
|
||||||
|
CloseUtils.closeQuietly(inputAudioStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Exception error) {
|
||||||
|
if (error == null) {
|
||||||
|
// No error - done.
|
||||||
|
finish();
|
||||||
|
} else {
|
||||||
|
handleError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copyStreamData(InputStream in, OutputStream out) throws IOException {
|
||||||
|
// Copy 8K chunk at a time.
|
||||||
|
byte[] data = new byte[8 * 1024];
|
||||||
|
int numBytes;
|
||||||
|
while ((numBytes = in.read(data)) > 0) {
|
||||||
|
out.write(data, 0, numBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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.example.android.voicemail.common.core;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a single voicemail stored in the voicemail content provider.
|
||||||
|
* <p>
|
||||||
|
* The presence of a field is indicated by a corresponding 'has' method.
|
||||||
|
*/
|
||||||
|
public interface Voicemail {
|
||||||
|
/**
|
||||||
|
* Which mailbox the message is sitting in.
|
||||||
|
* <p>
|
||||||
|
* Note that inbox and deleted are alone insufficient, because we may have a provider that is
|
||||||
|
* not able to undelete (re-upload) a message. Thus we need a state to represent the (common)
|
||||||
|
* case where the user has deleted a message (which results in the message being removed from
|
||||||
|
* the server) and then restored the message (where we are unable to re-upload the message to
|
||||||
|
* the server). That's what the undeleted state is for.
|
||||||
|
* <p>
|
||||||
|
* The presence of an undeleted mailbox prevents the voicemail source from having to keep a list
|
||||||
|
* of all such deleted-then-restored message ids, without which it would be unable to tell the
|
||||||
|
* difference between a message that has been deleted-then-restored by the user and a message
|
||||||
|
* which has been deleted on the server and should now be removed (for example one removed via
|
||||||
|
* an IVR).
|
||||||
|
*/
|
||||||
|
public enum Mailbox {
|
||||||
|
/** After being fetched from the server, a message usually starts in the inbox. */
|
||||||
|
INBOX(0),
|
||||||
|
/** Indicates that a message has been deleted. */
|
||||||
|
DELETED(1),
|
||||||
|
/** Restored from having been deleted, distinct from being in the inbox. */
|
||||||
|
UNDELETED(2);
|
||||||
|
|
||||||
|
private final int mValue;
|
||||||
|
|
||||||
|
private Mailbox(int value) {
|
||||||
|
mValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the DB value of this mailbox state. */
|
||||||
|
public int getValue() {
|
||||||
|
return mValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The identifier of the voicemail in the content provider.
|
||||||
|
* <p>
|
||||||
|
* This may be missing in the case of a new {@link Voicemail} that we plan to insert into the
|
||||||
|
* content provider, since until it has been inserted we don't know what id it should have. If
|
||||||
|
* none is specified, we return -1.
|
||||||
|
*/
|
||||||
|
public long getId();
|
||||||
|
|
||||||
|
public boolean hasId();
|
||||||
|
|
||||||
|
/** The number of the person leaving the voicemail, empty string if unknown, null if not set. */
|
||||||
|
public String getNumber();
|
||||||
|
|
||||||
|
public boolean hasNumber();
|
||||||
|
|
||||||
|
/** The timestamp the voicemail was received, in millis since the epoch, zero if not set. */
|
||||||
|
public long getTimestampMillis();
|
||||||
|
|
||||||
|
public boolean hasTimestampMillis();
|
||||||
|
|
||||||
|
/** Gets the duration of the voicemail in millis, or zero if the field is not set. */
|
||||||
|
public long getDuration();
|
||||||
|
|
||||||
|
public boolean hasDuration();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the package name of the source that added this voicemail, or null if this field is
|
||||||
|
* not set.
|
||||||
|
*/
|
||||||
|
public String getSource();
|
||||||
|
|
||||||
|
public boolean hasSource();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the provider-specific data type stored with the voicemail, or null if this field is
|
||||||
|
* not set.
|
||||||
|
* <p>
|
||||||
|
* Provider data is typically used as an identifier to uniquely identify the voicemail against
|
||||||
|
* the voicemail server. This is likely to be something like the IMAP UID, or some other
|
||||||
|
* server-generated identifying string.
|
||||||
|
*/
|
||||||
|
// TODO:4: we should rename the provider data field to be called provider message id, which is
|
||||||
|
// more explicit. I think we should also rename the get id method to get content id or something
|
||||||
|
// like that.
|
||||||
|
public String getProviderData();
|
||||||
|
|
||||||
|
public boolean hasProviderData();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the Uri that can be used to refer to this voicemail, and to make it play.
|
||||||
|
* <p>
|
||||||
|
* Returns null if we don't know the Uri.
|
||||||
|
*/
|
||||||
|
public Uri getUri();
|
||||||
|
|
||||||
|
public boolean hasUri();
|
||||||
|
|
||||||
|
/** Tells us which mailbox the message is sitting in, returns null if this is not set. */
|
||||||
|
public Voicemail.Mailbox getMailbox();
|
||||||
|
|
||||||
|
public boolean hasMailbox();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells us if the voicemail message has been marked as read.
|
||||||
|
* <p>
|
||||||
|
* Always returns false if this field has not been set, i.e. if hasRead() returns false.
|
||||||
|
*/
|
||||||
|
public boolean isRead();
|
||||||
|
|
||||||
|
public boolean hasRead();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells us if there is content stored at the Uri.
|
||||||
|
*/
|
||||||
|
public boolean hasContent();
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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.example.android.voicemail.common.core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object that can be used to apply filter on voicemail queries made through the voicemail helper
|
||||||
|
* interface.
|
||||||
|
*/
|
||||||
|
public interface VoicemailFilter {
|
||||||
|
/** Returns the where clause for this filter. Returns null if the filter is empty. */
|
||||||
|
public String getWhereClause();
|
||||||
|
}
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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.example.android.voicemail.common.core;
|
||||||
|
|
||||||
|
import static com.android.providers.voicemail.api.VoicemailProvider.Tables.Voicemails.Columns.DATE;
|
||||||
|
import static com.android.providers.voicemail.api.VoicemailProvider.Tables.Voicemails.Columns.DURATION;
|
||||||
|
import static com.android.providers.voicemail.api.VoicemailProvider.Tables.Voicemails.Columns.NUMBER;
|
||||||
|
import static com.android.providers.voicemail.api.VoicemailProvider.Tables.Voicemails.Columns.PROVIDER;
|
||||||
|
import static com.android.providers.voicemail.api.VoicemailProvider.Tables.Voicemails.Columns.PROVIDER_DATA;
|
||||||
|
import static com.android.providers.voicemail.api.VoicemailProvider.Tables.Voicemails.Columns.READ_STATUS;
|
||||||
|
import static com.android.providers.voicemail.api.VoicemailProvider.Tables.Voicemails.Columns.STATE;
|
||||||
|
import static com.example.android.voicemail.common.utils.DbQueryUtils.concatenateClausesWithAnd;
|
||||||
|
import static com.example.android.voicemail.common.utils.DbQueryUtils.concatenateClausesWithOr;
|
||||||
|
|
||||||
|
import com.example.android.voicemail.common.core.Voicemail.Mailbox;
|
||||||
|
import com.example.android.voicemail.common.utils.DbQueryUtils;
|
||||||
|
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import com.android.providers.voicemail.api.VoicemailProvider;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory class to create {@link VoicemailFilter} objects for various filtering needs.
|
||||||
|
* <p>
|
||||||
|
* Factory methods like {@link #createWithMailbox(Mailbox)}, {@link #createWithReadStatus(boolean)} and
|
||||||
|
* {@link #createWithMatchingFields(Voicemail)} can be used to create a voicemail filter that matches the
|
||||||
|
* value of the specific field.
|
||||||
|
* <p>
|
||||||
|
* It it possible to combine multiple filters with OR or AND operation using the methods
|
||||||
|
* {@link #createWithOrOf(VoicemailFilter...)} and {@link #createWithAndOf(VoicemailFilter...)} respectively.
|
||||||
|
* <p>
|
||||||
|
* {@link #createWithWhereClause(String)} can be used to create an arbitrary filter for a specific where
|
||||||
|
* clause. Using this method requires the knowledge of the name of columns used in voicemail
|
||||||
|
* content provider database and is therefore less recommended.
|
||||||
|
*/
|
||||||
|
public class VoicemailFilterFactory {
|
||||||
|
/** Predefined filter for inbox only messages. */
|
||||||
|
public static final VoicemailFilter INBOX_MESSAGES_FILTER = createWithOrOf(
|
||||||
|
createWithMailbox(Mailbox.INBOX), createWithMailbox(Mailbox.UNDELETED));
|
||||||
|
/** Predefined filter for trashed messages. */
|
||||||
|
public static final VoicemailFilter TRASHED_MESSAGES_FILTER =
|
||||||
|
createWithMailbox(Mailbox.DELETED);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a voicemail filter with the specified where clause. Use this method only if you know
|
||||||
|
* and want to directly use the column names of the content provider. For most of the usages one
|
||||||
|
* the other factory methods should be good enough.
|
||||||
|
*/
|
||||||
|
public static VoicemailFilter createWithWhereClause(final String whereClause) {
|
||||||
|
return new VoicemailFilter() {
|
||||||
|
@Override
|
||||||
|
public String getWhereClause() {
|
||||||
|
return TextUtils.isEmpty(whereClause) ? null : whereClause;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getWhereClause();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a filter with fields matching the ones set in the supplied voicemail object. */
|
||||||
|
public static VoicemailFilter createWithMatchingFields(Voicemail fieldMatch) {
|
||||||
|
if (fieldMatch == null) {
|
||||||
|
throw new IllegalArgumentException("Cannot create filter null fieldMatch");
|
||||||
|
}
|
||||||
|
return VoicemailFilterFactory.createWithWhereClause(getWhereClauseForMatchingFields(fieldMatch));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a voicemail filter with the specified mailbox state. */
|
||||||
|
public static VoicemailFilter createWithMailbox(Mailbox mailbox) {
|
||||||
|
return createWithMatchingFields(VoicemailImpl.createEmptyBuilder().setMailbox(mailbox).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a voicemail filter with the specified read status. */
|
||||||
|
public static VoicemailFilter createWithReadStatus(boolean isRead) {
|
||||||
|
return createWithMatchingFields(VoicemailImpl.createEmptyBuilder().setIsRead(isRead).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Combine multiple filters with OR clause. */
|
||||||
|
public static VoicemailFilter createWithAndOf(VoicemailFilter... filters) {
|
||||||
|
return createWithWhereClause(concatenateClausesWithAnd(getClauses(filters)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Combine multiple filters with AND clause. */
|
||||||
|
public static VoicemailFilter createWithOrOf(VoicemailFilter... filters) {
|
||||||
|
return createWithWhereClause(concatenateClausesWithOr(getClauses(filters)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String[] getClauses(VoicemailFilter[] filters) {
|
||||||
|
String[] clauses = new String[filters.length];
|
||||||
|
for (int i = 0; i < filters.length; ++i) {
|
||||||
|
clauses[i] = filters[i].getWhereClause();
|
||||||
|
}
|
||||||
|
return clauses;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getWhereClauseForMatchingFields(Voicemail fieldMatch) {
|
||||||
|
List<String> clauses = new ArrayList<String>();
|
||||||
|
if (fieldMatch.hasRead()) {
|
||||||
|
clauses.add(getEqualityClause(READ_STATUS, fieldMatch.isRead() ? "1" : "0"));
|
||||||
|
}
|
||||||
|
if (fieldMatch.hasMailbox()) {
|
||||||
|
clauses.add(getEqualityClause(STATE,
|
||||||
|
Integer.toString(fieldMatch.getMailbox().getValue())));
|
||||||
|
}
|
||||||
|
if (fieldMatch.hasNumber()) {
|
||||||
|
clauses.add(getEqualityClause(NUMBER, fieldMatch.getNumber()));
|
||||||
|
}
|
||||||
|
if (fieldMatch.hasSource()) {
|
||||||
|
clauses.add(getEqualityClause(PROVIDER, fieldMatch.getSource()));
|
||||||
|
}
|
||||||
|
if (fieldMatch.hasProviderData()) {
|
||||||
|
clauses.add(getEqualityClause(PROVIDER_DATA, fieldMatch.getProviderData()));
|
||||||
|
}
|
||||||
|
if (fieldMatch.hasDuration()) {
|
||||||
|
clauses.add(getEqualityClause(DURATION, Long.toString(fieldMatch.getDuration())));
|
||||||
|
}
|
||||||
|
if (fieldMatch.hasTimestampMillis()) {
|
||||||
|
clauses.add(getEqualityClause(DATE, Long.toString(fieldMatch.getTimestampMillis())));
|
||||||
|
}
|
||||||
|
// Empty filter.
|
||||||
|
if (clauses.size() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return concatenateClausesWithAnd(clauses.toArray(new String[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getEqualityClause(String field, String value) {
|
||||||
|
return DbQueryUtils.getEqualityClause(VoicemailProvider.Tables.Voicemails.NAME, field,
|
||||||
|
value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,270 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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.example.android.voicemail.common.core;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple immutable data object to represent a voicemail.
|
||||||
|
*/
|
||||||
|
public final class VoicemailImpl implements Voicemail {
|
||||||
|
private final Long mTimestamp;
|
||||||
|
private final String mNumber;
|
||||||
|
private final Long mId;
|
||||||
|
private final Long mDuration;
|
||||||
|
private final String mSource;
|
||||||
|
private final String mProviderData;
|
||||||
|
private final Uri mUri;
|
||||||
|
private final Voicemail.Mailbox mMailbox;
|
||||||
|
private final Boolean mIsRead;
|
||||||
|
private final boolean mHasContent;
|
||||||
|
|
||||||
|
// TODO: 5. We should probably consider changing "number" everywhere to "contact", given that
|
||||||
|
// it's not clear that these will be restricted to telephone numbers.
|
||||||
|
|
||||||
|
private VoicemailImpl(
|
||||||
|
Long timestamp,
|
||||||
|
String number,
|
||||||
|
Long id,
|
||||||
|
Long duration,
|
||||||
|
String source,
|
||||||
|
String providerData,
|
||||||
|
Uri uri,
|
||||||
|
Voicemail.Mailbox mailbox,
|
||||||
|
Boolean isRead,
|
||||||
|
boolean hasContent) {
|
||||||
|
mId = id;
|
||||||
|
mNumber = number;
|
||||||
|
mDuration = duration;
|
||||||
|
mTimestamp = timestamp;
|
||||||
|
mSource = source;
|
||||||
|
mProviderData = providerData;
|
||||||
|
mUri = uri;
|
||||||
|
mMailbox = mailbox;
|
||||||
|
mIsRead = isRead;
|
||||||
|
mHasContent = hasContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a {@link Builder} for a new {@link Voicemail} to be inserted.
|
||||||
|
* <p>
|
||||||
|
* The number and the timestamp are mandatory for insertion.
|
||||||
|
*/
|
||||||
|
public static Builder createForInsertion(long timestamp, String number) {
|
||||||
|
return new Builder().setNumber(number).setTimestamp(timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a {@link Builder} for updating a {@link Voicemail}.
|
||||||
|
* <p>
|
||||||
|
* Only the id of the voicemail to be updated is mandatory.
|
||||||
|
*/
|
||||||
|
public static Builder createForUpdate(long id) {
|
||||||
|
return new Builder().setId(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a {@link Builder} for a new {@link Voicemail}, such as one suitable for returning from
|
||||||
|
* a list of results or creating from scratch.
|
||||||
|
*/
|
||||||
|
public static Builder createEmptyBuilder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder pattern for creating a {@link VoicemailImpl}.
|
||||||
|
* <p>
|
||||||
|
* All fields are optional, and can be set with the various {@code setXXX} methods.
|
||||||
|
*/
|
||||||
|
public static class Builder {
|
||||||
|
private Long mBuilderTimestamp;
|
||||||
|
private String mBuilderNumber;
|
||||||
|
private Long mBuilderId;
|
||||||
|
private Long mBuilderDuration;
|
||||||
|
private String mBuilderSource;
|
||||||
|
private String mBuilderProviderData;
|
||||||
|
private Uri mBuilderUri;
|
||||||
|
private Voicemail.Mailbox mBuilderMailbox;
|
||||||
|
private Boolean mBuilderIsRead;
|
||||||
|
private boolean mBuilderHasContent;
|
||||||
|
|
||||||
|
/** You should use the correct factory method to construct a builder. */
|
||||||
|
private Builder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setNumber(String number) {
|
||||||
|
mBuilderNumber = number;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setTimestamp(long timestamp) {
|
||||||
|
mBuilderTimestamp = timestamp;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setId(long id) {
|
||||||
|
mBuilderId = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setDuration(long duration) {
|
||||||
|
mBuilderDuration = duration;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setSource(String source) {
|
||||||
|
mBuilderSource = source;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setProviderData(String providerData) {
|
||||||
|
mBuilderProviderData = providerData;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setUri(Uri uri) {
|
||||||
|
mBuilderUri = uri;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setMailbox(Voicemail.Mailbox mailbox) {
|
||||||
|
mBuilderMailbox = mailbox;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setIsRead(boolean isRead) {
|
||||||
|
mBuilderIsRead = isRead;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setHasContent(boolean hasContent) {
|
||||||
|
mBuilderHasContent = hasContent;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public VoicemailImpl build() {
|
||||||
|
return new VoicemailImpl(mBuilderTimestamp, mBuilderNumber, mBuilderId,
|
||||||
|
mBuilderDuration,
|
||||||
|
mBuilderSource, mBuilderProviderData, mBuilderUri, mBuilderMailbox,
|
||||||
|
mBuilderIsRead,
|
||||||
|
mBuilderHasContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getId() {
|
||||||
|
return hasId() ? mId : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasId() {
|
||||||
|
return mId != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNumber() {
|
||||||
|
return mNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNumber() {
|
||||||
|
return mNumber != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getTimestampMillis() {
|
||||||
|
return hasTimestampMillis() ? mTimestamp : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasTimestampMillis() {
|
||||||
|
return mTimestamp != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getDuration() {
|
||||||
|
return hasDuration() ? mDuration : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasDuration() {
|
||||||
|
return mDuration != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSource() {
|
||||||
|
return mSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasSource() {
|
||||||
|
return mSource != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProviderData() {
|
||||||
|
return mProviderData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasProviderData() {
|
||||||
|
return mProviderData != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Uri getUri() {
|
||||||
|
return mUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasUri() {
|
||||||
|
return mUri != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mailbox getMailbox() {
|
||||||
|
return mMailbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasMailbox() {
|
||||||
|
return mMailbox != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRead() {
|
||||||
|
return hasRead() ? mIsRead : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasRead() {
|
||||||
|
return mIsRead != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasContent() {
|
||||||
|
return mHasContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "VoicemailImpl [mTimestamp=" + mTimestamp + ", mNumber=" + mNumber + ", mId=" + mId
|
||||||
|
+ ", mDuration=" + mDuration + ", mSource=" + mSource + ", mProviderData="
|
||||||
|
+ mProviderData + ", mUri=" + mUri + ", mMailbox=" + mMailbox + ", mIsRead="
|
||||||
|
+ mIsRead + ", mHasContent=" + mHasContent + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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.example.android.voicemail.common.core;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores and retrieves relevant bits of voicemails in an Intent.
|
||||||
|
*/
|
||||||
|
public class VoicemailIntentUtils {
|
||||||
|
/** The String used when storing provider data in intents. */
|
||||||
|
public static final String PROVIDER_DATA_KEY = VoicemailImpl.class.getName() + ".PROVIDER_DATA";
|
||||||
|
|
||||||
|
// Private constructor, utility class.
|
||||||
|
private VoicemailIntentUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the {@link Voicemail#getProviderData()} value into an intent.
|
||||||
|
*
|
||||||
|
* @see #extractIdentifierFromIntent(Intent)
|
||||||
|
*/
|
||||||
|
public static void storeIdentifierInIntent(Intent intent, Voicemail message) {
|
||||||
|
intent.putExtra(PROVIDER_DATA_KEY, message.getProviderData());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the {@link Voicemail#getProviderData()} from an intent.
|
||||||
|
* <p>
|
||||||
|
* Returns null if the Intent contains no such identifier, or has no extras.
|
||||||
|
*
|
||||||
|
* @see #storeIdentifierInIntent(Intent, Voicemail)
|
||||||
|
*/
|
||||||
|
public static String extractIdentifierFromIntent(Intent intent) {
|
||||||
|
Bundle extras = intent.getExtras();
|
||||||
|
return (extras == null ? null : extras.getString(PROVIDER_DATA_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies the extras stored by {@link #storeIdentifierInIntent(Intent, Voicemail)} between two
|
||||||
|
* intents.
|
||||||
|
*/
|
||||||
|
public static void copyExtrasBetween(Intent from, Intent to) {
|
||||||
|
Bundle extras = from.getExtras();
|
||||||
|
if (extras.containsKey(PROVIDER_DATA_KEY)) {
|
||||||
|
to.putExtra(PROVIDER_DATA_KEY, extras.getString(PROVIDER_DATA_KEY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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.example.android.voicemail.common.core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The payload for a voicemail, usually audio data.
|
||||||
|
*/
|
||||||
|
public interface VoicemailPayload {
|
||||||
|
public String getMimeType();
|
||||||
|
|
||||||
|
public byte[] getBytes();
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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.example.android.voicemail.common.core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Concrete implementation of {@link VoicemailPayload} interface.
|
||||||
|
*/
|
||||||
|
public class VoicemailPayloadImpl implements VoicemailPayload {
|
||||||
|
private final String mMimeType;
|
||||||
|
private final byte[] mBytes;
|
||||||
|
|
||||||
|
public VoicemailPayloadImpl(String mimeType, byte[] bytes) {
|
||||||
|
mMimeType = mimeType;
|
||||||
|
mBytes = bytes.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getBytes() {
|
||||||
|
return mBytes.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMimeType() {
|
||||||
|
return mMimeType;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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.example.android.voicemail.common.core;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import com.android.providers.voicemail.api.VoicemailProvider;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a simple interface to manipulate voicemails within the voicemail content provider.
|
||||||
|
* <p>
|
||||||
|
* Methods on this interface throw checked exceptions only where the corresponding underlying
|
||||||
|
* methods perform an operation that itself requires a checked exception. In all other cases a
|
||||||
|
* {@link RuntimeException} will be thrown here.
|
||||||
|
* <p>
|
||||||
|
* These methods are blocking, and will return control to the caller only when the operation
|
||||||
|
* completes. You should not call any of these methods from your main ui thread, as this may result
|
||||||
|
* in your application becoming unresponsive.
|
||||||
|
*/
|
||||||
|
public interface VoicemailProviderHelper {
|
||||||
|
|
||||||
|
/** Sort order to return results by. */
|
||||||
|
public enum SortOrder {
|
||||||
|
ASCENDING,
|
||||||
|
DESCENDING,
|
||||||
|
/** Default sort order returned by DB. (Typically Ascending, but no guarantees made). */
|
||||||
|
DEFAULT
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all voicemails accessible to this voicemail content provider.
|
||||||
|
*
|
||||||
|
* @return the number of voicemails deleted
|
||||||
|
*/
|
||||||
|
public int deleteAll();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts a new voicemail into the voicemail content provider.
|
||||||
|
*
|
||||||
|
* @param voicemail data to be inserted
|
||||||
|
* @return {@link Uri} of the newly inserted {@link Voicemail}
|
||||||
|
* @throws IllegalArgumentException if any of the following are true:
|
||||||
|
* <ul>
|
||||||
|
* <li>your voicemail is missing a timestamp</li>
|
||||||
|
* <li>your voiceamil is missing a number</li>
|
||||||
|
* <li>your voicemail is missing the provider id field</li>
|
||||||
|
* <li>voicemail has an id (which would indicate that it has already been inserted)
|
||||||
|
* </li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public Uri insert(Voicemail voicemail);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link Voicemail} whose provider data matches the given value.
|
||||||
|
* <p>
|
||||||
|
* It is expected that there be one such voicemail. Returns null if no such voicemail exists,
|
||||||
|
* and returns one chosen arbitrarily if more than one exists.
|
||||||
|
*/
|
||||||
|
public Voicemail findVoicemailByProviderData(String providerData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link Voicemail} corresponding to a given Uri. The uri must correspond to a
|
||||||
|
* unique voicemail record.
|
||||||
|
* <p>
|
||||||
|
* Returns null if no voicemail was found that exactly matched the given uri.
|
||||||
|
*/
|
||||||
|
public Voicemail findVoicemailByUri(Uri uri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates an existing voicemail in the content provider.
|
||||||
|
* <p>
|
||||||
|
* Note that <b>only the fields that are set</b> on the {@link Voicemail} that you provide will
|
||||||
|
* be used to perform the update. The remaining fields will be left unmodified. To mark a
|
||||||
|
* voicemail as read, create a new {@link Voicemail} that is marked as read, and call update.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if you provide a {@link Voicemail} that already has a Uri
|
||||||
|
* set, because we don't support altering the Uri of a voicemail, and this most
|
||||||
|
* likely implies that you're using this api incorrectly
|
||||||
|
* @return the number of rows that were updated
|
||||||
|
*/
|
||||||
|
public int update(Uri uri, Voicemail voicemail);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the OutputStream to write the voicemail content with the given mime type.
|
||||||
|
* <p>
|
||||||
|
* <b>Remember to close the OutputStream after you're done writing.</b>
|
||||||
|
*
|
||||||
|
* @throws IOException if there is a problem creating the file or no voicemail is found matching
|
||||||
|
* the given Uri
|
||||||
|
*/
|
||||||
|
public OutputStream setVoicemailContent(Uri voicemailUri, String mimeType) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all the voicemails accessible to this voicemail content provider.
|
||||||
|
*
|
||||||
|
* @return the list of voicemails, no guarantee is made about the ordering
|
||||||
|
*/
|
||||||
|
public List<Voicemail> getAllVoicemails();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as {@link #getAllVoicemails()} but also sorts them by the requested column and allows to
|
||||||
|
* set a filter.
|
||||||
|
*
|
||||||
|
* @param filter The filter to apply while retrieving voicemails.
|
||||||
|
* @param sortColumn The column to sort by. Must be one of the values defined in
|
||||||
|
* {@link VoicemailProvider.Tables.Voicemails.Columns}.
|
||||||
|
* @param sortOrder Order to sort by
|
||||||
|
* @return the list of voicemails, sorted by the requested DB column in specified sort order.
|
||||||
|
*/
|
||||||
|
public List<Voicemail> getAllVoicemails(VoicemailFilter filter,
|
||||||
|
String sortColumn, SortOrder sortOrder);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Uri for the voicemail with the specified message Id.
|
||||||
|
*/
|
||||||
|
public Uri getUriForVoicemailWithId(long id);
|
||||||
|
}
|
||||||
@@ -0,0 +1,305 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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.example.android.voicemail.common.core;
|
||||||
|
|
||||||
|
import static com.android.providers.voicemail.api.VoicemailProvider.CONTENT_URI_PROVIDER_ID_QUERY;
|
||||||
|
import static com.android.providers.voicemail.api.VoicemailProvider.Tables.Voicemails.Columns.DATA_MIME_TYPE;
|
||||||
|
import static com.android.providers.voicemail.api.VoicemailProvider.Tables.Voicemails.Columns.DATE;
|
||||||
|
import static com.android.providers.voicemail.api.VoicemailProvider.Tables.Voicemails.Columns.DURATION;
|
||||||
|
import static com.android.providers.voicemail.api.VoicemailProvider.Tables.Voicemails.Columns.NUMBER;
|
||||||
|
import static com.android.providers.voicemail.api.VoicemailProvider.Tables.Voicemails.Columns.PROVIDER;
|
||||||
|
import static com.android.providers.voicemail.api.VoicemailProvider.Tables.Voicemails.Columns.PROVIDER_DATA;
|
||||||
|
import static com.android.providers.voicemail.api.VoicemailProvider.Tables.Voicemails.Columns.READ_STATUS;
|
||||||
|
import static com.android.providers.voicemail.api.VoicemailProvider.Tables.Voicemails.Columns.STATE;
|
||||||
|
import static com.android.providers.voicemail.api.VoicemailProvider.Tables.Voicemails.Columns._DATA_FILE_EXISTS;
|
||||||
|
import static com.android.providers.voicemail.api.VoicemailProvider.Tables.Voicemails.Columns._ID;
|
||||||
|
|
||||||
|
import com.example.android.voicemail.common.logging.Logger;
|
||||||
|
import com.example.android.voicemail.common.utils.CloseUtils;
|
||||||
|
import com.example.android.voicemail.common.utils.DbQueryUtils;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.ContentUris;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
|
||||||
|
import com.android.providers.voicemail.api.VoicemailProvider;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of the {@link VoicemailProviderHelper} interface.
|
||||||
|
*/
|
||||||
|
public final class VoicemailProviderHelpers implements VoicemailProviderHelper {
|
||||||
|
private static final Logger logger = Logger.getLogger(VoicemailProviderHelpers.class);
|
||||||
|
|
||||||
|
/** Full projection on the voicemail table, giving us all the columns. */
|
||||||
|
private static final String[] FULL_PROJECTION = new String[] {
|
||||||
|
_ID, _DATA_FILE_EXISTS, NUMBER, DURATION, DATE, PROVIDER, PROVIDER_DATA, READ_STATUS,
|
||||||
|
STATE
|
||||||
|
};
|
||||||
|
|
||||||
|
private final ContentResolver mContentResolver;
|
||||||
|
private final Uri mBaseUri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of {@link VoicemailProviderHelpers} that wraps the supplied content
|
||||||
|
* provider.
|
||||||
|
*
|
||||||
|
* @param contentResolver the ContentResolver used for opening the output stream to read and
|
||||||
|
* write to the file
|
||||||
|
*/
|
||||||
|
private VoicemailProviderHelpers(Uri baseUri, ContentResolver contentResolver) {
|
||||||
|
mContentResolver = contentResolver;
|
||||||
|
mBaseUri = baseUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a VoicemailProviderHelper with full access to all voicemails.
|
||||||
|
* <p>
|
||||||
|
* Requires the manifest permissions
|
||||||
|
* <code>com.android.providers.voicemail.permission.READ_WRITE_ALL_VOICEMAIL</code> and
|
||||||
|
* <code>com.android.providers.voicemail.permission.READ_WRITE_OWN_VOICEMAIL</code>.
|
||||||
|
*/
|
||||||
|
public static VoicemailProviderHelper createFullVoicemailProvider(Context context) {
|
||||||
|
return new VoicemailProviderHelpers(VoicemailProvider.CONTENT_URI,
|
||||||
|
context.getContentResolver());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a VoicemailProviderHelper with limited access to voicemails created by this
|
||||||
|
* source.
|
||||||
|
* <p>
|
||||||
|
* Requires the manifest permission
|
||||||
|
* <code>com.android.providers.voicemail.permission.READ_WRITE_OWN_VOICEMAIL</code>.
|
||||||
|
*/
|
||||||
|
public static VoicemailProviderHelper createPackageScopedVoicemailProvider(Context context) {
|
||||||
|
Uri providerUri = Uri.withAppendedPath(VoicemailProvider.CONTENT_URI_PROVIDER_QUERY,
|
||||||
|
context.getPackageName());
|
||||||
|
return new VoicemailProviderHelpers(providerUri, context.getContentResolver());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Uri insert(Voicemail voicemail) {
|
||||||
|
check(!voicemail.hasId(), "Inserted voicemails must not have an id", voicemail);
|
||||||
|
check(voicemail.hasTimestampMillis(), "Inserted voicemails must have a timestamp",
|
||||||
|
voicemail);
|
||||||
|
check(voicemail.hasNumber(), "Inserted voicemails must have a number", voicemail);
|
||||||
|
logger.d(String.format("Inserting new voicemail: %s", voicemail));
|
||||||
|
ContentValues contentValues = getContentValues(voicemail);
|
||||||
|
return mContentResolver.insert(mBaseUri, contentValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int update(Uri uri, Voicemail voicemail) {
|
||||||
|
check(!voicemail.hasUri(), "Can't update the Uri of a voicemail", voicemail);
|
||||||
|
logger.d("Updating voicemail: " + voicemail + " for uri: " + uri);
|
||||||
|
ContentValues values = getContentValues(voicemail);
|
||||||
|
return mContentResolver.update(uri, values, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OutputStream setVoicemailContent(Uri voicemailUri, String mimeType) throws IOException {
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(DATA_MIME_TYPE, mimeType);
|
||||||
|
int updatedCount = mContentResolver.update(voicemailUri, values, null, null);
|
||||||
|
if (updatedCount != 1) {
|
||||||
|
throw new IOException("Updating voicemail should have updated 1 row, was: "
|
||||||
|
+ updatedCount);
|
||||||
|
}
|
||||||
|
logger.d(String.format("Writing new voicemail content: %s", voicemailUri));
|
||||||
|
return mContentResolver.openOutputStream(voicemailUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Voicemail findVoicemailByProviderData(String providerData) {
|
||||||
|
Cursor cursor = null;
|
||||||
|
try {
|
||||||
|
cursor = mContentResolver.query(mBaseUri, FULL_PROJECTION,
|
||||||
|
DbQueryUtils.getEqualityClause(
|
||||||
|
VoicemailProvider.Tables.Voicemails.NAME, PROVIDER_DATA, providerData),
|
||||||
|
null, null);
|
||||||
|
if (cursor.getCount() != 1) {
|
||||||
|
logger.w("Expected 1 voicemail matching providerData " + providerData + ", got " +
|
||||||
|
cursor.getCount());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
cursor.moveToFirst();
|
||||||
|
return getVoicemailFromCursor(cursor);
|
||||||
|
} finally {
|
||||||
|
CloseUtils.closeQuietly(cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Voicemail findVoicemailByUri(Uri uri) {
|
||||||
|
Cursor cursor = null;
|
||||||
|
try {
|
||||||
|
cursor = mContentResolver.query(uri, FULL_PROJECTION, null, null, null);
|
||||||
|
if (cursor.getCount() != 1) {
|
||||||
|
logger.w("Expected 1 voicemail matching uri " + uri + ", got " + cursor.getCount());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
cursor.moveToFirst();
|
||||||
|
Voicemail voicemail = getVoicemailFromCursor(cursor);
|
||||||
|
// Make sure this is an exact match.
|
||||||
|
if (voicemail.getUri().equals(uri)) {
|
||||||
|
return voicemail;
|
||||||
|
} else {
|
||||||
|
logger.w("Queried uri: " + uri + " do not represent a unique voicemail record.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
CloseUtils.closeQuietly(cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Uri getUriForVoicemailWithId(long id) {
|
||||||
|
return ContentUris.withAppendedId(mBaseUri, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that an assertion is true.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if the assertion is false, along with a suitable message
|
||||||
|
* including a toString() representation of the voicemail
|
||||||
|
*/
|
||||||
|
private void check(boolean assertion, String message, Voicemail voicemail) {
|
||||||
|
if (!assertion) {
|
||||||
|
throw new IllegalArgumentException(message + ": " + voicemail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int deleteAll() {
|
||||||
|
logger.i(String.format("Deleting all voicemails"));
|
||||||
|
return mContentResolver.delete(mBaseUri, "", new String[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Voicemail> getAllVoicemails() {
|
||||||
|
return getAllVoicemails(null, null, SortOrder.DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Voicemail> getAllVoicemails(VoicemailFilter filter,
|
||||||
|
String sortColumn, SortOrder sortOrder) {
|
||||||
|
logger.i(String.format("Fetching all voicemails"));
|
||||||
|
Cursor cursor = null;
|
||||||
|
try {
|
||||||
|
cursor = mContentResolver.query(mBaseUri, FULL_PROJECTION,
|
||||||
|
filter != null ? filter.getWhereClause() : null,
|
||||||
|
null, getSortBy(sortColumn, sortOrder));
|
||||||
|
List<Voicemail> results = new ArrayList<Voicemail>(cursor.getCount());
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
// A performance optimisation is possible here.
|
||||||
|
// The helper method extracts the column indices once every time it is called,
|
||||||
|
// whilst
|
||||||
|
// we could extract them all up front (without the benefit of the re-use of the
|
||||||
|
// helper
|
||||||
|
// method code).
|
||||||
|
// At the moment I'm pretty sure the benefits outweigh the costs, so leaving as-is.
|
||||||
|
results.add(getVoicemailFromCursor(cursor));
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
} finally {
|
||||||
|
CloseUtils.closeQuietly(cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSortBy(String column, SortOrder sortOrder) {
|
||||||
|
if (column == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
switch (sortOrder) {
|
||||||
|
case ASCENDING:
|
||||||
|
return column + " ASC";
|
||||||
|
case DESCENDING:
|
||||||
|
return column + " DESC";
|
||||||
|
case DEFAULT:
|
||||||
|
return column;
|
||||||
|
}
|
||||||
|
// Should never reach here.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private VoicemailImpl getVoicemailFromCursor(Cursor cursor) {
|
||||||
|
long id = cursor.getLong(cursor.getColumnIndexOrThrow(_ID));
|
||||||
|
String provider = cursor.getString(cursor.getColumnIndexOrThrow(PROVIDER));
|
||||||
|
Uri voicemailUri = ContentUris.withAppendedId(
|
||||||
|
Uri.withAppendedPath(CONTENT_URI_PROVIDER_ID_QUERY, provider), id);
|
||||||
|
VoicemailImpl voicemail = VoicemailImpl
|
||||||
|
.createEmptyBuilder()
|
||||||
|
.setTimestamp(cursor.getLong(cursor.getColumnIndexOrThrow(DATE)))
|
||||||
|
.setNumber(cursor.getString(cursor.getColumnIndexOrThrow(NUMBER)))
|
||||||
|
.setId(id)
|
||||||
|
.setDuration(cursor.getLong(cursor.getColumnIndexOrThrow(DURATION)))
|
||||||
|
.setSource(provider)
|
||||||
|
.setProviderData(cursor.getString(cursor.getColumnIndexOrThrow(PROVIDER_DATA)))
|
||||||
|
.setUri(voicemailUri)
|
||||||
|
.setHasContent(cursor.getInt(cursor.getColumnIndexOrThrow(_DATA_FILE_EXISTS)) == 1)
|
||||||
|
.setIsRead(cursor.getInt(cursor.getColumnIndexOrThrow(READ_STATUS)) == 1)
|
||||||
|
.setMailbox(
|
||||||
|
mapValueToMailBoxEnum(cursor.getInt(cursor.getColumnIndexOrThrow(STATE))))
|
||||||
|
.build();
|
||||||
|
return voicemail;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Voicemail.Mailbox mapValueToMailBoxEnum(int value) {
|
||||||
|
for (Voicemail.Mailbox mailbox : Voicemail.Mailbox.values()) {
|
||||||
|
if (mailbox.getValue() == value) {
|
||||||
|
return mailbox;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Value: " + value + " not valid for Voicemail.Mailbox.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps structured {@link Voicemail} to {@link ContentValues} understood by content provider.
|
||||||
|
*/
|
||||||
|
private ContentValues getContentValues(Voicemail voicemail) {
|
||||||
|
ContentValues contentValues = new ContentValues();
|
||||||
|
if (voicemail.hasTimestampMillis()) {
|
||||||
|
contentValues.put(DATE, String.valueOf(voicemail.getTimestampMillis()));
|
||||||
|
}
|
||||||
|
if (voicemail.hasNumber()) {
|
||||||
|
contentValues.put(NUMBER, voicemail.getNumber());
|
||||||
|
}
|
||||||
|
if (voicemail.hasDuration()) {
|
||||||
|
contentValues.put(DURATION, String.valueOf(voicemail.getDuration()));
|
||||||
|
}
|
||||||
|
if (voicemail.hasSource()) {
|
||||||
|
contentValues.put(PROVIDER, voicemail.getSource());
|
||||||
|
}
|
||||||
|
if (voicemail.hasProviderData()) {
|
||||||
|
contentValues.put(PROVIDER_DATA, voicemail.getProviderData());
|
||||||
|
}
|
||||||
|
if (voicemail.hasRead()) {
|
||||||
|
contentValues.put(READ_STATUS, voicemail.isRead() ? 1 : 0);
|
||||||
|
}
|
||||||
|
if (voicemail.hasMailbox()) {
|
||||||
|
contentValues.put(STATE, voicemail.getMailbox().getValue());
|
||||||
|
}
|
||||||
|
return contentValues;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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.example.android.voicemail.common.inject;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this annotation to mark the fields of your Activity as being injectable.
|
||||||
|
* <p>
|
||||||
|
* See the {@link Injector} class for more details of how this operates.
|
||||||
|
*/
|
||||||
|
@Target({ ElementType.FIELD })
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface InjectView {
|
||||||
|
/**
|
||||||
|
* The resource id of the View to find and inject.
|
||||||
|
*/
|
||||||
|
public int value();
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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.example.android.voicemail.common.inject;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Very lightweight form of injection, inspired by RoboGuice, for injecting common ui elements.
|
||||||
|
* <p>
|
||||||
|
* Usage is very simple. In your Activity, define some fields as follows:
|
||||||
|
*
|
||||||
|
* <pre class="code">
|
||||||
|
* @InjectView(R.id.fetch_button)
|
||||||
|
* private Button mFetchButton;
|
||||||
|
* @InjectView(R.id.submit_button)
|
||||||
|
* private Button mSubmitButton;
|
||||||
|
* @InjectView(R.id.main_view)
|
||||||
|
* private TextView mTextView;
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* Then, inside your Activity's onCreate() method, perform the injection like this:
|
||||||
|
*
|
||||||
|
* <pre class="code">
|
||||||
|
* setContentView(R.layout.main_layout);
|
||||||
|
* Injector.get(this).inject();
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* See the {@link #inject()} method for full details of how it works. Note that the fields are
|
||||||
|
* fetched and assigned at the time you call {@link #inject()}, consequently you should not do this
|
||||||
|
* until after you've called the setContentView() method.
|
||||||
|
*/
|
||||||
|
public final class Injector {
|
||||||
|
private final Activity mActivity;
|
||||||
|
|
||||||
|
private Injector(Activity activity) {
|
||||||
|
mActivity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an {@link Injector} capable of injecting fields for the given Activity.
|
||||||
|
*/
|
||||||
|
public static Injector get(Activity activity) {
|
||||||
|
return new Injector(activity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injects all fields that are marked with the {@link InjectView} annotation.
|
||||||
|
* <p>
|
||||||
|
* For each field marked with the InjectView annotation, a call to
|
||||||
|
* {@link Activity#findViewById(int)} will be made, passing in the resource id stored in the
|
||||||
|
* value() method of the InjectView annotation as the int parameter, and the result of this call
|
||||||
|
* will be assigned to the field.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if injection fails, common causes being that you have used an
|
||||||
|
* invalid id value, or you haven't called setContentView() on your Activity.
|
||||||
|
*/
|
||||||
|
public void inject() {
|
||||||
|
for (Field field : mActivity.getClass().getDeclaredFields()) {
|
||||||
|
for (Annotation annotation : field.getAnnotations()) {
|
||||||
|
if (annotation.annotationType().equals(InjectView.class)) {
|
||||||
|
try {
|
||||||
|
Class<?> fieldType = field.getType();
|
||||||
|
int idValue = InjectView.class.cast(annotation).value();
|
||||||
|
field.setAccessible(true);
|
||||||
|
Object injectedValue = fieldType.cast(mActivity.findViewById(idValue));
|
||||||
|
if (injectedValue == null) {
|
||||||
|
throw new IllegalStateException("findViewById(" + idValue
|
||||||
|
+ ") gave null for " +
|
||||||
|
field + ", can't inject");
|
||||||
|
}
|
||||||
|
field.set(mActivity, injectedValue);
|
||||||
|
field.setAccessible(false);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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.example.android.voicemail.common.logging;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simplifies usage of Android logging class {@link Log} by abstracting the TAG field that is
|
||||||
|
* required to be passed to every logging method. Also, allows automatic insertion of the owner
|
||||||
|
* class name prefix to log outputs for better debugging.
|
||||||
|
* <p>
|
||||||
|
* Use {@link #getLogger(Class)} to create an instance of Logger that automatically inserts the
|
||||||
|
* class name as a prefix to each log output. If you do not want the class name to be prefixed to
|
||||||
|
* log output then use {@link #getLogger()} to create the instance of Logger.
|
||||||
|
*/
|
||||||
|
public class Logger {
|
||||||
|
private static final String APP_TAG = "VoicemailSample";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this method if you want your class name to be prefixed to each log output.
|
||||||
|
*/
|
||||||
|
public static Logger getLogger(Class<?> classZ) {
|
||||||
|
return new Logger(classZ.getSimpleName() + ": ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this factory method if you DO NOT want your class name to be prefixed into the log
|
||||||
|
* output.
|
||||||
|
*/
|
||||||
|
public static Logger getLogger() {
|
||||||
|
return new Logger();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String mLogPrefix;
|
||||||
|
|
||||||
|
/** No custom log prefix used. */
|
||||||
|
private Logger() {
|
||||||
|
mLogPrefix = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Use the supplied custom prefix in log output. */
|
||||||
|
private Logger(String logPrefix) {
|
||||||
|
mLogPrefix = logPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMsg(String msg) {
|
||||||
|
if (mLogPrefix != null) {
|
||||||
|
return mLogPrefix + msg;
|
||||||
|
} else {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void i(String msg) {
|
||||||
|
Log.i(APP_TAG, getMsg(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void i(String msg, Throwable t) {
|
||||||
|
Log.i(APP_TAG, getMsg(msg), t);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void d(String msg) {
|
||||||
|
Log.d(APP_TAG, getMsg(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void d(String msg, Throwable t) {
|
||||||
|
Log.d(APP_TAG, getMsg(msg), t);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void w(String msg) {
|
||||||
|
Log.w(APP_TAG, getMsg(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void w(String msg, Throwable t) {
|
||||||
|
Log.w(APP_TAG, getMsg(msg), t);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void e(String msg) {
|
||||||
|
Log.e(APP_TAG, getMsg(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void e(String msg, Throwable t) {
|
||||||
|
Log.e(APP_TAG, getMsg(msg), t);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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.example.android.voicemail.common.ui;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show common Dialogs.
|
||||||
|
* <p>
|
||||||
|
* Contains methods to show common types of Dialog. This is done both for ease of code re-use and to
|
||||||
|
* improve testability. See the implementation {@link DialogHelperImpl} for details.
|
||||||
|
*/
|
||||||
|
public interface DialogHelper {
|
||||||
|
public void showErrorMessageDialog(int titleId, Exception exception);
|
||||||
|
|
||||||
|
public void showErrorMessageDialog(String title, Exception exception);
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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.example.android.voicemail.common.ui;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses an {@link Activity} to show Dialogs.
|
||||||
|
* <p>
|
||||||
|
* Instantiate this class inside your Activity.
|
||||||
|
*
|
||||||
|
* <pre class="code">
|
||||||
|
* private final DialogHelperImpl mDialogHelper = new DialogHelperImpl(this);
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* Override your Activity's onCreateDialog(int, Bundle) method, as follows:
|
||||||
|
*
|
||||||
|
* <pre class="code">
|
||||||
|
* @Override
|
||||||
|
* protected Dialog onCreateDialog(int id, Bundle bundle) {
|
||||||
|
* return mDialogHelper.handleOnCreateDialog(id, bundle);
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* Now you can pass mDialogHelper around as a {@link DialogHelper} interface, and code that wants to
|
||||||
|
* show a Dialog can call a method like this:
|
||||||
|
*
|
||||||
|
* <pre class="code">
|
||||||
|
* mDialogHelper.showErrorMessageDialog("An exception occurred!", e);
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* If you want more flexibility, and want to mix this implementation with your own dialogs, then you
|
||||||
|
* should do something like this in your Activity:
|
||||||
|
*
|
||||||
|
* <pre class="code">
|
||||||
|
* @Override
|
||||||
|
* protected Dialog onCreateDialog(int id, Bundle bundle) {
|
||||||
|
* switch (id) {
|
||||||
|
* case ID_MY_OTHER_DIALOG:
|
||||||
|
* return new AlertDialog.Builder(this)
|
||||||
|
* .setTitle("something")
|
||||||
|
* .create();
|
||||||
|
* default:
|
||||||
|
* return mDialogHelper.handleOnCreateDialog(id, bundle);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Just be careful that you don't pick any IDs that conflict with those used by this class (which
|
||||||
|
* are documented in the public static final fields).
|
||||||
|
*/
|
||||||
|
public class DialogHelperImpl implements DialogHelper {
|
||||||
|
public static final int DIALOG_ID_EXCEPTION = 88953588;
|
||||||
|
|
||||||
|
private static final String KEY_EXCEPTION = "exception";
|
||||||
|
private static final String KEY_TITLE = "title";
|
||||||
|
|
||||||
|
private final Activity mActivity;
|
||||||
|
|
||||||
|
public DialogHelperImpl(Activity activity) {
|
||||||
|
mActivity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showErrorMessageDialog(int titleId, Exception exception) {
|
||||||
|
showErrorMessageDialog(mActivity.getString(titleId), exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showErrorMessageDialog(String title, Exception exception) {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putString(KEY_TITLE, title);
|
||||||
|
bundle.putSerializable(KEY_EXCEPTION, exception);
|
||||||
|
mActivity.showDialog(DIALOG_ID_EXCEPTION, bundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* You should call this method from your Activity's onCreateDialog(int, Bundle) method.
|
||||||
|
*/
|
||||||
|
public Dialog handleOnCreateDialog(int id, Bundle args) {
|
||||||
|
if (id == DIALOG_ID_EXCEPTION) {
|
||||||
|
Exception exception = (Exception) args.getSerializable(KEY_EXCEPTION);
|
||||||
|
String title = args.getString(KEY_TITLE);
|
||||||
|
return new AlertDialog.Builder(mActivity)
|
||||||
|
.setTitle(title)
|
||||||
|
.setMessage(convertExceptionToErrorMessage(exception))
|
||||||
|
.setCancelable(true)
|
||||||
|
.create();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String convertExceptionToErrorMessage(Exception exception) {
|
||||||
|
StringBuilder sb = new StringBuilder().append(exception.getClass().getSimpleName());
|
||||||
|
if (exception.getMessage() != null) {
|
||||||
|
sb.append("\n");
|
||||||
|
sb.append(exception.getMessage());
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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.example.android.voicemail.common.utils;
|
||||||
|
|
||||||
|
import android.database.Cursor;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility methods for closing io streams and database cursors.
|
||||||
|
*/
|
||||||
|
public class CloseUtils {
|
||||||
|
private CloseUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the argument is non-null, close the Closeable ignoring any {@link IOException}.
|
||||||
|
*/
|
||||||
|
public static void closeQuietly(Closeable closeable) {
|
||||||
|
if (closeable != null) {
|
||||||
|
try {
|
||||||
|
closeable.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// Ignore.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** If the argument is non-null, close the cursor. */
|
||||||
|
public static void closeQuietly(Cursor cursor) {
|
||||||
|
if (cursor != null) {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 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.example.android.voicemail.common.utils;
|
||||||
|
|
||||||
|
import android.database.DatabaseUtils;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static methods for helping us build database query selection strings.
|
||||||
|
*/
|
||||||
|
public class DbQueryUtils {
|
||||||
|
// Static class with helper methods, so private constructor.
|
||||||
|
private DbQueryUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a WHERE clause assert equality of a field to a value. */
|
||||||
|
public static String getEqualityClause(String table, String field, String value) {
|
||||||
|
StringBuilder clause = new StringBuilder();
|
||||||
|
clause.append(table);
|
||||||
|
clause.append(".");
|
||||||
|
clause.append(field);
|
||||||
|
clause.append(" = ");
|
||||||
|
DatabaseUtils.appendEscapedSQLString(clause, value);
|
||||||
|
return clause.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Concatenates any number of clauses using "AND". */
|
||||||
|
// TODO: 0. It worries me that I can change the following "AND" to "OR" and the provider tests
|
||||||
|
// all pass. I can also remove the braces, and the tests all pass.
|
||||||
|
public static String concatenateClausesWithAnd(String... clauses) {
|
||||||
|
return concatenateClausesWithOperation("AND", clauses);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Concatenates any number of clauses using "OR". */
|
||||||
|
public static String concatenateClausesWithOr(String... clauses) {
|
||||||
|
return concatenateClausesWithOperation("OR", clauses);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Concatenates any number of clauses using the specified operation. */
|
||||||
|
public static String concatenateClausesWithOperation(String operation, String... clauses) {
|
||||||
|
// Nothing to concatenate.
|
||||||
|
if (clauses.length == 1) {
|
||||||
|
return clauses[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
for (String clause : clauses) {
|
||||||
|
if (!TextUtils.isEmpty(clause)) {
|
||||||
|
if (builder.length() > 0) {
|
||||||
|
builder.append(" ").append(operation).append(" ");
|
||||||
|
}
|
||||||
|
builder.append("(");
|
||||||
|
builder.append(clause);
|
||||||
|
builder.append(")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user