packages/providers/ImProvider
リビジョン | e47715c8700a6e8bb8e1d992a42668ed48ec529e (tree) |
---|---|
日時 | 2009-09-21 03:32:08 |
作者 | Jean-Baptiste Queru <jbq@goog...> |
コミッター | Jean-Baptiste Queru |
Delete ImProvider
It was becoming too Google-specific to make sense
on the open-source side of things.
BUG=2116370
@@ -1,20 +0,0 @@ | ||
1 | -ifeq (0, 1) | |
2 | - | |
3 | -LOCAL_PATH:= $(call my-dir) | |
4 | -include $(CLEAR_VARS) | |
5 | - | |
6 | -LOCAL_MODULE_TAGS := user | |
7 | - | |
8 | -LOCAL_SRC_FILES := $(call all-java-files-under,src) | |
9 | - | |
10 | -LOCAL_JAVA_LIBRARIES := ext \ | |
11 | - | |
12 | -LOCAL_PACKAGE_NAME := ImProvider | |
13 | -LOCAL_CERTIFICATE := vendor/google/certs/app | |
14 | - | |
15 | -include $(BUILD_PACKAGE) | |
16 | - | |
17 | -# additionally, build sub-tests in a separate .apk | |
18 | -include $(call all-makefiles-under,$(LOCAL_PATH)) | |
19 | - | |
20 | -endif # ifeq (0, 1) |
@@ -1,32 +0,0 @@ | ||
1 | -<manifest xmlns:android="http://schemas.android.com/apk/res/android" | |
2 | - package="com.android.providers.im" | |
3 | - android:sharedUserId="com.google.uid.shared"> | |
4 | - | |
5 | - <permission android:name="com.android.providers.im.permission.READ_ONLY" | |
6 | - android:permissionGroup="android.permission-group.MESSAGES" | |
7 | - android:protectionLevel="dangerous" | |
8 | - android:label="@string/ro_perm_label" | |
9 | - android:description="@string/ro_perm_desc" /> | |
10 | - | |
11 | - <permission android:name="com.android.providers.im.permission.WRITE_ONLY" | |
12 | - android:permissionGroup="android.permission-group.MESSAGES" | |
13 | - android:protectionLevel="dangerous" | |
14 | - android:label="@string/wo_perm_label" | |
15 | - android:description="@string/wo_perm_desc" /> | |
16 | - | |
17 | - <uses-permission android:name="com.android.providers.im.permission.READ_ONLY" /> | |
18 | - <uses-permission android:name="com.android.providers.im.permission.WRITE_ONLY" /> | |
19 | - | |
20 | - <application android:process="com.google.process.gapps" | |
21 | - android:label="@string/app_label" | |
22 | - android:icon="@drawable/ic_launcher_im" | |
23 | - android:taskAffinity="android.task.im"> | |
24 | - | |
25 | - <provider android:name="ImProvider" android:authorities="im" | |
26 | - android:multiprocess="false" | |
27 | - android:readPermission="com.android.providers.im.permission.READ_ONLY" | |
28 | - android:writePermission="com.android.providers.im.permission.WRITE_ONLY" | |
29 | - android:grantUriPermissions="true" /> | |
30 | - | |
31 | - </application> | |
32 | -</manifest> |
@@ -1,190 +0,0 @@ | ||
1 | - | |
2 | - Copyright (c) 2005-2008, The Android Open Source Project | |
3 | - | |
4 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - you may not use this file except in compliance with the License. | |
6 | - | |
7 | - Unless required by applicable law or agreed to in writing, software | |
8 | - distributed under the License is distributed on an "AS IS" BASIS, | |
9 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
10 | - See the License for the specific language governing permissions and | |
11 | - limitations under the License. | |
12 | - | |
13 | - | |
14 | - Apache License | |
15 | - Version 2.0, January 2004 | |
16 | - http://www.apache.org/licenses/ | |
17 | - | |
18 | - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
19 | - | |
20 | - 1. Definitions. | |
21 | - | |
22 | - "License" shall mean the terms and conditions for use, reproduction, | |
23 | - and distribution as defined by Sections 1 through 9 of this document. | |
24 | - | |
25 | - "Licensor" shall mean the copyright owner or entity authorized by | |
26 | - the copyright owner that is granting the License. | |
27 | - | |
28 | - "Legal Entity" shall mean the union of the acting entity and all | |
29 | - other entities that control, are controlled by, or are under common | |
30 | - control with that entity. For the purposes of this definition, | |
31 | - "control" means (i) the power, direct or indirect, to cause the | |
32 | - direction or management of such entity, whether by contract or | |
33 | - otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
34 | - outstanding shares, or (iii) beneficial ownership of such entity. | |
35 | - | |
36 | - "You" (or "Your") shall mean an individual or Legal Entity | |
37 | - exercising permissions granted by this License. | |
38 | - | |
39 | - "Source" form shall mean the preferred form for making modifications, | |
40 | - including but not limited to software source code, documentation | |
41 | - source, and configuration files. | |
42 | - | |
43 | - "Object" form shall mean any form resulting from mechanical | |
44 | - transformation or translation of a Source form, including but | |
45 | - not limited to compiled object code, generated documentation, | |
46 | - and conversions to other media types. | |
47 | - | |
48 | - "Work" shall mean the work of authorship, whether in Source or | |
49 | - Object form, made available under the License, as indicated by a | |
50 | - copyright notice that is included in or attached to the work | |
51 | - (an example is provided in the Appendix below). | |
52 | - | |
53 | - "Derivative Works" shall mean any work, whether in Source or Object | |
54 | - form, that is based on (or derived from) the Work and for which the | |
55 | - editorial revisions, annotations, elaborations, or other modifications | |
56 | - represent, as a whole, an original work of authorship. For the purposes | |
57 | - of this License, Derivative Works shall not include works that remain | |
58 | - separable from, or merely link (or bind by name) to the interfaces of, | |
59 | - the Work and Derivative Works thereof. | |
60 | - | |
61 | - "Contribution" shall mean any work of authorship, including | |
62 | - the original version of the Work and any modifications or additions | |
63 | - to that Work or Derivative Works thereof, that is intentionally | |
64 | - submitted to Licensor for inclusion in the Work by the copyright owner | |
65 | - or by an individual or Legal Entity authorized to submit on behalf of | |
66 | - the copyright owner. For the purposes of this definition, "submitted" | |
67 | - means any form of electronic, verbal, or written communication sent | |
68 | - to the Licensor or its representatives, including but not limited to | |
69 | - communication on electronic mailing lists, source code control systems, | |
70 | - and issue tracking systems that are managed by, or on behalf of, the | |
71 | - Licensor for the purpose of discussing and improving the Work, but | |
72 | - excluding communication that is conspicuously marked or otherwise | |
73 | - designated in writing by the copyright owner as "Not a Contribution." | |
74 | - | |
75 | - "Contributor" shall mean Licensor and any individual or Legal Entity | |
76 | - on behalf of whom a Contribution has been received by Licensor and | |
77 | - subsequently incorporated within the Work. | |
78 | - | |
79 | - 2. Grant of Copyright License. Subject to the terms and conditions of | |
80 | - this License, each Contributor hereby grants to You a perpetual, | |
81 | - worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
82 | - copyright license to reproduce, prepare Derivative Works of, | |
83 | - publicly display, publicly perform, sublicense, and distribute the | |
84 | - Work and such Derivative Works in Source or Object form. | |
85 | - | |
86 | - 3. Grant of Patent License. Subject to the terms and conditions of | |
87 | - this License, each Contributor hereby grants to You a perpetual, | |
88 | - worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
89 | - (except as stated in this section) patent license to make, have made, | |
90 | - use, offer to sell, sell, import, and otherwise transfer the Work, | |
91 | - where such license applies only to those patent claims licensable | |
92 | - by such Contributor that are necessarily infringed by their | |
93 | - Contribution(s) alone or by combination of their Contribution(s) | |
94 | - with the Work to which such Contribution(s) was submitted. If You | |
95 | - institute patent litigation against any entity (including a | |
96 | - cross-claim or counterclaim in a lawsuit) alleging that the Work | |
97 | - or a Contribution incorporated within the Work constitutes direct | |
98 | - or contributory patent infringement, then any patent licenses | |
99 | - granted to You under this License for that Work shall terminate | |
100 | - as of the date such litigation is filed. | |
101 | - | |
102 | - 4. Redistribution. You may reproduce and distribute copies of the | |
103 | - Work or Derivative Works thereof in any medium, with or without | |
104 | - modifications, and in Source or Object form, provided that You | |
105 | - meet the following conditions: | |
106 | - | |
107 | - (a) You must give any other recipients of the Work or | |
108 | - Derivative Works a copy of this License; and | |
109 | - | |
110 | - (b) You must cause any modified files to carry prominent notices | |
111 | - stating that You changed the files; and | |
112 | - | |
113 | - (c) You must retain, in the Source form of any Derivative Works | |
114 | - that You distribute, all copyright, patent, trademark, and | |
115 | - attribution notices from the Source form of the Work, | |
116 | - excluding those notices that do not pertain to any part of | |
117 | - the Derivative Works; and | |
118 | - | |
119 | - (d) If the Work includes a "NOTICE" text file as part of its | |
120 | - distribution, then any Derivative Works that You distribute must | |
121 | - include a readable copy of the attribution notices contained | |
122 | - within such NOTICE file, excluding those notices that do not | |
123 | - pertain to any part of the Derivative Works, in at least one | |
124 | - of the following places: within a NOTICE text file distributed | |
125 | - as part of the Derivative Works; within the Source form or | |
126 | - documentation, if provided along with the Derivative Works; or, | |
127 | - within a display generated by the Derivative Works, if and | |
128 | - wherever such third-party notices normally appear. The contents | |
129 | - of the NOTICE file are for informational purposes only and | |
130 | - do not modify the License. You may add Your own attribution | |
131 | - notices within Derivative Works that You distribute, alongside | |
132 | - or as an addendum to the NOTICE text from the Work, provided | |
133 | - that such additional attribution notices cannot be construed | |
134 | - as modifying the License. | |
135 | - | |
136 | - You may add Your own copyright statement to Your modifications and | |
137 | - may provide additional or different license terms and conditions | |
138 | - for use, reproduction, or distribution of Your modifications, or | |
139 | - for any such Derivative Works as a whole, provided Your use, | |
140 | - reproduction, and distribution of the Work otherwise complies with | |
141 | - the conditions stated in this License. | |
142 | - | |
143 | - 5. Submission of Contributions. Unless You explicitly state otherwise, | |
144 | - any Contribution intentionally submitted for inclusion in the Work | |
145 | - by You to the Licensor shall be under the terms and conditions of | |
146 | - this License, without any additional terms or conditions. | |
147 | - Notwithstanding the above, nothing herein shall supersede or modify | |
148 | - the terms of any separate license agreement you may have executed | |
149 | - with Licensor regarding such Contributions. | |
150 | - | |
151 | - 6. Trademarks. This License does not grant permission to use the trade | |
152 | - names, trademarks, service marks, or product names of the Licensor, | |
153 | - except as required for reasonable and customary use in describing the | |
154 | - origin of the Work and reproducing the content of the NOTICE file. | |
155 | - | |
156 | - 7. Disclaimer of Warranty. Unless required by applicable law or | |
157 | - agreed to in writing, Licensor provides the Work (and each | |
158 | - Contributor provides its Contributions) on an "AS IS" BASIS, | |
159 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
160 | - implied, including, without limitation, any warranties or conditions | |
161 | - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
162 | - PARTICULAR PURPOSE. You are solely responsible for determining the | |
163 | - appropriateness of using or redistributing the Work and assume any | |
164 | - risks associated with Your exercise of permissions under this License. | |
165 | - | |
166 | - 8. Limitation of Liability. In no event and under no legal theory, | |
167 | - whether in tort (including negligence), contract, or otherwise, | |
168 | - unless required by applicable law (such as deliberate and grossly | |
169 | - negligent acts) or agreed to in writing, shall any Contributor be | |
170 | - liable to You for damages, including any direct, indirect, special, | |
171 | - incidental, or consequential damages of any character arising as a | |
172 | - result of this License or out of the use or inability to use the | |
173 | - Work (including but not limited to damages for loss of goodwill, | |
174 | - work stoppage, computer failure or malfunction, or any and all | |
175 | - other commercial damages or losses), even if such Contributor | |
176 | - has been advised of the possibility of such damages. | |
177 | - | |
178 | - 9. Accepting Warranty or Additional Liability. While redistributing | |
179 | - the Work or Derivative Works thereof, You may choose to offer, | |
180 | - and charge a fee for, acceptance of support, warranty, indemnity, | |
181 | - or other liability obligations and/or rights consistent with this | |
182 | - License. However, in accepting such obligations, You may act only | |
183 | - on Your own behalf and on Your sole responsibility, not on behalf | |
184 | - of any other Contributor, and only if You agree to indemnify, | |
185 | - defend, and hold each Contributor harmless for any liability | |
186 | - incurred by, or claims asserted against, such Contributor by reason | |
187 | - of your accepting any such warranty or additional liability. | |
188 | - | |
189 | - END OF TERMS AND CONDITIONS | |
190 | - |
@@ -1,25 +0,0 @@ | ||
1 | -<?xml version="1.0" encoding="utf-8"?> | |
2 | -<!-- | |
3 | -/* bubble_with_chats.xml | |
4 | -** | |
5 | -** Copyright 2009, Google Inc. | |
6 | -** | |
7 | -** Licensed under the Apache License, Version 2.0 (the "License"); | |
8 | -** you may not use this file except in compliance with the License. | |
9 | -** You may obtain a copy of the License at | |
10 | -** | |
11 | -** http://www.apache.org/licenses/LICENSE-2.0 | |
12 | -** | |
13 | -** Unless required by applicable law or agreed to in writing, software | |
14 | -** distributed under the License is distributed on an "AS IS" BASIS, | |
15 | -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
16 | -** See the License for the specific language governing permissions and | |
17 | -** limitations under the License. | |
18 | -*/ | |
19 | ---> | |
20 | -<selector xmlns:android="http://schemas.android.com/apk/res/android"> | |
21 | - <item android:state_pressed="true" android:drawable="@drawable/im_bubble_pressed" /> | |
22 | - <item android:state_selected="true" android:drawable="@drawable/im_bubble_highlight" /> | |
23 | - <item android:state_enabled="false" android:drawable="@drawable/im_bubble_normal" /> | |
24 | - <item android:drawable="@drawable/im_bubble_normal" /> | |
25 | -</selector> |
@@ -1,23 +0,0 @@ | ||
1 | -<?xml version="1.0" encoding="UTF-8"?> | |
2 | -<!-- Copyright (C) 2009 The Android Open Source Project | |
3 | - | |
4 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - you may not use this file except in compliance with the License. | |
6 | - You may obtain a copy of the License at | |
7 | - | |
8 | - http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - | |
10 | - Unless required by applicable law or agreed to in writing, software | |
11 | - distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - See the License for the specific language governing permissions and | |
14 | - limitations under the License. | |
15 | ---> | |
16 | -<resources xmlns:android="http://schemas.android.com/apk/res/android" | |
17 | - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> | |
18 | - <string name="ro_perm_label" msgid="8923213594676670163">"číst zprávy chatu"</string> | |
19 | - <string name="ro_perm_desc" msgid="6154703403388711809">"Povoluje aplikacím číst data z poskytovatele obsahu chatu."</string> | |
20 | - <string name="wo_perm_label" msgid="4771652386754813294">"chatovat"</string> | |
21 | - <string name="wo_perm_desc" msgid="470416777693330370">"Povoluje aplikacím zapisovat data do poskytovatele obsahu chatových zpráv."</string> | |
22 | - <string name="app_label" msgid="1767858328304473363">"Úložiště chatu"</string> | |
23 | -</resources> |
@@ -1,23 +0,0 @@ | ||
1 | -<?xml version="1.0" encoding="UTF-8"?> | |
2 | -<!-- Copyright (C) 2009 The Android Open Source Project | |
3 | - | |
4 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - you may not use this file except in compliance with the License. | |
6 | - You may obtain a copy of the License at | |
7 | - | |
8 | - http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - | |
10 | - Unless required by applicable law or agreed to in writing, software | |
11 | - distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - See the License for the specific language governing permissions and | |
14 | - limitations under the License. | |
15 | ---> | |
16 | -<resources xmlns:android="http://schemas.android.com/apk/res/android" | |
17 | - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> | |
18 | - <string name="ro_perm_label" msgid="8923213594676670163">"læs chatbeskeder"</string> | |
19 | - <string name="ro_perm_desc" msgid="6154703403388711809">"Tillader programmer at læse data fra IM-indholdsleverandøren."</string> | |
20 | - <string name="wo_perm_label" msgid="4771652386754813294">"skriv chatbeskeder"</string> | |
21 | - <string name="wo_perm_desc" msgid="470416777693330370">"Tillader programmer at skrive data til IM-indholdsleverandøren."</string> | |
22 | - <string name="app_label" msgid="1767858328304473363">"IM-lagring"</string> | |
23 | -</resources> |
@@ -1,23 +0,0 @@ | ||
1 | -<?xml version="1.0" encoding="UTF-8"?> | |
2 | -<!-- Copyright (C) 2009 The Android Open Source Project | |
3 | - | |
4 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - you may not use this file except in compliance with the License. | |
6 | - You may obtain a copy of the License at | |
7 | - | |
8 | - http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - | |
10 | - Unless required by applicable law or agreed to in writing, software | |
11 | - distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - See the License for the specific language governing permissions and | |
14 | - limitations under the License. | |
15 | ---> | |
16 | -<resources xmlns:android="http://schemas.android.com/apk/res/android" | |
17 | - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> | |
18 | - <string name="ro_perm_label" msgid="8923213594676670163">"Chat-Nachrichten lesen"</string> | |
19 | - <string name="ro_perm_desc" msgid="6154703403388711809">"Ermöglicht Anwendungen das Lesen von Daten des IM-Content-Providers."</string> | |
20 | - <string name="wo_perm_label" msgid="4771652386754813294">"Chat-Nachrichten verfassen"</string> | |
21 | - <string name="wo_perm_desc" msgid="470416777693330370">"Ermöglicht Anwendungen das Schreiben von Daten an den IM-Content-Provider."</string> | |
22 | - <string name="app_label" msgid="1767858328304473363">"IM-Speicher"</string> | |
23 | -</resources> |
@@ -1,23 +0,0 @@ | ||
1 | -<?xml version="1.0" encoding="UTF-8"?> | |
2 | -<!-- Copyright (C) 2009 The Android Open Source Project | |
3 | - | |
4 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - you may not use this file except in compliance with the License. | |
6 | - You may obtain a copy of the License at | |
7 | - | |
8 | - http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - | |
10 | - Unless required by applicable law or agreed to in writing, software | |
11 | - distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - See the License for the specific language governing permissions and | |
14 | - limitations under the License. | |
15 | ---> | |
16 | -<resources xmlns:android="http://schemas.android.com/apk/res/android" | |
17 | - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> | |
18 | - <string name="ro_perm_label" msgid="8923213594676670163">"ανάγνωση άμεσων μηνυμάτων"</string> | |
19 | - <string name="ro_perm_desc" msgid="6154703403388711809">"Επιτρέπει στις εφαρμογές την ανάγνωση δεδομένων από τον πάροχο περιεχομένου ανταλλαγής άμεσων μηνυμάτων (IM)."</string> | |
20 | - <string name="wo_perm_label" msgid="4771652386754813294">"εγγραφή άμεσων μηνυμάτων"</string> | |
21 | - <string name="wo_perm_desc" msgid="470416777693330370">"Επιτρέπει στις εφαρμογές την εγγραφή δεδομένων από τον πάροχο περιεχομένου ανταλλαγής άμεσων μηνυμάτων (IM)."</string> | |
22 | - <string name="app_label" msgid="1767858328304473363">"Αποθηκευτικός χώρος ανταλλαγής άμεσων μηνυμάτων (ΙΜ)"</string> | |
23 | -</resources> |
@@ -1,23 +0,0 @@ | ||
1 | -<?xml version="1.0" encoding="UTF-8"?> | |
2 | -<!-- Copyright (C) 2009 The Android Open Source Project | |
3 | - | |
4 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - you may not use this file except in compliance with the License. | |
6 | - You may obtain a copy of the License at | |
7 | - | |
8 | - http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - | |
10 | - Unless required by applicable law or agreed to in writing, software | |
11 | - distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - See the License for the specific language governing permissions and | |
14 | - limitations under the License. | |
15 | ---> | |
16 | -<resources xmlns:android="http://schemas.android.com/apk/res/android" | |
17 | - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> | |
18 | - <string name="ro_perm_label" msgid="8923213594676670163">"leer mensajes instantáneos"</string> | |
19 | - <string name="ro_perm_desc" msgid="6154703403388711809">"Permite a una aplicación leer datos del proveedor de contenido de mensajería instantánea"</string> | |
20 | - <string name="wo_perm_label" msgid="4771652386754813294">"escribir mensajes instantáneos"</string> | |
21 | - <string name="wo_perm_desc" msgid="470416777693330370">"Permite a una aplicación introducir datos en el proveedor de contenido de mensajería instantánea"</string> | |
22 | - <string name="app_label" msgid="1767858328304473363">"Almacenamiento de MI"</string> | |
23 | -</resources> |
@@ -1,23 +0,0 @@ | ||
1 | -<?xml version="1.0" encoding="UTF-8"?> | |
2 | -<!-- Copyright (C) 2009 The Android Open Source Project | |
3 | - | |
4 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - you may not use this file except in compliance with the License. | |
6 | - You may obtain a copy of the License at | |
7 | - | |
8 | - http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - | |
10 | - Unless required by applicable law or agreed to in writing, software | |
11 | - distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - See the License for the specific language governing permissions and | |
14 | - limitations under the License. | |
15 | ---> | |
16 | -<resources xmlns:android="http://schemas.android.com/apk/res/android" | |
17 | - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> | |
18 | - <string name="ro_perm_label" msgid="8923213594676670163">"leer mensajes instantáneos"</string> | |
19 | - <string name="ro_perm_desc" msgid="6154703403388711809">"Permite que las aplicaciones lean datos del proveedor de contenido de MI."</string> | |
20 | - <string name="wo_perm_label" msgid="4771652386754813294">"escribir mensajes instantáneos"</string> | |
21 | - <string name="wo_perm_desc" msgid="470416777693330370">"Permite que las aplicaciones escriban datos en el proveedor de contenido de MI."</string> | |
22 | - <string name="app_label" msgid="1767858328304473363">"Almacenamiento de MI"</string> | |
23 | -</resources> |
@@ -1,23 +0,0 @@ | ||
1 | -<?xml version="1.0" encoding="UTF-8"?> | |
2 | -<!-- Copyright (C) 2009 The Android Open Source Project | |
3 | - | |
4 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - you may not use this file except in compliance with the License. | |
6 | - You may obtain a copy of the License at | |
7 | - | |
8 | - http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - | |
10 | - Unless required by applicable law or agreed to in writing, software | |
11 | - distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - See the License for the specific language governing permissions and | |
14 | - limitations under the License. | |
15 | ---> | |
16 | -<resources xmlns:android="http://schemas.android.com/apk/res/android" | |
17 | - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> | |
18 | - <string name="ro_perm_label" msgid="8923213594676670163">"Lecture des messages instantanés"</string> | |
19 | - <string name="ro_perm_desc" msgid="6154703403388711809">"Permet aux applications de lire les données du prestataire de contenu de messagerie instantanée."</string> | |
20 | - <string name="wo_perm_label" msgid="4771652386754813294">"écrire des messages instantanées"</string> | |
21 | - <string name="wo_perm_desc" msgid="470416777693330370">"Permet aux applications d\'écrire les données pour le prestataire de contenu de messagerie instantanée."</string> | |
22 | - <string name="app_label" msgid="1767858328304473363">"Stockage MI"</string> | |
23 | -</resources> |
@@ -1,23 +0,0 @@ | ||
1 | -<?xml version="1.0" encoding="UTF-8"?> | |
2 | -<!-- Copyright (C) 2009 The Android Open Source Project | |
3 | - | |
4 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - you may not use this file except in compliance with the License. | |
6 | - You may obtain a copy of the License at | |
7 | - | |
8 | - http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - | |
10 | - Unless required by applicable law or agreed to in writing, software | |
11 | - distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - See the License for the specific language governing permissions and | |
14 | - limitations under the License. | |
15 | ---> | |
16 | -<resources xmlns:android="http://schemas.android.com/apk/res/android" | |
17 | - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> | |
18 | - <string name="ro_perm_label" msgid="8923213594676670163">"leggere messaggi immediati"</string> | |
19 | - <string name="ro_perm_desc" msgid="6154703403388711809">"Consente alle applicazioni di leggere dati del provider di contenuti IM."</string> | |
20 | - <string name="wo_perm_label" msgid="4771652386754813294">"scrivere messaggi immediati"</string> | |
21 | - <string name="wo_perm_desc" msgid="470416777693330370">"Consente alle applicazioni di scrivere dati per il provider di contenuti IM."</string> | |
22 | - <string name="app_label" msgid="1767858328304473363">"Memoria IM"</string> | |
23 | -</resources> |
@@ -1,23 +0,0 @@ | ||
1 | -<?xml version="1.0" encoding="UTF-8"?> | |
2 | -<!-- Copyright (C) 2009 The Android Open Source Project | |
3 | - | |
4 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - you may not use this file except in compliance with the License. | |
6 | - You may obtain a copy of the License at | |
7 | - | |
8 | - http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - | |
10 | - Unless required by applicable law or agreed to in writing, software | |
11 | - distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - See the License for the specific language governing permissions and | |
14 | - limitations under the License. | |
15 | ---> | |
16 | -<resources xmlns:android="http://schemas.android.com/apk/res/android" | |
17 | - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> | |
18 | - <string name="ro_perm_label" msgid="8923213594676670163">"インスタントメッセージを表示"</string> | |
19 | - <string name="ro_perm_desc" msgid="6154703403388711809">"IMコンテンツプロバイダーからのデータ読み取りをアプリケーションに許可します。"</string> | |
20 | - <string name="wo_perm_label" msgid="4771652386754813294">"インスタントメッセージを作成"</string> | |
21 | - <string name="wo_perm_desc" msgid="470416777693330370">"IMコンテンツプロバイダーからのデータ書き込みをアプリケーションに許可します。"</string> | |
22 | - <string name="app_label" msgid="1767858328304473363">"IMストレージ"</string> | |
23 | -</resources> |
@@ -1,23 +0,0 @@ | ||
1 | -<?xml version="1.0" encoding="UTF-8"?> | |
2 | -<!-- Copyright (C) 2009 The Android Open Source Project | |
3 | - | |
4 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - you may not use this file except in compliance with the License. | |
6 | - You may obtain a copy of the License at | |
7 | - | |
8 | - http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - | |
10 | - Unless required by applicable law or agreed to in writing, software | |
11 | - distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - See the License for the specific language governing permissions and | |
14 | - limitations under the License. | |
15 | ---> | |
16 | -<resources xmlns:android="http://schemas.android.com/apk/res/android" | |
17 | - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> | |
18 | - <string name="ro_perm_label" msgid="8923213594676670163">"인스턴트 메시지 읽기"</string> | |
19 | - <string name="ro_perm_desc" msgid="6154703403388711809">"응용프로그램이 메신저 콘텐츠 제공업체에서 제공한 데이터를 읽을 수 있도록 합니다."</string> | |
20 | - <string name="wo_perm_label" msgid="4771652386754813294">"인스턴트 메시지 쓰기"</string> | |
21 | - <string name="wo_perm_desc" msgid="470416777693330370">"응용프로그램이 메신저 콘텐츠 제공업체에 데이터를 쓸 수 있도록 합니다."</string> | |
22 | - <string name="app_label" msgid="1767858328304473363">"메신저 저장공간"</string> | |
23 | -</resources> |
@@ -1,23 +0,0 @@ | ||
1 | -<?xml version="1.0" encoding="UTF-8"?> | |
2 | -<!-- Copyright (C) 2009 The Android Open Source Project | |
3 | - | |
4 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - you may not use this file except in compliance with the License. | |
6 | - You may obtain a copy of the License at | |
7 | - | |
8 | - http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - | |
10 | - Unless required by applicable law or agreed to in writing, software | |
11 | - distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - See the License for the specific language governing permissions and | |
14 | - limitations under the License. | |
15 | ---> | |
16 | -<resources xmlns:android="http://schemas.android.com/apk/res/android" | |
17 | - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> | |
18 | - <string name="ro_perm_label" msgid="8923213594676670163">"les lynmeldinger"</string> | |
19 | - <string name="ro_perm_desc" msgid="6154703403388711809">"Tillat applikasjoner å lese data fra lynmeldingsoperatøren."</string> | |
20 | - <string name="wo_perm_label" msgid="4771652386754813294">"write instant messages"</string> | |
21 | - <string name="wo_perm_desc" msgid="470416777693330370">"Tillat applikasjonerå skrive data til lynmeldingsoperatøren."</string> | |
22 | - <string name="app_label" msgid="1767858328304473363">"IM-lagring"</string> | |
23 | -</resources> |
@@ -1,23 +0,0 @@ | ||
1 | -<?xml version="1.0" encoding="UTF-8"?> | |
2 | -<!-- Copyright (C) 2009 The Android Open Source Project | |
3 | - | |
4 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - you may not use this file except in compliance with the License. | |
6 | - You may obtain a copy of the License at | |
7 | - | |
8 | - http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - | |
10 | - Unless required by applicable law or agreed to in writing, software | |
11 | - distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - See the License for the specific language governing permissions and | |
14 | - limitations under the License. | |
15 | ---> | |
16 | -<resources xmlns:android="http://schemas.android.com/apk/res/android" | |
17 | - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> | |
18 | - <string name="ro_perm_label" msgid="8923213594676670163">"chatberichten lezen"</string> | |
19 | - <string name="ro_perm_desc" msgid="6154703403388711809">"Toepassingen toestaan gegevens te lezen van de provider van chatinhoud."</string> | |
20 | - <string name="wo_perm_label" msgid="4771652386754813294">"chatberichten schrijven"</string> | |
21 | - <string name="wo_perm_desc" msgid="470416777693330370">"Toepassingen toestaan gegevens te schrijven naar de provider van chatinhoud."</string> | |
22 | - <string name="app_label" msgid="1767858328304473363">"Opslag van chatberichten"</string> | |
23 | -</resources> |
@@ -1,23 +0,0 @@ | ||
1 | -<?xml version="1.0" encoding="UTF-8"?> | |
2 | -<!-- Copyright (C) 2009 The Android Open Source Project | |
3 | - | |
4 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - you may not use this file except in compliance with the License. | |
6 | - You may obtain a copy of the License at | |
7 | - | |
8 | - http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - | |
10 | - Unless required by applicable law or agreed to in writing, software | |
11 | - distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - See the License for the specific language governing permissions and | |
14 | - limitations under the License. | |
15 | ---> | |
16 | -<resources xmlns:android="http://schemas.android.com/apk/res/android" | |
17 | - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> | |
18 | - <string name="ro_perm_label" msgid="8923213594676670163">"odczytaj wiadomości czatu"</string> | |
19 | - <string name="ro_perm_desc" msgid="6154703403388711809">"Zezwala aplikacjom na odczytywanie danych od dostawcy zawartości czatu."</string> | |
20 | - <string name="wo_perm_label" msgid="4771652386754813294">"zapisz wiadomości czatu"</string> | |
21 | - <string name="wo_perm_desc" msgid="470416777693330370">"Zezwala aplikacjom na zapisywanie danych do dostawcy zawartości czatu."</string> | |
22 | - <string name="app_label" msgid="1767858328304473363">"Pamięć czatów"</string> | |
23 | -</resources> |
@@ -1,23 +0,0 @@ | ||
1 | -<?xml version="1.0" encoding="UTF-8"?> | |
2 | -<!-- Copyright (C) 2009 The Android Open Source Project | |
3 | - | |
4 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - you may not use this file except in compliance with the License. | |
6 | - You may obtain a copy of the License at | |
7 | - | |
8 | - http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - | |
10 | - Unless required by applicable law or agreed to in writing, software | |
11 | - distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - See the License for the specific language governing permissions and | |
14 | - limitations under the License. | |
15 | ---> | |
16 | -<resources xmlns:android="http://schemas.android.com/apk/res/android" | |
17 | - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> | |
18 | - <string name="ro_perm_label" msgid="8923213594676670163">"ler mensagens instantâneas"</string> | |
19 | - <string name="ro_perm_desc" msgid="6154703403388711809">"Permite que as aplicações leiam dados a partir do fornecedor de conteúdo de MI."</string> | |
20 | - <string name="wo_perm_label" msgid="4771652386754813294">"escrever mensagens instantâneas"</string> | |
21 | - <string name="wo_perm_desc" msgid="470416777693330370">"Permite que as aplicações escrevam dados para o fornecedor de conteúdo de MI."</string> | |
22 | - <string name="app_label" msgid="1767858328304473363">"Armazenamento de MI"</string> | |
23 | -</resources> |
@@ -1,23 +0,0 @@ | ||
1 | -<?xml version="1.0" encoding="UTF-8"?> | |
2 | -<!-- Copyright (C) 2009 The Android Open Source Project | |
3 | - | |
4 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - you may not use this file except in compliance with the License. | |
6 | - You may obtain a copy of the License at | |
7 | - | |
8 | - http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - | |
10 | - Unless required by applicable law or agreed to in writing, software | |
11 | - distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - See the License for the specific language governing permissions and | |
14 | - limitations under the License. | |
15 | ---> | |
16 | -<resources xmlns:android="http://schemas.android.com/apk/res/android" | |
17 | - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> | |
18 | - <string name="ro_perm_label" msgid="8923213594676670163">"ler mensagens instantâneas"</string> | |
19 | - <string name="ro_perm_desc" msgid="6154703403388711809">"Permite que os aplicativos leiam os dados do provedor de conteúdo de mensagem instantânea."</string> | |
20 | - <string name="wo_perm_label" msgid="4771652386754813294">"gravar mensagens instantâneas"</string> | |
21 | - <string name="wo_perm_desc" msgid="470416777693330370">"Permite que os aplicativos gravem os dados no provedor de conteúdo de mensagem instantânea."</string> | |
22 | - <string name="app_label" msgid="1767858328304473363">"Armazenamento de mensagens instantâneas"</string> | |
23 | -</resources> |
@@ -1,23 +0,0 @@ | ||
1 | -<?xml version="1.0" encoding="UTF-8"?> | |
2 | -<!-- Copyright (C) 2009 The Android Open Source Project | |
3 | - | |
4 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - you may not use this file except in compliance with the License. | |
6 | - You may obtain a copy of the License at | |
7 | - | |
8 | - http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - | |
10 | - Unless required by applicable law or agreed to in writing, software | |
11 | - distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - See the License for the specific language governing permissions and | |
14 | - limitations under the License. | |
15 | ---> | |
16 | -<resources xmlns:android="http://schemas.android.com/apk/res/android" | |
17 | - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> | |
18 | - <string name="ro_perm_label" msgid="8923213594676670163">"читать мгновенные сообщения"</string> | |
19 | - <string name="ro_perm_desc" msgid="6154703403388711809">"Позволяет приложениям считывать данные через поставщика чата."</string> | |
20 | - <string name="wo_perm_label" msgid="4771652386754813294">"писать мгновенные сообщения"</string> | |
21 | - <string name="wo_perm_desc" msgid="470416777693330370">"Позволяет приложениям записывать данные через поставщика чата."</string> | |
22 | - <string name="app_label" msgid="1767858328304473363">"Память мобильных сообщений"</string> | |
23 | -</resources> |
@@ -1,23 +0,0 @@ | ||
1 | -<?xml version="1.0" encoding="UTF-8"?> | |
2 | -<!-- Copyright (C) 2009 The Android Open Source Project | |
3 | - | |
4 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - you may not use this file except in compliance with the License. | |
6 | - You may obtain a copy of the License at | |
7 | - | |
8 | - http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - | |
10 | - Unless required by applicable law or agreed to in writing, software | |
11 | - distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - See the License for the specific language governing permissions and | |
14 | - limitations under the License. | |
15 | ---> | |
16 | -<resources xmlns:android="http://schemas.android.com/apk/res/android" | |
17 | - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> | |
18 | - <string name="ro_perm_label" msgid="8923213594676670163">"läsa chattmeddelanden"</string> | |
19 | - <string name="ro_perm_desc" msgid="6154703403388711809">"Tillåter att ett program läser data från chattens innehållsleverantör."</string> | |
20 | - <string name="wo_perm_label" msgid="4771652386754813294">"skriva chattmeddelanden"</string> | |
21 | - <string name="wo_perm_desc" msgid="470416777693330370">"Tillåter att ett program skriver data till innehållsleverantören för chatten."</string> | |
22 | - <string name="app_label" msgid="1767858328304473363">"Chattlagring"</string> | |
23 | -</resources> |
@@ -1,23 +0,0 @@ | ||
1 | -<?xml version="1.0" encoding="UTF-8"?> | |
2 | -<!-- Copyright (C) 2009 The Android Open Source Project | |
3 | - | |
4 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - you may not use this file except in compliance with the License. | |
6 | - You may obtain a copy of the License at | |
7 | - | |
8 | - http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - | |
10 | - Unless required by applicable law or agreed to in writing, software | |
11 | - distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - See the License for the specific language governing permissions and | |
14 | - limitations under the License. | |
15 | ---> | |
16 | -<resources xmlns:android="http://schemas.android.com/apk/res/android" | |
17 | - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> | |
18 | - <string name="ro_perm_label" msgid="8923213594676670163">"anlık iletileri oku"</string> | |
19 | - <string name="ro_perm_desc" msgid="6154703403388711809">"Uygulamalara, IM içerik sağlayıcısından gelen verileri okuma izni verir."</string> | |
20 | - <string name="wo_perm_label" msgid="4771652386754813294">"anlık iletiler yazın"</string> | |
21 | - <string name="wo_perm_desc" msgid="470416777693330370">"Uygulamaların IM içerik sağlayıcısına veri yazmasına izin verir."</string> | |
22 | - <string name="app_label" msgid="1767858328304473363">"IM Deposu"</string> | |
23 | -</resources> |
@@ -1,23 +0,0 @@ | ||
1 | -<?xml version="1.0" encoding="UTF-8"?> | |
2 | -<!-- Copyright (C) 2009 The Android Open Source Project | |
3 | - | |
4 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - you may not use this file except in compliance with the License. | |
6 | - You may obtain a copy of the License at | |
7 | - | |
8 | - http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - | |
10 | - Unless required by applicable law or agreed to in writing, software | |
11 | - distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - See the License for the specific language governing permissions and | |
14 | - limitations under the License. | |
15 | ---> | |
16 | -<resources xmlns:android="http://schemas.android.com/apk/res/android" | |
17 | - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> | |
18 | - <string name="ro_perm_label" msgid="8923213594676670163">"阅读即时消息"</string> | |
19 | - <string name="ro_perm_desc" msgid="6154703403388711809">"允许应用程序从即时消息内容提供者处读取数据。"</string> | |
20 | - <string name="wo_perm_label" msgid="4771652386754813294">"编写即时消息"</string> | |
21 | - <string name="wo_perm_desc" msgid="470416777693330370">"允许应用程序向即时消息内容提供者写入数据。"</string> | |
22 | - <string name="app_label" msgid="1767858328304473363">"即时消息存储"</string> | |
23 | -</resources> |
@@ -1,23 +0,0 @@ | ||
1 | -<?xml version="1.0" encoding="UTF-8"?> | |
2 | -<!-- Copyright (C) 2009 The Android Open Source Project | |
3 | - | |
4 | - Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - you may not use this file except in compliance with the License. | |
6 | - You may obtain a copy of the License at | |
7 | - | |
8 | - http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - | |
10 | - Unless required by applicable law or agreed to in writing, software | |
11 | - distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - See the License for the specific language governing permissions and | |
14 | - limitations under the License. | |
15 | ---> | |
16 | -<resources xmlns:android="http://schemas.android.com/apk/res/android" | |
17 | - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> | |
18 | - <string name="ro_perm_label" msgid="8923213594676670163">"讀取即時訊息"</string> | |
19 | - <string name="ro_perm_desc" msgid="6154703403388711809">"允許應用程式讀取來自即時訊息內容提供者的資料。"</string> | |
20 | - <string name="wo_perm_label" msgid="4771652386754813294">"撰寫即時訊息"</string> | |
21 | - <string name="wo_perm_desc" msgid="470416777693330370">"允許應用程式將資料寫入即時訊息內容提供者。"</string> | |
22 | - <string name="app_label" msgid="1767858328304473363">"即時通訊儲存空間"</string> | |
23 | -</resources> |
@@ -1,33 +0,0 @@ | ||
1 | -<?xml version="1.0" encoding="utf-8"?> | |
2 | -<!-- | |
3 | -** | |
4 | -** Copyright 2007, The Android Open Source Project | |
5 | -** | |
6 | -** Licensed under the Apache License, Version 2.0 (the "License"); | |
7 | -** you may not use this file except in compliance with the License. | |
8 | -** You may obtain a copy of the License at | |
9 | -** | |
10 | -** http://www.apache.org/licenses/LICENSE-2.0 | |
11 | -** | |
12 | -** Unless required by applicable law or agreed to in writing, software | |
13 | -** distributed under the License is distributed on an "AS IS" BASIS, | |
14 | -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
15 | -** See the License for the specific language governing permissions and | |
16 | -** limitations under the License. | |
17 | -*/ | |
18 | ---> | |
19 | -<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> | |
20 | - <string name="ro_perm_label">read instant messages</string> | |
21 | - <string name="ro_perm_desc"> | |
22 | - Allows applications to read data from the IM content provider. | |
23 | - </string> | |
24 | - | |
25 | - <string name="wo_perm_label">write instant messages</string> | |
26 | - <string name="wo_perm_desc"> | |
27 | - Allows applications to write data to the IM content provider. | |
28 | - </string> | |
29 | - | |
30 | - <!-- The application label --> | |
31 | - <string name="app_label">IM Storage</string> | |
32 | - | |
33 | -</resources> |
@@ -1,3333 +0,0 @@ | ||
1 | -/* | |
2 | - * Copyright (C) 2007 The Android Open Source Project | |
3 | - * | |
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - * you may not use this file except in compliance with the License. | |
6 | - * You may obtain a copy of the License at | |
7 | - * | |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - * | |
10 | - * Unless required by applicable law or agreed to in writing, software | |
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - * See the License for the specific language governing permissions and | |
14 | - * limitations under the License. | |
15 | - */ | |
16 | - | |
17 | -package com.android.providers.im; | |
18 | - | |
19 | -import android.content.ContentProvider; | |
20 | -import android.content.ContentValues; | |
21 | -import android.content.Context; | |
22 | -import android.content.UriMatcher; | |
23 | -import android.content.ContentResolver; | |
24 | -import android.database.Cursor; | |
25 | -import android.database.DatabaseUtils; | |
26 | -import android.database.sqlite.SQLiteConstraintException; | |
27 | -import android.database.sqlite.SQLiteDatabase; | |
28 | -import android.database.sqlite.SQLiteOpenHelper; | |
29 | -import android.database.sqlite.SQLiteQueryBuilder; | |
30 | -import android.net.Uri; | |
31 | -import android.os.ParcelFileDescriptor; | |
32 | -import android.provider.Im; | |
33 | -import android.text.TextUtils; | |
34 | -import android.util.Log; | |
35 | - | |
36 | - | |
37 | -import java.io.FileNotFoundException; | |
38 | -import java.io.UnsupportedEncodingException; | |
39 | -import java.net.URLDecoder; | |
40 | -import java.util.ArrayList; | |
41 | -import java.util.HashMap; | |
42 | - | |
43 | -/** | |
44 | - * A content provider for IM | |
45 | - */ | |
46 | -public class ImProvider extends ContentProvider { | |
47 | - private static final String LOG_TAG = "imProvider"; | |
48 | - private static final boolean DBG = false; | |
49 | - | |
50 | - private static final String AUTHORITY = "im"; | |
51 | - | |
52 | - private static final String TABLE_ACCOUNTS = "accounts"; | |
53 | - private static final String TABLE_PROVIDERS = "providers"; | |
54 | - private static final String TABLE_PROVIDER_SETTINGS = "providerSettings"; | |
55 | - | |
56 | - private static final String TABLE_CONTACTS = "contacts"; | |
57 | - private static final String TABLE_CONTACTS_ETAG = "contactsEtag"; | |
58 | - private static final String TABLE_BLOCKED_LIST = "blockedList"; | |
59 | - private static final String TABLE_CONTACT_LIST = "contactList"; | |
60 | - private static final String TABLE_INVITATIONS = "invitations"; | |
61 | - private static final String TABLE_GROUP_MEMBERS = "groupMembers"; | |
62 | - private static final String TABLE_PRESENCE = "presence"; | |
63 | - private static final String USERNAME = "username"; | |
64 | - private static final String TABLE_CHATS = "chats"; | |
65 | - private static final String TABLE_AVATARS = "avatars"; | |
66 | - private static final String TABLE_SESSION_COOKIES = "sessionCookies"; | |
67 | - private static final String TABLE_MESSAGES = "messages"; | |
68 | - private static final String TABLE_IN_MEMORY_MESSAGES = "inMemoryMessages"; | |
69 | - private static final String TABLE_ACCOUNT_STATUS = "accountStatus"; | |
70 | - private static final String TABLE_BRANDING_RESOURCE_MAP_CACHE = "brandingResMapCache"; | |
71 | - | |
72 | - // tables for mcs and rmq | |
73 | - private static final String TABLE_OUTGOING_RMQ_MESSAGES = "outgoingRmqMessages"; | |
74 | - private static final String TABLE_LAST_RMQ_ID = "lastrmqid"; | |
75 | - private static final String TABLE_S2D_RMQ_IDS = "s2dRmqIds"; | |
76 | - | |
77 | - | |
78 | - private static final String DATABASE_NAME = "im.db"; | |
79 | - private static final int DATABASE_VERSION = 52; | |
80 | - | |
81 | - protected static final int MATCH_PROVIDERS = 1; | |
82 | - protected static final int MATCH_PROVIDERS_BY_ID = 2; | |
83 | - protected static final int MATCH_PROVIDERS_WITH_ACCOUNT = 3; | |
84 | - protected static final int MATCH_ACCOUNTS = 10; | |
85 | - protected static final int MATCH_ACCOUNTS_BY_ID = 11; | |
86 | - protected static final int MATCH_CONTACTS = 18; | |
87 | - protected static final int MATCH_CONTACTS_JOIN_PRESENCE = 19; | |
88 | - protected static final int MATCH_CONTACTS_BAREBONE = 20; | |
89 | - protected static final int MATCH_CHATTING_CONTACTS = 21; | |
90 | - protected static final int MATCH_CONTACTS_BY_PROVIDER = 22; | |
91 | - protected static final int MATCH_CHATTING_CONTACTS_BY_PROVIDER = 23; | |
92 | - protected static final int MATCH_NO_CHATTING_CONTACTS_BY_PROVIDER = 24; | |
93 | - protected static final int MATCH_ONLINE_CONTACTS_BY_PROVIDER = 25; | |
94 | - protected static final int MATCH_OFFLINE_CONTACTS_BY_PROVIDER = 26; | |
95 | - protected static final int MATCH_CONTACT = 27; | |
96 | - protected static final int MATCH_CONTACTS_BULK = 28; | |
97 | - protected static final int MATCH_ONLINE_CONTACT_COUNT = 30; | |
98 | - protected static final int MATCH_BLOCKED_CONTACTS = 31; | |
99 | - protected static final int MATCH_CONTACTLISTS = 32; | |
100 | - protected static final int MATCH_CONTACTLISTS_BY_PROVIDER = 33; | |
101 | - protected static final int MATCH_CONTACTLIST = 34; | |
102 | - protected static final int MATCH_BLOCKEDLIST = 35; | |
103 | - protected static final int MATCH_BLOCKEDLIST_BY_PROVIDER = 36; | |
104 | - protected static final int MATCH_CONTACTS_ETAGS = 37; | |
105 | - protected static final int MATCH_CONTACTS_ETAG = 38; | |
106 | - protected static final int MATCH_PRESENCE = 40; | |
107 | - protected static final int MATCH_PRESENCE_ID = 41; | |
108 | - protected static final int MATCH_PRESENCE_BY_ACCOUNT = 42; | |
109 | - protected static final int MATCH_PRESENCE_SEED_BY_ACCOUNT = 43; | |
110 | - protected static final int MATCH_PRESENCE_BULK = 44; | |
111 | - | |
112 | - protected static final int MATCH_MESSAGES = 50; | |
113 | - protected static final int MATCH_MESSAGES_BY_CONTACT = 51; | |
114 | - protected static final int MATCH_MESSAGES_BY_THREAD_ID = 52; | |
115 | - protected static final int MATCH_MESSAGES_BY_PROVIDER = 53; | |
116 | - protected static final int MATCH_MESSAGES_BY_ACCOUNT = 54; | |
117 | - protected static final int MATCH_MESSAGE = 55; | |
118 | - protected static final int MATCH_OTR_MESSAGES = 56; | |
119 | - protected static final int MATCH_OTR_MESSAGES_BY_CONTACT = 57; | |
120 | - protected static final int MATCH_OTR_MESSAGES_BY_THREAD_ID = 58; | |
121 | - protected static final int MATCH_OTR_MESSAGES_BY_PROVIDER = 59; | |
122 | - protected static final int MATCH_OTR_MESSAGES_BY_ACCOUNT = 60; | |
123 | - protected static final int MATCH_OTR_MESSAGE = 61; | |
124 | - | |
125 | - protected static final int MATCH_GROUP_MEMBERS = 65; | |
126 | - protected static final int MATCH_GROUP_MEMBERS_BY_GROUP = 66; | |
127 | - protected static final int MATCH_AVATARS = 70; | |
128 | - protected static final int MATCH_AVATAR = 71; | |
129 | - protected static final int MATCH_AVATAR_BY_PROVIDER = 72; | |
130 | - protected static final int MATCH_CHATS = 80; | |
131 | - protected static final int MATCH_CHATS_BY_ACCOUNT = 81; | |
132 | - protected static final int MATCH_CHATS_ID = 82; | |
133 | - protected static final int MATCH_SESSIONS = 83; | |
134 | - protected static final int MATCH_SESSIONS_BY_PROVIDER = 84; | |
135 | - protected static final int MATCH_PROVIDER_SETTINGS = 90; | |
136 | - protected static final int MATCH_PROVIDER_SETTINGS_BY_ID = 91; | |
137 | - protected static final int MATCH_PROVIDER_SETTINGS_BY_ID_AND_NAME = 92; | |
138 | - protected static final int MATCH_INVITATIONS = 100; | |
139 | - protected static final int MATCH_INVITATION = 101; | |
140 | - protected static final int MATCH_ACCOUNTS_STATUS = 104; | |
141 | - protected static final int MATCH_ACCOUNT_STATUS = 105; | |
142 | - protected static final int MATCH_BRANDING_RESOURCE_MAP_CACHE = 106; | |
143 | - | |
144 | - // mcs url matcher | |
145 | - protected static final int MATCH_OUTGOING_RMQ_MESSAGES = 200; | |
146 | - protected static final int MATCH_OUTGOING_RMQ_MESSAGE = 201; | |
147 | - protected static final int MATCH_OUTGOING_HIGHEST_RMQ_ID = 202; | |
148 | - protected static final int MATCH_LAST_RMQ_ID = 203; | |
149 | - protected static final int MATCH_S2D_RMQ_IDS = 204; | |
150 | - | |
151 | - | |
152 | - protected final UriMatcher mUrlMatcher = new UriMatcher(UriMatcher.NO_MATCH); | |
153 | - private final String mTransientDbName; | |
154 | - | |
155 | - private static final HashMap<String, String> sProviderAccountsProjectionMap; | |
156 | - private static final HashMap<String, String> sContactsProjectionMap; | |
157 | - private static final HashMap<String, String> sContactListProjectionMap; | |
158 | - private static final HashMap<String, String> sBlockedListProjectionMap; | |
159 | - private static final HashMap<String, String> sMessagesProjectionMap; | |
160 | - private static final HashMap<String, String> sInMemoryMessagesProjectionMap; | |
161 | - | |
162 | - | |
163 | - private static final String PROVIDER_JOIN_ACCOUNT_TABLE = | |
164 | - "providers LEFT OUTER JOIN accounts ON " + | |
165 | - "(providers._id = accounts.provider AND accounts.active = 1) " + | |
166 | - "LEFT OUTER JOIN accountStatus ON (accounts._id = accountStatus.account)"; | |
167 | - | |
168 | - | |
169 | - private static final String CONTACT_JOIN_PRESENCE_TABLE = | |
170 | - "contacts LEFT OUTER JOIN presence ON (contacts._id = presence.contact_id)"; | |
171 | - | |
172 | - private static final String CONTACT_JOIN_PRESENCE_CHAT_TABLE = | |
173 | - CONTACT_JOIN_PRESENCE_TABLE + | |
174 | - " LEFT OUTER JOIN chats ON (contacts._id = chats.contact_id)"; | |
175 | - | |
176 | - private static final String CONTACT_JOIN_PRESENCE_CHAT_AVATAR_TABLE = | |
177 | - CONTACT_JOIN_PRESENCE_CHAT_TABLE + | |
178 | - " LEFT OUTER JOIN avatars ON (contacts.username = avatars.contact" + | |
179 | - " AND contacts.account = avatars.account_id)"; | |
180 | - | |
181 | - private static final String BLOCKEDLIST_JOIN_AVATAR_TABLE = | |
182 | - "blockedList LEFT OUTER JOIN avatars ON (blockedList.username = avatars.contact" + | |
183 | - " AND blockedList.account = avatars.account_id)"; | |
184 | - | |
185 | - private static final String MESSAGE_JOIN_CONTACT_TABLE = | |
186 | - "messages LEFT OUTER JOIN contacts ON (contacts._id = messages.thread_id)"; | |
187 | - | |
188 | - private static final String IN_MEMORY_MESSAGES_JOIN_CONTACT_TABLE = | |
189 | - "inMemoryMessages LEFT OUTER JOIN contacts ON " + | |
190 | - "(contacts._id = inMemoryMessages.thread_id)"; | |
191 | - | |
192 | - /** | |
193 | - * The where clause for filtering out blocked contacts | |
194 | - */ | |
195 | - private static final String NON_BLOCKED_CONTACTS_WHERE_CLAUSE = "(" | |
196 | - + Im.Contacts.TYPE + " IS NULL OR " | |
197 | - + Im.Contacts.TYPE + "!=" | |
198 | - + String.valueOf(Im.Contacts.TYPE_BLOCKED) | |
199 | - + ")"; | |
200 | - | |
201 | - private static final String BLOCKED_CONTACTS_WHERE_CLAUSE = | |
202 | - "(contacts." + Im.Contacts.TYPE + "=" + Im.Contacts.TYPE_BLOCKED + ")"; | |
203 | - | |
204 | - private static final String CONTACT_ID = TABLE_CONTACTS + '.' + Im.Contacts._ID; | |
205 | - private static final String PRESENCE_CONTACT_ID = TABLE_PRESENCE + '.' + Im.Presence.CONTACT_ID; | |
206 | - | |
207 | - protected SQLiteOpenHelper mOpenHelper; | |
208 | - private final String mDatabaseName; | |
209 | - private final int mDatabaseVersion; | |
210 | - | |
211 | - private final String[] BACKFILL_PROJECTION = { | |
212 | - Im.Chats._ID, Im.Chats.SHORTCUT, Im.Chats.LAST_MESSAGE_DATE | |
213 | - }; | |
214 | - | |
215 | - private final String[] FIND_SHORTCUT_PROJECTION = { | |
216 | - Im.Chats._ID, Im.Chats.SHORTCUT | |
217 | - }; | |
218 | - | |
219 | - // contact id query projection | |
220 | - private static final String[] CONTACT_ID_PROJECTION = new String[] { | |
221 | - Im.Contacts._ID, // 0 | |
222 | - }; | |
223 | - private static final int CONTACT_ID_COLUMN = 0; | |
224 | - | |
225 | - // contact id query selection for "seed presence" operation | |
226 | - private static final String CONTACTS_WITH_NO_PRESENCE_SELECTION = | |
227 | - Im.Contacts.ACCOUNT + "=?" + " AND " + Im.Contacts._ID + | |
228 | - " in (select " + CONTACT_ID + " from " + TABLE_CONTACTS + | |
229 | - " left outer join " + TABLE_PRESENCE + " on " + CONTACT_ID + '=' + | |
230 | - PRESENCE_CONTACT_ID + " where " + PRESENCE_CONTACT_ID + " IS NULL)"; | |
231 | - | |
232 | - // contact id query selection args 1 | |
233 | - private String[] mQueryContactIdSelectionArgs1 = new String[1]; | |
234 | - | |
235 | - // contact id query selection for getContactId() | |
236 | - private static final String CONTACT_ID_QUERY_SELECTION = | |
237 | - Im.Contacts.ACCOUNT + "=? AND " + Im.Contacts.USERNAME + "=?"; | |
238 | - | |
239 | - // contact id query selection args 2 | |
240 | - private String[] mQueryContactIdSelectionArgs2 = new String[2]; | |
241 | - | |
242 | - | |
243 | - | |
244 | - private class DatabaseHelper extends SQLiteOpenHelper { | |
245 | - | |
246 | - DatabaseHelper(Context context) { | |
247 | - super(context, mDatabaseName, null, mDatabaseVersion); | |
248 | - } | |
249 | - | |
250 | - @Override | |
251 | - public void onCreate(SQLiteDatabase db) { | |
252 | - | |
253 | - if (DBG) log("DatabaseHelper.onCreate"); | |
254 | - | |
255 | - db.execSQL("CREATE TABLE " + TABLE_PROVIDERS + " (" + | |
256 | - "_id INTEGER PRIMARY KEY," + | |
257 | - "name TEXT," + // eg AIM | |
258 | - "fullname TEXT," + // eg AOL Instance Messenger | |
259 | - "category TEXT," + // a category used for forming intent | |
260 | - "signup_url TEXT" + // web url to visit to create a new account | |
261 | - ");"); | |
262 | - | |
263 | - db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " (" + | |
264 | - "_id INTEGER PRIMARY KEY," + | |
265 | - "name TEXT," + | |
266 | - "provider INTEGER," + | |
267 | - "username TEXT," + | |
268 | - "pw TEXT," + | |
269 | - "active INTEGER NOT NULL DEFAULT 0," + | |
270 | - "locked INTEGER NOT NULL DEFAULT 0," + | |
271 | - "keep_signed_in INTEGER NOT NULL DEFAULT 0," + | |
272 | - "last_login_state INTEGER NOT NULL DEFAULT 0," + | |
273 | - "UNIQUE (provider, username)" + | |
274 | - ");"); | |
275 | - | |
276 | - createContactsTables(db); | |
277 | - createMessageChatTables(db, true /* create show_ts column */); | |
278 | - | |
279 | - db.execSQL("CREATE TABLE " + TABLE_AVATARS + " (" + | |
280 | - "_id INTEGER PRIMARY KEY," + | |
281 | - "contact TEXT," + | |
282 | - "provider_id INTEGER," + | |
283 | - "account_id INTEGER," + | |
284 | - "hash TEXT," + | |
285 | - "data BLOB," + // raw image data | |
286 | - "UNIQUE (account_id, contact)" + | |
287 | - ");"); | |
288 | - | |
289 | - db.execSQL("CREATE TABLE " + TABLE_PROVIDER_SETTINGS + " (" + | |
290 | - "_id INTEGER PRIMARY KEY," + | |
291 | - "provider INTEGER," + | |
292 | - "name TEXT," + | |
293 | - "value TEXT," + | |
294 | - "UNIQUE (provider, name)" + | |
295 | - ");"); | |
296 | - | |
297 | - db.execSQL("create TABLE " + TABLE_BRANDING_RESOURCE_MAP_CACHE + " (" + | |
298 | - "_id INTEGER PRIMARY KEY," + | |
299 | - "provider_id INTEGER," + | |
300 | - "app_res_id INTEGER," + | |
301 | - "plugin_res_id INTEGER" + | |
302 | - ");"); | |
303 | - | |
304 | - // clean up account specific data when an account is deleted. | |
305 | - db.execSQL("CREATE TRIGGER account_cleanup " + | |
306 | - "DELETE ON " + TABLE_ACCOUNTS + | |
307 | - " BEGIN " + | |
308 | - "DELETE FROM " + TABLE_AVATARS + " WHERE account_id= OLD._id;" + | |
309 | - "END"); | |
310 | - | |
311 | - // add a database trigger to clean up associated provider settings | |
312 | - // while deleting a provider | |
313 | - db.execSQL("CREATE TRIGGER provider_cleanup " + | |
314 | - "DELETE ON " + TABLE_PROVIDERS + | |
315 | - " BEGIN " + | |
316 | - "DELETE FROM " + TABLE_PROVIDER_SETTINGS + " WHERE provider= OLD._id;" + | |
317 | - "END"); | |
318 | - | |
319 | - // the following are tables for mcs | |
320 | - db.execSQL("create TABLE " + TABLE_OUTGOING_RMQ_MESSAGES + " (" + | |
321 | - "_id INTEGER PRIMARY KEY," + | |
322 | - "rmq_id INTEGER," + | |
323 | - "type INTEGER," + | |
324 | - "ts INTEGER," + | |
325 | - "data TEXT" + | |
326 | - ");"); | |
327 | - | |
328 | - db.execSQL("create TABLE " + TABLE_LAST_RMQ_ID + " (" + | |
329 | - "_id INTEGER PRIMARY KEY," + | |
330 | - "rmq_id INTEGER" + | |
331 | - ");"); | |
332 | - | |
333 | - db.execSQL("create TABLE " + TABLE_S2D_RMQ_IDS + " (" + | |
334 | - "_id INTEGER PRIMARY KEY," + | |
335 | - "rmq_id INTEGER" + | |
336 | - ");"); | |
337 | - } | |
338 | - | |
339 | - @Override | |
340 | - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { | |
341 | - Log.d(LOG_TAG, "Upgrading database from version " + oldVersion + " to " + newVersion); | |
342 | - | |
343 | - switch (oldVersion) { | |
344 | - case 43: // this is the db version shipped in Dream 1.0 | |
345 | - // no-op: no schema changed from 43 to 44. The db version was changed to flush | |
346 | - // old provider settings, so new provider setting (including new name/value | |
347 | - // pairs) could be inserted by the plugins. | |
348 | - | |
349 | - // follow thru. | |
350 | - case 44: | |
351 | - if (newVersion <= 44) { | |
352 | - return; | |
353 | - } | |
354 | - | |
355 | - db.beginTransaction(); | |
356 | - try { | |
357 | - // add category column to the providers table | |
358 | - db.execSQL("ALTER TABLE " + TABLE_PROVIDERS + " ADD COLUMN category TEXT;"); | |
359 | - // add otr column to the contacts table | |
360 | - db.execSQL("ALTER TABLE " + TABLE_CONTACTS + " ADD COLUMN otr INTEGER;"); | |
361 | - | |
362 | - db.setTransactionSuccessful(); | |
363 | - } catch (Throwable ex) { | |
364 | - Log.e(LOG_TAG, ex.getMessage(), ex); | |
365 | - break; // force to destroy all old data; | |
366 | - } finally { | |
367 | - db.endTransaction(); | |
368 | - } | |
369 | - | |
370 | - case 45: | |
371 | - if (newVersion <= 45) { | |
372 | - return; | |
373 | - } | |
374 | - | |
375 | - db.beginTransaction(); | |
376 | - try { | |
377 | - // add an otr_etag column to contact etag table | |
378 | - db.execSQL( | |
379 | - "ALTER TABLE " + TABLE_CONTACTS_ETAG + " ADD COLUMN otr_etag TEXT;"); | |
380 | - db.setTransactionSuccessful(); | |
381 | - } catch (Throwable ex) { | |
382 | - Log.e(LOG_TAG, ex.getMessage(), ex); | |
383 | - break; // force to destroy all old data; | |
384 | - } finally { | |
385 | - db.endTransaction(); | |
386 | - } | |
387 | - | |
388 | - case 46: | |
389 | - if (newVersion <= 46) { | |
390 | - return; | |
391 | - } | |
392 | - | |
393 | - db.beginTransaction(); | |
394 | - try { | |
395 | - // add branding resource map cache table | |
396 | - db.execSQL("create TABLE " + TABLE_BRANDING_RESOURCE_MAP_CACHE + " (" + | |
397 | - "_id INTEGER PRIMARY KEY," + | |
398 | - "provider_id INTEGER," + | |
399 | - "app_res_id INTEGER," + | |
400 | - "plugin_res_id INTEGER" + | |
401 | - ");"); | |
402 | - db.setTransactionSuccessful(); | |
403 | - } catch (Throwable ex) { | |
404 | - Log.e(LOG_TAG, ex.getMessage(), ex); | |
405 | - break; // force to destroy all old data; | |
406 | - } finally { | |
407 | - db.endTransaction(); | |
408 | - } | |
409 | - | |
410 | - case 47: | |
411 | - if (newVersion <= 47) { | |
412 | - return; | |
413 | - } | |
414 | - | |
415 | - db.beginTransaction(); | |
416 | - try { | |
417 | - // when upgrading from version 47, don't create the show_ts column | |
418 | - // here. The upgrade step in 51 will add the show_ts column to the | |
419 | - // messages table. If we created the messages table with show_ts here, | |
420 | - // we'll get a duplicate column error later. | |
421 | - createMessageChatTables(db, false /* don't create show_ts column */); | |
422 | - db.setTransactionSuccessful(); | |
423 | - } catch (Throwable ex) { | |
424 | - Log.e(LOG_TAG, ex.getMessage(), ex); | |
425 | - break; // force to destroy all old data; | |
426 | - } finally { | |
427 | - db.endTransaction(); | |
428 | - } | |
429 | - | |
430 | - // fall thru. | |
431 | - | |
432 | - case 48: | |
433 | - case 49: | |
434 | - case 50: | |
435 | - if (newVersion <= 50) { | |
436 | - return; | |
437 | - } | |
438 | - | |
439 | - db.beginTransaction(); | |
440 | - try { | |
441 | - // add rmq2 s2d ids table | |
442 | - db.execSQL("create TABLE " + TABLE_S2D_RMQ_IDS + " (" + | |
443 | - "_id INTEGER PRIMARY KEY," + | |
444 | - "rmq_id INTEGER" + | |
445 | - ");"); | |
446 | - db.setTransactionSuccessful(); | |
447 | - } catch (Throwable ex) { | |
448 | - Log.e(LOG_TAG, ex.getMessage(), ex); | |
449 | - break; // force to destroy all old data; | |
450 | - } finally { | |
451 | - db.endTransaction(); | |
452 | - } | |
453 | - | |
454 | - case 51: | |
455 | - if (newVersion <= 51) { | |
456 | - return; | |
457 | - } | |
458 | - | |
459 | - db.beginTransaction(); | |
460 | - try { | |
461 | - db.execSQL( | |
462 | - "ALTER TABLE " + TABLE_MESSAGES + " ADD COLUMN show_ts INTEGER;"); | |
463 | - db.setTransactionSuccessful(); | |
464 | - } catch (Throwable ex) { | |
465 | - Log.e(LOG_TAG, ex.getMessage(), ex); | |
466 | - break; // force to destroy all old data; | |
467 | - } finally { | |
468 | - db.endTransaction(); | |
469 | - } | |
470 | - | |
471 | - return; | |
472 | - } | |
473 | - | |
474 | - Log.w(LOG_TAG, "Couldn't upgrade db to " + newVersion + ". Destroying old data."); | |
475 | - destroyOldTables(db); | |
476 | - onCreate(db); | |
477 | - } | |
478 | - | |
479 | - private void destroyOldTables(SQLiteDatabase db) { | |
480 | - db.execSQL("DROP TABLE IF EXISTS " + TABLE_PROVIDERS); | |
481 | - db.execSQL("DROP TABLE IF EXISTS " + TABLE_ACCOUNTS); | |
482 | - db.execSQL("DROP TABLE IF EXISTS " + TABLE_CONTACT_LIST); | |
483 | - db.execSQL("DROP TABLE IF EXISTS " + TABLE_BLOCKED_LIST); | |
484 | - db.execSQL("DROP TABLE IF EXISTS " + TABLE_CONTACTS); | |
485 | - db.execSQL("DROP TABLE IF EXISTS " + TABLE_CONTACTS_ETAG); | |
486 | - db.execSQL("DROP TABLE IF EXISTS " + TABLE_AVATARS); | |
487 | - db.execSQL("DROP TABLE IF EXISTS " + TABLE_PROVIDER_SETTINGS); | |
488 | - db.execSQL("DROP TABLE IF EXISTS " + TABLE_BRANDING_RESOURCE_MAP_CACHE); | |
489 | - db.execSQL("DROP TABLE IF EXISTS " + TABLE_MESSAGES); | |
490 | - db.execSQL("DROP TABLE IF EXISTS " + TABLE_CHATS); | |
491 | - | |
492 | - // mcs/rmq stuff | |
493 | - db.execSQL("DROP TABLE IF EXISTS " + TABLE_OUTGOING_RMQ_MESSAGES); | |
494 | - db.execSQL("DROP TABLE IF EXISTS " + TABLE_LAST_RMQ_ID); | |
495 | - db.execSQL("DROP TABLE IF EXISTS " + TABLE_S2D_RMQ_IDS); | |
496 | - } | |
497 | - | |
498 | - private void createContactsTables(SQLiteDatabase db) { | |
499 | - if (DBG) log("createContactsTables"); | |
500 | - | |
501 | - StringBuilder buf = new StringBuilder(); | |
502 | - String contactsTableName = TABLE_CONTACTS; | |
503 | - | |
504 | - // creating the "contacts" table | |
505 | - buf.append("CREATE TABLE IF NOT EXISTS "); | |
506 | - buf.append(contactsTableName); | |
507 | - buf.append(" ("); | |
508 | - buf.append("_id INTEGER PRIMARY KEY,"); | |
509 | - buf.append("username TEXT,"); | |
510 | - buf.append("nickname TEXT,"); | |
511 | - | |
512 | - buf.append("provider INTEGER,"); | |
513 | - buf.append("account INTEGER,"); | |
514 | - buf.append("contactList INTEGER,"); | |
515 | - buf.append("type INTEGER,"); | |
516 | - buf.append("subscriptionStatus INTEGER,"); | |
517 | - buf.append("subscriptionType INTEGER,"); | |
518 | - | |
519 | - // the following are derived from Google Contact Extension, we don't include all | |
520 | - // the attributes, just the ones we can use. | |
521 | - // (see http://code.google.com/apis/talk/jep_extensions/roster_attributes.html) | |
522 | - // | |
523 | - // qc: quick contact (derived from message count) | |
524 | - // rejected: if the contact has ever been rejected by the user | |
525 | - buf.append("qc INTEGER,"); | |
526 | - buf.append("rejected INTEGER,"); | |
527 | - | |
528 | - // Off the record status | |
529 | - buf.append("otr INTEGER"); | |
530 | - | |
531 | - buf.append(");"); | |
532 | - | |
533 | - db.execSQL(buf.toString()); | |
534 | - | |
535 | - buf.delete(0, buf.length()); | |
536 | - | |
537 | - // creating contact etag table | |
538 | - buf.append("CREATE TABLE IF NOT EXISTS "); | |
539 | - buf.append(TABLE_CONTACTS_ETAG); | |
540 | - buf.append(" ("); | |
541 | - buf.append("_id INTEGER PRIMARY KEY,"); | |
542 | - buf.append("etag TEXT,"); | |
543 | - buf.append("otr_etag TEXT,"); | |
544 | - buf.append("account INTEGER UNIQUE"); | |
545 | - buf.append(");"); | |
546 | - | |
547 | - db.execSQL(buf.toString()); | |
548 | - | |
549 | - buf.delete(0, buf.length()); | |
550 | - | |
551 | - // creating the "contactList" table | |
552 | - buf.append("CREATE TABLE IF NOT EXISTS "); | |
553 | - buf.append(TABLE_CONTACT_LIST); | |
554 | - buf.append(" ("); | |
555 | - buf.append("_id INTEGER PRIMARY KEY,"); | |
556 | - buf.append("name TEXT,"); | |
557 | - buf.append("provider INTEGER,"); | |
558 | - buf.append("account INTEGER"); | |
559 | - buf.append(");"); | |
560 | - | |
561 | - db.execSQL(buf.toString()); | |
562 | - | |
563 | - buf.delete(0, buf.length()); | |
564 | - | |
565 | - // creating the "blockedList" table | |
566 | - buf.append("CREATE TABLE IF NOT EXISTS "); | |
567 | - buf.append(TABLE_BLOCKED_LIST); | |
568 | - buf.append(" ("); | |
569 | - buf.append("_id INTEGER PRIMARY KEY,"); | |
570 | - buf.append("username TEXT,"); | |
571 | - buf.append("nickname TEXT,"); | |
572 | - buf.append("provider INTEGER,"); | |
573 | - buf.append("account INTEGER"); | |
574 | - buf.append(");"); | |
575 | - | |
576 | - db.execSQL(buf.toString()); | |
577 | - } | |
578 | - | |
579 | - private void createMessageChatTables(SQLiteDatabase db, | |
580 | - boolean addShowTsColumnForMessagesTable) { | |
581 | - if (DBG) log("createMessageChatTables"); | |
582 | - | |
583 | - // message table | |
584 | - StringBuilder buf = new StringBuilder(); | |
585 | - buf.append("CREATE TABLE IF NOT EXISTS "); | |
586 | - buf.append(TABLE_MESSAGES); | |
587 | - buf.append(" (_id INTEGER PRIMARY KEY,"); | |
588 | - buf.append("thread_id INTEGER,"); | |
589 | - buf.append("nickname TEXT,"); | |
590 | - buf.append("body TEXT,"); | |
591 | - buf.append("date INTEGER,"); | |
592 | - buf.append("type INTEGER,"); | |
593 | - buf.append("packet_id TEXT UNIQUE,"); | |
594 | - buf.append("err_code INTEGER NOT NULL DEFAULT 0,"); | |
595 | - buf.append("err_msg TEXT,"); | |
596 | - buf.append("is_muc INTEGER"); | |
597 | - | |
598 | - if (addShowTsColumnForMessagesTable) { | |
599 | - buf.append(",show_ts INTEGER"); | |
600 | - } | |
601 | - | |
602 | - buf.append(");"); | |
603 | - | |
604 | - String sqlStatement = buf.toString(); | |
605 | - | |
606 | - if (DBG) log("create message table: " + sqlStatement); | |
607 | - db.execSQL(sqlStatement); | |
608 | - | |
609 | - buf.delete(0, buf.length()); | |
610 | - buf.append("CREATE TABLE IF NOT EXISTS "); | |
611 | - buf.append(TABLE_CHATS); | |
612 | - buf.append(" (_id INTEGER PRIMARY KEY,"); | |
613 | - buf.append("contact_id INTEGER UNIQUE,"); | |
614 | - buf.append("jid_resource TEXT,"); // the JID resource for the user, for non-group chats | |
615 | - buf.append("groupchat INTEGER,"); // 1 if group chat, 0 if not TODO: remove this column | |
616 | - buf.append("last_unread_message TEXT,"); // the last unread message | |
617 | - buf.append("last_message_date INTEGER,"); // in seconds | |
618 | - buf.append("unsent_composed_message TEXT,"); // a composed, but not sent message | |
619 | - buf.append("shortcut INTEGER);"); // which of 10 slots (if any) this chat occupies | |
620 | - | |
621 | - // chat sessions, including single person chats and group chats | |
622 | - sqlStatement = buf.toString(); | |
623 | - | |
624 | - if (DBG) log("create chat table: " + sqlStatement); | |
625 | - db.execSQL(sqlStatement); | |
626 | - | |
627 | - buf.delete(0, buf.length()); | |
628 | - buf.append("CREATE TRIGGER IF NOT EXISTS contact_cleanup "); | |
629 | - buf.append("DELETE ON contacts "); | |
630 | - buf.append("BEGIN "); | |
631 | - buf.append("DELETE FROM ").append(TABLE_CHATS).append(" WHERE contact_id = OLD._id;"); | |
632 | - buf.append("DELETE FROM ").append(TABLE_MESSAGES).append(" WHERE thread_id = OLD._id;"); | |
633 | - buf.append("END"); | |
634 | - | |
635 | - sqlStatement = buf.toString(); | |
636 | - | |
637 | - if (DBG) log("create trigger: " + sqlStatement); | |
638 | - db.execSQL(sqlStatement); | |
639 | - } | |
640 | - | |
641 | - private void createInMemoryMessageTables(SQLiteDatabase db, String tablePrefix) { | |
642 | - String tableName = (tablePrefix != null) ? | |
643 | - tablePrefix+TABLE_IN_MEMORY_MESSAGES : TABLE_IN_MEMORY_MESSAGES; | |
644 | - | |
645 | - db.execSQL("CREATE TABLE IF NOT EXISTS " + tableName + " (" + | |
646 | - "_id INTEGER PRIMARY KEY," + | |
647 | - "thread_id INTEGER," + | |
648 | - "nickname TEXT," + | |
649 | - "body TEXT," + | |
650 | - "date INTEGER," + // in millisec | |
651 | - "type INTEGER," + | |
652 | - "packet_id TEXT UNIQUE," + | |
653 | - "err_code INTEGER NOT NULL DEFAULT 0," + | |
654 | - "err_msg TEXT," + | |
655 | - "is_muc INTEGER," + | |
656 | - "show_ts INTEGER" + | |
657 | - ");"); | |
658 | - | |
659 | - } | |
660 | - | |
661 | - @Override | |
662 | - public void onOpen(SQLiteDatabase db) { | |
663 | - if (db.isReadOnly()) { | |
664 | - Log.w(LOG_TAG, "ImProvider database opened in read only mode."); | |
665 | - Log.w(LOG_TAG, "Transient tables not created."); | |
666 | - return; | |
667 | - } | |
668 | - | |
669 | - if (DBG) log("##### createTransientTables"); | |
670 | - | |
671 | - // Create transient tables | |
672 | - String cpDbName; | |
673 | - db.execSQL("ATTACH DATABASE ':memory:' AS " + mTransientDbName + ";"); | |
674 | - cpDbName = mTransientDbName + "."; | |
675 | - | |
676 | - // in-memory message table | |
677 | - createInMemoryMessageTables(db, cpDbName); | |
678 | - | |
679 | - // presence | |
680 | - db.execSQL("CREATE TABLE IF NOT EXISTS " + cpDbName + TABLE_PRESENCE + " ("+ | |
681 | - "_id INTEGER PRIMARY KEY," + | |
682 | - "contact_id INTEGER UNIQUE," + | |
683 | - "jid_resource TEXT," + // jid resource for the presence | |
684 | - "client_type INTEGER," + // client type | |
685 | - "priority INTEGER," + // presence priority (XMPP) | |
686 | - "mode INTEGER," + // presence mode | |
687 | - "status TEXT" + // custom status | |
688 | - ");"); | |
689 | - | |
690 | - // group chat invitations | |
691 | - db.execSQL("CREATE TABLE IF NOT EXISTS " + cpDbName + TABLE_INVITATIONS + " (" + | |
692 | - "_id INTEGER PRIMARY KEY," + | |
693 | - "providerId INTEGER," + | |
694 | - "accountId INTEGER," + | |
695 | - "inviteId TEXT," + | |
696 | - "sender TEXT," + | |
697 | - "groupName TEXT," + | |
698 | - "note TEXT," + | |
699 | - "status INTEGER" + | |
700 | - ");"); | |
701 | - | |
702 | - // group chat members | |
703 | - db.execSQL("CREATE TABLE IF NOT EXISTS " + cpDbName + TABLE_GROUP_MEMBERS + " (" + | |
704 | - "_id INTEGER PRIMARY KEY," + | |
705 | - "groupId INTEGER," + | |
706 | - "username TEXT," + | |
707 | - "nickname TEXT" + | |
708 | - ");"); | |
709 | - | |
710 | - db.execSQL("CREATE TABLE IF NOT EXISTS " + cpDbName + TABLE_ACCOUNT_STATUS + " (" + | |
711 | - "_id INTEGER PRIMARY KEY," + | |
712 | - "account INTEGER UNIQUE," + | |
713 | - "presenceStatus INTEGER," + | |
714 | - "connStatus INTEGER" + | |
715 | - ");" | |
716 | - ); | |
717 | - | |
718 | - /* when we moved the contact table out of transient_db and into the main db, the | |
719 | - presence and groupchat cleanup triggers don't work anymore. It seems we can't | |
720 | - create triggers that reference objects in a different database! | |
721 | - | |
722 | - // Insert a default presence for newly inserted contact | |
723 | - db.execSQL("CREATE TRIGGER IF NOT EXISTS contact_create_presence " + | |
724 | - "AFTER INSERT ON " + contactsTableName + | |
725 | - " WHEN NEW.type != " + Im.Contacts.TYPE_GROUP + | |
726 | - " BEGIN " + | |
727 | - "INSERT INTO presence (contact_id) VALUES (NEW._id);" + | |
728 | - " END"); | |
729 | - | |
730 | - // Remove the presence when the contact is removed. | |
731 | - db.execSQL("CREATE TRIGGER IF NOT EXISTS contact_presence_cleanup " + | |
732 | - "DELETE ON " + contactsTableName + | |
733 | - " BEGIN " + | |
734 | - "DELETE FROM presence WHERE contact_id = OLD._id;" + | |
735 | - "END"); | |
736 | - | |
737 | - // Cleans up group members and group messages when a group chat is deleted | |
738 | - db.execSQL("CREATE TRIGGER IF NOT EXISTS " + cpDbName + "group_cleanup " + | |
739 | - "DELETE ON " + cpDbName + contactsTableName + | |
740 | - " FOR EACH ROW WHEN OLD.type = " + Im.Contacts.TYPE_GROUP + | |
741 | - " BEGIN " + | |
742 | - "DELETE FROM groupMembers WHERE groupId = OLD._id;" + | |
743 | - "DELETE FROM groupMessages WHERE groupId = OLD._id;" + | |
744 | - " END"); | |
745 | - */ | |
746 | - | |
747 | - // only store the session cookies in memory right now. This means | |
748 | - // that we don't persist them across device reboot | |
749 | - db.execSQL("CREATE TABLE IF NOT EXISTS " + cpDbName + TABLE_SESSION_COOKIES + " ("+ | |
750 | - "_id INTEGER PRIMARY KEY," + | |
751 | - "provider INTEGER," + | |
752 | - "account INTEGER," + | |
753 | - "name TEXT," + | |
754 | - "value TEXT" + | |
755 | - ");"); | |
756 | - | |
757 | - } | |
758 | - } | |
759 | - | |
760 | - static { | |
761 | - sProviderAccountsProjectionMap = new HashMap<String, String>(); | |
762 | - sProviderAccountsProjectionMap.put(Im.Provider._ID, | |
763 | - "providers._id AS _id"); | |
764 | - sProviderAccountsProjectionMap.put(Im.Provider._COUNT, | |
765 | - "COUNT(*) AS _account"); | |
766 | - sProviderAccountsProjectionMap.put(Im.Provider.NAME, | |
767 | - "providers.name AS name"); | |
768 | - sProviderAccountsProjectionMap.put(Im.Provider.FULLNAME, | |
769 | - "providers.fullname AS fullname"); | |
770 | - sProviderAccountsProjectionMap.put(Im.Provider.CATEGORY, | |
771 | - "providers.category AS category"); | |
772 | - sProviderAccountsProjectionMap.put(Im.Provider.ACTIVE_ACCOUNT_ID, | |
773 | - "accounts._id AS account_id"); | |
774 | - sProviderAccountsProjectionMap.put(Im.Provider.ACTIVE_ACCOUNT_USERNAME, | |
775 | - "accounts.username AS account_username"); | |
776 | - sProviderAccountsProjectionMap.put(Im.Provider.ACTIVE_ACCOUNT_PW, | |
777 | - "accounts.pw AS account_pw"); | |
778 | - sProviderAccountsProjectionMap.put(Im.Provider.ACTIVE_ACCOUNT_LOCKED, | |
779 | - "accounts.locked AS account_locked"); | |
780 | - sProviderAccountsProjectionMap.put(Im.Provider.ACTIVE_ACCOUNT_KEEP_SIGNED_IN, | |
781 | - "accounts.keep_signed_in AS account_keepSignedIn"); | |
782 | - sProviderAccountsProjectionMap.put(Im.Provider.ACCOUNT_PRESENCE_STATUS, | |
783 | - "accountStatus.presenceStatus AS account_presenceStatus"); | |
784 | - sProviderAccountsProjectionMap.put(Im.Provider.ACCOUNT_CONNECTION_STATUS, | |
785 | - "accountStatus.connStatus AS account_connStatus"); | |
786 | - | |
787 | - // contacts projection map | |
788 | - sContactsProjectionMap = new HashMap<String, String>(); | |
789 | - | |
790 | - // Base column | |
791 | - sContactsProjectionMap.put(Im.Contacts._ID, "contacts._id AS _id"); | |
792 | - sContactsProjectionMap.put(Im.Contacts._COUNT, "COUNT(*) AS _count"); | |
793 | - | |
794 | - // contacts column | |
795 | - sContactsProjectionMap.put(Im.Contacts._ID, "contacts._id as _id"); | |
796 | - sContactsProjectionMap.put(Im.Contacts.USERNAME, "contacts.username as username"); | |
797 | - sContactsProjectionMap.put(Im.Contacts.NICKNAME, "contacts.nickname as nickname"); | |
798 | - sContactsProjectionMap.put(Im.Contacts.PROVIDER, "contacts.provider as provider"); | |
799 | - sContactsProjectionMap.put(Im.Contacts.ACCOUNT, "contacts.account as account"); | |
800 | - sContactsProjectionMap.put(Im.Contacts.CONTACTLIST, "contacts.contactList as contactList"); | |
801 | - sContactsProjectionMap.put(Im.Contacts.TYPE, "contacts.type as type"); | |
802 | - sContactsProjectionMap.put(Im.Contacts.SUBSCRIPTION_STATUS, | |
803 | - "contacts.subscriptionStatus as subscriptionStatus"); | |
804 | - sContactsProjectionMap.put(Im.Contacts.SUBSCRIPTION_TYPE, | |
805 | - "contacts.subscriptionType as subscriptionType"); | |
806 | - sContactsProjectionMap.put(Im.Contacts.QUICK_CONTACT, "contacts.qc as qc"); | |
807 | - sContactsProjectionMap.put(Im.Contacts.REJECTED, "contacts.rejected as rejected"); | |
808 | - | |
809 | - // Presence columns | |
810 | - sContactsProjectionMap.put(Im.Presence.CONTACT_ID, | |
811 | - "presence.contact_id AS contact_id"); | |
812 | - sContactsProjectionMap.put(Im.Contacts.PRESENCE_STATUS, | |
813 | - "presence.mode AS mode"); | |
814 | - sContactsProjectionMap.put(Im.Contacts.PRESENCE_CUSTOM_STATUS, | |
815 | - "presence.status AS status"); | |
816 | - sContactsProjectionMap.put(Im.Contacts.CLIENT_TYPE, | |
817 | - "presence.client_type AS client_type"); | |
818 | - | |
819 | - // Chats columns | |
820 | - sContactsProjectionMap.put(Im.Contacts.CHATS_CONTACT, | |
821 | - "chats.contact_id AS chats_contact_id"); | |
822 | - sContactsProjectionMap.put(Im.Chats.JID_RESOURCE, | |
823 | - "chats.jid_resource AS jid_resource"); | |
824 | - sContactsProjectionMap.put(Im.Chats.GROUP_CHAT, | |
825 | - "chats.groupchat AS groupchat"); | |
826 | - sContactsProjectionMap.put(Im.Contacts.LAST_UNREAD_MESSAGE, | |
827 | - "chats.last_unread_message AS last_unread_message"); | |
828 | - sContactsProjectionMap.put(Im.Contacts.LAST_MESSAGE_DATE, | |
829 | - "chats.last_message_date AS last_message_date"); | |
830 | - sContactsProjectionMap.put(Im.Contacts.UNSENT_COMPOSED_MESSAGE, | |
831 | - "chats.unsent_composed_message AS unsent_composed_message"); | |
832 | - sContactsProjectionMap.put(Im.Contacts.SHORTCUT, "chats.SHORTCUT AS shortcut"); | |
833 | - | |
834 | - // Avatars columns | |
835 | - sContactsProjectionMap.put(Im.Contacts.AVATAR_HASH, "avatars.hash AS avatars_hash"); | |
836 | - sContactsProjectionMap.put(Im.Contacts.AVATAR_DATA, "avatars.data AS avatars_data"); | |
837 | - | |
838 | - // contactList projection map | |
839 | - sContactListProjectionMap = new HashMap<String, String>(); | |
840 | - sContactListProjectionMap.put(Im.ContactList._ID, "contactList._id AS _id"); | |
841 | - sContactListProjectionMap.put(Im.ContactList._COUNT, "COUNT(*) AS _count"); | |
842 | - sContactListProjectionMap.put(Im.ContactList.NAME, "name"); | |
843 | - sContactListProjectionMap.put(Im.ContactList.PROVIDER, "provider"); | |
844 | - sContactListProjectionMap.put(Im.ContactList.ACCOUNT, "account"); | |
845 | - | |
846 | - // blockedList projection map | |
847 | - sBlockedListProjectionMap = new HashMap<String, String>(); | |
848 | - sBlockedListProjectionMap.put(Im.BlockedList._ID, "blockedList._id AS _id"); | |
849 | - sBlockedListProjectionMap.put(Im.BlockedList._COUNT, "COUNT(*) AS _count"); | |
850 | - sBlockedListProjectionMap.put(Im.BlockedList.USERNAME, "username"); | |
851 | - sBlockedListProjectionMap.put(Im.BlockedList.NICKNAME, "nickname"); | |
852 | - sBlockedListProjectionMap.put(Im.BlockedList.PROVIDER, "provider"); | |
853 | - sBlockedListProjectionMap.put(Im.BlockedList.ACCOUNT, "account"); | |
854 | - sBlockedListProjectionMap.put(Im.BlockedList.AVATAR_DATA, | |
855 | - "avatars.data AS avatars_data"); | |
856 | - | |
857 | - // messages projection map | |
858 | - sMessagesProjectionMap = new HashMap<String, String>(); | |
859 | - sMessagesProjectionMap.put(Im.Messages._ID, "messages._id AS _id"); | |
860 | - sMessagesProjectionMap.put(Im.Messages._COUNT, "COUNT(*) AS _count"); | |
861 | - sMessagesProjectionMap.put(Im.Messages.THREAD_ID, "messages.thread_id AS thread_id"); | |
862 | - sMessagesProjectionMap.put(Im.Messages.PACKET_ID, "messages.packet_id AS packet_id"); | |
863 | - sMessagesProjectionMap.put(Im.Messages.NICKNAME, "messages.nickname AS nickname"); | |
864 | - sMessagesProjectionMap.put(Im.Messages.BODY, "messages.body AS body"); | |
865 | - sMessagesProjectionMap.put(Im.Messages.DATE, "messages.date AS date"); | |
866 | - sMessagesProjectionMap.put(Im.Messages.TYPE, "messages.type AS type"); | |
867 | - sMessagesProjectionMap.put(Im.Messages.ERROR_CODE, "messages.err_code AS err_code"); | |
868 | - sMessagesProjectionMap.put(Im.Messages.ERROR_MESSAGE, "messages.err_msg AS err_msg"); | |
869 | - sMessagesProjectionMap.put(Im.Messages.IS_GROUP_CHAT, "messages.is_muc AS is_muc"); | |
870 | - sMessagesProjectionMap.put(Im.Messages.DISPLAY_SENT_TIME, "messages.show_ts AS show_ts"); | |
871 | - // contacts columns | |
872 | - sMessagesProjectionMap.put(Im.Messages.CONTACT, "contacts.username AS contact"); | |
873 | - sMessagesProjectionMap.put(Im.Contacts.PROVIDER, "contacts.provider AS provider"); | |
874 | - sMessagesProjectionMap.put(Im.Contacts.ACCOUNT, "contacts.account AS account"); | |
875 | - sMessagesProjectionMap.put("contact_type", "contacts.type AS contact_type"); | |
876 | - | |
877 | - sInMemoryMessagesProjectionMap = new HashMap<String, String>(); | |
878 | - sInMemoryMessagesProjectionMap.put(Im.Messages._ID, | |
879 | - "inMemoryMessages._id AS _id"); | |
880 | - sInMemoryMessagesProjectionMap.put(Im.Messages._COUNT, | |
881 | - "COUNT(*) AS _count"); | |
882 | - sInMemoryMessagesProjectionMap.put(Im.Messages.THREAD_ID, | |
883 | - "inMemoryMessages.thread_id AS thread_id"); | |
884 | - sInMemoryMessagesProjectionMap.put(Im.Messages.PACKET_ID, | |
885 | - "inMemoryMessages.packet_id AS packet_id"); | |
886 | - sInMemoryMessagesProjectionMap.put(Im.Messages.NICKNAME, | |
887 | - "inMemoryMessages.nickname AS nickname"); | |
888 | - sInMemoryMessagesProjectionMap.put(Im.Messages.BODY, | |
889 | - "inMemoryMessages.body AS body"); | |
890 | - sInMemoryMessagesProjectionMap.put(Im.Messages.DATE, | |
891 | - "inMemoryMessages.date AS date"); | |
892 | - sInMemoryMessagesProjectionMap.put(Im.Messages.TYPE, | |
893 | - "inMemoryMessages.type AS type"); | |
894 | - sInMemoryMessagesProjectionMap.put(Im.Messages.ERROR_CODE, | |
895 | - "inMemoryMessages.err_code AS err_code"); | |
896 | - sInMemoryMessagesProjectionMap.put(Im.Messages.ERROR_MESSAGE, | |
897 | - "inMemoryMessages.err_msg AS err_msg"); | |
898 | - sInMemoryMessagesProjectionMap.put(Im.Messages.IS_GROUP_CHAT, | |
899 | - "inMemoryMessages.is_muc AS is_muc"); | |
900 | - sInMemoryMessagesProjectionMap.put(Im.Messages.DISPLAY_SENT_TIME, | |
901 | - "inMemoryMessages.show_ts AS show_ts"); | |
902 | - // contacts columns | |
903 | - sInMemoryMessagesProjectionMap.put(Im.Messages.CONTACT, "contacts.username AS contact"); | |
904 | - sInMemoryMessagesProjectionMap.put(Im.Contacts.PROVIDER, "contacts.provider AS provider"); | |
905 | - sInMemoryMessagesProjectionMap.put(Im.Contacts.ACCOUNT, "contacts.account AS account"); | |
906 | - sInMemoryMessagesProjectionMap.put("contact_type", "contacts.type AS contact_type"); | |
907 | - } | |
908 | - | |
909 | - public ImProvider() { | |
910 | - this(DATABASE_NAME, DATABASE_VERSION); | |
911 | - | |
912 | - setupImUrlMatchers(AUTHORITY); | |
913 | - setupMcsUrlMatchers(AUTHORITY); | |
914 | - } | |
915 | - | |
916 | - protected ImProvider(String dbName, int dbVersion) { | |
917 | - mDatabaseName = dbName; | |
918 | - mDatabaseVersion = dbVersion; | |
919 | - mTransientDbName = "transient_" + dbName.replace(".", "_"); | |
920 | - } | |
921 | - | |
922 | - private void setupImUrlMatchers(String authority) { | |
923 | - mUrlMatcher.addURI(authority, "providers", MATCH_PROVIDERS); | |
924 | - mUrlMatcher.addURI(authority, "providers/#", MATCH_PROVIDERS_BY_ID); | |
925 | - mUrlMatcher.addURI(authority, "providers/account", MATCH_PROVIDERS_WITH_ACCOUNT); | |
926 | - | |
927 | - mUrlMatcher.addURI(authority, "accounts", MATCH_ACCOUNTS); | |
928 | - mUrlMatcher.addURI(authority, "accounts/#", MATCH_ACCOUNTS_BY_ID); | |
929 | - | |
930 | - mUrlMatcher.addURI(authority, "contacts", MATCH_CONTACTS); | |
931 | - mUrlMatcher.addURI(authority, "contactsWithPresence", MATCH_CONTACTS_JOIN_PRESENCE); | |
932 | - mUrlMatcher.addURI(authority, "contactsBarebone", MATCH_CONTACTS_BAREBONE); | |
933 | - mUrlMatcher.addURI(authority, "contacts/#/#", MATCH_CONTACTS_BY_PROVIDER); | |
934 | - mUrlMatcher.addURI(authority, "contacts/chatting", MATCH_CHATTING_CONTACTS); | |
935 | - mUrlMatcher.addURI(authority, "contacts/chatting/#/#", MATCH_CHATTING_CONTACTS_BY_PROVIDER); | |
936 | - mUrlMatcher.addURI(authority, "contacts/online/#/#", MATCH_ONLINE_CONTACTS_BY_PROVIDER); | |
937 | - mUrlMatcher.addURI(authority, "contacts/offline/#/#", MATCH_OFFLINE_CONTACTS_BY_PROVIDER); | |
938 | - mUrlMatcher.addURI(authority, "contacts/#", MATCH_CONTACT); | |
939 | - mUrlMatcher.addURI(authority, "contacts/blocked", MATCH_BLOCKED_CONTACTS); | |
940 | - mUrlMatcher.addURI(authority, "bulk_contacts", MATCH_CONTACTS_BULK); | |
941 | - mUrlMatcher.addURI(authority, "contacts/onlineCount", MATCH_ONLINE_CONTACT_COUNT); | |
942 | - | |
943 | - mUrlMatcher.addURI(authority, "contactLists", MATCH_CONTACTLISTS); | |
944 | - mUrlMatcher.addURI(authority, "contactLists/#/#", MATCH_CONTACTLISTS_BY_PROVIDER); | |
945 | - mUrlMatcher.addURI(authority, "contactLists/#", MATCH_CONTACTLIST); | |
946 | - mUrlMatcher.addURI(authority, "blockedList", MATCH_BLOCKEDLIST); | |
947 | - mUrlMatcher.addURI(authority, "blockedList/#/#", MATCH_BLOCKEDLIST_BY_PROVIDER); | |
948 | - | |
949 | - mUrlMatcher.addURI(authority, "contactsEtag", MATCH_CONTACTS_ETAGS); | |
950 | - mUrlMatcher.addURI(authority, "contactsEtag/#", MATCH_CONTACTS_ETAG); | |
951 | - | |
952 | - mUrlMatcher.addURI(authority, "presence", MATCH_PRESENCE); | |
953 | - mUrlMatcher.addURI(authority, "presence/#", MATCH_PRESENCE_ID); | |
954 | - mUrlMatcher.addURI(authority, "presence/account/#", MATCH_PRESENCE_BY_ACCOUNT); | |
955 | - mUrlMatcher.addURI(authority, "seed_presence/account/#", MATCH_PRESENCE_SEED_BY_ACCOUNT); | |
956 | - mUrlMatcher.addURI(authority, "bulk_presence", MATCH_PRESENCE_BULK); | |
957 | - | |
958 | - mUrlMatcher.addURI(authority, "messages", MATCH_MESSAGES); | |
959 | - mUrlMatcher.addURI(authority, "messagesByAcctAndContact/#/*", MATCH_MESSAGES_BY_CONTACT); | |
960 | - mUrlMatcher.addURI(authority, "messagesByThreadId/#", MATCH_MESSAGES_BY_THREAD_ID); | |
961 | - mUrlMatcher.addURI(authority, "messagesByProvider/#", MATCH_MESSAGES_BY_PROVIDER); | |
962 | - mUrlMatcher.addURI(authority, "messagesByAccount/#", MATCH_MESSAGES_BY_ACCOUNT); | |
963 | - mUrlMatcher.addURI(authority, "messages/#", MATCH_MESSAGE); | |
964 | - | |
965 | - mUrlMatcher.addURI(authority, "otrMessages", MATCH_OTR_MESSAGES); | |
966 | - mUrlMatcher.addURI(authority, "otrMessagesByAcctAndContact/#/*", | |
967 | - MATCH_OTR_MESSAGES_BY_CONTACT); | |
968 | - mUrlMatcher.addURI(authority, "otrMessagesByThreadId/#", MATCH_OTR_MESSAGES_BY_THREAD_ID); | |
969 | - mUrlMatcher.addURI(authority, "otrMessagesByProvider/#", MATCH_OTR_MESSAGES_BY_PROVIDER); | |
970 | - mUrlMatcher.addURI(authority, "otrMessagesByAccount/#", MATCH_OTR_MESSAGES_BY_ACCOUNT); | |
971 | - mUrlMatcher.addURI(authority, "otrMessages/#", MATCH_OTR_MESSAGE); | |
972 | - | |
973 | - mUrlMatcher.addURI(authority, "groupMembers", MATCH_GROUP_MEMBERS); | |
974 | - mUrlMatcher.addURI(authority, "groupMembers/#", MATCH_GROUP_MEMBERS_BY_GROUP); | |
975 | - | |
976 | - mUrlMatcher.addURI(authority, "avatars", MATCH_AVATARS); | |
977 | - mUrlMatcher.addURI(authority, "avatars/#", MATCH_AVATAR); | |
978 | - mUrlMatcher.addURI(authority, "avatarsBy/#/#", MATCH_AVATAR_BY_PROVIDER); | |
979 | - mUrlMatcher.addURI(authority, "chats", MATCH_CHATS); | |
980 | - mUrlMatcher.addURI(authority, "chats/account/#", MATCH_CHATS_BY_ACCOUNT); | |
981 | - mUrlMatcher.addURI(authority, "chats/#", MATCH_CHATS_ID); | |
982 | - | |
983 | - mUrlMatcher.addURI(authority, "sessionCookies", MATCH_SESSIONS); | |
984 | - mUrlMatcher.addURI(authority, "sessionCookiesBy/#/#", MATCH_SESSIONS_BY_PROVIDER); | |
985 | - mUrlMatcher.addURI(authority, "providerSettings", MATCH_PROVIDER_SETTINGS); | |
986 | - mUrlMatcher.addURI(authority, "providerSettings/#", MATCH_PROVIDER_SETTINGS_BY_ID); | |
987 | - mUrlMatcher.addURI(authority, "providerSettings/#/*", | |
988 | - MATCH_PROVIDER_SETTINGS_BY_ID_AND_NAME); | |
989 | - | |
990 | - mUrlMatcher.addURI(authority, "invitations", MATCH_INVITATIONS); | |
991 | - mUrlMatcher.addURI(authority, "invitations/#", MATCH_INVITATION); | |
992 | - | |
993 | - mUrlMatcher.addURI(authority, "accountStatus", MATCH_ACCOUNTS_STATUS); | |
994 | - mUrlMatcher.addURI(authority, "accountStatus/#", MATCH_ACCOUNT_STATUS); | |
995 | - | |
996 | - mUrlMatcher.addURI(authority, "brandingResMapCache", MATCH_BRANDING_RESOURCE_MAP_CACHE); | |
997 | - } | |
998 | - | |
999 | - private void setupMcsUrlMatchers(String authority) { | |
1000 | - mUrlMatcher.addURI(authority, "outgoingRmqMessages", MATCH_OUTGOING_RMQ_MESSAGES); | |
1001 | - mUrlMatcher.addURI(authority, "outgoingRmqMessages/#", MATCH_OUTGOING_RMQ_MESSAGE); | |
1002 | - mUrlMatcher.addURI(authority, "outgoingHighestRmqId", MATCH_OUTGOING_HIGHEST_RMQ_ID); | |
1003 | - mUrlMatcher.addURI(authority, "lastRmqId", MATCH_LAST_RMQ_ID); | |
1004 | - mUrlMatcher.addURI(authority, "s2dids", MATCH_S2D_RMQ_IDS); | |
1005 | - } | |
1006 | - | |
1007 | - @Override | |
1008 | - public boolean onCreate() { | |
1009 | - mOpenHelper = new DatabaseHelper(getContext()); | |
1010 | - return true; | |
1011 | - } | |
1012 | - | |
1013 | - @Override | |
1014 | - public final int update(final Uri url, final ContentValues values, | |
1015 | - final String selection, final String[] selectionArgs) { | |
1016 | - | |
1017 | - int result = 0; | |
1018 | - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); | |
1019 | - db.beginTransaction(); | |
1020 | - try { | |
1021 | - result = updateInternal(url, values, selection, selectionArgs); | |
1022 | - db.setTransactionSuccessful(); | |
1023 | - } finally { | |
1024 | - db.endTransaction(); | |
1025 | - } | |
1026 | - if (result > 0) { | |
1027 | - getContext().getContentResolver() | |
1028 | - .notifyChange(url, null /* observer */, false /* sync */); | |
1029 | - } | |
1030 | - return result; | |
1031 | - } | |
1032 | - | |
1033 | - @Override | |
1034 | - public final int delete(final Uri url, final String selection, | |
1035 | - final String[] selectionArgs) { | |
1036 | - int result; | |
1037 | - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); | |
1038 | - db.beginTransaction(); | |
1039 | - try { | |
1040 | - result = deleteInternal(url, selection, selectionArgs); | |
1041 | - db.setTransactionSuccessful(); | |
1042 | - } finally { | |
1043 | - db.endTransaction(); | |
1044 | - } | |
1045 | - if (result > 0) { | |
1046 | - getContext().getContentResolver() | |
1047 | - .notifyChange(url, null /* observer */, false /* sync */); | |
1048 | - } | |
1049 | - return result; | |
1050 | - } | |
1051 | - | |
1052 | - @Override | |
1053 | - public final Uri insert(final Uri url, final ContentValues values) { | |
1054 | - Uri result; | |
1055 | - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); | |
1056 | - db.beginTransaction(); | |
1057 | - try { | |
1058 | - result = insertInternal(url, values); | |
1059 | - db.setTransactionSuccessful(); | |
1060 | - } finally { | |
1061 | - db.endTransaction(); | |
1062 | - } | |
1063 | - if (result != null) { | |
1064 | - getContext().getContentResolver() | |
1065 | - .notifyChange(url, null /* observer */, false /* sync */); | |
1066 | - } | |
1067 | - return result; | |
1068 | - } | |
1069 | - | |
1070 | - @Override | |
1071 | - public final Cursor query(final Uri url, final String[] projection, | |
1072 | - final String selection, final String[] selectionArgs, | |
1073 | - final String sortOrder) { | |
1074 | - return queryInternal(url, projection, selection, selectionArgs, sortOrder); | |
1075 | - } | |
1076 | - | |
1077 | - public Cursor queryInternal(Uri url, String[] projectionIn, | |
1078 | - String selection, String[] selectionArgs, String sort) { | |
1079 | - SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); | |
1080 | - StringBuilder whereClause = new StringBuilder(); | |
1081 | - if(selection != null) { | |
1082 | - whereClause.append(selection); | |
1083 | - } | |
1084 | - String groupBy = null; | |
1085 | - String limit = null; | |
1086 | - | |
1087 | - // Generate the body of the query | |
1088 | - int match = mUrlMatcher.match(url); | |
1089 | - | |
1090 | - if (DBG) { | |
1091 | - log("query " + url + ", match " + match + ", where " + selection); | |
1092 | - if (selectionArgs != null) { | |
1093 | - for (String selectionArg : selectionArgs) { | |
1094 | - log(" selectionArg: " + selectionArg); | |
1095 | - } | |
1096 | - } | |
1097 | - } | |
1098 | - | |
1099 | - switch (match) { | |
1100 | - case MATCH_PROVIDERS_BY_ID: | |
1101 | - appendWhere(whereClause, Im.Provider._ID, "=", url.getPathSegments().get(1)); | |
1102 | - // fall thru. | |
1103 | - | |
1104 | - case MATCH_PROVIDERS: | |
1105 | - qb.setTables(TABLE_PROVIDERS); | |
1106 | - break; | |
1107 | - | |
1108 | - case MATCH_PROVIDERS_WITH_ACCOUNT: | |
1109 | - qb.setTables(PROVIDER_JOIN_ACCOUNT_TABLE); | |
1110 | - qb.setProjectionMap(sProviderAccountsProjectionMap); | |
1111 | - break; | |
1112 | - | |
1113 | - case MATCH_ACCOUNTS_BY_ID: | |
1114 | - appendWhere(whereClause, Im.Account._ID, "=", url.getPathSegments().get(1)); | |
1115 | - // falls down | |
1116 | - case MATCH_ACCOUNTS: | |
1117 | - qb.setTables(TABLE_ACCOUNTS); | |
1118 | - break; | |
1119 | - | |
1120 | - case MATCH_CONTACTS: | |
1121 | - qb.setTables(CONTACT_JOIN_PRESENCE_CHAT_AVATAR_TABLE); | |
1122 | - qb.setProjectionMap(sContactsProjectionMap); | |
1123 | - break; | |
1124 | - | |
1125 | - case MATCH_CONTACTS_JOIN_PRESENCE: | |
1126 | - qb.setTables(CONTACT_JOIN_PRESENCE_TABLE); | |
1127 | - qb.setProjectionMap(sContactsProjectionMap); | |
1128 | - break; | |
1129 | - | |
1130 | - case MATCH_CONTACTS_BAREBONE: | |
1131 | - qb.setTables(TABLE_CONTACTS); | |
1132 | - break; | |
1133 | - | |
1134 | - case MATCH_CHATTING_CONTACTS: | |
1135 | - qb.setTables(CONTACT_JOIN_PRESENCE_CHAT_AVATAR_TABLE); | |
1136 | - qb.setProjectionMap(sContactsProjectionMap); | |
1137 | - appendWhere(whereClause, "chats.last_message_date IS NOT NULL"); | |
1138 | - // no need to add the non blocked contacts clause because | |
1139 | - // blocked contacts can't have conversations. | |
1140 | - break; | |
1141 | - | |
1142 | - case MATCH_CONTACTS_BY_PROVIDER: | |
1143 | - buildQueryContactsByProvider(qb, whereClause, url); | |
1144 | - appendWhere(whereClause, NON_BLOCKED_CONTACTS_WHERE_CLAUSE); | |
1145 | - break; | |
1146 | - | |
1147 | - case MATCH_CHATTING_CONTACTS_BY_PROVIDER: | |
1148 | - buildQueryContactsByProvider(qb, whereClause, url); | |
1149 | - appendWhere(whereClause, "chats.last_message_date IS NOT NULL"); | |
1150 | - // no need to add the non blocked contacts clause because | |
1151 | - // blocked contacts can't have conversations. | |
1152 | - break; | |
1153 | - | |
1154 | - case MATCH_NO_CHATTING_CONTACTS_BY_PROVIDER: | |
1155 | - buildQueryContactsByProvider(qb, whereClause, url); | |
1156 | - appendWhere(whereClause, "chats.last_message_date IS NULL"); | |
1157 | - appendWhere(whereClause, NON_BLOCKED_CONTACTS_WHERE_CLAUSE); | |
1158 | - break; | |
1159 | - | |
1160 | - case MATCH_ONLINE_CONTACTS_BY_PROVIDER: | |
1161 | - buildQueryContactsByProvider(qb, whereClause, url); | |
1162 | - appendWhere(whereClause, Im.Contacts.PRESENCE_STATUS, "!=", Im.Presence.OFFLINE); | |
1163 | - appendWhere(whereClause, NON_BLOCKED_CONTACTS_WHERE_CLAUSE); | |
1164 | - break; | |
1165 | - | |
1166 | - case MATCH_OFFLINE_CONTACTS_BY_PROVIDER: | |
1167 | - buildQueryContactsByProvider(qb, whereClause, url); | |
1168 | - appendWhere(whereClause, Im.Contacts.PRESENCE_STATUS, "=", Im.Presence.OFFLINE); | |
1169 | - appendWhere(whereClause, NON_BLOCKED_CONTACTS_WHERE_CLAUSE); | |
1170 | - break; | |
1171 | - | |
1172 | - case MATCH_BLOCKED_CONTACTS: | |
1173 | - qb.setTables(CONTACT_JOIN_PRESENCE_CHAT_AVATAR_TABLE); | |
1174 | - qb.setProjectionMap(sContactsProjectionMap); | |
1175 | - appendWhere(whereClause, BLOCKED_CONTACTS_WHERE_CLAUSE); | |
1176 | - break; | |
1177 | - | |
1178 | - case MATCH_CONTACT: | |
1179 | - qb.setTables(CONTACT_JOIN_PRESENCE_CHAT_AVATAR_TABLE); | |
1180 | - qb.setProjectionMap(sContactsProjectionMap); | |
1181 | - appendWhere(whereClause, "contacts._id", "=", url.getPathSegments().get(1)); | |
1182 | - break; | |
1183 | - | |
1184 | - case MATCH_ONLINE_CONTACT_COUNT: | |
1185 | - qb.setTables(CONTACT_JOIN_PRESENCE_CHAT_TABLE); | |
1186 | - qb.setProjectionMap(sContactsProjectionMap); | |
1187 | - appendWhere(whereClause, Im.Contacts.PRESENCE_STATUS, "!=", Im.Presence.OFFLINE); | |
1188 | - appendWhere(whereClause, "chats.last_message_date IS NULL"); | |
1189 | - appendWhere(whereClause, NON_BLOCKED_CONTACTS_WHERE_CLAUSE); | |
1190 | - groupBy = Im.Contacts.CONTACTLIST; | |
1191 | - break; | |
1192 | - | |
1193 | - case MATCH_CONTACTLISTS_BY_PROVIDER: | |
1194 | - appendWhere(whereClause, Im.ContactList.ACCOUNT, "=", | |
1195 | - url.getPathSegments().get(2)); | |
1196 | - // fall through | |
1197 | - case MATCH_CONTACTLISTS: | |
1198 | - qb.setTables(TABLE_CONTACT_LIST); | |
1199 | - qb.setProjectionMap(sContactListProjectionMap); | |
1200 | - break; | |
1201 | - | |
1202 | - case MATCH_CONTACTLIST: | |
1203 | - qb.setTables(TABLE_CONTACT_LIST); | |
1204 | - appendWhere(whereClause, Im.ContactList._ID, "=", url.getPathSegments().get(1)); | |
1205 | - break; | |
1206 | - | |
1207 | - case MATCH_BLOCKEDLIST: | |
1208 | - qb.setTables(BLOCKEDLIST_JOIN_AVATAR_TABLE); | |
1209 | - qb.setProjectionMap(sBlockedListProjectionMap); | |
1210 | - break; | |
1211 | - | |
1212 | - case MATCH_BLOCKEDLIST_BY_PROVIDER: | |
1213 | - qb.setTables(BLOCKEDLIST_JOIN_AVATAR_TABLE); | |
1214 | - qb.setProjectionMap(sBlockedListProjectionMap); | |
1215 | - appendWhere(whereClause, Im.BlockedList.ACCOUNT, "=", | |
1216 | - url.getPathSegments().get(2)); | |
1217 | - break; | |
1218 | - | |
1219 | - case MATCH_CONTACTS_ETAGS: | |
1220 | - qb.setTables(TABLE_CONTACTS_ETAG); | |
1221 | - break; | |
1222 | - | |
1223 | - case MATCH_CONTACTS_ETAG: | |
1224 | - qb.setTables(TABLE_CONTACTS_ETAG); | |
1225 | - appendWhere(whereClause, "_id", "=", url.getPathSegments().get(1)); | |
1226 | - break; | |
1227 | - | |
1228 | - case MATCH_MESSAGES_BY_THREAD_ID: | |
1229 | - appendWhere(whereClause, Im.Messages.THREAD_ID, "=", url.getPathSegments().get(1)); | |
1230 | - // fall thru. | |
1231 | - | |
1232 | - case MATCH_MESSAGES: | |
1233 | - qb.setTables(TABLE_MESSAGES); | |
1234 | - | |
1235 | - final String selectionClause = whereClause.toString(); | |
1236 | - final String query1 = qb.buildQuery(projectionIn, selectionClause, | |
1237 | - null, null, null, null, null /* limit */); | |
1238 | - | |
1239 | - // Build the second query for frequent | |
1240 | - qb = new SQLiteQueryBuilder(); | |
1241 | - qb.setTables(TABLE_IN_MEMORY_MESSAGES); | |
1242 | - final String query2 = qb.buildQuery(projectionIn, | |
1243 | - selectionClause, null, null, null, null, null /* limit */); | |
1244 | - | |
1245 | - // Put them together | |
1246 | - final String query = qb.buildUnionQuery(new String[] {query1, query2}, sort, null); | |
1247 | - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); | |
1248 | - Cursor c = db.rawQueryWithFactory(null, query, null, TABLE_MESSAGES); | |
1249 | - if ((c != null) && !isTemporary()) { | |
1250 | - c.setNotificationUri(getContext().getContentResolver(), url); | |
1251 | - } | |
1252 | - return c; | |
1253 | - | |
1254 | - case MATCH_MESSAGE: | |
1255 | - qb.setTables(TABLE_MESSAGES); | |
1256 | - appendWhere(whereClause, Im.Messages._ID, "=", url.getPathSegments().get(1)); | |
1257 | - break; | |
1258 | - | |
1259 | - case MATCH_MESSAGES_BY_CONTACT: | |
1260 | - qb.setTables(MESSAGE_JOIN_CONTACT_TABLE); | |
1261 | - qb.setProjectionMap(sMessagesProjectionMap); | |
1262 | - | |
1263 | - appendWhere(whereClause, Im.Contacts.ACCOUNT, "=", url.getPathSegments().get(1)); | |
1264 | - appendWhere(whereClause, "contacts.username", "=", | |
1265 | - decodeURLSegment(url.getPathSegments().get(2))); | |
1266 | - | |
1267 | - final String sel = whereClause.toString(); | |
1268 | - final String q1 = qb.buildQuery(projectionIn, sel, null, null, null, null, null); | |
1269 | - | |
1270 | - // Build the second query for frequent | |
1271 | - qb = new SQLiteQueryBuilder(); | |
1272 | - qb.setTables(IN_MEMORY_MESSAGES_JOIN_CONTACT_TABLE); | |
1273 | - qb.setProjectionMap(sInMemoryMessagesProjectionMap); | |
1274 | - final String q2 = qb.buildQuery(projectionIn, sel, null, null, null, null, null); | |
1275 | - | |
1276 | - // Put them together | |
1277 | - final String q3 = qb.buildUnionQuery(new String[] {q1, q2}, sort, null); | |
1278 | - final SQLiteDatabase db2 = mOpenHelper.getWritableDatabase(); | |
1279 | - Cursor c2 = db2.rawQueryWithFactory(null, q3, null, MESSAGE_JOIN_CONTACT_TABLE); | |
1280 | - if ((c2 != null) && !isTemporary()) { | |
1281 | - c2.setNotificationUri(getContext().getContentResolver(), url); | |
1282 | - } | |
1283 | - return c2; | |
1284 | - | |
1285 | - case MATCH_INVITATIONS: | |
1286 | - qb.setTables(TABLE_INVITATIONS); | |
1287 | - break; | |
1288 | - | |
1289 | - case MATCH_INVITATION: | |
1290 | - qb.setTables(TABLE_INVITATIONS); | |
1291 | - appendWhere(whereClause, Im.Invitation._ID, "=", url.getPathSegments().get(1)); | |
1292 | - break; | |
1293 | - | |
1294 | - case MATCH_GROUP_MEMBERS: | |
1295 | - qb.setTables(TABLE_GROUP_MEMBERS); | |
1296 | - break; | |
1297 | - | |
1298 | - case MATCH_GROUP_MEMBERS_BY_GROUP: | |
1299 | - qb.setTables(TABLE_GROUP_MEMBERS); | |
1300 | - appendWhere(whereClause, Im.GroupMembers.GROUP, "=", url.getPathSegments().get(1)); | |
1301 | - break; | |
1302 | - | |
1303 | - case MATCH_AVATARS: | |
1304 | - qb.setTables(TABLE_AVATARS); | |
1305 | - break; | |
1306 | - | |
1307 | - case MATCH_AVATAR_BY_PROVIDER: | |
1308 | - qb.setTables(TABLE_AVATARS); | |
1309 | - appendWhere(whereClause, Im.Avatars.ACCOUNT, "=", url.getPathSegments().get(2)); | |
1310 | - break; | |
1311 | - | |
1312 | - case MATCH_CHATS: | |
1313 | - qb.setTables(TABLE_CHATS); | |
1314 | - break; | |
1315 | - | |
1316 | - case MATCH_CHATS_ID: | |
1317 | - qb.setTables(TABLE_CHATS); | |
1318 | - appendWhere(whereClause, Im.Chats.CONTACT_ID, "=", url.getPathSegments().get(1)); | |
1319 | - break; | |
1320 | - | |
1321 | - case MATCH_CHATS_BY_ACCOUNT: | |
1322 | - qb.setTables(TABLE_CHATS); | |
1323 | - String accountStr = decodeURLSegment(url.getLastPathSegment()); | |
1324 | - appendWhere(whereClause, buildContactIdSelection(Im.Chats.CONTACT_ID, | |
1325 | - Im.Contacts.ACCOUNT + "='" + accountStr + "'")); | |
1326 | - break; | |
1327 | - | |
1328 | - case MATCH_PRESENCE: | |
1329 | - qb.setTables(TABLE_PRESENCE); | |
1330 | - break; | |
1331 | - | |
1332 | - case MATCH_PRESENCE_ID: | |
1333 | - qb.setTables(TABLE_PRESENCE); | |
1334 | - appendWhere(whereClause, Im.Presence.CONTACT_ID, "=", url.getPathSegments().get(1)); | |
1335 | - break; | |
1336 | - | |
1337 | - case MATCH_SESSIONS: | |
1338 | - qb.setTables(TABLE_SESSION_COOKIES); | |
1339 | - break; | |
1340 | - | |
1341 | - case MATCH_SESSIONS_BY_PROVIDER: | |
1342 | - qb.setTables(TABLE_SESSION_COOKIES); | |
1343 | - appendWhere(whereClause, Im.SessionCookies.ACCOUNT, "=", url.getPathSegments().get(2)); | |
1344 | - break; | |
1345 | - | |
1346 | - case MATCH_PROVIDER_SETTINGS_BY_ID_AND_NAME: | |
1347 | - appendWhere(whereClause, Im.ProviderSettings.NAME, "=", url.getPathSegments().get(2)); | |
1348 | - // fall through | |
1349 | - case MATCH_PROVIDER_SETTINGS_BY_ID: | |
1350 | - appendWhere(whereClause, Im.ProviderSettings.PROVIDER, "=", url.getPathSegments().get(1)); | |
1351 | - // fall through | |
1352 | - case MATCH_PROVIDER_SETTINGS: | |
1353 | - qb.setTables(TABLE_PROVIDER_SETTINGS); | |
1354 | - break; | |
1355 | - | |
1356 | - case MATCH_ACCOUNTS_STATUS: | |
1357 | - qb.setTables(TABLE_ACCOUNT_STATUS); | |
1358 | - break; | |
1359 | - | |
1360 | - case MATCH_ACCOUNT_STATUS: | |
1361 | - qb.setTables(TABLE_ACCOUNT_STATUS); | |
1362 | - appendWhere(whereClause, Im.AccountStatus.ACCOUNT, "=", | |
1363 | - url.getPathSegments().get(1)); | |
1364 | - break; | |
1365 | - | |
1366 | - case MATCH_BRANDING_RESOURCE_MAP_CACHE: | |
1367 | - qb.setTables(TABLE_BRANDING_RESOURCE_MAP_CACHE); | |
1368 | - break; | |
1369 | - | |
1370 | - // mcs and rmq queries | |
1371 | - case MATCH_OUTGOING_RMQ_MESSAGES: | |
1372 | - qb.setTables(TABLE_OUTGOING_RMQ_MESSAGES); | |
1373 | - break; | |
1374 | - | |
1375 | - case MATCH_OUTGOING_HIGHEST_RMQ_ID: | |
1376 | - qb.setTables(TABLE_OUTGOING_RMQ_MESSAGES); | |
1377 | - sort = "rmq_id DESC"; | |
1378 | - limit = "1"; | |
1379 | - break; | |
1380 | - | |
1381 | - case MATCH_LAST_RMQ_ID: | |
1382 | - qb.setTables(TABLE_LAST_RMQ_ID); | |
1383 | - limit = "1"; | |
1384 | - break; | |
1385 | - | |
1386 | - case MATCH_S2D_RMQ_IDS: | |
1387 | - qb.setTables(TABLE_S2D_RMQ_IDS); | |
1388 | - break; | |
1389 | - | |
1390 | - default: | |
1391 | - throw new IllegalArgumentException("Unknown URL " + url); | |
1392 | - } | |
1393 | - | |
1394 | - // run the query | |
1395 | - final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); | |
1396 | - Cursor c = null; | |
1397 | - | |
1398 | - try { | |
1399 | - c = qb.query(db, projectionIn, whereClause.toString(), selectionArgs, | |
1400 | - groupBy, null, sort, limit); | |
1401 | - if (c != null) { | |
1402 | - switch(match) { | |
1403 | - case MATCH_CHATTING_CONTACTS: | |
1404 | - case MATCH_CONTACTS_BY_PROVIDER: | |
1405 | - case MATCH_CHATTING_CONTACTS_BY_PROVIDER: | |
1406 | - case MATCH_ONLINE_CONTACTS_BY_PROVIDER: | |
1407 | - case MATCH_OFFLINE_CONTACTS_BY_PROVIDER: | |
1408 | - case MATCH_CONTACTS_BAREBONE: | |
1409 | - case MATCH_CONTACTS_JOIN_PRESENCE: | |
1410 | - case MATCH_ONLINE_CONTACT_COUNT: | |
1411 | - url = Im.Contacts.CONTENT_URI; | |
1412 | - break; | |
1413 | - } | |
1414 | - if (DBG) log("set notify url " + url); | |
1415 | - c.setNotificationUri(getContext().getContentResolver(), url); | |
1416 | - } | |
1417 | - } catch (Exception ex) { | |
1418 | - Log.e(LOG_TAG, "query db caught ", ex); | |
1419 | - } | |
1420 | - | |
1421 | - return c; | |
1422 | - } | |
1423 | - | |
1424 | - private void buildQueryContactsByProvider(SQLiteQueryBuilder qb, | |
1425 | - StringBuilder whereClause, Uri url) { | |
1426 | - qb.setTables(CONTACT_JOIN_PRESENCE_CHAT_AVATAR_TABLE); | |
1427 | - qb.setProjectionMap(sContactsProjectionMap); | |
1428 | - // we don't really need the provider id in query. account id is enough. | |
1429 | - appendWhere(whereClause, Im.Contacts.ACCOUNT, "=", url.getLastPathSegment()); | |
1430 | - } | |
1431 | - | |
1432 | - @Override | |
1433 | - public String getType(Uri url) { | |
1434 | - int match = mUrlMatcher.match(url); | |
1435 | - switch (match) { | |
1436 | - case MATCH_PROVIDERS: | |
1437 | - return Im.Provider.CONTENT_TYPE; | |
1438 | - | |
1439 | - case MATCH_PROVIDERS_BY_ID: | |
1440 | - return Im.Provider.CONTENT_ITEM_TYPE; | |
1441 | - | |
1442 | - case MATCH_ACCOUNTS: | |
1443 | - return Im.Account.CONTENT_TYPE; | |
1444 | - | |
1445 | - case MATCH_ACCOUNTS_BY_ID: | |
1446 | - return Im.Account.CONTENT_ITEM_TYPE; | |
1447 | - | |
1448 | - case MATCH_CONTACTS: | |
1449 | - case MATCH_CONTACTS_BY_PROVIDER: | |
1450 | - case MATCH_ONLINE_CONTACTS_BY_PROVIDER: | |
1451 | - case MATCH_OFFLINE_CONTACTS_BY_PROVIDER: | |
1452 | - case MATCH_CONTACTS_BULK: | |
1453 | - case MATCH_CONTACTS_BAREBONE: | |
1454 | - case MATCH_CONTACTS_JOIN_PRESENCE: | |
1455 | - return Im.Contacts.CONTENT_TYPE; | |
1456 | - | |
1457 | - case MATCH_CONTACT: | |
1458 | - return Im.Contacts.CONTENT_ITEM_TYPE; | |
1459 | - | |
1460 | - case MATCH_CONTACTLISTS: | |
1461 | - case MATCH_CONTACTLISTS_BY_PROVIDER: | |
1462 | - return Im.ContactList.CONTENT_TYPE; | |
1463 | - | |
1464 | - case MATCH_CONTACTLIST: | |
1465 | - return Im.ContactList.CONTENT_ITEM_TYPE; | |
1466 | - | |
1467 | - case MATCH_BLOCKEDLIST: | |
1468 | - case MATCH_BLOCKEDLIST_BY_PROVIDER: | |
1469 | - return Im.BlockedList.CONTENT_TYPE; | |
1470 | - | |
1471 | - case MATCH_CONTACTS_ETAGS: | |
1472 | - case MATCH_CONTACTS_ETAG: | |
1473 | - return Im.ContactsEtag.CONTENT_TYPE; | |
1474 | - | |
1475 | - case MATCH_MESSAGES: | |
1476 | - case MATCH_MESSAGES_BY_CONTACT: | |
1477 | - case MATCH_MESSAGES_BY_THREAD_ID: | |
1478 | - case MATCH_MESSAGES_BY_PROVIDER: | |
1479 | - case MATCH_MESSAGES_BY_ACCOUNT: | |
1480 | - case MATCH_OTR_MESSAGES: | |
1481 | - case MATCH_OTR_MESSAGES_BY_CONTACT: | |
1482 | - case MATCH_OTR_MESSAGES_BY_THREAD_ID: | |
1483 | - case MATCH_OTR_MESSAGES_BY_PROVIDER: | |
1484 | - case MATCH_OTR_MESSAGES_BY_ACCOUNT: | |
1485 | - return Im.Messages.CONTENT_TYPE; | |
1486 | - | |
1487 | - case MATCH_MESSAGE: | |
1488 | - case MATCH_OTR_MESSAGE: | |
1489 | - return Im.Messages.CONTENT_ITEM_TYPE; | |
1490 | - | |
1491 | - case MATCH_PRESENCE: | |
1492 | - case MATCH_PRESENCE_BULK: | |
1493 | - return Im.Presence.CONTENT_TYPE; | |
1494 | - | |
1495 | - case MATCH_AVATARS: | |
1496 | - return Im.Avatars.CONTENT_TYPE; | |
1497 | - | |
1498 | - case MATCH_AVATAR: | |
1499 | - return Im.Avatars.CONTENT_ITEM_TYPE; | |
1500 | - | |
1501 | - case MATCH_CHATS: | |
1502 | - return Im.Chats.CONTENT_TYPE; | |
1503 | - | |
1504 | - case MATCH_CHATS_ID: | |
1505 | - return Im.Chats.CONTENT_ITEM_TYPE; | |
1506 | - | |
1507 | - case MATCH_INVITATIONS: | |
1508 | - return Im.Invitation.CONTENT_TYPE; | |
1509 | - | |
1510 | - case MATCH_INVITATION: | |
1511 | - return Im.Invitation.CONTENT_ITEM_TYPE; | |
1512 | - | |
1513 | - case MATCH_GROUP_MEMBERS: | |
1514 | - case MATCH_GROUP_MEMBERS_BY_GROUP: | |
1515 | - return Im.GroupMembers.CONTENT_TYPE; | |
1516 | - | |
1517 | - case MATCH_SESSIONS: | |
1518 | - case MATCH_SESSIONS_BY_PROVIDER: | |
1519 | - return Im.SessionCookies.CONTENT_TYPE; | |
1520 | - | |
1521 | - case MATCH_PROVIDER_SETTINGS: | |
1522 | - return Im.ProviderSettings.CONTENT_TYPE; | |
1523 | - | |
1524 | - case MATCH_ACCOUNTS_STATUS: | |
1525 | - return Im.AccountStatus.CONTENT_TYPE; | |
1526 | - | |
1527 | - case MATCH_ACCOUNT_STATUS: | |
1528 | - return Im.AccountStatus.CONTENT_ITEM_TYPE; | |
1529 | - | |
1530 | - default: | |
1531 | - throw new IllegalArgumentException("Unknown URL"); | |
1532 | - } | |
1533 | - } | |
1534 | - | |
1535 | - // package scope for testing. | |
1536 | - boolean insertBulkContacts(ContentValues values) { | |
1537 | - //if (DBG) log("insertBulkContacts: begin"); | |
1538 | - | |
1539 | - ArrayList<String> usernames = values.getStringArrayList(Im.Contacts.USERNAME); | |
1540 | - ArrayList<String> nicknames = values.getStringArrayList(Im.Contacts.NICKNAME); | |
1541 | - int usernameCount = usernames.size(); | |
1542 | - int nicknameCount = nicknames.size(); | |
1543 | - | |
1544 | - if (usernameCount != nicknameCount) { | |
1545 | - Log.e(LOG_TAG, "[ImProvider] insertBulkContacts: input bundle " + | |
1546 | - "username & nickname lists have diff. length!"); | |
1547 | - return false; | |
1548 | - } | |
1549 | - | |
1550 | - ArrayList<String> contactTypeArray = values.getStringArrayList(Im.Contacts.TYPE); | |
1551 | - ArrayList<String> subscriptionStatusArray = | |
1552 | - values.getStringArrayList(Im.Contacts.SUBSCRIPTION_STATUS); | |
1553 | - ArrayList<String> subscriptionTypeArray = | |
1554 | - values.getStringArrayList(Im.Contacts.SUBSCRIPTION_TYPE); | |
1555 | - ArrayList<String> quickContactArray = values.getStringArrayList(Im.Contacts.QUICK_CONTACT); | |
1556 | - ArrayList<String> rejectedArray = values.getStringArrayList(Im.Contacts.REJECTED); | |
1557 | - int sum = 0; | |
1558 | - | |
1559 | - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); | |
1560 | - | |
1561 | - db.beginTransaction(); | |
1562 | - try { | |
1563 | - Long provider = values.getAsLong(Im.Contacts.PROVIDER); | |
1564 | - Long account = values.getAsLong(Im.Contacts.ACCOUNT); | |
1565 | - Long listId = values.getAsLong(Im.Contacts.CONTACTLIST); | |
1566 | - | |
1567 | - ContentValues contactValues = new ContentValues(); | |
1568 | - contactValues.put(Im.Contacts.PROVIDER, provider); | |
1569 | - contactValues.put(Im.Contacts.ACCOUNT, account); | |
1570 | - contactValues.put(Im.Contacts.CONTACTLIST, listId); | |
1571 | - ContentValues presenceValues = new ContentValues(); | |
1572 | - presenceValues.put(Im.Presence.PRESENCE_STATUS, | |
1573 | - Im.Presence.OFFLINE); | |
1574 | - | |
1575 | - for (int i=0; i<usernameCount; i++) { | |
1576 | - String username = usernames.get(i); | |
1577 | - String nickname = nicknames.get(i); | |
1578 | - int type = 0; | |
1579 | - int subscriptionStatus = 0; | |
1580 | - int subscriptionType = 0; | |
1581 | - int quickContact = 0; | |
1582 | - int rejected = 0; | |
1583 | - | |
1584 | - try { | |
1585 | - type = Integer.parseInt(contactTypeArray.get(i)); | |
1586 | - if (subscriptionStatusArray != null) { | |
1587 | - subscriptionStatus = Integer.parseInt(subscriptionStatusArray.get(i)); | |
1588 | - } | |
1589 | - if (subscriptionTypeArray != null) { | |
1590 | - subscriptionType = Integer.parseInt(subscriptionTypeArray.get(i)); | |
1591 | - } | |
1592 | - if (quickContactArray != null) { | |
1593 | - quickContact = Integer.parseInt(quickContactArray.get(i)); | |
1594 | - } | |
1595 | - if (rejectedArray != null) { | |
1596 | - rejected = Integer.parseInt(rejectedArray.get(i)); | |
1597 | - } | |
1598 | - } catch (NumberFormatException ex) { | |
1599 | - Log.e(LOG_TAG, "insertBulkContacts: caught " + ex); | |
1600 | - } | |
1601 | - | |
1602 | - /* | |
1603 | - if (DBG) log("insertBulkContacts[" + i + "] username=" + | |
1604 | - username + ", nickname=" + nickname + ", type=" + type + | |
1605 | - ", subscriptionStatus=" + subscriptionStatus + ", subscriptionType=" + | |
1606 | - subscriptionType + ", qc=" + quickContact); | |
1607 | - */ | |
1608 | - | |
1609 | - contactValues.put(Im.Contacts.USERNAME, username); | |
1610 | - contactValues.put(Im.Contacts.NICKNAME, nickname); | |
1611 | - contactValues.put(Im.Contacts.TYPE, type); | |
1612 | - if (subscriptionStatusArray != null) { | |
1613 | - contactValues.put(Im.Contacts.SUBSCRIPTION_STATUS, subscriptionStatus); | |
1614 | - } | |
1615 | - if (subscriptionTypeArray != null) { | |
1616 | - contactValues.put(Im.Contacts.SUBSCRIPTION_TYPE, subscriptionType); | |
1617 | - } | |
1618 | - if (quickContactArray != null) { | |
1619 | - contactValues.put(Im.Contacts.QUICK_CONTACT, quickContact); | |
1620 | - } | |
1621 | - if (rejectedArray != null) { | |
1622 | - contactValues.put(Im.Contacts.REJECTED, rejected); | |
1623 | - } | |
1624 | - | |
1625 | - long rowId; | |
1626 | - | |
1627 | - /* save this code for when we add constraint (account, username) to the contacts | |
1628 | - table | |
1629 | - try { | |
1630 | - rowId = db.insertOrThrow(TABLE_CONTACTS, USERNAME, contactValues); | |
1631 | - } catch (android.database.sqlite.SQLiteConstraintException ex) { | |
1632 | - if (DBG) log("insertBulkContacts: insert " + username + " caught " + ex); | |
1633 | - | |
1634 | - // append username to the selection clause | |
1635 | - updateSelection.delete(0, updateSelection.length()); | |
1636 | - updateSelection.append(Im.Contacts.USERNAME); | |
1637 | - updateSelection.append("=?"); | |
1638 | - updateSelectionArgs[0] = username; | |
1639 | - | |
1640 | - int updated = db.update(TABLE_CONTACTS, contactValues, | |
1641 | - updateSelection.toString(), updateSelectionArgs); | |
1642 | - | |
1643 | - if (DBG && updated != 1) { | |
1644 | - log("insertBulkContacts: update " + username + " failed!"); | |
1645 | - } | |
1646 | - } | |
1647 | - */ | |
1648 | - | |
1649 | - rowId = db.insert(TABLE_CONTACTS, USERNAME, contactValues); | |
1650 | - if (rowId > 0) { | |
1651 | - sum++; | |
1652 | - | |
1653 | - // seed the presence for the new contact | |
1654 | - if (DBG) log("### seedPresence for contact id " + rowId); | |
1655 | - presenceValues.put(Im.Presence.CONTACT_ID, rowId); | |
1656 | - | |
1657 | - try { | |
1658 | - db.insert(TABLE_PRESENCE, null, presenceValues); | |
1659 | - } catch (android.database.sqlite.SQLiteConstraintException ex) { | |
1660 | - Log.w(LOG_TAG, "insertBulkContacts: seeding presence caught " + ex); | |
1661 | - } | |
1662 | - } | |
1663 | - | |
1664 | - // yield the lock if anyone else is trying to | |
1665 | - // perform a db operation here. | |
1666 | - db.yieldIfContended(); | |
1667 | - } | |
1668 | - | |
1669 | - db.setTransactionSuccessful(); | |
1670 | - } finally { | |
1671 | - db.endTransaction(); | |
1672 | - } | |
1673 | - | |
1674 | - // We know that we succeeded becuase endTransaction throws if the transaction failed. | |
1675 | - if (DBG) log("insertBulkContacts: added " + sum + " contacts!"); | |
1676 | - return true; | |
1677 | - } | |
1678 | - | |
1679 | - // package scope for testing. | |
1680 | - int updateBulkContacts(ContentValues values, String userWhere) { | |
1681 | - ArrayList<String> usernames = values.getStringArrayList(Im.Contacts.USERNAME); | |
1682 | - ArrayList<String> nicknames = values.getStringArrayList(Im.Contacts.NICKNAME); | |
1683 | - | |
1684 | - int usernameCount = usernames.size(); | |
1685 | - int nicknameCount = nicknames.size(); | |
1686 | - | |
1687 | - if (usernameCount != nicknameCount) { | |
1688 | - Log.e(LOG_TAG, "[ImProvider] updateBulkContacts: input bundle " + | |
1689 | - "username & nickname lists have diff. length!"); | |
1690 | - return 0; | |
1691 | - } | |
1692 | - | |
1693 | - ArrayList<String> contactTypeArray = values.getStringArrayList(Im.Contacts.TYPE); | |
1694 | - ArrayList<String> subscriptionStatusArray = | |
1695 | - values.getStringArrayList(Im.Contacts.SUBSCRIPTION_STATUS); | |
1696 | - ArrayList<String> subscriptionTypeArray = | |
1697 | - values.getStringArrayList(Im.Contacts.SUBSCRIPTION_TYPE); | |
1698 | - ArrayList<String> quickContactArray = values.getStringArrayList(Im.Contacts.QUICK_CONTACT); | |
1699 | - ArrayList<String> rejectedArray = values.getStringArrayList(Im.Contacts.REJECTED); | |
1700 | - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); | |
1701 | - | |
1702 | - db.beginTransaction(); | |
1703 | - int sum = 0; | |
1704 | - | |
1705 | - try { | |
1706 | - Long provider = values.getAsLong(Im.Contacts.PROVIDER); | |
1707 | - Long account = values.getAsLong(Im.Contacts.ACCOUNT); | |
1708 | - | |
1709 | - ContentValues contactValues = new ContentValues(); | |
1710 | - contactValues.put(Im.Contacts.PROVIDER, provider); | |
1711 | - contactValues.put(Im.Contacts.ACCOUNT, account); | |
1712 | - | |
1713 | - StringBuilder updateSelection = new StringBuilder(); | |
1714 | - String[] updateSelectionArgs = new String[1]; | |
1715 | - | |
1716 | - for (int i=0; i<usernameCount; i++) { | |
1717 | - String username = usernames.get(i); | |
1718 | - String nickname = nicknames.get(i); | |
1719 | - int type = 0; | |
1720 | - int subscriptionStatus = 0; | |
1721 | - int subscriptionType = 0; | |
1722 | - int quickContact = 0; | |
1723 | - int rejected = 0; | |
1724 | - | |
1725 | - try { | |
1726 | - type = Integer.parseInt(contactTypeArray.get(i)); | |
1727 | - subscriptionStatus = Integer.parseInt(subscriptionStatusArray.get(i)); | |
1728 | - subscriptionType = Integer.parseInt(subscriptionTypeArray.get(i)); | |
1729 | - quickContact = Integer.parseInt(quickContactArray.get(i)); | |
1730 | - rejected = Integer.parseInt(rejectedArray.get(i)); | |
1731 | - } catch (NumberFormatException ex) { | |
1732 | - Log.e(LOG_TAG, "insertBulkContacts: caught " + ex); | |
1733 | - } | |
1734 | - | |
1735 | - if (DBG) log("updateBulkContacts[" + i + "] username=" + | |
1736 | - username + ", nickname=" + nickname + ", type=" + type + | |
1737 | - ", subscriptionStatus=" + subscriptionStatus + ", subscriptionType=" + | |
1738 | - subscriptionType + ", qc=" + quickContact); | |
1739 | - | |
1740 | - contactValues.put(Im.Contacts.USERNAME, username); | |
1741 | - contactValues.put(Im.Contacts.NICKNAME, nickname); | |
1742 | - contactValues.put(Im.Contacts.TYPE, type); | |
1743 | - contactValues.put(Im.Contacts.SUBSCRIPTION_STATUS, subscriptionStatus); | |
1744 | - contactValues.put(Im.Contacts.SUBSCRIPTION_TYPE, subscriptionType); | |
1745 | - contactValues.put(Im.Contacts.QUICK_CONTACT, quickContact); | |
1746 | - contactValues.put(Im.Contacts.REJECTED, rejected); | |
1747 | - | |
1748 | - // append username to the selection clause | |
1749 | - updateSelection.delete(0, updateSelection.length()); | |
1750 | - updateSelection.append(userWhere); | |
1751 | - updateSelection.append(" AND "); | |
1752 | - updateSelection.append(Im.Contacts.USERNAME); | |
1753 | - updateSelection.append("=?"); | |
1754 | - | |
1755 | - updateSelectionArgs[0] = username; | |
1756 | - | |
1757 | - int numUpdated = db.update(TABLE_CONTACTS, contactValues, | |
1758 | - updateSelection.toString(), updateSelectionArgs); | |
1759 | - if (numUpdated == 0) { | |
1760 | - Log.e(LOG_TAG, "[ImProvider] updateBulkContacts: " + | |
1761 | - " update failed for selection = " + updateSelection); | |
1762 | - } else { | |
1763 | - sum += numUpdated; | |
1764 | - } | |
1765 | - | |
1766 | - // yield the lock if anyone else is trying to | |
1767 | - // perform a db operation here. | |
1768 | - db.yieldIfContended(); | |
1769 | - } | |
1770 | - | |
1771 | - db.setTransactionSuccessful(); | |
1772 | - } finally { | |
1773 | - db.endTransaction(); | |
1774 | - } | |
1775 | - | |
1776 | - if (DBG) log("updateBulkContacts: " + sum + " entries updated"); | |
1777 | - return sum; | |
1778 | - } | |
1779 | - | |
1780 | - /** | |
1781 | - * make sure the presence for all contacts of a given account is set to offline, and | |
1782 | - * each contact has a presence row associated with it. However, this method does not remove | |
1783 | - * presences for which the corresponding contacts no longer exist. That's probably ok since | |
1784 | - * presence is kept in memory, so it won't stay around for too long. Here is the algorithm. | |
1785 | - * | |
1786 | - * 1. for all presence that have a corresponding contact, make it OFFLINE. This is one sqlite | |
1787 | - * call. | |
1788 | - * 2. query for all the contacts that don't have a presence, and add a presence row for them. | |
1789 | - * | |
1790 | - * TODO simplify the presence management! The desire is to have a presence row for each | |
1791 | - * TODO contact in the database, so later we can just call update() on the presence rows | |
1792 | - * TODO instead of checking for the existence of presence first. The assumption is we get | |
1793 | - * TODO presence updates much more frequently. However, the logic to maintain that goal is | |
1794 | - * TODO overly complicated. One possible solution is to use insert_or_replace the presence rows | |
1795 | - * TODO when updating the presence. That way we don't always need to maintain an empty presence | |
1796 | - * TODO row for each contact. | |
1797 | - * | |
1798 | - * @param account the account of the contacts for which we want to create seed presence rows. | |
1799 | - */ | |
1800 | - private void seedInitialPresenceByAccount(long account) { | |
1801 | - SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); | |
1802 | - qb.setTables(TABLE_CONTACTS); | |
1803 | - qb.setProjectionMap(sContactsProjectionMap); | |
1804 | - | |
1805 | - mQueryContactIdSelectionArgs1[0] = String.valueOf(account); | |
1806 | - | |
1807 | - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); | |
1808 | - db.beginTransaction(); | |
1809 | - | |
1810 | - Cursor c = null; | |
1811 | - | |
1812 | - try { | |
1813 | - ContentValues presenceValues = new ContentValues(); | |
1814 | - presenceValues.put(Im.Presence.PRESENCE_STATUS, Im.Presence.OFFLINE); | |
1815 | - presenceValues.put(Im.Presence.PRESENCE_CUSTOM_STATUS, ""); | |
1816 | - | |
1817 | - // update all the presence for the account so they are offline | |
1818 | - StringBuilder buf = new StringBuilder(); | |
1819 | - buf.append(Im.Presence.CONTACT_ID); | |
1820 | - buf.append(" in (select "); | |
1821 | - buf.append(Im.Contacts._ID); | |
1822 | - buf.append(" from "); | |
1823 | - buf.append(TABLE_CONTACTS); | |
1824 | - buf.append(" where "); | |
1825 | - buf.append(Im.Contacts.ACCOUNT); | |
1826 | - buf.append("=?) "); | |
1827 | - | |
1828 | - String selection = buf.toString(); | |
1829 | - if (DBG) log("seedInitialPresence: reset presence selection=" + selection); | |
1830 | - | |
1831 | - int count = db.update(TABLE_PRESENCE, presenceValues, selection, | |
1832 | - mQueryContactIdSelectionArgs1); | |
1833 | - if (DBG) log("seedInitialPresence: reset " + count + " presence rows to OFFLINE"); | |
1834 | - | |
1835 | - // for in-memory presence table, add a presence row for each contact that | |
1836 | - // doesn't have a presence. in-memory presence table isn't reliable, and goes away | |
1837 | - // when device reboot or IMProvider process dies, so we can't rely on each contact | |
1838 | - // have a corresponding presence. | |
1839 | - if (DBG) { | |
1840 | - log("seedInitialPresence: contacts_with_no_presence_selection => " + | |
1841 | - CONTACTS_WITH_NO_PRESENCE_SELECTION); | |
1842 | - } | |
1843 | - | |
1844 | - c = qb.query(db, | |
1845 | - CONTACT_ID_PROJECTION, | |
1846 | - CONTACTS_WITH_NO_PRESENCE_SELECTION, | |
1847 | - mQueryContactIdSelectionArgs1, | |
1848 | - null, null, null, null); | |
1849 | - | |
1850 | - if (DBG) log("seedInitialPresence: found " + c.getCount() + " contacts w/o presence"); | |
1851 | - | |
1852 | - count = 0; | |
1853 | - | |
1854 | - while (c.moveToNext()) { | |
1855 | - long id = c.getLong(CONTACT_ID_COLUMN); | |
1856 | - presenceValues.put(Im.Presence.CONTACT_ID, id); | |
1857 | - | |
1858 | - try { | |
1859 | - if (db.insert(TABLE_PRESENCE, null, presenceValues) > 0) { | |
1860 | - count++; | |
1861 | - } | |
1862 | - } catch (SQLiteConstraintException ex) { | |
1863 | - // we could possibly catch this exception, since there could be a presence | |
1864 | - // row with the same contact_id. That's fine, just ignore the error | |
1865 | - if (DBG) log("seedInitialPresence: insert presence for contact_id " + id + | |
1866 | - " failed, caught " + ex); | |
1867 | - } | |
1868 | - } | |
1869 | - | |
1870 | - if (DBG) log("seedInitialPresence: added " + count + " new presence rows"); | |
1871 | - | |
1872 | - db.setTransactionSuccessful(); | |
1873 | - } finally { | |
1874 | - if (c != null) { | |
1875 | - c.close(); | |
1876 | - } | |
1877 | - db.endTransaction(); | |
1878 | - } | |
1879 | - } | |
1880 | - | |
1881 | - private int updateBulkPresence(ContentValues values, String userWhere, String[] whereArgs) { | |
1882 | - ArrayList<String> usernames = values.getStringArrayList(Im.Contacts.USERNAME); | |
1883 | - int count = usernames.size(); | |
1884 | - Long account = values.getAsLong(Im.Contacts.ACCOUNT); | |
1885 | - | |
1886 | - ArrayList<String> priorityArray = values.getStringArrayList(Im.Presence.PRIORITY); | |
1887 | - ArrayList<String> modeArray = values.getStringArrayList(Im.Presence.PRESENCE_STATUS); | |
1888 | - ArrayList<String> statusArray = values.getStringArrayList( | |
1889 | - Im.Presence.PRESENCE_CUSTOM_STATUS); | |
1890 | - ArrayList<String> clientTypeArray = values.getStringArrayList(Im.Presence.CLIENT_TYPE); | |
1891 | - ArrayList<String> resourceArray = values.getStringArrayList(Im.Presence.JID_RESOURCE); | |
1892 | - | |
1893 | - // append username to the selection clause | |
1894 | - StringBuilder buf = new StringBuilder(); | |
1895 | - | |
1896 | - if (!TextUtils.isEmpty(userWhere)) { | |
1897 | - buf.append(userWhere); | |
1898 | - buf.append(" AND "); | |
1899 | - } | |
1900 | - | |
1901 | - buf.append(Im.Presence.CONTACT_ID); | |
1902 | - buf.append(" in (select "); | |
1903 | - buf.append(Im.Contacts._ID); | |
1904 | - buf.append(" from "); | |
1905 | - buf.append(TABLE_CONTACTS); | |
1906 | - buf.append(" where "); | |
1907 | - buf.append(Im.Contacts.ACCOUNT); | |
1908 | - buf.append("=? AND "); | |
1909 | - | |
1910 | - // use username LIKE ? for case insensitive comparison | |
1911 | - buf.append(Im.Contacts.USERNAME); | |
1912 | - buf.append(" LIKE ?) AND ("); | |
1913 | - | |
1914 | - buf.append(Im.Presence.PRIORITY); | |
1915 | - buf.append("<=? OR "); | |
1916 | - buf.append(Im.Presence.PRIORITY); | |
1917 | - buf.append(" IS NULL OR "); | |
1918 | - buf.append(Im.Presence.JID_RESOURCE); | |
1919 | - buf.append("=?)"); | |
1920 | - | |
1921 | - String selection = buf.toString(); | |
1922 | - | |
1923 | - if (DBG) log("updateBulkPresence: selection => " + selection); | |
1924 | - | |
1925 | - int numArgs = (whereArgs != null ? whereArgs.length + 4 : 4); | |
1926 | - String[] selectionArgs = new String[numArgs]; | |
1927 | - int selArgsIndex = 0; | |
1928 | - | |
1929 | - if (whereArgs != null) { | |
1930 | - for (selArgsIndex=0; selArgsIndex<numArgs-1; selArgsIndex++) { | |
1931 | - selectionArgs[selArgsIndex] = whereArgs[selArgsIndex]; | |
1932 | - } | |
1933 | - } | |
1934 | - | |
1935 | - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); | |
1936 | - | |
1937 | - db.beginTransaction(); | |
1938 | - int sum = 0; | |
1939 | - | |
1940 | - try { | |
1941 | - ContentValues presenceValues = new ContentValues(); | |
1942 | - | |
1943 | - for (int i=0; i<count; i++) { | |
1944 | - String username = usernames.get(i); | |
1945 | - int priority = 0; | |
1946 | - int mode = 0; | |
1947 | - String status = statusArray.get(i); | |
1948 | - String jidResource = resourceArray == null ? "" : resourceArray.get(i); | |
1949 | - int clientType = Im.Presence.CLIENT_TYPE_DEFAULT; | |
1950 | - | |
1951 | - try { | |
1952 | - if (priorityArray != null) { | |
1953 | - priority = Integer.parseInt(priorityArray.get(i)); | |
1954 | - } | |
1955 | - if (modeArray != null) { | |
1956 | - mode = Integer.parseInt(modeArray.get(i)); | |
1957 | - } | |
1958 | - if (clientTypeArray != null) { | |
1959 | - clientType = Integer.parseInt(clientTypeArray.get(i)); | |
1960 | - } | |
1961 | - } catch (NumberFormatException ex) { | |
1962 | - Log.e(LOG_TAG, "[ImProvider] updateBulkPresence: caught " + ex); | |
1963 | - } | |
1964 | - | |
1965 | - /* | |
1966 | - if (DBG) { | |
1967 | - log("updateBulkPresence[" + i + "] username=" + username + ", priority=" + | |
1968 | - priority + ", mode=" + mode + ", status=" + status + ", resource=" + | |
1969 | - jidResource + ", clientType=" + clientType); | |
1970 | - } | |
1971 | - */ | |
1972 | - | |
1973 | - if (modeArray != null) { | |
1974 | - presenceValues.put(Im.Presence.PRESENCE_STATUS, mode); | |
1975 | - } | |
1976 | - if (priorityArray != null) { | |
1977 | - presenceValues.put(Im.Presence.PRIORITY, priority); | |
1978 | - } | |
1979 | - presenceValues.put(Im.Presence.PRESENCE_CUSTOM_STATUS, status); | |
1980 | - if (clientTypeArray != null) { | |
1981 | - presenceValues.put(Im.Presence.CLIENT_TYPE, clientType); | |
1982 | - } | |
1983 | - | |
1984 | - if (!TextUtils.isEmpty(jidResource)) { | |
1985 | - presenceValues.put(Im.Presence.JID_RESOURCE, jidResource); | |
1986 | - } | |
1987 | - | |
1988 | - // fill in the selection args | |
1989 | - int idx = selArgsIndex; | |
1990 | - selectionArgs[idx++] = String.valueOf(account); | |
1991 | - selectionArgs[idx++] = username; | |
1992 | - selectionArgs[idx++] = String.valueOf(priority); | |
1993 | - selectionArgs[idx] = jidResource; | |
1994 | - | |
1995 | - int numUpdated = db.update(TABLE_PRESENCE, | |
1996 | - presenceValues, selection, selectionArgs); | |
1997 | - if (numUpdated == 0) { | |
1998 | - Log.e(LOG_TAG, "[ImProvider] updateBulkPresence: failed for " + username); | |
1999 | - } else { | |
2000 | - sum += numUpdated; | |
2001 | - } | |
2002 | - | |
2003 | - // yield the lock if anyone else is trying to | |
2004 | - // perform a db operation here. | |
2005 | - db.yieldIfContended(); | |
2006 | - } | |
2007 | - | |
2008 | - db.setTransactionSuccessful(); | |
2009 | - } finally { | |
2010 | - db.endTransaction(); | |
2011 | - } | |
2012 | - | |
2013 | - if (DBG) log("updateBulkPresence: " + sum + " entries updated"); | |
2014 | - return sum; | |
2015 | - } | |
2016 | - | |
2017 | - private Uri insertInternal(Uri url, ContentValues initialValues) { | |
2018 | - Uri resultUri = null; | |
2019 | - long rowID = 0; | |
2020 | - long account = 0; | |
2021 | - String contact = null; | |
2022 | - long threadId = 0; | |
2023 | - | |
2024 | - boolean notifyContactListContentUri = false; | |
2025 | - boolean notifyContactContentUri = false; | |
2026 | - boolean notifyMessagesContentUri = false; | |
2027 | - boolean notifyMessagesByContactContentUri = false; | |
2028 | - boolean notifyMessagesByThreadIdContentUri = false; | |
2029 | - boolean notifyProviderAccountContentUri = false; | |
2030 | - | |
2031 | - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); | |
2032 | - int match = mUrlMatcher.match(url); | |
2033 | - | |
2034 | - if (DBG) log("insert to " + url + ", match " + match); | |
2035 | - switch (match) { | |
2036 | - case MATCH_PROVIDERS: | |
2037 | - // Insert into the providers table | |
2038 | - rowID = db.insert(TABLE_PROVIDERS, "name", initialValues); | |
2039 | - if (rowID > 0) { | |
2040 | - resultUri = Uri.parse(Im.Provider.CONTENT_URI + "/" + rowID); | |
2041 | - } | |
2042 | - notifyProviderAccountContentUri = true; | |
2043 | - break; | |
2044 | - | |
2045 | - case MATCH_ACCOUNTS: | |
2046 | - // Insert into the accounts table | |
2047 | - rowID = db.insert(TABLE_ACCOUNTS, "name", initialValues); | |
2048 | - if (rowID > 0) { | |
2049 | - resultUri = Uri.parse(Im.Account.CONTENT_URI + "/" + rowID); | |
2050 | - } | |
2051 | - notifyProviderAccountContentUri = true; | |
2052 | - break; | |
2053 | - | |
2054 | - case MATCH_CONTACTS_BY_PROVIDER: | |
2055 | - appendValuesFromUrl(initialValues, url, Im.Contacts.PROVIDER, | |
2056 | - Im.Contacts.ACCOUNT); | |
2057 | - // fall through | |
2058 | - case MATCH_CONTACTS: | |
2059 | - case MATCH_CONTACTS_BAREBONE: | |
2060 | - // Insert into the contacts table | |
2061 | - rowID = db.insert(TABLE_CONTACTS, "username", initialValues); | |
2062 | - if (rowID > 0) { | |
2063 | - resultUri = Uri.parse(Im.Contacts.CONTENT_URI + "/" + rowID); | |
2064 | - } | |
2065 | - | |
2066 | - notifyContactContentUri = true; | |
2067 | - break; | |
2068 | - | |
2069 | - case MATCH_CONTACTS_BULK: | |
2070 | - if (insertBulkContacts(initialValues)) { | |
2071 | - // notify change using the "content://im/contacts" url, | |
2072 | - // so the change will be observed by listeners interested | |
2073 | - // in contacts changes. | |
2074 | - resultUri = Im.Contacts.CONTENT_URI; | |
2075 | - } | |
2076 | - notifyContactContentUri = true; | |
2077 | - break; | |
2078 | - | |
2079 | - case MATCH_CONTACTLISTS_BY_PROVIDER: | |
2080 | - appendValuesFromUrl(initialValues, url, Im.ContactList.PROVIDER, | |
2081 | - Im.ContactList.ACCOUNT); | |
2082 | - // fall through | |
2083 | - case MATCH_CONTACTLISTS: | |
2084 | - // Insert into the contactList table | |
2085 | - rowID = db.insert(TABLE_CONTACT_LIST, "name", initialValues); | |
2086 | - if (rowID > 0) { | |
2087 | - resultUri = Uri.parse(Im.ContactList.CONTENT_URI + "/" + rowID); | |
2088 | - } | |
2089 | - notifyContactListContentUri = true; | |
2090 | - break; | |
2091 | - | |
2092 | - case MATCH_BLOCKEDLIST_BY_PROVIDER: | |
2093 | - appendValuesFromUrl(initialValues, url, Im.BlockedList.PROVIDER, | |
2094 | - Im.BlockedList.ACCOUNT); | |
2095 | - // fall through | |
2096 | - case MATCH_BLOCKEDLIST: | |
2097 | - // Insert into the blockedList table | |
2098 | - rowID = db.insert(TABLE_BLOCKED_LIST, "username", initialValues); | |
2099 | - if (rowID > 0) { | |
2100 | - resultUri = Uri.parse(Im.BlockedList.CONTENT_URI + "/" + rowID); | |
2101 | - } | |
2102 | - | |
2103 | - break; | |
2104 | - | |
2105 | - case MATCH_CONTACTS_ETAGS: | |
2106 | - rowID = db.replace(TABLE_CONTACTS_ETAG, Im.ContactsEtag.ETAG, initialValues); | |
2107 | - if (rowID > 0) { | |
2108 | - resultUri = Uri.parse(Im.ContactsEtag.CONTENT_URI + "/" + rowID); | |
2109 | - } | |
2110 | - break; | |
2111 | - | |
2112 | - case MATCH_MESSAGES_BY_CONTACT: | |
2113 | - String accountStr = decodeURLSegment(url.getPathSegments().get(1)); | |
2114 | - try { | |
2115 | - account = Long.parseLong(accountStr); | |
2116 | - } catch (NumberFormatException ex) { | |
2117 | - throw new IllegalArgumentException(); | |
2118 | - } | |
2119 | - | |
2120 | - contact = decodeURLSegment(url.getPathSegments().get(2)); | |
2121 | - initialValues.put(Im.Messages.THREAD_ID, getContactId(db, accountStr, contact)); | |
2122 | - | |
2123 | - notifyMessagesContentUri = true; | |
2124 | - | |
2125 | - // Insert into the messages table. | |
2126 | - rowID = db.insert(TABLE_MESSAGES, "thread_id", initialValues); | |
2127 | - if (rowID > 0) { | |
2128 | - resultUri = Uri.parse(Im.Messages.CONTENT_URI + "/" + rowID); | |
2129 | - } | |
2130 | - | |
2131 | - break; | |
2132 | - | |
2133 | - case MATCH_MESSAGES_BY_THREAD_ID: | |
2134 | - appendValuesFromUrl(initialValues, url, Im.Messages.THREAD_ID); | |
2135 | - // fall through | |
2136 | - | |
2137 | - case MATCH_MESSAGES: | |
2138 | - // Insert into the messages table. | |
2139 | - notifyMessagesContentUri = true; | |
2140 | - rowID = db.insert(TABLE_MESSAGES, "thread_id", initialValues); | |
2141 | - if (rowID > 0) { | |
2142 | - resultUri = Uri.parse(Im.Messages.CONTENT_URI + "/" + rowID); | |
2143 | - } | |
2144 | - | |
2145 | - break; | |
2146 | - | |
2147 | - case MATCH_OTR_MESSAGES_BY_CONTACT: | |
2148 | - String accountStr2 = decodeURLSegment(url.getPathSegments().get(1)); | |
2149 | - | |
2150 | - try { | |
2151 | - account = Long.parseLong(accountStr2); | |
2152 | - } catch (NumberFormatException ex) { | |
2153 | - throw new IllegalArgumentException(); | |
2154 | - } | |
2155 | - | |
2156 | - contact = decodeURLSegment(url.getPathSegments().get(2)); | |
2157 | - initialValues.put(Im.Messages.THREAD_ID, getContactId(db, accountStr2, contact)); | |
2158 | - | |
2159 | - notifyMessagesByContactContentUri = true; | |
2160 | - | |
2161 | - // Insert into the in-memory messages table. | |
2162 | - rowID = db.insert(TABLE_IN_MEMORY_MESSAGES, "thread_id", initialValues); | |
2163 | - if (rowID > 0) { | |
2164 | - resultUri = Uri.parse(Im.Messages.OTR_MESSAGES_CONTENT_URI + "/" + rowID); | |
2165 | - } | |
2166 | - | |
2167 | - break; | |
2168 | - | |
2169 | - case MATCH_OTR_MESSAGES_BY_THREAD_ID: | |
2170 | - try { | |
2171 | - threadId = Long.parseLong(decodeURLSegment(url.getPathSegments().get(1))); | |
2172 | - } catch (NumberFormatException ex) { | |
2173 | - throw new IllegalArgumentException(); | |
2174 | - } | |
2175 | - | |
2176 | - initialValues.put(Im.Messages.THREAD_ID, threadId); | |
2177 | - | |
2178 | - notifyMessagesByThreadIdContentUri = true; | |
2179 | - // fall through | |
2180 | - | |
2181 | - case MATCH_OTR_MESSAGES: | |
2182 | - // Insert into the messages table. | |
2183 | - rowID = db.insert(TABLE_IN_MEMORY_MESSAGES, "thread_id", initialValues); | |
2184 | - if (rowID > 0) { | |
2185 | - resultUri = Uri.parse(Im.Messages.OTR_MESSAGES_CONTENT_URI + "/" + rowID); | |
2186 | - } | |
2187 | - | |
2188 | - break; | |
2189 | - | |
2190 | - case MATCH_INVITATIONS: | |
2191 | - rowID = db.insert(TABLE_INVITATIONS, null, initialValues); | |
2192 | - if (rowID > 0) { | |
2193 | - resultUri = Uri.parse(Im.Invitation.CONTENT_URI + "/" + rowID); | |
2194 | - } | |
2195 | - break; | |
2196 | - | |
2197 | - case MATCH_GROUP_MEMBERS: | |
2198 | - rowID = db.insert(TABLE_GROUP_MEMBERS, "nickname", initialValues); | |
2199 | - if (rowID > 0) { | |
2200 | - resultUri = Uri.parse(Im.GroupMembers.CONTENT_URI + "/" + rowID); | |
2201 | - } | |
2202 | - break; | |
2203 | - | |
2204 | - case MATCH_GROUP_MEMBERS_BY_GROUP: | |
2205 | - appendValuesFromUrl(initialValues, url, Im.GroupMembers.GROUP); | |
2206 | - rowID = db.insert(TABLE_GROUP_MEMBERS, "nickname", initialValues); | |
2207 | - if (rowID > 0) { | |
2208 | - resultUri = Uri.parse(Im.GroupMembers.CONTENT_URI + "/" + rowID); | |
2209 | - } | |
2210 | - break; | |
2211 | - | |
2212 | - case MATCH_AVATAR_BY_PROVIDER: | |
2213 | - appendValuesFromUrl(initialValues, url, Im.Avatars.PROVIDER, Im.Avatars.ACCOUNT); | |
2214 | - // fall through | |
2215 | - case MATCH_AVATARS: | |
2216 | - // Insert into the avatars table | |
2217 | - rowID = db.replace(TABLE_AVATARS, "contact", initialValues); | |
2218 | - if (rowID > 0) { | |
2219 | - resultUri = Uri.parse(Im.Avatars.CONTENT_URI + "/" + rowID); | |
2220 | - } | |
2221 | - break; | |
2222 | - | |
2223 | - case MATCH_CHATS_ID: | |
2224 | - appendValuesFromUrl(initialValues, url, Im.Chats.CONTACT_ID); | |
2225 | - // fall through | |
2226 | - case MATCH_CHATS: | |
2227 | - // Insert into the chats table | |
2228 | - initialValues.put(Im.Chats.SHORTCUT, -1); | |
2229 | - rowID = db.replace(TABLE_CHATS, Im.Chats.CONTACT_ID, initialValues); | |
2230 | - if (rowID > 0) { | |
2231 | - resultUri = Uri.parse(Im.Chats.CONTENT_URI + "/" + rowID); | |
2232 | - addToQuickSwitch(rowID); | |
2233 | - } | |
2234 | - notifyContactContentUri = true; | |
2235 | - break; | |
2236 | - | |
2237 | - case MATCH_PRESENCE: | |
2238 | - rowID = db.replace(TABLE_PRESENCE, null, initialValues); | |
2239 | - if (rowID > 0) { | |
2240 | - resultUri = Uri.parse(Im.Presence.CONTENT_URI + "/" + rowID); | |
2241 | - } | |
2242 | - notifyContactContentUri = true; | |
2243 | - break; | |
2244 | - | |
2245 | - case MATCH_PRESENCE_SEED_BY_ACCOUNT: | |
2246 | - try { | |
2247 | - seedInitialPresenceByAccount(Long.parseLong(url.getLastPathSegment())); | |
2248 | - resultUri = Im.Presence.CONTENT_URI; | |
2249 | - } catch (NumberFormatException ex) { | |
2250 | - throw new IllegalArgumentException(); | |
2251 | - } | |
2252 | - break; | |
2253 | - | |
2254 | - case MATCH_SESSIONS_BY_PROVIDER: | |
2255 | - appendValuesFromUrl(initialValues, url, Im.SessionCookies.PROVIDER, | |
2256 | - Im.SessionCookies.ACCOUNT); | |
2257 | - // fall through | |
2258 | - case MATCH_SESSIONS: | |
2259 | - rowID = db.insert(TABLE_SESSION_COOKIES, null, initialValues); | |
2260 | - if(rowID > 0) { | |
2261 | - resultUri = Uri.parse(Im.SessionCookies.CONTENT_URI + "/" + rowID); | |
2262 | - } | |
2263 | - break; | |
2264 | - | |
2265 | - case MATCH_PROVIDER_SETTINGS: | |
2266 | - rowID = db.replace(TABLE_PROVIDER_SETTINGS, null, initialValues); | |
2267 | - if (rowID > 0) { | |
2268 | - resultUri = Uri.parse(Im.ProviderSettings.CONTENT_URI + "/" + rowID); | |
2269 | - } | |
2270 | - break; | |
2271 | - | |
2272 | - case MATCH_ACCOUNTS_STATUS: | |
2273 | - rowID = db.replace(TABLE_ACCOUNT_STATUS, null, initialValues); | |
2274 | - if (rowID > 0) { | |
2275 | - resultUri = Uri.parse(Im.AccountStatus.CONTENT_URI + "/" + rowID); | |
2276 | - } | |
2277 | - notifyProviderAccountContentUri = true; | |
2278 | - break; | |
2279 | - | |
2280 | - case MATCH_BRANDING_RESOURCE_MAP_CACHE: | |
2281 | - rowID = db.insert(TABLE_BRANDING_RESOURCE_MAP_CACHE, null, initialValues); | |
2282 | - if (rowID > 0) { | |
2283 | - resultUri = Uri.parse(Im.BrandingResourceMapCache.CONTENT_URI + "/" + rowID); | |
2284 | - } | |
2285 | - break; | |
2286 | - | |
2287 | - // mcs/rmq stuff | |
2288 | - case MATCH_OUTGOING_RMQ_MESSAGES: | |
2289 | - rowID = db.insert(TABLE_OUTGOING_RMQ_MESSAGES, null, initialValues); | |
2290 | - if (rowID > 0) { | |
2291 | - resultUri = Uri.parse(Im.OutgoingRmq.CONTENT_URI + "/" + rowID); | |
2292 | - } | |
2293 | - break; | |
2294 | - | |
2295 | - case MATCH_LAST_RMQ_ID: | |
2296 | - rowID = db.replace(TABLE_LAST_RMQ_ID, null, initialValues); | |
2297 | - if (rowID > 0) { | |
2298 | - resultUri = Uri.parse(Im.LastRmqId.CONTENT_URI + "/" + rowID); | |
2299 | - } | |
2300 | - break; | |
2301 | - | |
2302 | - case MATCH_S2D_RMQ_IDS: | |
2303 | - rowID = db.insert(TABLE_S2D_RMQ_IDS, null, initialValues); | |
2304 | - if (rowID > 0) { | |
2305 | - resultUri = Uri.parse(Im.ServerToDeviceRmqIds.CONTENT_URI + "/" + rowID); | |
2306 | - } | |
2307 | - break; | |
2308 | - | |
2309 | - default: | |
2310 | - throw new UnsupportedOperationException("Cannot insert into URL: " + url); | |
2311 | - } | |
2312 | - // TODO: notify the data change observer? | |
2313 | - | |
2314 | - if (resultUri != null) { | |
2315 | - ContentResolver resolver = getContext().getContentResolver(); | |
2316 | - | |
2317 | - // In most case, we query contacts with presence and chats joined, thus | |
2318 | - // we should also notify that contacts changes when presence or chats changed. | |
2319 | - if (notifyContactContentUri) { | |
2320 | - resolver.notifyChange(Im.Contacts.CONTENT_URI, null); | |
2321 | - } | |
2322 | - | |
2323 | - if (notifyContactListContentUri) { | |
2324 | - resolver.notifyChange(Im.ContactList.CONTENT_URI, null); | |
2325 | - } | |
2326 | - | |
2327 | - if (notifyMessagesContentUri) { | |
2328 | - resolver.notifyChange(Im.Messages.CONTENT_URI, null); | |
2329 | - } | |
2330 | - | |
2331 | - if (notifyMessagesByContactContentUri) { | |
2332 | - resolver.notifyChange(Im.Messages.CONTENT_URI, null); | |
2333 | - resolver.notifyChange(Im.Messages.getContentUriByContact(account, contact), null); | |
2334 | - } | |
2335 | - | |
2336 | - if (notifyMessagesByThreadIdContentUri) { | |
2337 | - resolver.notifyChange(Im.Messages.CONTENT_URI, null); | |
2338 | - resolver.notifyChange(Im.Messages.getContentUriByThreadId(threadId), null); | |
2339 | - } | |
2340 | - | |
2341 | - if (notifyProviderAccountContentUri) { | |
2342 | - if (DBG) log("notify insert for " + Im.Provider.CONTENT_URI_WITH_ACCOUNT); | |
2343 | - resolver.notifyChange(Im.Provider.CONTENT_URI_WITH_ACCOUNT, null); | |
2344 | - } | |
2345 | - } | |
2346 | - return resultUri; | |
2347 | - } | |
2348 | - | |
2349 | - private void appendValuesFromUrl(ContentValues values, Uri url, String...columns){ | |
2350 | - if(url.getPathSegments().size() <= columns.length) { | |
2351 | - throw new IllegalArgumentException("Not enough values in url"); | |
2352 | - } | |
2353 | - for(int i = 0; i < columns.length; i++){ | |
2354 | - if(values.containsKey(columns[i])){ | |
2355 | - throw new UnsupportedOperationException("Cannot override the value for " + columns[i]); | |
2356 | - } | |
2357 | - values.put(columns[i], decodeURLSegment(url.getPathSegments().get(i + 1))); | |
2358 | - } | |
2359 | - } | |
2360 | - | |
2361 | - private long getContactId(final SQLiteDatabase db, | |
2362 | - final String accountId, final String contact) { | |
2363 | - SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); | |
2364 | - qb.setTables(TABLE_CONTACTS); | |
2365 | - qb.setProjectionMap(sContactsProjectionMap); | |
2366 | - | |
2367 | - mQueryContactIdSelectionArgs2[0] = accountId; | |
2368 | - mQueryContactIdSelectionArgs2[1] = contact; | |
2369 | - | |
2370 | - Cursor c = qb.query(db, | |
2371 | - CONTACT_ID_PROJECTION, | |
2372 | - CONTACT_ID_QUERY_SELECTION, | |
2373 | - mQueryContactIdSelectionArgs2, | |
2374 | - null, null, null, null); | |
2375 | - | |
2376 | - long contactId = 0; | |
2377 | - | |
2378 | - try { | |
2379 | - if (c.moveToFirst()) { | |
2380 | - contactId = c.getLong(CONTACT_ID_COLUMN); | |
2381 | - } | |
2382 | - } finally { | |
2383 | - c.close(); | |
2384 | - } | |
2385 | - | |
2386 | - return contactId; | |
2387 | - } | |
2388 | - | |
2389 | - // Quick-switch management | |
2390 | - // The chat UI provides slots (0, 9, .., 1) for the first 10 chats. This allows you to | |
2391 | - // quickly switch between these chats by chording menu+#. We number from the right end of | |
2392 | - // the number row and move leftward to make an easier two-hand chord with the menu button | |
2393 | - // on the left side of the keyboard. | |
2394 | - private void addToQuickSwitch(long newRow) { | |
2395 | - // Since there are fewer than 10, there must be an empty slot. Let's find it. | |
2396 | - int slot = findEmptyQuickSwitchSlot(); | |
2397 | - | |
2398 | - if (slot == -1) { | |
2399 | - return; | |
2400 | - } | |
2401 | - | |
2402 | - updateSlotForChat(newRow, slot); | |
2403 | - } | |
2404 | - | |
2405 | - // If there are more than 10 chats and one with a quick switch slot ends then pick a chat | |
2406 | - // that doesn't have a slot and have it inhabit the newly emptied slot. | |
2407 | - private void backfillQuickSwitchSlots() { | |
2408 | - // Find all the chats without a quick switch slot, and order | |
2409 | - Cursor c = query(Im.Chats.CONTENT_URI, | |
2410 | - BACKFILL_PROJECTION, | |
2411 | - Im.Chats.SHORTCUT + "=-1", null, Im.Chats.LAST_MESSAGE_DATE + " DESC"); | |
2412 | - | |
2413 | - try { | |
2414 | - if (c.getCount() < 1) { | |
2415 | - return; | |
2416 | - } | |
2417 | - | |
2418 | - int slot = findEmptyQuickSwitchSlot(); | |
2419 | - | |
2420 | - if (slot != -1) { | |
2421 | - c.moveToFirst(); | |
2422 | - | |
2423 | - long id = c.getLong(c.getColumnIndex(Im.Chats._ID)); | |
2424 | - | |
2425 | - updateSlotForChat(id, slot); | |
2426 | - } | |
2427 | - } finally { | |
2428 | - c.close(); | |
2429 | - } | |
2430 | - } | |
2431 | - | |
2432 | - private int updateSlotForChat(long chatId, int slot) { | |
2433 | - ContentValues values = new ContentValues(); | |
2434 | - | |
2435 | - values.put(Im.Chats.SHORTCUT, slot); | |
2436 | - | |
2437 | - return update(Im.Chats.CONTENT_URI, values, Im.Chats._ID + "=?", | |
2438 | - new String[] { Long.toString(chatId) }); | |
2439 | - } | |
2440 | - | |
2441 | - private int findEmptyQuickSwitchSlot() { | |
2442 | - Cursor c = queryInternal(Im.Chats.CONTENT_URI, FIND_SHORTCUT_PROJECTION, null, null, null); | |
2443 | - final int N = c.getCount(); | |
2444 | - | |
2445 | - try { | |
2446 | - // If there are 10 or more chats then all the quick switch slots are already filled | |
2447 | - if (N >= 10) { | |
2448 | - return -1; | |
2449 | - } | |
2450 | - | |
2451 | - int slots = 0; | |
2452 | - int column = c.getColumnIndex(Im.Chats.SHORTCUT); | |
2453 | - | |
2454 | - // The map is here because numbers go from 0-9, but we want to assign slots in | |
2455 | - // 0, 9, 8, ..., 1 order to match the right-to-left reading of the number row | |
2456 | - // on the keyboard. | |
2457 | - int[] map = new int[] { 0, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; | |
2458 | - | |
2459 | - // Mark all the slots that are in use | |
2460 | - // The shortcuts represent actual keyboard number row keys, and not ordinals. | |
2461 | - // So 7 would mean the shortcut is the 7 key on the keyboard and NOT the 7th | |
2462 | - // shortcut. The passing of slot through map[] below maps these keyboard key | |
2463 | - // shortcuts into an ordinal bit position in the 'slots' bitfield. | |
2464 | - for (c.moveToFirst(); ! c.isAfterLast(); c.moveToNext()) { | |
2465 | - int slot = c.getInt(column); | |
2466 | - | |
2467 | - if (slot != -1) { | |
2468 | - slots |= (1 << map[slot]); | |
2469 | - } | |
2470 | - } | |
2471 | - | |
2472 | - // Try to find an empty one | |
2473 | - // As we exit this, the push of i through map[] maps the ordinal bit position | |
2474 | - // in the 'slots' bitfield onto a key on the number row of the device keyboard. | |
2475 | - // The keyboard key is what is used to designate the shortcut. | |
2476 | - for (int i = 0; i < 10; i++) { | |
2477 | - if ((slots & (1 << i)) == 0) { | |
2478 | - return map[i]; | |
2479 | - } | |
2480 | - } | |
2481 | - | |
2482 | - return -1; | |
2483 | - } finally { | |
2484 | - c.close(); | |
2485 | - } | |
2486 | - } | |
2487 | - | |
2488 | - /** | |
2489 | - * manual trigger for deleting contacts | |
2490 | - */ | |
2491 | - private static final String DELETE_PRESENCE_SELECTION = | |
2492 | - Im.Presence.CONTACT_ID + " in (select " + | |
2493 | - PRESENCE_CONTACT_ID + " from " + TABLE_PRESENCE + " left outer join " + TABLE_CONTACTS + | |
2494 | - " on " + PRESENCE_CONTACT_ID + '=' + CONTACT_ID + " where " + CONTACT_ID + " IS NULL)"; | |
2495 | - | |
2496 | - private static final String CHATS_CONTACT_ID = TABLE_CHATS + '.' + Im.Chats.CONTACT_ID; | |
2497 | - private static final String DELETE_CHATS_SELECTION = Im.Chats.CONTACT_ID + " in (select "+ | |
2498 | - CHATS_CONTACT_ID + " from " + TABLE_CHATS + " left outer join " + TABLE_CONTACTS + | |
2499 | - " on " + CHATS_CONTACT_ID + '=' + CONTACT_ID + " where " + CONTACT_ID + " IS NULL)"; | |
2500 | - | |
2501 | - private static final String GROUP_MEMBER_ID = TABLE_GROUP_MEMBERS + '.' + Im.GroupMembers.GROUP; | |
2502 | - private static final String DELETE_GROUP_MEMBER_SELECTION = | |
2503 | - Im.GroupMembers.GROUP + " in (select "+ | |
2504 | - GROUP_MEMBER_ID + " from " + TABLE_GROUP_MEMBERS + " left outer join " + TABLE_CONTACTS + | |
2505 | - " on " + GROUP_MEMBER_ID + '=' + CONTACT_ID + " where " + CONTACT_ID + " IS NULL)"; | |
2506 | - | |
2507 | - private static final String GROUP_MESSAGES_ID = TABLE_MESSAGES + '.' + Im.Messages.THREAD_ID; | |
2508 | - private static final String DELETE_GROUP_MESSAGES_SELECTION = | |
2509 | - Im.Messages.THREAD_ID + " in (select "+ GROUP_MESSAGES_ID + " from " + | |
2510 | - TABLE_MESSAGES + " left outer join " + TABLE_CONTACTS + " on " + | |
2511 | - GROUP_MESSAGES_ID + '=' + CONTACT_ID + " where " + CONTACT_ID + " IS NULL)"; | |
2512 | - | |
2513 | - private void performContactRemovalCleanup(long contactId) { | |
2514 | - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); | |
2515 | - | |
2516 | - if (contactId > 0) { | |
2517 | - StringBuilder buf = new StringBuilder(); | |
2518 | - | |
2519 | - // delete presence | |
2520 | - buf.append(Im.Presence.CONTACT_ID).append('=').append(contactId); | |
2521 | - deleteWithSelection(db, TABLE_PRESENCE, buf.toString(), null); | |
2522 | - | |
2523 | - // delete group memebers | |
2524 | - buf.delete(0, buf.length()); | |
2525 | - buf.append(Im.GroupMembers.GROUP).append('=').append(contactId); | |
2526 | - deleteWithSelection(db, TABLE_GROUP_MEMBERS, buf.toString(), null); | |
2527 | - } else { | |
2528 | - // delete presence | |
2529 | - deleteWithSelection(db, TABLE_PRESENCE, DELETE_PRESENCE_SELECTION, null); | |
2530 | - | |
2531 | - // delete group members | |
2532 | - deleteWithSelection(db, TABLE_GROUP_MEMBERS, DELETE_GROUP_MEMBER_SELECTION, null); | |
2533 | - } | |
2534 | - } | |
2535 | - | |
2536 | - private void deleteWithSelection(SQLiteDatabase db, String tableName, | |
2537 | - String selection, String[] selectionArgs) { | |
2538 | - if (DBG) log("deleteWithSelection: table " + tableName + ", selection => " + selection); | |
2539 | - int count = db.delete(tableName, selection, selectionArgs); | |
2540 | - if (DBG) log("deleteWithSelection: deleted " + count + " rows"); | |
2541 | - } | |
2542 | - | |
2543 | - private String buildContactIdSelection(String columnName, String contactSelection) { | |
2544 | - StringBuilder buf = new StringBuilder(); | |
2545 | - | |
2546 | - buf.append(columnName); | |
2547 | - buf.append(" in (select "); | |
2548 | - buf.append(Im.Contacts._ID); | |
2549 | - buf.append(" from "); | |
2550 | - buf.append(TABLE_CONTACTS); | |
2551 | - buf.append(" where "); | |
2552 | - buf.append(contactSelection); | |
2553 | - buf.append(")"); | |
2554 | - | |
2555 | - return buf.toString(); | |
2556 | - } | |
2557 | - | |
2558 | - private int deleteInternal(Uri url, String userWhere, String[] whereArgs) { | |
2559 | - String tableToChange; | |
2560 | - | |
2561 | - // In some cases a given url requires that we delete rows from more than one | |
2562 | - // table. The motivating example is deleting messages from both the on disk | |
2563 | - // and in memory messages tables. | |
2564 | - String tableToChange2 = null; | |
2565 | - String idColumnName = null; | |
2566 | - String changedItemId = null; | |
2567 | - String provider = null; | |
2568 | - String accountStr = null; | |
2569 | - long account = 0; | |
2570 | - String contact = null; | |
2571 | - long threadId = 0; | |
2572 | - | |
2573 | - StringBuilder whereClause = new StringBuilder(); | |
2574 | - if(userWhere != null) { | |
2575 | - whereClause.append(userWhere); | |
2576 | - } | |
2577 | - | |
2578 | - boolean notifyMessagesContentUri = false; | |
2579 | - boolean notifyMessagesByContactContentUri = false; | |
2580 | - boolean notifyMessagesByThreadIdContentUri = false; | |
2581 | - boolean notifyContactListContentUri = false; | |
2582 | - boolean notifyProviderAccountContentUri = false; | |
2583 | - int match = mUrlMatcher.match(url); | |
2584 | - | |
2585 | - boolean contactDeleted = false; | |
2586 | - long deletedContactId = 0; | |
2587 | - | |
2588 | - boolean backfillQuickSwitchSlots = false; | |
2589 | - | |
2590 | - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); | |
2591 | - | |
2592 | - switch (match) { | |
2593 | - case MATCH_PROVIDERS: | |
2594 | - tableToChange = TABLE_PROVIDERS; | |
2595 | - notifyProviderAccountContentUri = true; | |
2596 | - break; | |
2597 | - | |
2598 | - case MATCH_ACCOUNTS_BY_ID: | |
2599 | - changedItemId = url.getPathSegments().get(1); | |
2600 | - // fall through | |
2601 | - case MATCH_ACCOUNTS: | |
2602 | - tableToChange = TABLE_ACCOUNTS; | |
2603 | - notifyProviderAccountContentUri = true; | |
2604 | - break; | |
2605 | - | |
2606 | - case MATCH_ACCOUNT_STATUS: | |
2607 | - changedItemId = url.getPathSegments().get(1); | |
2608 | - // fall through | |
2609 | - case MATCH_ACCOUNTS_STATUS: | |
2610 | - tableToChange = TABLE_ACCOUNT_STATUS; | |
2611 | - notifyProviderAccountContentUri = true; | |
2612 | - break; | |
2613 | - | |
2614 | - case MATCH_CONTACTS: | |
2615 | - case MATCH_CONTACTS_BAREBONE: | |
2616 | - tableToChange = TABLE_CONTACTS; | |
2617 | - contactDeleted = true; | |
2618 | - break; | |
2619 | - | |
2620 | - case MATCH_CONTACT: | |
2621 | - tableToChange = TABLE_CONTACTS; | |
2622 | - changedItemId = url.getPathSegments().get(1); | |
2623 | - | |
2624 | - try { | |
2625 | - deletedContactId = Long.parseLong(changedItemId); | |
2626 | - } catch (NumberFormatException ex) { | |
2627 | - throw new IllegalArgumentException(); | |
2628 | - } | |
2629 | - | |
2630 | - contactDeleted = true; | |
2631 | - break; | |
2632 | - | |
2633 | - case MATCH_CONTACTS_BY_PROVIDER: | |
2634 | - tableToChange = TABLE_CONTACTS; | |
2635 | - appendWhere(whereClause, Im.Contacts.ACCOUNT, "=", url.getPathSegments().get(2)); | |
2636 | - contactDeleted = true; | |
2637 | - break; | |
2638 | - | |
2639 | - case MATCH_CONTACTLISTS_BY_PROVIDER: | |
2640 | - appendWhere(whereClause, Im.ContactList.ACCOUNT, "=", | |
2641 | - url.getPathSegments().get(2)); | |
2642 | - // fall through | |
2643 | - case MATCH_CONTACTLISTS: | |
2644 | - tableToChange = TABLE_CONTACT_LIST; | |
2645 | - notifyContactListContentUri = true; | |
2646 | - break; | |
2647 | - | |
2648 | - case MATCH_CONTACTLIST: | |
2649 | - tableToChange = TABLE_CONTACT_LIST; | |
2650 | - changedItemId = url.getPathSegments().get(1); | |
2651 | - break; | |
2652 | - | |
2653 | - case MATCH_BLOCKEDLIST: | |
2654 | - tableToChange = TABLE_BLOCKED_LIST; | |
2655 | - break; | |
2656 | - | |
2657 | - case MATCH_BLOCKEDLIST_BY_PROVIDER: | |
2658 | - tableToChange = TABLE_BLOCKED_LIST; | |
2659 | - appendWhere(whereClause, Im.BlockedList.ACCOUNT, "=", url.getPathSegments().get(2)); | |
2660 | - break; | |
2661 | - | |
2662 | - case MATCH_CONTACTS_ETAGS: | |
2663 | - tableToChange = TABLE_CONTACTS_ETAG; | |
2664 | - break; | |
2665 | - | |
2666 | - case MATCH_CONTACTS_ETAG: | |
2667 | - tableToChange = TABLE_CONTACTS_ETAG; | |
2668 | - changedItemId = url.getPathSegments().get(1); | |
2669 | - break; | |
2670 | - | |
2671 | - case MATCH_MESSAGES: | |
2672 | - tableToChange = TABLE_MESSAGES; | |
2673 | - break; | |
2674 | - | |
2675 | - case MATCH_MESSAGES_BY_CONTACT: | |
2676 | - tableToChange = TABLE_MESSAGES; | |
2677 | - tableToChange2 = TABLE_IN_MEMORY_MESSAGES; | |
2678 | - | |
2679 | - accountStr = decodeURLSegment(url.getPathSegments().get(1)); | |
2680 | - try { | |
2681 | - account = Long.parseLong(accountStr); | |
2682 | - } catch (NumberFormatException ex) { | |
2683 | - throw new IllegalArgumentException(); | |
2684 | - } | |
2685 | - | |
2686 | - contact = decodeURLSegment(url.getPathSegments().get(2)); | |
2687 | - appendWhere(whereClause, Im.Messages.THREAD_ID, "=", | |
2688 | - getContactId(db, accountStr, contact)); | |
2689 | - | |
2690 | - notifyMessagesContentUri = true; | |
2691 | - break; | |
2692 | - | |
2693 | - case MATCH_MESSAGES_BY_THREAD_ID: | |
2694 | - tableToChange = TABLE_MESSAGES; | |
2695 | - tableToChange2 = TABLE_IN_MEMORY_MESSAGES; | |
2696 | - | |
2697 | - try { | |
2698 | - threadId = Long.parseLong(decodeURLSegment(url.getPathSegments().get(1))); | |
2699 | - } catch (NumberFormatException ex) { | |
2700 | - throw new IllegalArgumentException(); | |
2701 | - } | |
2702 | - | |
2703 | - appendWhere(whereClause, Im.Messages.THREAD_ID, "=", threadId); | |
2704 | - | |
2705 | - notifyMessagesContentUri = true; | |
2706 | - break; | |
2707 | - | |
2708 | - case MATCH_MESSAGES_BY_PROVIDER: | |
2709 | - tableToChange = TABLE_MESSAGES; | |
2710 | - | |
2711 | - provider = decodeURLSegment(url.getPathSegments().get(1)); | |
2712 | - appendWhere(whereClause, buildContactIdSelection(Im.Messages.THREAD_ID, | |
2713 | - Im.Contacts.PROVIDER + "='" + provider + "'")); | |
2714 | - | |
2715 | - notifyMessagesContentUri = true; | |
2716 | - break; | |
2717 | - | |
2718 | - case MATCH_MESSAGES_BY_ACCOUNT: | |
2719 | - tableToChange = TABLE_MESSAGES; | |
2720 | - | |
2721 | - accountStr = decodeURLSegment(url.getPathSegments().get(1)); | |
2722 | - appendWhere(whereClause, buildContactIdSelection(Im.Messages.THREAD_ID, | |
2723 | - Im.Contacts.ACCOUNT + "='" + accountStr + "'")); | |
2724 | - | |
2725 | - notifyMessagesContentUri = true; | |
2726 | - break; | |
2727 | - | |
2728 | - case MATCH_MESSAGE: | |
2729 | - tableToChange = TABLE_MESSAGES; | |
2730 | - changedItemId = url.getPathSegments().get(1); | |
2731 | - notifyMessagesContentUri = true; | |
2732 | - break; | |
2733 | - | |
2734 | - case MATCH_OTR_MESSAGES: | |
2735 | - tableToChange = TABLE_IN_MEMORY_MESSAGES; | |
2736 | - break; | |
2737 | - | |
2738 | - case MATCH_OTR_MESSAGES_BY_CONTACT: | |
2739 | - tableToChange = TABLE_IN_MEMORY_MESSAGES; | |
2740 | - | |
2741 | - accountStr = decodeURLSegment(url.getPathSegments().get(1)); | |
2742 | - try { | |
2743 | - account = Long.parseLong(accountStr); | |
2744 | - } catch (NumberFormatException ex) { | |
2745 | - throw new IllegalArgumentException(); | |
2746 | - } | |
2747 | - | |
2748 | - contact = decodeURLSegment(url.getPathSegments().get(2)); | |
2749 | - appendWhere(whereClause, Im.Messages.THREAD_ID, "=", | |
2750 | - getContactId(db, accountStr, contact)); | |
2751 | - | |
2752 | - notifyMessagesByContactContentUri = true; | |
2753 | - break; | |
2754 | - | |
2755 | - case MATCH_OTR_MESSAGES_BY_THREAD_ID: | |
2756 | - tableToChange = TABLE_IN_MEMORY_MESSAGES; | |
2757 | - | |
2758 | - try { | |
2759 | - threadId = Long.parseLong(decodeURLSegment(url.getPathSegments().get(1))); | |
2760 | - } catch (NumberFormatException ex) { | |
2761 | - throw new IllegalArgumentException(); | |
2762 | - } | |
2763 | - | |
2764 | - appendWhere(whereClause, Im.Messages.THREAD_ID, "=", threadId); | |
2765 | - | |
2766 | - notifyMessagesByThreadIdContentUri = true; | |
2767 | - break; | |
2768 | - | |
2769 | - case MATCH_OTR_MESSAGES_BY_PROVIDER: | |
2770 | - tableToChange = TABLE_IN_MEMORY_MESSAGES; | |
2771 | - | |
2772 | - provider = decodeURLSegment(url.getPathSegments().get(1)); | |
2773 | - appendWhere(whereClause, buildContactIdSelection(Im.Messages.THREAD_ID, | |
2774 | - Im.Contacts.PROVIDER + "='" + provider + "'")); | |
2775 | - | |
2776 | - if (DBG) log("delete (MATCH_OTR_MESSAGES_BY_PROVIDER) sel => " + whereClause); | |
2777 | - notifyMessagesContentUri = true; | |
2778 | - break; | |
2779 | - | |
2780 | - case MATCH_OTR_MESSAGES_BY_ACCOUNT: | |
2781 | - tableToChange = TABLE_IN_MEMORY_MESSAGES; | |
2782 | - | |
2783 | - accountStr = decodeURLSegment(url.getPathSegments().get(1)); | |
2784 | - appendWhere(whereClause, buildContactIdSelection(Im.Messages.THREAD_ID, | |
2785 | - Im.Contacts.ACCOUNT + "='" + accountStr + "'")); | |
2786 | - | |
2787 | - if (DBG) log("delete (MATCH_OTR_MESSAGES_BY_ACCOUNT) sel => " + whereClause); | |
2788 | - notifyMessagesContentUri = true; | |
2789 | - break; | |
2790 | - | |
2791 | - case MATCH_OTR_MESSAGE: | |
2792 | - tableToChange = TABLE_IN_MEMORY_MESSAGES; | |
2793 | - changedItemId = url.getPathSegments().get(1); | |
2794 | - notifyMessagesContentUri = true; | |
2795 | - break; | |
2796 | - | |
2797 | - case MATCH_GROUP_MEMBERS: | |
2798 | - tableToChange = TABLE_GROUP_MEMBERS; | |
2799 | - break; | |
2800 | - | |
2801 | - case MATCH_GROUP_MEMBERS_BY_GROUP: | |
2802 | - tableToChange = TABLE_GROUP_MEMBERS; | |
2803 | - appendWhere(whereClause, Im.GroupMembers.GROUP, "=", url.getPathSegments().get(1)); | |
2804 | - break; | |
2805 | - | |
2806 | - case MATCH_INVITATIONS: | |
2807 | - tableToChange = TABLE_INVITATIONS; | |
2808 | - break; | |
2809 | - | |
2810 | - case MATCH_INVITATION: | |
2811 | - tableToChange = TABLE_INVITATIONS; | |
2812 | - changedItemId = url.getPathSegments().get(1); | |
2813 | - break; | |
2814 | - | |
2815 | - case MATCH_AVATARS: | |
2816 | - tableToChange = TABLE_AVATARS; | |
2817 | - break; | |
2818 | - | |
2819 | - case MATCH_AVATAR: | |
2820 | - tableToChange = TABLE_AVATARS; | |
2821 | - changedItemId = url.getPathSegments().get(1); | |
2822 | - break; | |
2823 | - | |
2824 | - case MATCH_AVATAR_BY_PROVIDER: | |
2825 | - tableToChange = TABLE_AVATARS; | |
2826 | - changedItemId = url.getPathSegments().get(2); | |
2827 | - idColumnName = Im.Avatars.ACCOUNT; | |
2828 | - break; | |
2829 | - | |
2830 | - case MATCH_CHATS: | |
2831 | - tableToChange = TABLE_CHATS; | |
2832 | - backfillQuickSwitchSlots = true; | |
2833 | - break; | |
2834 | - | |
2835 | - case MATCH_CHATS_BY_ACCOUNT: | |
2836 | - tableToChange = TABLE_CHATS; | |
2837 | - | |
2838 | - accountStr = decodeURLSegment(url.getLastPathSegment()); | |
2839 | - appendWhere(whereClause, buildContactIdSelection(Im.Chats.CONTACT_ID, | |
2840 | - Im.Contacts.ACCOUNT + "='" + accountStr + "'")); | |
2841 | - | |
2842 | - if (DBG) log("delete (MATCH_CHATS_BY_ACCOUNT) sel => " + whereClause); | |
2843 | - | |
2844 | - changedItemId = null; | |
2845 | - break; | |
2846 | - | |
2847 | - case MATCH_CHATS_ID: | |
2848 | - tableToChange = TABLE_CHATS; | |
2849 | - changedItemId = url.getPathSegments().get(1); | |
2850 | - idColumnName = Im.Chats.CONTACT_ID; | |
2851 | - break; | |
2852 | - | |
2853 | - case MATCH_PRESENCE: | |
2854 | - tableToChange = TABLE_PRESENCE; | |
2855 | - break; | |
2856 | - | |
2857 | - case MATCH_PRESENCE_ID: | |
2858 | - tableToChange = TABLE_PRESENCE; | |
2859 | - changedItemId = url.getPathSegments().get(1); | |
2860 | - idColumnName = Im.Presence.CONTACT_ID; | |
2861 | - break; | |
2862 | - | |
2863 | - case MATCH_PRESENCE_BY_ACCOUNT: | |
2864 | - tableToChange = TABLE_PRESENCE; | |
2865 | - | |
2866 | - accountStr = decodeURLSegment(url.getLastPathSegment()); | |
2867 | - appendWhere(whereClause, buildContactIdSelection(Im.Presence.CONTACT_ID, | |
2868 | - Im.Contacts.ACCOUNT + "='" + accountStr + "'")); | |
2869 | - | |
2870 | - if (DBG) log("delete (MATCH_PRESENCE_BY_ACCOUNT): sel => " + whereClause); | |
2871 | - changedItemId = null; | |
2872 | - break; | |
2873 | - | |
2874 | - case MATCH_SESSIONS: | |
2875 | - tableToChange = TABLE_SESSION_COOKIES; | |
2876 | - break; | |
2877 | - | |
2878 | - case MATCH_SESSIONS_BY_PROVIDER: | |
2879 | - tableToChange = TABLE_SESSION_COOKIES; | |
2880 | - changedItemId = url.getPathSegments().get(2); | |
2881 | - idColumnName = Im.SessionCookies.ACCOUNT; | |
2882 | - break; | |
2883 | - | |
2884 | - case MATCH_PROVIDER_SETTINGS_BY_ID: | |
2885 | - tableToChange = TABLE_PROVIDER_SETTINGS; | |
2886 | - changedItemId = url.getPathSegments().get(1); | |
2887 | - idColumnName = Im.ProviderSettings.PROVIDER; | |
2888 | - break; | |
2889 | - | |
2890 | - case MATCH_PROVIDER_SETTINGS_BY_ID_AND_NAME: | |
2891 | - tableToChange = TABLE_PROVIDER_SETTINGS; | |
2892 | - | |
2893 | - String providerId = url.getPathSegments().get(1); | |
2894 | - String name = url.getPathSegments().get(2); | |
2895 | - | |
2896 | - appendWhere(whereClause, Im.ProviderSettings.PROVIDER, "=", providerId); | |
2897 | - appendWhere(whereClause, Im.ProviderSettings.NAME, "=", name); | |
2898 | - break; | |
2899 | - | |
2900 | - case MATCH_BRANDING_RESOURCE_MAP_CACHE: | |
2901 | - tableToChange = TABLE_BRANDING_RESOURCE_MAP_CACHE; | |
2902 | - break; | |
2903 | - | |
2904 | - // mcs/rmq stuff | |
2905 | - case MATCH_OUTGOING_RMQ_MESSAGES: | |
2906 | - tableToChange = TABLE_OUTGOING_RMQ_MESSAGES; | |
2907 | - break; | |
2908 | - | |
2909 | - case MATCH_LAST_RMQ_ID: | |
2910 | - tableToChange = TABLE_LAST_RMQ_ID; | |
2911 | - break; | |
2912 | - | |
2913 | - case MATCH_S2D_RMQ_IDS: | |
2914 | - tableToChange = TABLE_S2D_RMQ_IDS; | |
2915 | - break; | |
2916 | - | |
2917 | - default: | |
2918 | - throw new UnsupportedOperationException("Cannot delete that URL: " + url); | |
2919 | - } | |
2920 | - | |
2921 | - if (idColumnName == null) { | |
2922 | - idColumnName = "_id"; | |
2923 | - } | |
2924 | - | |
2925 | - if (changedItemId != null) { | |
2926 | - appendWhere(whereClause, idColumnName, "=", changedItemId); | |
2927 | - } | |
2928 | - | |
2929 | - if (DBG) log("delete from " + url + " WHERE " + whereClause); | |
2930 | - | |
2931 | - int count = db.delete(tableToChange, whereClause.toString(), whereArgs); | |
2932 | - | |
2933 | - // see the comment at the declaration of tableToChange2 for an explanation | |
2934 | - if (tableToChange2 != null){ | |
2935 | - count += db.delete(tableToChange2, whereClause.toString(), whereArgs); | |
2936 | - } | |
2937 | - | |
2938 | - if (contactDeleted && count > 0) { | |
2939 | - // since the contact cleanup triggers no longer work for cross database tables, | |
2940 | - // we have to do it by hand here. | |
2941 | - performContactRemovalCleanup(deletedContactId); | |
2942 | - } | |
2943 | - | |
2944 | - if (count > 0) { | |
2945 | - ContentResolver resolver = getContext().getContentResolver(); | |
2946 | - | |
2947 | - // In most case, we query contacts with presence and chats joined, thus | |
2948 | - // we should also notify that contacts changes when presence or chats changed. | |
2949 | - if (match == MATCH_CHATS || match == MATCH_CHATS_ID | |
2950 | - || match == MATCH_PRESENCE || match == MATCH_PRESENCE_ID | |
2951 | - || match == MATCH_CONTACTS_BAREBONE) { | |
2952 | - resolver.notifyChange(Im.Contacts.CONTENT_URI, null); | |
2953 | - } | |
2954 | - | |
2955 | - if (notifyMessagesContentUri) { | |
2956 | - resolver.notifyChange(Im.Messages.CONTENT_URI, null); | |
2957 | - } | |
2958 | - | |
2959 | - if (notifyMessagesByContactContentUri) { | |
2960 | - resolver.notifyChange(Im.Messages.CONTENT_URI, null); | |
2961 | - resolver.notifyChange(Im.Messages.getContentUriByContact(account, contact), null); | |
2962 | - } | |
2963 | - | |
2964 | - if (notifyMessagesByThreadIdContentUri) { | |
2965 | - resolver.notifyChange(Im.Messages.CONTENT_URI, null); | |
2966 | - resolver.notifyChange(Im.Messages.getContentUriByThreadId(threadId), null); | |
2967 | - } | |
2968 | - | |
2969 | - if (notifyContactListContentUri) { | |
2970 | - resolver.notifyChange(Im.ContactList.CONTENT_URI, null); | |
2971 | - } | |
2972 | - | |
2973 | - if (notifyProviderAccountContentUri) { | |
2974 | - if (DBG) log("notify delete for " + Im.Provider.CONTENT_URI_WITH_ACCOUNT); | |
2975 | - resolver.notifyChange(Im.Provider.CONTENT_URI_WITH_ACCOUNT, null); | |
2976 | - } | |
2977 | - | |
2978 | - if (backfillQuickSwitchSlots) { | |
2979 | - backfillQuickSwitchSlots(); | |
2980 | - } | |
2981 | - } | |
2982 | - | |
2983 | - return count; | |
2984 | - } | |
2985 | - | |
2986 | - private int updateInternal(Uri url, ContentValues values, String userWhere, | |
2987 | - String[] whereArgs) { | |
2988 | - String tableToChange; | |
2989 | - String idColumnName = null; | |
2990 | - String changedItemId = null; | |
2991 | - String accountStr = null; | |
2992 | - long account = 0; | |
2993 | - String contact = null; | |
2994 | - long threadId = 0; | |
2995 | - int count; | |
2996 | - | |
2997 | - StringBuilder whereClause = new StringBuilder(); | |
2998 | - if(userWhere != null) { | |
2999 | - whereClause.append(userWhere); | |
3000 | - } | |
3001 | - | |
3002 | - boolean notifyMessagesContentUri = false; | |
3003 | - boolean notifyMessagesByContactContentUri = false; | |
3004 | - boolean notifyMessagesByThreadIdContentUri = false; | |
3005 | - boolean notifyContactListContentUri = false; | |
3006 | - boolean notifyProviderAccountContentUri = false; | |
3007 | - | |
3008 | - int match = mUrlMatcher.match(url); | |
3009 | - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); | |
3010 | - | |
3011 | - switch (match) { | |
3012 | - case MATCH_PROVIDERS_BY_ID: | |
3013 | - changedItemId = url.getPathSegments().get(1); | |
3014 | - // fall through | |
3015 | - case MATCH_PROVIDERS: | |
3016 | - tableToChange = TABLE_PROVIDERS; | |
3017 | - break; | |
3018 | - | |
3019 | - case MATCH_ACCOUNTS_BY_ID: | |
3020 | - changedItemId = url.getPathSegments().get(1); | |
3021 | - // fall through | |
3022 | - case MATCH_ACCOUNTS: | |
3023 | - tableToChange = TABLE_ACCOUNTS; | |
3024 | - notifyProviderAccountContentUri = true; | |
3025 | - break; | |
3026 | - | |
3027 | - case MATCH_ACCOUNT_STATUS: | |
3028 | - changedItemId = url.getPathSegments().get(1); | |
3029 | - // fall through | |
3030 | - case MATCH_ACCOUNTS_STATUS: | |
3031 | - tableToChange = TABLE_ACCOUNT_STATUS; | |
3032 | - notifyProviderAccountContentUri = true; | |
3033 | - break; | |
3034 | - | |
3035 | - case MATCH_CONTACTS: | |
3036 | - case MATCH_CONTACTS_BAREBONE: | |
3037 | - tableToChange = TABLE_CONTACTS; | |
3038 | - break; | |
3039 | - | |
3040 | - case MATCH_CONTACTS_BY_PROVIDER: | |
3041 | - tableToChange = TABLE_CONTACTS; | |
3042 | - changedItemId = url.getPathSegments().get(2); | |
3043 | - idColumnName = Im.Contacts.ACCOUNT; | |
3044 | - break; | |
3045 | - | |
3046 | - case MATCH_CONTACT: | |
3047 | - tableToChange = TABLE_CONTACTS; | |
3048 | - changedItemId = url.getPathSegments().get(1); | |
3049 | - break; | |
3050 | - | |
3051 | - case MATCH_CONTACTS_BULK: | |
3052 | - count = updateBulkContacts(values, userWhere); | |
3053 | - // notify change using the "content://im/contacts" url, | |
3054 | - // so the change will be observed by listeners interested | |
3055 | - // in contacts changes. | |
3056 | - if (count > 0) { | |
3057 | - getContext().getContentResolver().notifyChange( | |
3058 | - Im.Contacts.CONTENT_URI, null); | |
3059 | - } | |
3060 | - return count; | |
3061 | - | |
3062 | - case MATCH_CONTACTLIST: | |
3063 | - tableToChange = TABLE_CONTACT_LIST; | |
3064 | - changedItemId = url.getPathSegments().get(1); | |
3065 | - notifyContactListContentUri = true; | |
3066 | - break; | |
3067 | - | |
3068 | - case MATCH_CONTACTS_ETAGS: | |
3069 | - tableToChange = TABLE_CONTACTS_ETAG; | |
3070 | - break; | |
3071 | - | |
3072 | - case MATCH_CONTACTS_ETAG: | |
3073 | - tableToChange = TABLE_CONTACTS_ETAG; | |
3074 | - changedItemId = url.getPathSegments().get(1); | |
3075 | - break; | |
3076 | - | |
3077 | - case MATCH_MESSAGES: | |
3078 | - tableToChange = TABLE_MESSAGES; | |
3079 | - break; | |
3080 | - | |
3081 | - case MATCH_MESSAGES_BY_CONTACT: | |
3082 | - tableToChange = TABLE_MESSAGES; | |
3083 | - | |
3084 | - accountStr = decodeURLSegment(url.getPathSegments().get(1)); | |
3085 | - try { | |
3086 | - account = Long.parseLong(accountStr); | |
3087 | - } catch (NumberFormatException ex) { | |
3088 | - throw new IllegalArgumentException(); | |
3089 | - } | |
3090 | - | |
3091 | - contact = decodeURLSegment(url.getPathSegments().get(2)); | |
3092 | - appendWhere(whereClause, Im.Messages.THREAD_ID, "=", | |
3093 | - getContactId(db, accountStr, contact)); | |
3094 | - | |
3095 | - notifyMessagesContentUri = true; | |
3096 | - break; | |
3097 | - | |
3098 | - case MATCH_MESSAGES_BY_THREAD_ID: | |
3099 | - tableToChange = TABLE_MESSAGES; | |
3100 | - | |
3101 | - try { | |
3102 | - threadId = Long.parseLong(decodeURLSegment(url.getPathSegments().get(1))); | |
3103 | - } catch (NumberFormatException ex) { | |
3104 | - throw new IllegalArgumentException(); | |
3105 | - } | |
3106 | - | |
3107 | - appendWhere(whereClause, Im.Messages.THREAD_ID, "=", threadId); | |
3108 | - | |
3109 | - notifyMessagesContentUri = true; | |
3110 | - break; | |
3111 | - | |
3112 | - case MATCH_MESSAGE: | |
3113 | - tableToChange = TABLE_MESSAGES; | |
3114 | - changedItemId = url.getPathSegments().get(1); | |
3115 | - notifyMessagesContentUri = true; | |
3116 | - break; | |
3117 | - | |
3118 | - case MATCH_OTR_MESSAGES: | |
3119 | - tableToChange = TABLE_IN_MEMORY_MESSAGES; | |
3120 | - break; | |
3121 | - | |
3122 | - case MATCH_OTR_MESSAGES_BY_CONTACT: | |
3123 | - tableToChange = TABLE_IN_MEMORY_MESSAGES; | |
3124 | - | |
3125 | - accountStr = decodeURLSegment(url.getPathSegments().get(1)); | |
3126 | - try { | |
3127 | - account = Long.parseLong(accountStr); | |
3128 | - } catch (NumberFormatException ex) { | |
3129 | - throw new IllegalArgumentException(); | |
3130 | - } | |
3131 | - | |
3132 | - contact = decodeURLSegment(url.getPathSegments().get(2)); | |
3133 | - appendWhere(whereClause, Im.Messages.THREAD_ID, "=", | |
3134 | - getContactId(db, accountStr, contact)); | |
3135 | - | |
3136 | - notifyMessagesByContactContentUri = true; | |
3137 | - break; | |
3138 | - | |
3139 | - case MATCH_OTR_MESSAGES_BY_THREAD_ID: | |
3140 | - tableToChange = TABLE_IN_MEMORY_MESSAGES; | |
3141 | - | |
3142 | - try { | |
3143 | - threadId = Long.parseLong(decodeURLSegment(url.getPathSegments().get(1))); | |
3144 | - } catch (NumberFormatException ex) { | |
3145 | - throw new IllegalArgumentException(); | |
3146 | - } | |
3147 | - | |
3148 | - appendWhere(whereClause, Im.Messages.THREAD_ID, "=", threadId); | |
3149 | - | |
3150 | - notifyMessagesByThreadIdContentUri = true; | |
3151 | - break; | |
3152 | - | |
3153 | - case MATCH_OTR_MESSAGE: | |
3154 | - tableToChange = TABLE_IN_MEMORY_MESSAGES; | |
3155 | - changedItemId = url.getPathSegments().get(1); | |
3156 | - notifyMessagesContentUri = true; | |
3157 | - break; | |
3158 | - | |
3159 | - case MATCH_AVATARS: | |
3160 | - tableToChange = TABLE_AVATARS; | |
3161 | - break; | |
3162 | - | |
3163 | - case MATCH_AVATAR: | |
3164 | - tableToChange = TABLE_AVATARS; | |
3165 | - changedItemId = url.getPathSegments().get(1); | |
3166 | - break; | |
3167 | - | |
3168 | - case MATCH_AVATAR_BY_PROVIDER: | |
3169 | - tableToChange = TABLE_AVATARS; | |
3170 | - changedItemId = url.getPathSegments().get(2); | |
3171 | - idColumnName = Im.Avatars.ACCOUNT; | |
3172 | - break; | |
3173 | - | |
3174 | - case MATCH_CHATS: | |
3175 | - tableToChange = TABLE_CHATS; | |
3176 | - break; | |
3177 | - | |
3178 | - case MATCH_CHATS_ID: | |
3179 | - tableToChange = TABLE_CHATS; | |
3180 | - changedItemId = url.getPathSegments().get(1); | |
3181 | - idColumnName = Im.Chats.CONTACT_ID; | |
3182 | - break; | |
3183 | - | |
3184 | - case MATCH_PRESENCE: | |
3185 | - //if (DBG) log("update presence: where='" + userWhere + "'"); | |
3186 | - tableToChange = TABLE_PRESENCE; | |
3187 | - break; | |
3188 | - | |
3189 | - case MATCH_PRESENCE_ID: | |
3190 | - tableToChange = TABLE_PRESENCE; | |
3191 | - changedItemId = url.getPathSegments().get(1); | |
3192 | - idColumnName = Im.Presence.CONTACT_ID; | |
3193 | - break; | |
3194 | - | |
3195 | - case MATCH_PRESENCE_BULK: | |
3196 | - count = updateBulkPresence(values, userWhere, whereArgs); | |
3197 | - // notify change using the "content://im/contacts" url, | |
3198 | - // so the change will be observed by listeners interested | |
3199 | - // in contacts changes. | |
3200 | - if (count > 0) { | |
3201 | - getContext().getContentResolver().notifyChange(Im.Contacts.CONTENT_URI, null); | |
3202 | - } | |
3203 | - | |
3204 | - return count; | |
3205 | - | |
3206 | - case MATCH_INVITATION: | |
3207 | - tableToChange = TABLE_INVITATIONS; | |
3208 | - changedItemId = url.getPathSegments().get(1); | |
3209 | - break; | |
3210 | - | |
3211 | - case MATCH_SESSIONS: | |
3212 | - tableToChange = TABLE_SESSION_COOKIES; | |
3213 | - break; | |
3214 | - | |
3215 | - case MATCH_PROVIDER_SETTINGS_BY_ID_AND_NAME: | |
3216 | - tableToChange = TABLE_PROVIDER_SETTINGS; | |
3217 | - | |
3218 | - String providerId = url.getPathSegments().get(1); | |
3219 | - String name = url.getPathSegments().get(2); | |
3220 | - | |
3221 | - if (values.containsKey(Im.ProviderSettings.PROVIDER) || | |
3222 | - values.containsKey(Im.ProviderSettings.NAME)) { | |
3223 | - throw new SecurityException("Cannot override the value for provider|name"); | |
3224 | - } | |
3225 | - | |
3226 | - appendWhere(whereClause, Im.ProviderSettings.PROVIDER, "=", providerId); | |
3227 | - appendWhere(whereClause, Im.ProviderSettings.NAME, "=", name); | |
3228 | - | |
3229 | - break; | |
3230 | - | |
3231 | - case MATCH_OUTGOING_RMQ_MESSAGES: | |
3232 | - tableToChange = TABLE_OUTGOING_RMQ_MESSAGES; | |
3233 | - break; | |
3234 | - | |
3235 | - case MATCH_LAST_RMQ_ID: | |
3236 | - tableToChange = TABLE_LAST_RMQ_ID; | |
3237 | - break; | |
3238 | - | |
3239 | - case MATCH_S2D_RMQ_IDS: | |
3240 | - tableToChange = TABLE_S2D_RMQ_IDS; | |
3241 | - break; | |
3242 | - | |
3243 | - default: | |
3244 | - throw new UnsupportedOperationException("Cannot update URL: " + url); | |
3245 | - } | |
3246 | - | |
3247 | - if (idColumnName == null) { | |
3248 | - idColumnName = "_id"; | |
3249 | - } | |
3250 | - if(changedItemId != null) { | |
3251 | - appendWhere(whereClause, idColumnName, "=", changedItemId); | |
3252 | - } | |
3253 | - | |
3254 | - if (DBG) log("update " + url + " WHERE " + whereClause); | |
3255 | - | |
3256 | - count = db.update(tableToChange, values, whereClause.toString(), whereArgs); | |
3257 | - | |
3258 | - if (count > 0) { | |
3259 | - ContentResolver resolver = getContext().getContentResolver(); | |
3260 | - | |
3261 | - // In most case, we query contacts with presence and chats joined, thus | |
3262 | - // we should also notify that contacts changes when presence or chats changed. | |
3263 | - if (match == MATCH_CHATS || match == MATCH_CHATS_ID | |
3264 | - || match == MATCH_PRESENCE || match == MATCH_PRESENCE_ID | |
3265 | - || match == MATCH_CONTACTS_BAREBONE) { | |
3266 | - resolver.notifyChange(Im.Contacts.CONTENT_URI, null); | |
3267 | - } | |
3268 | - | |
3269 | - if (notifyMessagesContentUri) { | |
3270 | - if (DBG) log("notify change for " + Im.Messages.CONTENT_URI); | |
3271 | - resolver.notifyChange(Im.Messages.CONTENT_URI, null); | |
3272 | - } | |
3273 | - | |
3274 | - if (notifyMessagesByContactContentUri) { | |
3275 | - resolver.notifyChange(Im.Messages.CONTENT_URI, null); | |
3276 | - resolver.notifyChange(Im.Messages.getContentUriByContact(account, contact), null); | |
3277 | - } | |
3278 | - | |
3279 | - if (notifyMessagesByThreadIdContentUri) { | |
3280 | - resolver.notifyChange(Im.Messages.CONTENT_URI, null); | |
3281 | - resolver.notifyChange(Im.Messages.getContentUriByThreadId(threadId), null); | |
3282 | - } | |
3283 | - | |
3284 | - if (notifyContactListContentUri) { | |
3285 | - resolver.notifyChange(Im.ContactList.CONTENT_URI, null); | |
3286 | - } | |
3287 | - | |
3288 | - if (notifyProviderAccountContentUri) { | |
3289 | - if (DBG) log("notify change for " + Im.Provider.CONTENT_URI_WITH_ACCOUNT); | |
3290 | - resolver.notifyChange(Im.Provider.CONTENT_URI_WITH_ACCOUNT, null); | |
3291 | - } | |
3292 | - } | |
3293 | - | |
3294 | - return count; | |
3295 | - } | |
3296 | - | |
3297 | - @Override | |
3298 | - public ParcelFileDescriptor openFile(Uri uri, String mode) | |
3299 | - throws FileNotFoundException { | |
3300 | - return openFileHelper(uri, mode); | |
3301 | - } | |
3302 | - | |
3303 | - private static void appendWhere(StringBuilder where, String columnName, | |
3304 | - String condition, Object value) { | |
3305 | - if (where.length() > 0) { | |
3306 | - where.append(" AND "); | |
3307 | - } | |
3308 | - where.append(columnName).append(condition); | |
3309 | - if(value != null) { | |
3310 | - DatabaseUtils.appendValueToSql(where, value); | |
3311 | - } | |
3312 | - } | |
3313 | - | |
3314 | - private static void appendWhere(StringBuilder where, String clause) { | |
3315 | - if (where.length() > 0) { | |
3316 | - where.append(" AND "); | |
3317 | - } | |
3318 | - where.append(clause); | |
3319 | - } | |
3320 | - | |
3321 | - private static String decodeURLSegment(String segment) { | |
3322 | - try { | |
3323 | - return URLDecoder.decode(segment, "UTF-8"); | |
3324 | - } catch (UnsupportedEncodingException e) { | |
3325 | - // impossible | |
3326 | - return segment; | |
3327 | - } | |
3328 | - } | |
3329 | - | |
3330 | - static void log(String message) { | |
3331 | - Log.d(LOG_TAG, message); | |
3332 | - } | |
3333 | -} |
@@ -1,10 +0,0 @@ | ||
1 | -LOCAL_PATH:= $(call my-dir) | |
2 | - | |
3 | -######################## | |
4 | - | |
5 | -include $(CLEAR_VARS) | |
6 | - | |
7 | -# no tests to build for now | |
8 | - | |
9 | -# additionally, build sub-tests in a separate .apk | |
10 | -include $(call all-makefiles-under,$(LOCAL_PATH)) | |
\ No newline at end of file |
@@ -1,14 +0,0 @@ | ||
1 | -LOCAL_PATH:= $(call my-dir) | |
2 | -include $(CLEAR_VARS) | |
3 | - | |
4 | -# We only want this apk build for tests. | |
5 | -LOCAL_MODULE_TAGS := tests | |
6 | - | |
7 | -# Include all test java files. | |
8 | -LOCAL_SRC_FILES := $(call all-java-files-under, src) | |
9 | - | |
10 | -LOCAL_JAVA_LIBRARIES := android.test.runner | |
11 | -LOCAL_PACKAGE_NAME := ImProviderPermissionTests | |
12 | - | |
13 | -include $(BUILD_PACKAGE) | |
14 | - |
@@ -1,39 +0,0 @@ | ||
1 | -<?xml version="1.0" encoding="utf-8"?> | |
2 | -<!-- | |
3 | - * Copyright (C) 2009 Android Open Source Project | |
4 | - * | |
5 | - * Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | - * you may not use this file except in compliance with the License. | |
7 | - * You may obtain a copy of the License at | |
8 | - * | |
9 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | - * | |
11 | - * Unless required by applicable law or agreed to in writing, software | |
12 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
13 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | - * See the License for the specific language governing permissions and | |
15 | - * limitations under the License. | |
16 | - --> | |
17 | - | |
18 | -<manifest xmlns:android="http://schemas.android.com/apk/res/android" | |
19 | - package="com.android.providers.im.permission.tests"> | |
20 | - | |
21 | - <application> | |
22 | - <uses-library android:name="android.test.runner" /> | |
23 | - </application> | |
24 | - | |
25 | - <!-- | |
26 | - The tests in this package are intended to verify that protected APIs or data | |
27 | - cannot be accessed without the necessary permissions. Thus this manifest should not | |
28 | - include any uses-permissions tags | |
29 | - --> | |
30 | - | |
31 | - <!-- | |
32 | - The test declared in this instrumentation can be run via this command | |
33 | - "adb shell am instrument -w com.android.providers.im.permission.tests/android.test.InstrumentationTestRunner" | |
34 | - --> | |
35 | - <instrumentation android:name="android.test.InstrumentationTestRunner" | |
36 | - android:targetPackage="com.android.providers.im.permission.tests" | |
37 | - android:label="Tests for IM provider permissions"/> | |
38 | - | |
39 | -</manifest> |
@@ -1,53 +0,0 @@ | ||
1 | -/* | |
2 | - * Copyright (C) 2009 The Android Open Source Project | |
3 | - * | |
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | - * you may not use this file except in compliance with the License. | |
6 | - * You may obtain a copy of the License at | |
7 | - * | |
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | - * | |
10 | - * Unless required by applicable law or agreed to in writing, software | |
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | - * See the License for the specific language governing permissions and | |
14 | - * limitations under the License. | |
15 | - */ | |
16 | - | |
17 | -package com.android.providers.im.permission.tests; | |
18 | - | |
19 | -import java.io.IOException; | |
20 | - | |
21 | -import android.net.Uri; | |
22 | -import android.test.AndroidTestCase; | |
23 | -import android.test.suitebuilder.annotation.MediumTest; | |
24 | - | |
25 | -/** | |
26 | - * Verify that protected Im provider actions require specific permissions. | |
27 | - */ | |
28 | -public class ImProviderPermissionsTest extends AndroidTestCase { | |
29 | - | |
30 | - private static final String CONTENT_IM = "content://im"; | |
31 | - | |
32 | - /** | |
33 | - * Test that an untrusted app cannot read from the im provider | |
34 | - * <p>Tests Permission: | |
35 | - * {@link com.android.providers.im.Manifest.permission#READ_ONLY} | |
36 | - */ | |
37 | - @MediumTest | |
38 | - public void testReadImProvider() throws Exception { | |
39 | - assertReadingContentUriRequiresPermission(Uri.parse(CONTENT_IM), | |
40 | - "com.android.providers.im.permission.READ_ONLY"); | |
41 | - } | |
42 | - | |
43 | - /** | |
44 | - * Test that an untrusted app cannot write to the download provider | |
45 | - * <p>Tests Permission: | |
46 | - * {@link com.android.providers.downloads.Manifest.permission#ACCESS_DOWNLOAD_MANAGER} | |
47 | - */ | |
48 | - @MediumTest | |
49 | - public void testWriteImProvider() throws IOException { | |
50 | - assertWritingContentUriRequiresPermission(Uri.parse(CONTENT_IM), | |
51 | - "com.android.providers.im.permission.WRITE_ONLY"); | |
52 | - } | |
53 | -} |