1 /*
2  * Copyright (C) 2020 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.google.android.sample.rcsclient.util;
18 
19 import android.content.ContentProvider;
20 import android.content.ContentValues;
21 import android.content.Context;
22 import android.content.UriMatcher;
23 import android.database.Cursor;
24 import android.database.SQLException;
25 import android.database.sqlite.SQLiteDatabase;
26 import android.database.sqlite.SQLiteException;
27 import android.database.sqlite.SQLiteOpenHelper;
28 import android.database.sqlite.SQLiteQueryBuilder;
29 import android.net.Uri;
30 import android.provider.BaseColumns;
31 import android.text.TextUtils;
32 import android.util.Log;
33 
34 /** A database to store chat message. */
35 public class ChatProvider extends ContentProvider {
36     public static final Uri CHAT_URI = Uri.parse("content://rcsprovider/chat");
37     public static final Uri SUMMARY_URI = Uri.parse("content://rcsprovider/summary");
38     public static final String AUTHORITY = "rcsprovider";
39     private static final String TAG = "TestRcsApp.ChatProvider";
40     private static final int DATABASE_VERSION = 2;
41     private static final String CHAT_TABLE_NAME = "chat";
42     private static final String SUMMARY_TABLE_NAME = "summary";
43 
44     private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
45     private static final int URI_CHAT = 1;
46     private static final int URI_SUMMARY = 2;
47     private static final String QUERY_CHAT_TABLE = " SELECT * FROM " + CHAT_TABLE_NAME;
48 
49     static {
URI_MATCHER.addURI(AUTHORITY, R, URI_CHAT)50         URI_MATCHER.addURI(AUTHORITY, "chat", URI_CHAT);
URI_MATCHER.addURI(AUTHORITY, R, URI_SUMMARY)51         URI_MATCHER.addURI(AUTHORITY, "summary", URI_SUMMARY);
52     }
53 
54     private RcsDatabaseHelper mRcsHelper;
55 
56     @Override
onCreate()57     public boolean onCreate() {
58         mRcsHelper = new RcsDatabaseHelper(getContext());
59         return true;
60     }
61 
62     @Override
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)63     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
64             String sortOrder) {
65         SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
66         SQLiteDatabase db = mRcsHelper.getReadableDatabase();
67         int match = URI_MATCHER.match(uri);
68 
69         Log.d(TAG, "Query URI: " + match);
70         switch (match) {
71             case URI_CHAT:
72                 qb.setTables(CHAT_TABLE_NAME);
73                 return qb.query(db, projection, selection, selectionArgs, null, null, sortOrder);
74             case URI_SUMMARY:
75                 qb.setTables(SUMMARY_TABLE_NAME);
76                 return qb.query(db, projection, selection, selectionArgs, null, null, sortOrder);
77             default:
78                 Log.e(TAG, "no such uri");
79                 return null;
80         }
81     }
82 
83     @Override
insert(Uri uri, ContentValues contentValues)84     public Uri insert(Uri uri, ContentValues contentValues) {
85         SQLiteDatabase db = mRcsHelper.getWritableDatabase();
86         int match = URI_MATCHER.match(uri);
87         long id;
88         switch (match) {
89             case URI_CHAT:
90                 id = db.insert(CHAT_TABLE_NAME, "", contentValues);
91                 break;
92             case URI_SUMMARY:
93                 id = db.insert(SUMMARY_TABLE_NAME, "", contentValues);
94                 break;
95             default:
96                 Log.e(TAG, "no such uri");
97                 throw new SQLException("no such uri");
98         }
99         if (id > 0) {
100             Uri msgUri = Uri.withAppendedPath(uri, String.valueOf(id));
101             getContext().getContentResolver().notifyChange(uri, null);
102             Log.i(TAG, "msgUri:" + msgUri);
103             return msgUri;
104         } else {
105             throw new SQLException("fail to add chat message");
106         }
107     }
108 
109     @Override
delete(Uri uri, String s, String[] strings)110     public int delete(Uri uri, String s, String[] strings) {
111         return 0;
112     }
113 
114     @Override
update(Uri uri, ContentValues contentValues, String selection, String[] selectionArgs)115     public int update(Uri uri, ContentValues contentValues, String selection,
116             String[] selectionArgs) {
117         SQLiteDatabase db = mRcsHelper.getWritableDatabase();
118         int match = URI_MATCHER.match(uri);
119         int result = 0;
120         String tableName = "";
121         switch (match) {
122             case URI_CHAT:
123                 tableName = CHAT_TABLE_NAME;
124                 break;
125             case URI_SUMMARY:
126                 tableName = SUMMARY_TABLE_NAME;
127                 break;
128         }
129         if (!TextUtils.isEmpty(tableName)) {
130             result = db.updateWithOnConflict(tableName, contentValues,
131                     selection, selectionArgs, SQLiteDatabase.CONFLICT_REPLACE);
132             getContext().getContentResolver().notifyChange(uri, null);
133             Log.d(TAG, "Update uri: " + match + " result: " + result);
134         } else {
135             Log.d(TAG, "Update. Not support URI.");
136         }
137         return result;
138     }
139 
140     @Override
getType(Uri uri)141     public String getType(Uri uri) {
142         return null;
143     }
144 
145     /** Define columns for the chat table. */
146     public static class RcsColumns implements BaseColumns {
147         public static final String SRC_PHONE_NUMBER = "source_phone_number";
148         public static final String DEST_PHONE_NUMBER = "destination_phone_number";
149         public static final String CHAT_MESSAGE = "chat_message";
150         public static final String MSG_TIMESTAMP = "msg_timestamp";
151         public static final String IS_READ = "is_read";
152         public static final String RESULT = "result";
153     }
154 
155     /** Define columns for the summary table. */
156     public static class SummaryColumns implements BaseColumns {
157         public static final String REMOTE_PHONE_NUMBER = "remote_phone_number";
158         public static final String LATEST_MESSAGE = "latest_message";
159         public static final String MSG_TIMESTAMP = "msg_timestamp";
160         public static final String IS_READ = "is_read";
161     }
162 
163     /** Database helper */
164     public static final class RcsDatabaseHelper extends SQLiteOpenHelper {
165         public static final String SQL_CREATE_RCS_TABLE = "CREATE TABLE "
166                 + CHAT_TABLE_NAME
167                 + " ("
168                 + RcsColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
169                 + RcsColumns.SRC_PHONE_NUMBER + " Text DEFAULT NULL, "
170                 + RcsColumns.DEST_PHONE_NUMBER + " Text DEFAULT NULL, "
171                 + RcsColumns.CHAT_MESSAGE + " Text DEFAULT NULL, "
172                 + RcsColumns.MSG_TIMESTAMP + " LONG DEFAULT NULL, "
173                 + RcsColumns.IS_READ + " BOOLEAN DEFAULT false, "
174                 + RcsColumns.RESULT + " BOOLEAN DEFAULT true);";
175 
176         public static final String SQL_CREATE_SUMMARY_TABLE = "CREATE TABLE "
177                 + SUMMARY_TABLE_NAME
178                 + " ("
179                 + SummaryColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
180                 + SummaryColumns.REMOTE_PHONE_NUMBER + " Text DEFAULT NULL, "
181                 + SummaryColumns.LATEST_MESSAGE + " Text DEFAULT NULL, "
182                 + SummaryColumns.MSG_TIMESTAMP + " LONG DEFAULT NULL, "
183                 + SummaryColumns.IS_READ + " BOOLEAN DEFAULT false);";
184         private static final String DB_NAME = "RcsDatabase";
185 
RcsDatabaseHelper(Context context)186         public RcsDatabaseHelper(Context context) {
187             super(context, DB_NAME, null, DATABASE_VERSION);
188         }
189 
190         @Override
onCreate(SQLiteDatabase db)191         public void onCreate(SQLiteDatabase db) {
192             db.execSQL(SQL_CREATE_RCS_TABLE);
193             db.execSQL(SQL_CREATE_SUMMARY_TABLE);
194         }
195 
196         @Override
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)197         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
198             Log.d(TAG, "DB upgrade from " + oldVersion + " to " + newVersion);
199             db.beginTransaction();
200             try {
201                 switch (oldVersion) {
202                     case 1:
203                         upgradeDatabaseToVersion2(db);
204                         break;
205                     default: // fall out
206                 }
207                 db.setTransactionSuccessful();
208             } catch (Exception ex) {
209                 Log.e(TAG, ex.getMessage(), ex);
210             } finally {
211                 db.endTransaction();
212             }
213         }
214 
upgradeDatabaseToVersion2(SQLiteDatabase db)215         private static void upgradeDatabaseToVersion2(SQLiteDatabase db) {
216             try {
217                 Log.d(TAG, "upgradeDatabaseToVersion2");
218                 String alterTable = "ALTER TABLE " + CHAT_TABLE_NAME + " ADD COLUMN ";
219                 db.execSQL(alterTable + RcsColumns.RESULT + " BOOLEAN DEFAULT true");
220             } catch (SQLiteException e) {
221                 Log.w(TAG, "[upgradeDatabaseToVersion10] Exception adding column: " + e);
222             }
223         }
224     }
225 }
226