From 739648e909446c83fe1474636b8465a7ef91cce2 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 7 Dec 2015 00:33:50 +0100 Subject: [PATCH] ask for contact permissions when first opening StartConversationActivity --- .../persistance/FileBackend.java | 8 ++- .../services/XmppConnectionService.java | 14 +++-- .../ui/StartConversationActivity.java | 63 ++++++++++++++++--- .../conversations/utils/PhoneHelper.java | 11 ++++ src/main/res/values/strings.xml | 3 + 5 files changed, 83 insertions(+), 16 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java index febb09705d75385b6691c6e7677d2bb68f2f77dc..02b14c7df62edfa0978011627c3cdbd6a00f8557 100644 --- a/src/main/java/eu/siacs/conversations/persistance/FileBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/FileBackend.java @@ -410,6 +410,8 @@ public class FileBackend { } return cropCenterSquare(input, size); } + } catch (SecurityException e) { + return null; // happens for example on Android 6.0 if contacts permissions get revoked } catch (FileNotFoundException e) { return null; } finally { @@ -424,7 +426,7 @@ public class FileBackend { InputStream is = null; try { BitmapFactory.Options options = new BitmapFactory.Options(); - options.inSampleSize = calcSampleSize(image,Math.max(newHeight, newWidth)); + options.inSampleSize = calcSampleSize(image, Math.max(newHeight, newWidth)); is = mXmppConnectionService.getContentResolver().openInputStream(image); if (is == null) { return null; @@ -451,6 +453,8 @@ public class FileBackend { source.recycle(); } return dest; + } catch (SecurityException e) { + return null; //android 6.0 with revoked permissions for example } catch (FileNotFoundException e) { return null; } finally { @@ -479,7 +483,7 @@ public class FileBackend { return output; } - private int calcSampleSize(Uri image, int size) throws FileNotFoundException { + private int calcSampleSize(Uri image, int size) throws FileNotFoundException, SecurityException { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(mXmppConnectionService.getContentResolver().openInputStream(image), null, options); diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index dbc4825c96043b4b0b3fff99e8d94e775f8dd7a1..790e035c6a9148ec8217172f74bc4e18af917857 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -468,9 +468,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa break; case ACTION_MERGE_PHONE_CONTACTS: if (mRestoredFromDatabase) { - PhoneHelper.loadPhoneContacts(getApplicationContext(), - new CopyOnWriteArrayList(), - this); + loadPhoneContacts(); } return START_STICKY; case Intent.ACTION_SHUTDOWN: @@ -1097,9 +1095,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } getBitmapCache().evictAll(); Looper.prepare(); - PhoneHelper.loadPhoneContacts(getApplicationContext(), - new CopyOnWriteArrayList(), - XmppConnectionService.this); + loadPhoneContacts(); Log.d(Config.LOGTAG, "restoring messages"); for (Conversation conversation : conversations) { conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE)); @@ -1121,6 +1117,12 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } + public void loadPhoneContacts() { + PhoneHelper.loadPhoneContacts(getApplicationContext(), + new CopyOnWriteArrayList(), + XmppConnectionService.this); + } + public List getConversations() { return this.conversations; } diff --git a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java index df04567f103e364a2bf3264e54d6abcd4898d43b..f6a3864783e34713602267f77202a0076f5c84ca 100644 --- a/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java @@ -1,5 +1,6 @@ package eu.siacs.conversations.ui; +import android.Manifest; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.ActionBar; @@ -13,6 +14,7 @@ import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; +import android.content.pm.PackageManager; import android.net.Uri; import android.nfc.NdefMessage; import android.nfc.NdefRecord; @@ -51,6 +53,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; @@ -89,6 +92,9 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU private Invite mPendingInvite = null; private Menu mOptionsMenu; private EditText mSearchEditText; + private AtomicBoolean mRequestedContactsPermission = new AtomicBoolean(false); + private final int REQUEST_SYNC_CONTACTS = 0x3b28cf; + private MenuItem.OnActionExpandListener mOnActionExpandListener = new MenuItem.OnActionExpandListener() { @Override @@ -245,6 +251,12 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU } + @Override + public void onStart() { + super.onStart(); + askForContactsPermissions(); + } + protected void openConversationForContact(int position) { Contact contact = (Contact) contacts.get(position); Conversation conversation = xmppConnectionService @@ -369,7 +381,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU final Jid accountJid; try { if (Config.DOMAIN_LOCK != null) { - accountJid = Jid.fromParts((String) spinner.getSelectedItem(),Config.DOMAIN_LOCK,null); + accountJid = Jid.fromParts((String) spinner.getSelectedItem(), Config.DOMAIN_LOCK, null); } else { accountJid = Jid.fromString((String) spinner.getSelectedItem()); } @@ -432,7 +444,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU final Jid accountJid; try { if (Config.DOMAIN_LOCK != null) { - accountJid = Jid.fromParts((String) spinner.getSelectedItem(),Config.DOMAIN_LOCK,null); + accountJid = Jid.fromParts((String) spinner.getSelectedItem(), Config.DOMAIN_LOCK, null); } else { accountJid = Jid.fromString((String) spinner.getSelectedItem()); } @@ -447,7 +459,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU return; } final Account account = xmppConnectionService - .findAccountByJid(accountJid); + .findAccountByJid(accountJid); if (account == null) { dialog.dismiss(); return; @@ -456,7 +468,7 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU if (account.hasBookmarkFor(conferenceJid)) { jid.setError(getString(R.string.bookmark_already_exists)); } else { - final Bookmark bookmark = new Bookmark(account,conferenceJid.toBareJid()); + final Bookmark bookmark = new Bookmark(account, conferenceJid.toBareJid()); bookmark.setAutojoin(true); String nick = conferenceJid.getResourcepart(); if (nick != null && !nick.isEmpty()) { @@ -465,8 +477,8 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU account.getBookmarks().add(bookmark); xmppConnectionService.pushBookmarks(account); final Conversation conversation = xmppConnectionService - .findOrCreateConversation(account, - conferenceJid, true); + .findOrCreateConversation(account, + conferenceJid, true); conversation.setBookmark(bookmark); if (!conversation.getMucOptions().online()) { xmppConnectionService.joinMuc(conversation); @@ -476,8 +488,8 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU } } else { final Conversation conversation = xmppConnectionService - .findOrCreateConversation(account, - conferenceJid, true); + .findOrCreateConversation(account, + conferenceJid, true); if (!conversation.getMucOptions().online()) { xmppConnectionService.joinMuc(conversation); } @@ -580,6 +592,41 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU super.onActivityResult(requestCode, requestCode, intent); } + private void askForContactsPermissions() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) { + if (mRequestedContactsPermission.compareAndSet(false, true)) { + if (shouldShowRequestPermissionRationale(Manifest.permission.READ_CONTACTS)) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.sync_with_contacts); + builder.setMessage(R.string.sync_with_contacts_long); + builder.setPositiveButton(R.string.sync_now, new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + requestPermissions(new String[]{Manifest.permission.READ_CONTACTS}, REQUEST_SYNC_CONTACTS); + } + } + }); + builder.create().show(); + } else { + requestPermissions(new String[]{Manifest.permission.READ_CONTACTS}, 0); + } + } + } + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { + if (grantResults.length > 0) + if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { + if (requestCode == REQUEST_SYNC_CONTACTS && xmppConnectionServiceBound) { + xmppConnectionService.loadPhoneContacts(); + } + } + } + @Override protected void onBackendConnected() { this.mActivatedAccounts.clear(); diff --git a/src/main/java/eu/siacs/conversations/utils/PhoneHelper.java b/src/main/java/eu/siacs/conversations/utils/PhoneHelper.java index a37f60a0f13143c06fd2c23622dd8587441db304..893f9eb89997ec629daeb841e676e317688f01ca 100644 --- a/src/main/java/eu/siacs/conversations/utils/PhoneHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/PhoneHelper.java @@ -1,5 +1,6 @@ package eu.siacs.conversations.utils; +import android.Manifest; import android.content.Context; import android.content.CursorLoader; import android.content.Loader; @@ -7,6 +8,7 @@ import android.content.Loader.OnLoadCompleteListener; import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.provider.ContactsContract; import android.provider.ContactsContract.Profile; @@ -17,6 +19,11 @@ import java.util.concurrent.RejectedExecutionException; public class PhoneHelper { public static void loadPhoneContacts(Context context,final List phoneContacts, final OnPhoneContactsLoadedListener listener) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M + && context.checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) { + listener.onPhoneContactsLoaded(phoneContacts); + return; + } final String[] PROJECTION = new String[] { ContactsContract.Data._ID, ContactsContract.Data.DISPLAY_NAME, ContactsContract.Data.PHOTO_URI, @@ -74,6 +81,10 @@ public class PhoneHelper { } public static Uri getSefliUri(Context context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M + && context.checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) { + return null; + } String[] mProjection = new String[] { Profile._ID, Profile.PHOTO_URI }; Cursor mProfileCursor = context.getContentResolver().query( Profile.CONTENT_URI, mProjection, null, null, null); diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 780460400989c6ba5062ae74bde597f0a65192fb..25af5be1203f9bf8c7c2f0787b2ffc0bce6e0be8 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -558,4 +558,7 @@ Shared file with %s Shared image with %s Conversations need access to external storage + Synchronize with contacts + Conversations wants to match your XMPP roster with your contacts to show their full names and avatars.\n\nConversations will only read your contacts and match them locally without uploading them to your server. + Synchronize now