Skip to content

Commit a519ed8

Browse files
committed
[Android] read call logs content provider
next: add bottom sheet, provide updating, insertion and deletion of content provider bugs: user dictionary content provider is not working
1 parent 4c92b2e commit a519ed8

File tree

7 files changed

+281
-6
lines changed

7 files changed

+281
-6
lines changed

README.md

+40-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ To see more details by automatically importing icons with Android Studio Image A
8888
## Saving UI states
8989
1. ```ViewModel```
9090
2. ```onSaveInstanceState()```
91-
Have ```Do not keep activities``` be selected on system Settings page to test ```onSaveInstanceState``` and ```onRestoreInstanceState```
91+
Have ```Do not keep activities``` selected on system Settings page to test ```onSaveInstanceState``` and ```onRestoreInstanceState```
9292
Code: [LifecycleActivity]
9393
3. Persistent in local storage for complex or large data
9494

@@ -149,6 +149,40 @@ Foreground services and JobScheduler are alternatives to run app in the backgrou
149149
Code: [AppComponentsActivity], [MusicPlayerService], [MusicPlayerJobScheduler], [AndroidManifest]
150150
[Read more](https://developer.android.com/guide/components/services)
151151

152+
## Broadcast receiver
153+
You could either register receivers by dynamically extending ```BroadcastReceiver``` or statically declaring an implementation with the ```<receiver>``` tag in the AndroidManifest.xml
154+
155+
Code: [NetworkHealthService], [NetworkHealthJobScheduler], [InternetConnectivityReceiver]
156+
[Read more](https://developer.android.com/reference/android/content/BroadcastReceiver)
157+
158+
## Content Provider
159+
Create your own content providers to share data with other applications or access existing content providers in another applications.
160+
161+
### System content providers
162+
**In order to get the uri path, we are going to have a look at android source code.**
163+
1. Go to https://android.googlesource.com/platform/packages/providers/ and pick out needed providers
164+
2. Search ```<provider>``` tag in AndroidManifest, e.g. in https://android.googlesource.com/platform/packages/providers/UserDictionaryProvider/+/refs/tags/android-9.0.0_r33/AndroidManifest.xml
165+
```xml
166+
<provider android:name="CallLogProvider"
167+
android:authorities="call_log"
168+
android:syncable="false" android:multiprocess="false"
169+
android:exported="true"
170+
android:readPermission="android.permission.READ_CALL_LOG"
171+
android:writePermission="android.permission.WRITE_CALL_LOG">
172+
</provider>
173+
```
174+
3. Now we have host name (```android:authorities```), then, go to [CallLogProvider] (```android:name```) to get the table name
175+
4. Search "urimatcher" in [UserDictionaryProvider], and have a bunch of ```sURIMatcher.addURI()``` found. We figure out that one url is "content://call_log/calls"
176+
5. Grant permission: ```<uses-permission android:name="android.permission.READ_CALL_LOG" />```, ```<uses-permission android:name="android.permission.WRITE_CALL_LOG" />```
177+
178+
**CRUD - Create**
179+
180+
**CRUD - Read**
181+
182+
**CRUD - Update**
183+
184+
**CRUD - Delete**
185+
152186

153187
# Jetpack
154188

@@ -166,6 +200,11 @@ Code: [AppComponentsActivity], [MusicPlayerService], [MusicPlayerJobScheduler],
166200
[MusicPlayerService]:<https://github.com/Catherine22/AAD-Preparation/blob/master/app/src/main/java/com/catherine/materialdesignapp/activities/MusicPlayerService.java>
167201
[MusicPlayerJobScheduler]:<https://github.com/Catherine22/AAD-Preparation/blob/master/app/src/main/java/com/catherine/materialdesignapp/activities/MusicPlayerJobScheduler.java>
168202
[AndroidManifest]:<https://github.com/Catherine22/AAD-Preparation/blob/master/app/src/main/AndroidManifest.xml>
203+
[InternetConnectivityReceiver]:<https://github.com/Catherine22/AAD-Preparation/blob/master/app/src/main/java/com/catherine/materialdesignapp/receivers/InternetConnectivityReceiver.java>
204+
[NetworkHealthService]:<https://github.com/Catherine22/AAD-Preparation/blob/master/app/src/main/java/com/catherine/materialdesignapp/activities/NetworkHealthService.java>
205+
[NetworkHealthJobScheduler]:<https://github.com/Catherine22/AAD-Preparation/blob/master/app/src/main/java/com/catherine/materialdesignapp/activities/NetworkHealthJobScheduler.java>
206+
207+
[CallLogProvider]:<https://android.googlesource.com/platform/packages/providers/ContactsProvider/+/refs/tags/android-9.0.0_r34/src/com/android/providers/contacts/CallLogProvider.java>
169208

170209
[Grid and keyline shapes]:<https://material.io/design/iconography/#grid-keyline-shapes>
171210

app/src/main/AndroidManifest.xml

+4-1
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
package="com.catherine.materialdesignapp">
44

55
<uses-permission android:name="android.permission.INTERNET" />
6-
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
6+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!--network health-->
77
<uses-permission android:name="android.permission.WAKE_LOCK" />
8+
<uses-permission android:name="android.permission.READ_USER_DICTIONARY" /> <!--content provider-->
9+
<uses-permission android:name="android.permission.READ_CALL_LOG" /> <!--content provider-->
10+
<uses-permission android:name="android.permission.WRITE_CALL_LOG" /> <!--content provider-->
811

912
<application
1013
android:name=".MyApplication"

app/src/main/java/com/catherine/materialdesignapp/activities/AppComponentsActivity.java

+35-4
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,36 @@
11
package com.catherine.materialdesignapp.activities;
22

3+
import android.Manifest;
34
import android.annotation.TargetApi;
45
import android.app.job.JobInfo;
56
import android.app.job.JobScheduler;
67
import android.content.ComponentName;
78
import android.content.Intent;
89
import android.os.Build;
910
import android.os.Bundle;
11+
import android.support.annotation.Nullable;
1012
import android.support.v7.widget.Toolbar;
13+
import android.util.Log;
1114
import android.view.View;
1215
import android.view.View.OnClickListener;
1316
import android.widget.Button;
1417
import android.widget.RadioButton;
1518
import android.widget.RadioGroup;
1619

1720
import com.catherine.materialdesignapp.R;
21+
import com.catherine.materialdesignapp.content_providers.CallLogDao;
22+
import com.catherine.materialdesignapp.content_providers.UserDictionaryDao;
23+
import com.catherine.materialdesignapp.listeners.OnRequestPermissionsListener;
24+
import com.catherine.materialdesignapp.models.Word;
1825
import com.catherine.materialdesignapp.services.MusicPlayerJobScheduler;
1926
import com.catherine.materialdesignapp.services.MusicPlayerService;
2027

28+
import java.util.List;
29+
2130
import static com.catherine.materialdesignapp.services.BusyJobs.JOB_MUSIC_PLAYER;
2231

2332
public class AppComponentsActivity extends BaseActivity implements OnClickListener {
33+
public final static String TAG = AppComponentsActivity.class.getSimpleName();
2434

2535
private enum ServiceType {
2636
FOREGROUND, BACKGROUND, JOB_SCHEDULER
@@ -71,8 +81,6 @@ public void onCheckedChanged(RadioGroup group, int checkedId) {
7181
}
7282

7383
});
74-
75-
7684
}
7785

7886
@Override
@@ -97,10 +105,33 @@ public void onClick(View v) {
97105
}
98106
break;
99107
case R.id.btn_broadcast_receivers:
100-
108+
// TODO add one more broadcast receiver
101109
break;
102110
case R.id.btn_content_providers:
103-
111+
// UserDictionaryDao dao = new UserDictionaryDao();
112+
// dao.query();
113+
//
114+
// Word word = new Word("new word", 999, "new shortcut", "en_US");
115+
// dao.insert(word);
116+
117+
String[] permissions = {Manifest.permission.READ_CALL_LOG, Manifest.permission.WRITE_CALL_LOG};
118+
getPermissions(permissions, new OnRequestPermissionsListener() {
119+
@Override
120+
public void onGranted() {
121+
CallLogDao callLogDao = new CallLogDao();
122+
callLogDao.read();
123+
}
124+
125+
@Override
126+
public void onDenied(@Nullable List<String> deniedPermissions) {
127+
Log.d(TAG, "onDenied");
128+
}
129+
130+
@Override
131+
public void onRetry() {
132+
Log.d(TAG, "onRetry");
133+
}
134+
});
104135
break;
105136
}
106137
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package com.catherine.materialdesignapp.content_providers;
2+
3+
import android.content.ContentResolver;
4+
import android.content.ContentValues;
5+
import android.database.ContentObserver;
6+
import android.database.Cursor;
7+
import android.net.Uri;
8+
import android.os.Handler;
9+
import android.provider.CallLog;
10+
import android.util.Log;
11+
12+
import com.catherine.materialdesignapp.MyApplication;
13+
14+
import java.util.LinkedList;
15+
import java.util.List;
16+
import java.util.Locale;
17+
18+
public class CallLogDao extends Dao<String> {
19+
public final static String TAG = CallLogDao.class.getSimpleName();
20+
private final Uri database = Uri.parse("content://call_log/calls");
21+
private ContentResolver contentResolver;
22+
23+
public CallLogDao() {
24+
contentResolver = MyApplication.INSTANCE.getApplicationContext().getContentResolver();
25+
}
26+
27+
28+
public List<String> read() {
29+
String[] projection = {CallLog.Calls.NUMBER};
30+
Cursor cursor = contentResolver.query(database, projection, null, null, null);
31+
List<String> words = new LinkedList<>();
32+
if (cursor != null) {
33+
Log.d(TAG, "cursor counts: " + cursor.getCount());
34+
while (cursor.moveToNext()) {
35+
String number = cursor.getString(cursor.getColumnIndex(CallLog.Calls.NUMBER));
36+
words.add(number);
37+
Log.d(TAG, "Log: " + number);
38+
}
39+
cursor.close();
40+
}
41+
return words;
42+
}
43+
44+
@Override
45+
void insert(String newValue) {
46+
ContentValues newValues = new ContentValues();
47+
newValues.put(CallLog.Calls.NUMBER, newValue);
48+
contentResolver.registerContentObserver(database, true, new CallLogObserver(new Handler()));
49+
Uri newUri = contentResolver.insert(database, newValues);
50+
Log.d(TAG, String.format(Locale.US, "new uri: %s", newUri.getPath()));
51+
}
52+
53+
@Override
54+
void update(String oldValue, String newValue) {
55+
56+
}
57+
58+
@Override
59+
void delete(String value) {
60+
61+
}
62+
63+
private class CallLogObserver extends ContentObserver {
64+
65+
/**
66+
* Creates a content observer.
67+
*
68+
* @param handler The handler to run {@link #onChange} on, or null if none.
69+
*/
70+
CallLogObserver(Handler handler) {
71+
super(handler);
72+
}
73+
74+
@Override
75+
public void onChange(boolean selfChange) {
76+
super.onChange(selfChange);
77+
Log.d(TAG, String.format(Locale.US, "onChange: %b", selfChange));
78+
}
79+
80+
@Override
81+
public void onChange(boolean selfChange, Uri uri) {
82+
super.onChange(selfChange, uri);
83+
Log.d(TAG, String.format(Locale.US, "onChange: %b, %s", selfChange, uri.getPath()));
84+
contentResolver.unregisterContentObserver(this);
85+
}
86+
}
87+
88+
89+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.catherine.materialdesignapp.content_providers;
2+
3+
import java.util.List;
4+
5+
public abstract class Dao<T> {
6+
abstract List<T> read();
7+
8+
abstract void insert(T newValue);
9+
10+
abstract void update(T oldValue, T newValue);
11+
12+
abstract void delete(T value);
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package com.catherine.materialdesignapp.content_providers;
2+
3+
import android.content.ContentResolver;
4+
import android.content.ContentValues;
5+
import android.database.ContentObserver;
6+
import android.database.Cursor;
7+
import android.net.Uri;
8+
import android.os.Handler;
9+
import android.provider.UserDictionary;
10+
import android.util.Log;
11+
12+
import com.catherine.materialdesignapp.MyApplication;
13+
import com.catherine.materialdesignapp.models.Word;
14+
15+
import java.util.LinkedList;
16+
import java.util.List;
17+
18+
public class UserDictionaryDao {
19+
public final static String TAG = UserDictionaryDao.class.getSimpleName();
20+
private final Uri database = Uri.parse("content://user_dictionary/words");
21+
private ContentResolver contentResolver;
22+
23+
public UserDictionaryDao() {
24+
contentResolver = MyApplication.INSTANCE.getApplicationContext().getContentResolver();
25+
// contentResolver.registerContentObserver(database, true, new UserDictionaryObserver(new Handler()));
26+
}
27+
28+
public List<Word> query() {
29+
String[] projection = {UserDictionary.Words.WORD, UserDictionary.Words.FREQUENCY, UserDictionary.Words.SHORTCUT, UserDictionary.Words.LOCALE};
30+
Cursor cursor = contentResolver.query(database, projection, null, null, null);
31+
List<Word> words = new LinkedList<>();
32+
if (cursor != null) {
33+
Log.d(TAG, "cursor counts: " + cursor.getCount());
34+
while (cursor.moveToNext()) {
35+
Word word = new Word();
36+
word.word = cursor.getString(cursor.getColumnIndex(UserDictionary.Words.WORD));
37+
word.frequency = cursor.getInt(cursor.getColumnIndex(UserDictionary.Words.FREQUENCY));
38+
word.shortcut = cursor.getString(cursor.getColumnIndex(UserDictionary.Words.SHORTCUT));
39+
word.locale = cursor.getString(cursor.getColumnIndex(UserDictionary.Words.SHORTCUT));
40+
words.add(word);
41+
}
42+
cursor.close();
43+
}
44+
return words;
45+
}
46+
47+
public void insert(Word word) {
48+
ContentValues newValues = new ContentValues();
49+
newValues.put(UserDictionary.Words.SHORTCUT, word.locale);
50+
newValues.put(UserDictionary.Words.LOCALE, word.locale);
51+
newValues.put(UserDictionary.Words.WORD, word.word);
52+
newValues.put(UserDictionary.Words.FREQUENCY, word.frequency);
53+
contentResolver.registerContentObserver(database, true, new UserDictionaryObserver(new Handler()));
54+
contentResolver.insert(database, newValues);
55+
}
56+
57+
private class UserDictionaryObserver extends ContentObserver {
58+
UserDictionaryObserver(Handler handler) {
59+
super(handler);
60+
}
61+
62+
@Override
63+
public void onChange(boolean selfChange, Uri uri) {
64+
super.onChange(selfChange, uri);
65+
Log.d(TAG, "onChange:" + selfChange + "/uri:" + uri);
66+
contentResolver.unregisterContentObserver(this);
67+
}
68+
69+
@Override
70+
public boolean deliverSelfNotifications() {
71+
return super.deliverSelfNotifications();
72+
}
73+
74+
@Override
75+
public void onChange(boolean selfChange) {
76+
super.onChange(selfChange);
77+
Log.d(TAG, "onChange:" + selfChange);
78+
contentResolver.unregisterContentObserver(this);
79+
}
80+
}
81+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.catherine.materialdesignapp.models;
2+
3+
public class Word {
4+
public String word;
5+
public int frequency;
6+
public String shortcut;
7+
public String locale;
8+
9+
public Word() {
10+
11+
}
12+
13+
public Word(String word, int frequency, String shortcut, String locale) {
14+
this.word = word;
15+
this.frequency = frequency;
16+
this.shortcut = shortcut;
17+
this.locale = locale;
18+
}
19+
}

0 commit comments

Comments
 (0)