mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-08-22 09:58:08 +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">
|
android:versionName="1.6.2">
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="9"
|
<uses-sdk android:minSdkVersion="9"
|
||||||
android:targetSdkVersion="22" />
|
android:targetSdkVersion="25" />
|
||||||
|
|
||||||
<supports-screens
|
<supports-screens
|
||||||
android:anyDensity="true"
|
android:anyDensity="true"
|
||||||
|
@ -3,7 +3,7 @@ buildscript {
|
|||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
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
|
compileSdkVersion 25
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 9
|
minSdkVersion 9
|
||||||
targetSdkVersion 22 //Bumping to >22 means we have to support the new permissions model
|
targetSdkVersion 25
|
||||||
//multiDexEnabled true
|
//multiDexEnabled true
|
||||||
//testInstrumentationRunner "com.android.test.runner.MultiDexTestRunner"
|
//testInstrumentationRunner "com.android.test.runner.MultiDexTestRunner"
|
||||||
}
|
}
|
||||||
@ -57,7 +57,7 @@ android {
|
|||||||
minifyEnabled false
|
minifyEnabled false
|
||||||
useProguard 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
|
minifyEnabled true
|
||||||
useProguard true
|
useProguard true
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'
|
||||||
@ -83,5 +83,6 @@ dependencies {
|
|||||||
androidTestCompile 'org.mockito:mockito-core:1.10.19'
|
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 'com.google.dexmaker:dexmaker-mockito:1.1'// Because mockito has some problems with dex environment
|
||||||
androidTestCompile 'org.skyscreamer:jsonassert:1.3.0'
|
androidTestCompile 'org.skyscreamer:jsonassert:1.3.0'
|
||||||
|
testCompile 'junit:junit:4.12'
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -206,4 +206,8 @@
|
|||||||
<string name="open">Open</string>
|
<string name="open">Open</string>
|
||||||
<string name="close">Close</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>
|
</resources>
|
||||||
|
@ -82,6 +82,7 @@ public class Device implements BaseLink.PackageReceiver {
|
|||||||
private List<String> m_supportedPlugins = new ArrayList<>();
|
private List<String> m_supportedPlugins = new ArrayList<>();
|
||||||
private final ConcurrentHashMap<String, Plugin> plugins = new ConcurrentHashMap<>();
|
private final ConcurrentHashMap<String, Plugin> plugins = new ConcurrentHashMap<>();
|
||||||
private final ConcurrentHashMap<String, Plugin> failedPlugins = 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 Map<String, ArrayList<String>> pluginsByIncomingInterface = new HashMap<>();
|
||||||
|
|
||||||
private final SharedPreferences settings;
|
private final SharedPreferences settings;
|
||||||
@ -722,6 +723,16 @@ public class Device implements BaseLink.PackageReceiver {
|
|||||||
failedPlugins.put(pluginKey, plugin);
|
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;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -812,6 +823,10 @@ public class Device implements BaseLink.PackageReceiver {
|
|||||||
return failedPlugins;
|
return failedPlugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ConcurrentHashMap<String, Plugin> getPluginsWithoutPermissions() {
|
||||||
|
return pluginsWithoutPermissions;
|
||||||
|
}
|
||||||
|
|
||||||
public void addPluginsChangedListener(PluginsChangedListener listener) {
|
public void addPluginsChangedListener(PluginsChangedListener listener) {
|
||||||
pluginsChangedListeners.add(listener);
|
pluginsChangedListeners.add(listener);
|
||||||
}
|
}
|
||||||
|
@ -20,23 +20,33 @@
|
|||||||
|
|
||||||
package org.kde.kdeconnect.Plugins;
|
package org.kde.kdeconnect.Plugins;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.graphics.drawable.Drawable;
|
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.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
|
||||||
import org.kde.kdeconnect.Device;
|
import org.kde.kdeconnect.Device;
|
||||||
import org.kde.kdeconnect.NetworkPackage;
|
import org.kde.kdeconnect.NetworkPackage;
|
||||||
|
import org.kde.kdeconnect.UserInterface.MaterialActivity;
|
||||||
import org.kde.kdeconnect.UserInterface.PluginSettingsActivity;
|
import org.kde.kdeconnect.UserInterface.PluginSettingsActivity;
|
||||||
import org.kde.kdeconnect.UserInterface.SettingsActivity;
|
import org.kde.kdeconnect.UserInterface.SettingsActivity;
|
||||||
|
import org.kde.kdeconnect_tp.R;
|
||||||
|
|
||||||
public abstract class Plugin {
|
public abstract class Plugin {
|
||||||
|
|
||||||
protected Device device;
|
protected Device device;
|
||||||
protected Context context;
|
protected Context context;
|
||||||
|
protected int permissionExplanation = R.string.permission_explanation;
|
||||||
|
|
||||||
public final void setContext(Context context, Device device) {
|
public final void setContext(Context context, Device device) {
|
||||||
this.device = device;
|
this.device = device;
|
||||||
@ -167,14 +177,6 @@ public abstract class Plugin {
|
|||||||
*/
|
*/
|
||||||
public boolean onPackageReceived(NetworkPackage np) { return false; }
|
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
|
* Should return the list of NetworkPackage types that this plugin can handle
|
||||||
*/
|
*/
|
||||||
@ -205,4 +207,70 @@ public abstract class Plugin {
|
|||||||
return b;
|
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;
|
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.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.Helpers.StorageHelper;
|
||||||
import org.kde.kdeconnect.NetworkPackage;
|
import org.kde.kdeconnect.NetworkPackage;
|
||||||
import org.kde.kdeconnect.Plugins.Plugin;
|
import org.kde.kdeconnect.Plugins.Plugin;
|
||||||
@ -38,6 +46,9 @@ public class SftpPlugin extends Plugin {
|
|||||||
|
|
||||||
private static final SimpleSftpServer server = new SimpleSftpServer();
|
private static final SimpleSftpServer server = new SimpleSftpServer();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDisplayName() {
|
public String getDisplayName() {
|
||||||
return context.getResources().getString(R.string.pref_plugin_sftp);
|
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
|
//Kept for compatibility, in case "multiPaths" is not possible or the other end does not support it
|
||||||
np2.set("path", Environment.getExternalStorageDirectory().getAbsolutePath());
|
np2.set("path", Environment.getExternalStorageDirectory().getAbsolutePath());
|
||||||
|
|
||||||
File root = new File("/");
|
List<StorageHelper.StorageInfo> storageList = StorageHelper.getStorageList();
|
||||||
if (root.canExecute() && root.canRead()) {
|
ArrayList<String> paths = new ArrayList<>();
|
||||||
List<StorageHelper.StorageInfo> storageList = StorageHelper.getStorageList();
|
ArrayList<String> pathNames = new ArrayList<>();
|
||||||
ArrayList<String> paths = new ArrayList<>();
|
|
||||||
ArrayList<String> pathNames = new ArrayList<>();
|
|
||||||
|
|
||||||
for (StorageHelper.StorageInfo storage : storageList) {
|
for (StorageHelper.StorageInfo storage : storageList) {
|
||||||
paths.add(storage.path);
|
paths.add(storage.path);
|
||||||
StringBuilder res = new StringBuilder();
|
StringBuilder res = new StringBuilder();
|
||||||
|
|
||||||
if (storageList.size() > 1) {
|
if (storageList.size() > 1) {
|
||||||
if (!storage.removable) {
|
if (!storage.removable) {
|
||||||
res.append(context.getString(R.string.sftp_internal_storage));
|
res.append(context.getString(R.string.sftp_internal_storage));
|
||||||
} else if (storage.number > 1) {
|
} else if (storage.number > 1) {
|
||||||
res.append(context.getString(R.string.sftp_sdcard_num, storage.number));
|
res.append(context.getString(R.string.sftp_sdcard_num, storage.number));
|
||||||
} else {
|
|
||||||
res.append(context.getString(R.string.sftp_sdcard));
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
res.append(context.getString(R.string.sftp_all_files));
|
res.append(context.getString(R.string.sftp_sdcard));
|
||||||
}
|
}
|
||||||
String pathName = res.toString();
|
} else {
|
||||||
if (storage.readonly) {
|
res.append(context.getString(R.string.sftp_all_files));
|
||||||
res.append(" ");
|
}
|
||||||
res.append(context.getString(R.string.sftp_readonly));
|
String pathName = res.toString();
|
||||||
}
|
if (storage.readonly) {
|
||||||
pathNames.add(res.toString());
|
res.append(" ");
|
||||||
|
res.append(context.getString(R.string.sftp_readonly));
|
||||||
|
}
|
||||||
|
pathNames.add(res.toString());
|
||||||
|
|
||||||
//Shortcut for users that only want to browse camera pictures
|
//Shortcut for users that only want to browse camera pictures
|
||||||
String dcim = storage.path + "/DCIM/Camera";
|
String dcim = storage.path + "/DCIM/Camera";
|
||||||
if (new File(dcim).exists()) {
|
if (new File(dcim).exists()) {
|
||||||
paths.add(dcim);
|
paths.add(dcim);
|
||||||
if (storageList.size() > 1) {
|
if (storageList.size() > 1) {
|
||||||
pathNames.add(context.getString(R.string.sftp_camera) + "(" + pathName + ")");
|
pathNames.add(context.getString(R.string.sftp_camera) + "(" + pathName + ")");
|
||||||
} else {
|
} else {
|
||||||
pathNames.add(context.getString(R.string.sftp_camera));
|
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);
|
device.sendPackage(np2);
|
||||||
@ -129,6 +138,12 @@ public class SftpPlugin extends Plugin {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getRequiredPermissions() {
|
||||||
|
String[] perms = {Manifest.permission.READ_EXTERNAL_STORAGE};
|
||||||
|
return perms;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getSupportedPackageTypes() {
|
public String[] getSupportedPackageTypes() {
|
||||||
return new String[]{PACKAGE_TYPE_SFTP_REQUEST};
|
return new String[]{PACKAGE_TYPE_SFTP_REQUEST};
|
||||||
|
@ -20,7 +20,9 @@
|
|||||||
|
|
||||||
package org.kde.kdeconnect.Plugins.SharePlugin;
|
package org.kde.kdeconnect.Plugins.SharePlugin;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.app.AlertDialog;
|
||||||
import android.app.DownloadManager;
|
import android.app.DownloadManager;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
@ -30,6 +32,7 @@ import android.content.ContentResolver;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
@ -110,6 +113,17 @@ public class SharePlugin extends Plugin {
|
|||||||
|
|
||||||
Log.i("SharePlugin", "hasPayload");
|
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 InputStream input = np.getPayload();
|
||||||
final long fileLength = np.getPayloadSize();
|
final long fileLength = np.getPayloadSize();
|
||||||
final String originalFilename = np.getString("filename", Long.toString(System.currentTimeMillis()));
|
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 OutputStream destinationOutput = context.getContentResolver().openOutputStream(destinationDocument.getUri());
|
||||||
final Uri destinationUri = destinationDocument.getUri();
|
final Uri destinationUri = destinationDocument.getUri();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
final int notificationId = (int)System.currentTimeMillis();
|
final int notificationId = (int)System.currentTimeMillis();
|
||||||
Resources res = context.getResources();
|
Resources res = context.getResources();
|
||||||
final NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
|
final NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
|
||||||
@ -192,20 +208,27 @@ public class SharePlugin extends Plugin {
|
|||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.setProgress(100,100,false)
|
.setProgress(100,100,false)
|
||||||
.setOngoing(false);
|
.setOngoing(false);
|
||||||
if (successful) {
|
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
// Nougat requires share:// URIs instead of file:// URIs
|
||||||
intent.setDataAndType(destinationUri, mimeType);
|
// TODO use FileProvider for >Nougat
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
if(Build.VERSION.SDK_INT < 24) {
|
||||||
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
|
if (successful) {
|
||||||
stackBuilder.addNextIntent(intent);
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
|
intent.setDataAndType(destinationUri, mimeType);
|
||||||
builder.setContentText(res.getString(R.string.received_file_text, destinationDocument.getName()))
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
.setContentIntent(resultPendingIntent);
|
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);
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
if (prefs.getBoolean("share_notification_preference", true)) {
|
if (prefs.getBoolean("share_notification_preference", true)) {
|
||||||
builder.setDefaults(Notification.DEFAULT_ALL);
|
builder.setDefaults(Notification.DEFAULT_ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationHelper.notifyCompat(notificationManager, notificationId, builder.build());
|
NotificationHelper.notifyCompat(notificationManager, notificationId, builder.build());
|
||||||
|
|
||||||
if (successful) {
|
if (successful) {
|
||||||
@ -409,5 +432,10 @@ public class SharePlugin extends Plugin {
|
|||||||
return new String[]{PACKAGE_TYPE_SHARE_REQUEST};
|
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;
|
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.telephony.SmsManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@ -43,11 +47,6 @@ public class TelepathyPlugin extends Plugin {
|
|||||||
return context.getResources().getString(R.string.pref_plugin_telepathy_desc);
|
return context.getResources().getString(R.string.pref_plugin_telepathy_desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreate() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
}
|
}
|
||||||
@ -63,8 +62,18 @@ public class TelepathyPlugin extends Plugin {
|
|||||||
String phoneNo = np.getString("phoneNumber");
|
String phoneNo = np.getString("phoneNumber");
|
||||||
String sms = np.getString("messageBody");
|
String sms = np.getString("messageBody");
|
||||||
try {
|
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
|
//TODO: Notify other end
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
//TODO: Notify other end
|
//TODO: Notify other end
|
||||||
@ -176,4 +185,8 @@ public class TelepathyPlugin extends Plugin {
|
|||||||
return new String[]{};
|
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;
|
package org.kde.kdeconnect.Plugins.TelephonyPlugin;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.telephony.SmsMessage;
|
import android.telephony.SmsMessage;
|
||||||
import android.telephony.TelephonyManager;
|
import android.telephony.TelephonyManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@ -102,32 +105,40 @@ public class TelephonyPlugin extends Plugin {
|
|||||||
|
|
||||||
//Log.e("TelephonyPlugin", "callBroadcastReceived");
|
//Log.e("TelephonyPlugin", "callBroadcastReceived");
|
||||||
|
|
||||||
Map<String, String> contactInfo = ContactsHelper.phoneNumberLookup(context, phoneNumber);
|
|
||||||
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_TELEPHONY);
|
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_TELEPHONY);
|
||||||
|
|
||||||
if (phoneNumber != null) {
|
int permissionCheck = ContextCompat.checkSelfPermission(context,
|
||||||
np.set("phoneNumber", phoneNumber);
|
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 {
|
} else {
|
||||||
np.set("contactName", phoneNumber);
|
np.set("contactName", phoneNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contactInfo.containsKey("photoID")) {
|
if (phoneNumber != null) {
|
||||||
String photoUri = contactInfo.get("photoID");
|
np.set("phoneNumber", phoneNumber);
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
@ -208,18 +219,26 @@ public class TelephonyPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String phoneNumber = message.getOriginatingAddress();
|
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) {
|
if (phoneNumber != null) {
|
||||||
np.set("phoneNumber", phoneNumber);
|
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);
|
device.sendPackage(np);
|
||||||
}
|
}
|
||||||
@ -267,4 +286,9 @@ public class TelephonyPlugin extends Plugin {
|
|||||||
return new String[]{PACKAGE_TYPE_TELEPHONY};
|
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;
|
Device device;
|
||||||
|
|
||||||
TextView errorHeader;
|
TextView errorHeader;
|
||||||
|
TextView noPermissionsHeader;
|
||||||
|
|
||||||
MaterialActivity mActivity;
|
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);
|
ListView buttonsList = (ListView) rootView.findViewById(R.id.buttons_list);
|
||||||
ListAdapter adapter = new ListAdapter(mActivity, items);
|
ListAdapter adapter = new ListAdapter(mActivity, items);
|
||||||
buttonsList.setAdapter(adapter);
|
buttonsList.setAdapter(adapter);
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
package org.kde.kdeconnect.UserInterface;
|
package org.kde.kdeconnect.UserInterface;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.design.widget.NavigationView;
|
import android.support.design.widget.NavigationView;
|
||||||
|
import android.support.v4.app.ActivityCompat;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.view.GravityCompat;
|
import android.support.v4.view.GravityCompat;
|
||||||
import android.support.v4.widget.DrawerLayout;
|
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() {
|
public void renameDevice() {
|
||||||
final TextView nameView = (TextView) mNavigationView.findViewById(R.id.device_name);
|
final TextView nameView = (TextView) mNavigationView.findViewById(R.id.device_name);
|
||||||
final EditText deviceNameEdit = new EditText(MaterialActivity.this);
|
final EditText deviceNameEdit = new EditText(MaterialActivity.this);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user