mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-08-22 18:07:55 +00:00
Implement Android 6 Runtime Permissions
Differential Revision: https://phabricator.kde.org/D5876
This commit is contained in:
parent
e13090066c
commit
0b83cfe06d
@ -5,7 +5,7 @@
|
||||
android:versionName="1.6.2">
|
||||
|
||||
<uses-sdk android:minSdkVersion="9"
|
||||
android:targetSdkVersion="22" />
|
||||
android:targetSdkVersion="25" />
|
||||
|
||||
<supports-screens
|
||||
android:anyDensity="true"
|
||||
|
@ -3,7 +3,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.3.0'
|
||||
classpath 'com.android.tools.build:gradle:2.3.2'
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ android {
|
||||
compileSdkVersion 25
|
||||
defaultConfig {
|
||||
minSdkVersion 9
|
||||
targetSdkVersion 22 //Bumping to >22 means we have to support the new permissions model
|
||||
targetSdkVersion 25
|
||||
//multiDexEnabled true
|
||||
//testInstrumentationRunner "com.android.test.runner.MultiDexTestRunner"
|
||||
}
|
||||
@ -57,7 +57,7 @@ android {
|
||||
minifyEnabled false
|
||||
useProguard false
|
||||
}
|
||||
release { //keep on 'releae', set to 'all' when testing to make sure proguard is not deleting important stuff
|
||||
release { //keep on 'release', set to 'all' when testing to make sure proguard is not deleting important stuff
|
||||
minifyEnabled true
|
||||
useProguard true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'
|
||||
@ -83,5 +83,6 @@ dependencies {
|
||||
androidTestCompile 'org.mockito:mockito-core:1.10.19'
|
||||
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.1'// Because mockito has some problems with dex environment
|
||||
androidTestCompile 'org.skyscreamer:jsonassert:1.3.0'
|
||||
testCompile 'junit:junit:4.12'
|
||||
|
||||
}
|
||||
|
@ -206,4 +206,8 @@
|
||||
<string name="open">Open</string>
|
||||
<string name="close">Close</string>
|
||||
|
||||
<string name="no_permissions_storage">You need to grant permissions to access the storage</string>
|
||||
<string name="plugins_need_permission">Some Plugins need permissions to work (tap for more info):</string>
|
||||
<string name="permission_explanation">This plugin needs permissions to work</string>
|
||||
|
||||
</resources>
|
||||
|
@ -82,6 +82,7 @@ public class Device implements BaseLink.PackageReceiver {
|
||||
private List<String> m_supportedPlugins = new ArrayList<>();
|
||||
private final ConcurrentHashMap<String, Plugin> plugins = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<String, Plugin> failedPlugins = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<String, Plugin> pluginsWithoutPermissions = new ConcurrentHashMap<>();
|
||||
private Map<String, ArrayList<String>> pluginsByIncomingInterface = new HashMap<>();
|
||||
|
||||
private final SharedPreferences settings;
|
||||
@ -722,6 +723,16 @@ public class Device implements BaseLink.PackageReceiver {
|
||||
failedPlugins.put(pluginKey, plugin);
|
||||
}
|
||||
|
||||
if(!plugin.checkRequiredPermissions()){
|
||||
Log.e("KDE/addPlugin", "No permission " + pluginKey);
|
||||
plugins.remove(pluginKey);
|
||||
pluginsWithoutPermissions.put(pluginKey, plugin);
|
||||
success = false;
|
||||
} else {
|
||||
Log.i("KDE/addPlugin", "Permission OK " + pluginKey);
|
||||
pluginsWithoutPermissions.remove(pluginKey);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
@ -812,6 +823,10 @@ public class Device implements BaseLink.PackageReceiver {
|
||||
return failedPlugins;
|
||||
}
|
||||
|
||||
public ConcurrentHashMap<String, Plugin> getPluginsWithoutPermissions() {
|
||||
return pluginsWithoutPermissions;
|
||||
}
|
||||
|
||||
public void addPluginsChangedListener(PluginsChangedListener listener) {
|
||||
pluginsChangedListeners.add(listener);
|
||||
}
|
||||
|
@ -20,23 +20,33 @@
|
||||
|
||||
package org.kde.kdeconnect.Plugins;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.NetworkPackage;
|
||||
import org.kde.kdeconnect.UserInterface.MaterialActivity;
|
||||
import org.kde.kdeconnect.UserInterface.PluginSettingsActivity;
|
||||
import org.kde.kdeconnect.UserInterface.SettingsActivity;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
public abstract class Plugin {
|
||||
|
||||
protected Device device;
|
||||
protected Context context;
|
||||
protected int permissionExplanation = R.string.permission_explanation;
|
||||
|
||||
public final void setContext(Context context, Device device) {
|
||||
this.device = device;
|
||||
@ -167,14 +177,6 @@ public abstract class Plugin {
|
||||
*/
|
||||
public boolean onPackageReceived(NetworkPackage np) { return false; }
|
||||
|
||||
/**
|
||||
* If onCreate returns false, should create a dialog explaining
|
||||
* the problem (and how to fix it, if possible) to the user.
|
||||
*/
|
||||
public AlertDialog getErrorDialog(Activity deviceActivity) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should return the list of NetworkPackage types that this plugin can handle
|
||||
*/
|
||||
@ -205,4 +207,70 @@ public abstract class Plugin {
|
||||
return b;
|
||||
}
|
||||
|
||||
public String[] getRequiredPermissions() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
public String[] getOptionalPermissions() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
//Permission from Manifest.permission.*
|
||||
protected boolean isPermissionGranted(String permission) {
|
||||
int result = ContextCompat.checkSelfPermission(context, permission);
|
||||
return (result == PackageManager.PERMISSION_GRANTED);
|
||||
}
|
||||
|
||||
protected boolean arePermissionsGranted(String[] permissions) {
|
||||
for(String permission: permissions){
|
||||
if(!isPermissionGranted(permission)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected AlertDialog requestPermissionDialog(Activity activity, String permissions, @StringRes int reason) {
|
||||
return requestPermissionDialog(activity, new String[]{permissions}, reason);
|
||||
}
|
||||
|
||||
protected AlertDialog requestPermissionDialog(final Activity activity, final String[] permissions, @StringRes int reason){
|
||||
return new AlertDialog.Builder(activity)
|
||||
.setTitle(getDisplayName())
|
||||
.setMessage(reason)
|
||||
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
ActivityCompat.requestPermissions(activity, permissions, 0);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.cancel,new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
//Do nothing
|
||||
}
|
||||
})
|
||||
.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* If onCreate returns false, should create a dialog explaining
|
||||
* the problem (and how to fix it, if possible) to the user.
|
||||
*/
|
||||
|
||||
public AlertDialog getErrorDialog(Activity deviceActivity) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public AlertDialog getPermissionExplanationDialog(Activity deviceActivity) {
|
||||
return requestPermissionDialog(deviceActivity,getRequiredPermissions(), permissionExplanation);
|
||||
}
|
||||
|
||||
public boolean checkRequiredPermissions(){
|
||||
if (!arePermissionsGranted(getRequiredPermissions())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,8 +20,16 @@
|
||||
|
||||
package org.kde.kdeconnect.Plugins.SftpPlugin;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Environment;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.kde.kdeconnect.Helpers.StorageHelper;
|
||||
import org.kde.kdeconnect.NetworkPackage;
|
||||
import org.kde.kdeconnect.Plugins.Plugin;
|
||||
@ -38,6 +46,9 @@ public class SftpPlugin extends Plugin {
|
||||
|
||||
private static final SimpleSftpServer server = new SimpleSftpServer();
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return context.getResources().getString(R.string.pref_plugin_sftp);
|
||||
@ -75,50 +86,48 @@ public class SftpPlugin extends Plugin {
|
||||
//Kept for compatibility, in case "multiPaths" is not possible or the other end does not support it
|
||||
np2.set("path", Environment.getExternalStorageDirectory().getAbsolutePath());
|
||||
|
||||
File root = new File("/");
|
||||
if (root.canExecute() && root.canRead()) {
|
||||
List<StorageHelper.StorageInfo> storageList = StorageHelper.getStorageList();
|
||||
ArrayList<String> paths = new ArrayList<>();
|
||||
ArrayList<String> pathNames = new ArrayList<>();
|
||||
List<StorageHelper.StorageInfo> storageList = StorageHelper.getStorageList();
|
||||
ArrayList<String> paths = new ArrayList<>();
|
||||
ArrayList<String> pathNames = new ArrayList<>();
|
||||
|
||||
for (StorageHelper.StorageInfo storage : storageList) {
|
||||
paths.add(storage.path);
|
||||
StringBuilder res = new StringBuilder();
|
||||
for (StorageHelper.StorageInfo storage : storageList) {
|
||||
paths.add(storage.path);
|
||||
StringBuilder res = new StringBuilder();
|
||||
|
||||
if (storageList.size() > 1) {
|
||||
if (!storage.removable) {
|
||||
res.append(context.getString(R.string.sftp_internal_storage));
|
||||
} else if (storage.number > 1) {
|
||||
res.append(context.getString(R.string.sftp_sdcard_num, storage.number));
|
||||
} else {
|
||||
res.append(context.getString(R.string.sftp_sdcard));
|
||||
}
|
||||
if (storageList.size() > 1) {
|
||||
if (!storage.removable) {
|
||||
res.append(context.getString(R.string.sftp_internal_storage));
|
||||
} else if (storage.number > 1) {
|
||||
res.append(context.getString(R.string.sftp_sdcard_num, storage.number));
|
||||
} else {
|
||||
res.append(context.getString(R.string.sftp_all_files));
|
||||
res.append(context.getString(R.string.sftp_sdcard));
|
||||
}
|
||||
String pathName = res.toString();
|
||||
if (storage.readonly) {
|
||||
res.append(" ");
|
||||
res.append(context.getString(R.string.sftp_readonly));
|
||||
}
|
||||
pathNames.add(res.toString());
|
||||
} else {
|
||||
res.append(context.getString(R.string.sftp_all_files));
|
||||
}
|
||||
String pathName = res.toString();
|
||||
if (storage.readonly) {
|
||||
res.append(" ");
|
||||
res.append(context.getString(R.string.sftp_readonly));
|
||||
}
|
||||
pathNames.add(res.toString());
|
||||
|
||||
//Shortcut for users that only want to browse camera pictures
|
||||
String dcim = storage.path + "/DCIM/Camera";
|
||||
if (new File(dcim).exists()) {
|
||||
paths.add(dcim);
|
||||
if (storageList.size() > 1) {
|
||||
pathNames.add(context.getString(R.string.sftp_camera) + "(" + pathName + ")");
|
||||
} else {
|
||||
pathNames.add(context.getString(R.string.sftp_camera));
|
||||
}
|
||||
//Shortcut for users that only want to browse camera pictures
|
||||
String dcim = storage.path + "/DCIM/Camera";
|
||||
if (new File(dcim).exists()) {
|
||||
paths.add(dcim);
|
||||
if (storageList.size() > 1) {
|
||||
pathNames.add(context.getString(R.string.sftp_camera) + "(" + pathName + ")");
|
||||
} else {
|
||||
pathNames.add(context.getString(R.string.sftp_camera));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (paths.size() > 0) {
|
||||
np2.set("multiPaths", paths);
|
||||
np2.set("pathNames", pathNames);
|
||||
|
||||
if (paths.size() > 0) {
|
||||
np2.set("multiPaths", paths);
|
||||
np2.set("pathNames", pathNames);
|
||||
}
|
||||
}
|
||||
|
||||
device.sendPackage(np2);
|
||||
@ -129,6 +138,12 @@ public class SftpPlugin extends Plugin {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getRequiredPermissions() {
|
||||
String[] perms = {Manifest.permission.READ_EXTERNAL_STORAGE};
|
||||
return perms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedPackageTypes() {
|
||||
return new String[]{PACKAGE_TYPE_SFTP_REQUEST};
|
||||
|
@ -20,7 +20,9 @@
|
||||
|
||||
package org.kde.kdeconnect.Plugins.SharePlugin;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.DownloadManager;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
@ -30,6 +32,7 @@ import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.drawable.Drawable;
|
||||
@ -110,6 +113,17 @@ public class SharePlugin extends Plugin {
|
||||
|
||||
Log.i("SharePlugin", "hasPayload");
|
||||
|
||||
int permissionCheck = ContextCompat.checkSelfPermission(context,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||
|
||||
if(permissionCheck == PackageManager.PERMISSION_GRANTED) {
|
||||
|
||||
} else if(permissionCheck == PackageManager.PERMISSION_DENIED){
|
||||
// TODO Request Permission for storage
|
||||
Log.i("SharePlugin", "no Permission for Storage");
|
||||
return false;
|
||||
}
|
||||
|
||||
final InputStream input = np.getPayload();
|
||||
final long fileLength = np.getPayloadSize();
|
||||
final String originalFilename = np.getString("filename", Long.toString(System.currentTimeMillis()));
|
||||
@ -132,6 +146,8 @@ public class SharePlugin extends Plugin {
|
||||
final OutputStream destinationOutput = context.getContentResolver().openOutputStream(destinationDocument.getUri());
|
||||
final Uri destinationUri = destinationDocument.getUri();
|
||||
|
||||
|
||||
|
||||
final int notificationId = (int)System.currentTimeMillis();
|
||||
Resources res = context.getResources();
|
||||
final NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
|
||||
@ -192,20 +208,27 @@ public class SharePlugin extends Plugin {
|
||||
.setAutoCancel(true)
|
||||
.setProgress(100,100,false)
|
||||
.setOngoing(false);
|
||||
if (successful) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setDataAndType(destinationUri, mimeType);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
|
||||
stackBuilder.addNextIntent(intent);
|
||||
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
builder.setContentText(res.getString(R.string.received_file_text, destinationDocument.getName()))
|
||||
.setContentIntent(resultPendingIntent);
|
||||
|
||||
// Nougat requires share:// URIs instead of file:// URIs
|
||||
// TODO use FileProvider for >Nougat
|
||||
if(Build.VERSION.SDK_INT < 24) {
|
||||
if (successful) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setDataAndType(destinationUri, mimeType);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
|
||||
stackBuilder.addNextIntent(intent);
|
||||
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
builder.setContentText(res.getString(R.string.received_file_text, destinationDocument.getName()))
|
||||
.setContentIntent(resultPendingIntent);
|
||||
}
|
||||
}
|
||||
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
if (prefs.getBoolean("share_notification_preference", true)) {
|
||||
builder.setDefaults(Notification.DEFAULT_ALL);
|
||||
}
|
||||
|
||||
NotificationHelper.notifyCompat(notificationManager, notificationId, builder.build());
|
||||
|
||||
if (successful) {
|
||||
@ -409,5 +432,10 @@ public class SharePlugin extends Plugin {
|
||||
return new String[]{PACKAGE_TYPE_SHARE_REQUEST};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getRequiredPermissions() {
|
||||
String[] perms = {Manifest.permission.READ_EXTERNAL_STORAGE};
|
||||
return perms;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,6 +20,10 @@
|
||||
|
||||
package org.kde.kdeconnect.Plugins.TelepathyPlugin;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.telephony.SmsManager;
|
||||
import android.util.Log;
|
||||
|
||||
@ -43,11 +47,6 @@ public class TelepathyPlugin extends Plugin {
|
||||
return context.getResources().getString(R.string.pref_plugin_telepathy_desc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
}
|
||||
@ -63,8 +62,18 @@ public class TelepathyPlugin extends Plugin {
|
||||
String phoneNo = np.getString("phoneNumber");
|
||||
String sms = np.getString("messageBody");
|
||||
try {
|
||||
SmsManager smsManager = SmsManager.getDefault();
|
||||
smsManager.sendTextMessage(phoneNo, null, sms, null, null);
|
||||
|
||||
int permissionCheck = ContextCompat.checkSelfPermission(context,
|
||||
Manifest.permission.SEND_SMS);
|
||||
|
||||
if(permissionCheck == PackageManager.PERMISSION_GRANTED) {
|
||||
SmsManager smsManager = SmsManager.getDefault();
|
||||
smsManager.sendTextMessage(phoneNo, null, sms, null, null);
|
||||
Log.d("TelepathyPlugin", "SMS sent");
|
||||
} else if(permissionCheck == PackageManager.PERMISSION_DENIED){
|
||||
// TODO Request Permission SEND_SMS
|
||||
}
|
||||
|
||||
//TODO: Notify other end
|
||||
} catch (Exception e) {
|
||||
//TODO: Notify other end
|
||||
@ -176,4 +185,8 @@ public class TelepathyPlugin extends Plugin {
|
||||
return new String[]{};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getRequiredPermissions() {
|
||||
return new String[]{Manifest.permission.SEND_SMS/*, Manifest.permission.READ_CONTACTS*/};
|
||||
}
|
||||
}
|
||||
|
@ -20,13 +20,16 @@
|
||||
|
||||
package org.kde.kdeconnect.Plugins.TelephonyPlugin;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.telephony.SmsMessage;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.Log;
|
||||
@ -102,32 +105,40 @@ public class TelephonyPlugin extends Plugin {
|
||||
|
||||
//Log.e("TelephonyPlugin", "callBroadcastReceived");
|
||||
|
||||
Map<String, String> contactInfo = ContactsHelper.phoneNumberLookup(context, phoneNumber);
|
||||
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_TELEPHONY);
|
||||
|
||||
if (phoneNumber != null) {
|
||||
np.set("phoneNumber", phoneNumber);
|
||||
}
|
||||
int permissionCheck = ContextCompat.checkSelfPermission(context,
|
||||
Manifest.permission.READ_CONTACTS);
|
||||
|
||||
if(permissionCheck==PackageManager.PERMISSION_GRANTED) {
|
||||
|
||||
Map<String, String> contactInfo = ContactsHelper.phoneNumberLookup(context, phoneNumber);
|
||||
|
||||
if (contactInfo.containsKey("name")) {
|
||||
np.set("contactName", contactInfo.get("name"));
|
||||
}
|
||||
|
||||
if (contactInfo.containsKey("photoID")) {
|
||||
String photoUri = contactInfo.get("photoID");
|
||||
if (photoUri != null) {
|
||||
try {
|
||||
String base64photo = ContactsHelper.photoId64Encoded(context, photoUri);
|
||||
if (base64photo != null && !base64photo.isEmpty()) {
|
||||
np.set("phoneThumbnail", base64photo);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("TelephonyPlugin", "Failed to get contact photo");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (contactInfo.containsKey("name")) {
|
||||
np.set("contactName", contactInfo.get("name"));
|
||||
} else {
|
||||
np.set("contactName", phoneNumber);
|
||||
}
|
||||
|
||||
if (contactInfo.containsKey("photoID")) {
|
||||
String photoUri = contactInfo.get("photoID");
|
||||
if (photoUri != null) {
|
||||
try {
|
||||
String base64photo = ContactsHelper.photoId64Encoded(context, photoUri);
|
||||
if (base64photo != null && !base64photo.isEmpty()) {
|
||||
np.set("phoneThumbnail", base64photo);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("TelephonyPlugin", "Failed to get contact photo");
|
||||
}
|
||||
}
|
||||
|
||||
if (phoneNumber != null) {
|
||||
np.set("phoneNumber", phoneNumber);
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
@ -208,18 +219,26 @@ public class TelephonyPlugin extends Plugin {
|
||||
}
|
||||
|
||||
String phoneNumber = message.getOriginatingAddress();
|
||||
Map<String, String> contactInfo = ContactsHelper.phoneNumberLookup(context, phoneNumber);
|
||||
|
||||
int permissionCheck = ContextCompat.checkSelfPermission(context,
|
||||
Manifest.permission.READ_CONTACTS);
|
||||
|
||||
if(permissionCheck==PackageManager.PERMISSION_GRANTED) {
|
||||
Map<String, String> contactInfo = ContactsHelper.phoneNumberLookup(context, phoneNumber);
|
||||
|
||||
if (contactInfo.containsKey("name")) {
|
||||
np.set("contactName", contactInfo.get("name"));
|
||||
}
|
||||
|
||||
if (contactInfo.containsKey("photoID")) {
|
||||
np.set("phoneThumbnail", ContactsHelper.photoId64Encoded(context, contactInfo.get("photoID")));
|
||||
}
|
||||
}
|
||||
if (phoneNumber != null) {
|
||||
np.set("phoneNumber", phoneNumber);
|
||||
}
|
||||
|
||||
if (contactInfo.containsKey("name")) {
|
||||
np.set("contactName", contactInfo.get("name"));
|
||||
}
|
||||
|
||||
if (contactInfo.containsKey("photoID")) {
|
||||
np.set("phoneThumbnail", ContactsHelper.photoId64Encoded(context, contactInfo.get("photoID")));
|
||||
}
|
||||
|
||||
device.sendPackage(np);
|
||||
}
|
||||
@ -267,4 +286,9 @@ public class TelephonyPlugin extends Plugin {
|
||||
return new String[]{PACKAGE_TYPE_TELEPHONY};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getRequiredPermissions() {
|
||||
return new String[]{Manifest.permission.READ_PHONE_STATE, Manifest.permission.READ_CONTACTS, Manifest.permission.SEND_SMS};
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -68,6 +68,7 @@ public class DeviceFragment extends Fragment {
|
||||
Device device;
|
||||
|
||||
TextView errorHeader;
|
||||
TextView noPermissionsHeader;
|
||||
|
||||
MaterialActivity mActivity;
|
||||
|
||||
@ -389,6 +390,38 @@ public class DeviceFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
//Plugins without permissions List
|
||||
final ConcurrentHashMap<String, Plugin> permissionsNeeded = device.getPluginsWithoutPermissions();
|
||||
if (!permissionsNeeded.isEmpty()) {
|
||||
if (noPermissionsHeader == null) {
|
||||
noPermissionsHeader = new TextView(mActivity);
|
||||
noPermissionsHeader.setPadding(
|
||||
0,
|
||||
((int) (28 * getResources().getDisplayMetrics().density)),
|
||||
0,
|
||||
((int) (8 * getResources().getDisplayMetrics().density))
|
||||
);
|
||||
noPermissionsHeader.setOnClickListener(null);
|
||||
noPermissionsHeader.setOnLongClickListener(null);
|
||||
noPermissionsHeader.setText(getResources().getString(R.string.plugins_need_permission));
|
||||
}
|
||||
items.add(new CustomItem(noPermissionsHeader));
|
||||
for (Map.Entry<String, Plugin> entry : permissionsNeeded.entrySet()) {
|
||||
String pluginKey = entry.getKey();
|
||||
final Plugin plugin = entry.getValue();
|
||||
if (plugin == null) {
|
||||
items.add(new SmallEntryItem(pluginKey));
|
||||
} else {
|
||||
items.add(new SmallEntryItem(plugin.getDisplayName(), new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
plugin.getPermissionExplanationDialog(mActivity).show();
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListView buttonsList = (ListView) rootView.findViewById(R.id.buttons_list);
|
||||
ListAdapter adapter = new ListAdapter(mActivity, items);
|
||||
buttonsList.setAdapter(adapter);
|
||||
|
@ -1,14 +1,17 @@
|
||||
package org.kde.kdeconnect.UserInterface;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.design.widget.NavigationView;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.view.GravityCompat;
|
||||
import android.support.v4.widget.DrawerLayout;
|
||||
@ -294,6 +297,22 @@ public class MaterialActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||
for (int result : grantResults) {
|
||||
if (result == PackageManager.PERMISSION_GRANTED) {
|
||||
//New permission granted, reload plugins
|
||||
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
|
||||
@Override
|
||||
public void onServiceStart(BackgroundService service) {
|
||||
Device device = service.getDevice(mCurrentDevice);
|
||||
device.reloadPluginsFromSettings();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void renameDevice() {
|
||||
final TextView nameView = (TextView) mNavigationView.findViewById(R.id.device_name);
|
||||
final EditText deviceNameEdit = new EditText(MaterialActivity.this);
|
||||
|
Loading…
x
Reference in New Issue
Block a user