11package com .google .androidbrowserhelper .trusted ;
22
3+ import android .content .ComponentName ;
34import android .content .ContentResolver ;
45import android .content .ContentUris ;
56import android .content .Context ;
7+ import android .content .Intent ;
8+ import android .content .pm .PackageManager ;
9+ import android .content .pm .PackageManager .NameNotFoundException ;
610import android .database .Cursor ;
711import android .graphics .Bitmap ;
812import android .graphics .BitmapFactory ;
1418import androidx .annotation .NonNull ;
1519import androidx .annotation .Nullable ;
1620import androidx .browser .trusted .TrustedWebActivityCallbackRemote ;
21+ import androidx .core .content .FileProvider ;
1722import java .io .ByteArrayInputStream ;
23+ import java .io .File ;
24+ import java .io .FileOutputStream ;
25+ import java .io .IOException ;
1826import java .util .ArrayList ;
1927import java .util .HashMap ;
2028import java .util .Map ;
29+ import java .util .concurrent .ExecutorService ;
30+ import java .util .concurrent .Executors ;
2131
2232public class ContactDelegationExtraCommandHandler implements ExtraCommandHandler {
2333
2434 private static final String TAG = "stomoki ContactDeleg" ;
2535 static final String COMMAND_CHECK_CONTACT_PERMISSION = "checkContactPermission" ;
26- private static final String COMMAND_GET_CONTACT_DATA = "getContactData" ;
27- private static final String COMMAND_GET_CONTACT_ICON = "getContactIcon" ;
36+ public static final String COMMAND_FETCH_CONTACTS = "fetchContacts" ;
37+ public static final String COMMAND_FETCH_CONTACT_ICON = "fetchContactIcon" ;
38+
39+ private static final String FOLDER_NAME = "contact_icons" ;
2840
2941 private static final String [] PROJECTION = {
3042 ContactsContract .Contacts ._ID ,
3143 ContactsContract .Contacts .LOOKUP_KEY ,
3244 ContactsContract .Contacts .DISPLAY_NAME_PRIMARY ,
3345 };
3446
47+ private static final ExecutorService sExecutorService = Executors .newCachedThreadPool ();
48+
49+ private String mProviderPackage ;
50+ private String mFileProviderAuthority ;
51+
52+ private File mDir ;
53+
54+ private synchronized void init (Context context ) {
55+ if (mProviderPackage != null ) {
56+ return ;
57+ }
58+
59+ mDir = new File (context .getFilesDir (), FOLDER_NAME );
60+
61+ if (!mDir .exists ()) {
62+ boolean mkDirSuccessful = mDir .mkdir ();
63+ if (!mkDirSuccessful ) {
64+ Log .e (TAG , "Failed to create a directory for storing a splash image" );
65+ }
66+ }
67+
68+ PackageManager packageManager = context .getPackageManager ();
69+
70+ Log .d (TAG , "package = " + context .getPackageName ());
71+
72+ TwaProviderPicker .Action action = TwaProviderPicker .pickProvider (packageManager );
73+ mProviderPackage = action .provider ;
74+
75+ Log .d (TAG , "provider = " + mProviderPackage );
76+
77+ ComponentName componentName = new ComponentName (context , LauncherActivity .class );
78+
79+ try {
80+ mFileProviderAuthority =
81+ packageManager
82+ .getActivityInfo (componentName , PackageManager .GET_META_DATA )
83+ .metaData
84+ .getString ("android.support.customtabs.trusted.FILE_PROVIDER_AUTHORITY" );
85+ } catch (NameNotFoundException e ) {
86+ throw new RuntimeException (e );
87+ }
88+
89+ Log .d (TAG , "authority = " + mFileProviderAuthority );
90+ }
3591
3692 @ NonNull
3793 @ Override
38- public Bundle handleExtraCommand (Context context , String commandName , Bundle args ,
94+ public Bundle handleExtraCommand (
95+ Context context ,
96+ String commandName ,
97+ Bundle args ,
3998 @ Nullable TrustedWebActivityCallbackRemote callback ) {
99+ init (context );
100+
40101 Bundle result = new Bundle ();
41102 result .putBoolean (EXTRA_COMMAND_SUCCESS , false );
42103
@@ -48,34 +109,71 @@ public Bundle handleExtraCommand(Context context, String commandName, Bundle arg
48109 ContactPermissionRequestActivity .requestContactPermisson (context , callback );
49110 result .putBoolean (EXTRA_COMMAND_SUCCESS , true );
50111 break ;
51- case COMMAND_GET_CONTACT_DATA :
52- Log .d (TAG , "COMMAND_GET_CONTACT_DATA received" );
112+ case COMMAND_FETCH_CONTACTS :
113+ Log .d (TAG , "COMMAND_FETCH_CONTACTS received" );
53114 boolean includeNames = args .getBoolean ("includeNames" );
54115 boolean includeEmails = args .getBoolean ("includeEmails" );
55116 boolean includeTel = args .getBoolean ("includeTel" );
56117 boolean includeAddresses = args .getBoolean ("includeAddresses" );
57- callbackResult .putParcelableArrayList ("contacts" ,
58- getAllContacts (context , includeNames , includeEmails , includeTel ,
59- includeAddresses ));
60- try {
61- callback .runExtraCallback (COMMAND_GET_CONTACT_DATA , callbackResult );
62- } catch (RemoteException e ) {
63- e .printStackTrace ();
64- }
118+
119+ sExecutorService .submit (
120+ () -> {
121+ callbackResult .putParcelableArrayList (
122+ "contacts" ,
123+ getAllContacts (
124+ context , includeNames , includeEmails , includeTel ,
125+ includeAddresses ));
126+ try {
127+ callback .runExtraCallback (COMMAND_FETCH_CONTACTS , callbackResult );
128+ } catch (RemoteException e ) {
129+ e .printStackTrace ();
130+ }
131+ });
65132 result .putBoolean (EXTRA_COMMAND_SUCCESS , true );
66133 break ;
67- case COMMAND_GET_CONTACT_ICON :
68- Log .d (TAG , "COMMAND_GET_CONTACT_ICON received" );
134+ case COMMAND_FETCH_CONTACT_ICON :
135+ Log .d (TAG , "COMMAND_FETCH_CONTACT_ICON received" );
69136
70137 String id = args .getString ("id" );
71138 int iconSize = args .getInt ("size" );
72139
73- callbackResult .putParcelable ("icon" , getIcon (context , id , iconSize ));
74- try {
75- callback .runExtraCallback (COMMAND_GET_CONTACT_ICON , callbackResult );
76- } catch (RemoteException e ) {
77- e .printStackTrace ();
78- }
140+ sExecutorService .submit (
141+ () -> {
142+ Bitmap icon = getIcon (context , id , iconSize );
143+
144+ if (icon != null ) {
145+ File file = new File (mDir , "contact_icon_" + id );
146+
147+ Log .d (TAG , "file = " + file .getAbsolutePath ());
148+
149+ try (FileOutputStream os = new FileOutputStream (file )) {
150+ icon .compress (Bitmap .CompressFormat .PNG , 100 , os );
151+ os .flush ();
152+ } catch (IOException e ) {
153+ throw new RuntimeException (e );
154+ }
155+
156+ Log .d (TAG , "file written" );
157+
158+ synchronized (context ) {
159+ Uri uri = FileProvider .getUriForFile (context ,
160+ mFileProviderAuthority , file );
161+
162+ Log .d (TAG , "uri = " + uri );
163+
164+ context .grantUriPermission (
165+ mProviderPackage , uri , Intent .FLAG_GRANT_READ_URI_PERMISSION );
166+
167+ callbackResult .putParcelable ("icon" , uri );
168+ }
169+ }
170+ try {
171+ callback .runExtraCallback (COMMAND_FETCH_CONTACT_ICON , callbackResult );
172+ } catch (RemoteException e ) {
173+ e .printStackTrace ();
174+ }
175+ });
176+
79177 result .putBoolean (EXTRA_COMMAND_SUCCESS , true );
80178 break ;
81179 default :
@@ -86,8 +184,12 @@ public Bundle handleExtraCommand(Context context, String commandName, Bundle arg
86184 return result ;
87185 }
88186
89- private Map <String , ArrayList <String >> getDetails (ContentResolver contentResolver ,
90- Uri source , String idColumn , String dataColumn , String sortOrder ) {
187+ private Map <String , ArrayList <String >> getDetails (
188+ ContentResolver contentResolver ,
189+ Uri source ,
190+ String idColumn ,
191+ String dataColumn ,
192+ String sortOrder ) {
91193 Map <String , ArrayList <String >> map = new HashMap <>();
92194
93195 Cursor cursor = contentResolver .query (source , null , null , null , sortOrder );
@@ -160,8 +262,7 @@ private Map<String, ArrayList<Bundle>> getAddressDetails(ContentResolver content
160262 String formattedAddress =
161263 cursor .getString (
162264 cursor .getColumnIndexOrThrow (
163- ContactsContract .CommonDataKinds .StructuredPostal
164- .FORMATTED_ADDRESS ));
265+ ContactsContract .CommonDataKinds .StructuredPostal .FORMATTED_ADDRESS ));
165266 String postcode =
166267 cursor .getString (
167268 cursor .getColumnIndexOrThrow (
@@ -198,14 +299,18 @@ private Map<String, ArrayList<Bundle>> getAddressDetails(ContentResolver content
198299 return map ;
199300 }
200301
201- public ArrayList <Bundle > getAllContacts (Context context , boolean includeNames ,
302+ public ArrayList <Bundle > getAllContacts (
303+ Context context ,
304+ boolean includeNames ,
202305 boolean includeEmails ,
203- boolean includeTel , boolean includeAddresses ) {
306+ boolean includeTel ,
307+ boolean includeAddresses ) {
204308 ContentResolver contentResolver = context .getContentResolver ();
205309
206310 Map <String , ArrayList <String >> emailMap =
207311 includeEmails
208- ? getDetails (contentResolver ,
312+ ? getDetails (
313+ contentResolver ,
209314 ContactsContract .CommonDataKinds .Email .CONTENT_URI ,
210315 ContactsContract .CommonDataKinds .Email .CONTACT_ID ,
211316 ContactsContract .CommonDataKinds .Email .DATA ,
@@ -217,7 +322,8 @@ public ArrayList<Bundle> getAllContacts(Context context, boolean includeNames,
217322
218323 Map <String , ArrayList <String >> phoneMap =
219324 includeTel
220- ? getDetails (contentResolver ,
325+ ? getDetails (
326+ contentResolver ,
221327 ContactsContract .CommonDataKinds .Phone .CONTENT_URI ,
222328 ContactsContract .CommonDataKinds .Phone .CONTACT_ID ,
223329 ContactsContract .CommonDataKinds .Phone .DATA ,
@@ -250,16 +356,14 @@ public ArrayList<Bundle> getAllContacts(Context context, boolean includeNames,
250356
251357 ArrayList <Bundle > contacts = new ArrayList <>(cursor .getCount ());
252358 do {
253- String id =
254- cursor .getString ( cursor . getColumnIndexOrThrow (ContactsContract .Contacts ._ID ));
359+ String id = cursor . getString (
360+ cursor .getColumnIndexOrThrow (ContactsContract .Contacts ._ID ));
255361 String name =
256362 cursor .getString (
257- cursor .getColumnIndexOrThrow (
258- ContactsContract .Contacts .DISPLAY_NAME_PRIMARY ));
363+ cursor .getColumnIndexOrThrow (ContactsContract .Contacts .DISPLAY_NAME_PRIMARY ));
259364 ArrayList <String > email = emailMap != null ? emailMap .get (id ) : null ;
260365 ArrayList <String > tel = phoneMap != null ? phoneMap .get (id ) : null ;
261- ArrayList <Bundle > address =
262- addressMap != null ? addressMap .get (id ) : null ;
366+ ArrayList <Bundle > address = addressMap != null ? addressMap .get (id ) : null ;
263367
264368 if (includeNames || email != null || tel != null || address != null ) {
265369 Bundle contact = new Bundle ();
@@ -282,17 +386,12 @@ private Bitmap getIcon(Context context, String id, int iconSize) {
282386 ContentResolver contentResolver = context .getContentResolver ();
283387
284388 Uri contactUri =
285- ContentUris .withAppendedId (
286- ContactsContract .Contacts .CONTENT_URI , Long .parseLong (id ));
389+ ContentUris .withAppendedId (ContactsContract .Contacts .CONTENT_URI , Long .parseLong (id ));
287390 Uri photoUri =
288391 Uri .withAppendedPath (contactUri , ContactsContract .Contacts .Photo .CONTENT_DIRECTORY );
289392 Cursor cursor =
290393 contentResolver .query (
291- photoUri ,
292- new String []{ContactsContract .Contacts .Photo .PHOTO },
293- null ,
294- null ,
295- null );
394+ photoUri , new String []{ContactsContract .Contacts .Photo .PHOTO }, null , null , null );
296395 if (cursor == null ) {
297396 return null ;
298397 }
@@ -301,9 +400,7 @@ private Bitmap getIcon(Context context, String id, int iconSize) {
301400 byte [] data = cursor .getBlob (0 );
302401 if (data != null ) {
303402 Bitmap icon = BitmapFactory .decodeStream (new ByteArrayInputStream (data ));
304- return iconSize > 0
305- ? Bitmap .createScaledBitmap (
306- icon , iconSize , iconSize , true )
403+ return iconSize > 0 ? Bitmap .createScaledBitmap (icon , iconSize , iconSize , true )
307404 : icon ;
308405 }
309406 }
0 commit comments