mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-08-29 05:07:40 +00:00
[Android] Add photo plugin
Summary: When a request arrive open the camera and send the taken pic back. Reviewers: #kde_connect, broulik, albertvaka Reviewed By: #kde_connect, albertvaka Subscribers: albertvaka, ngraham, kdeconnect Tags: #kde_connect Differential Revision: https://phabricator.kde.org/D18142
This commit is contained in:
parent
085fb1025b
commit
43ae02d68f
@ -257,6 +257,7 @@
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="org.kde.kdeconnect.UserInterface.DeviceSettingsActivity" />
|
||||
</activity>
|
||||
<activity android:name="org.kde.kdeconnect.Plugins.PhotoPlugin.PhotoActivity" />
|
||||
|
||||
</application>
|
||||
|
||||
|
13
res/drawable/ic_camera.xml
Normal file
13
res/drawable/ic_camera.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:width="24dp"
|
||||
android:height="24dp">
|
||||
<path
|
||||
android:pathData="M15.2 12A3.2 3.2 0 0 1 12 15.2 3.2 3.2 0 0 1 8.8 12 3.2 3.2 0 0 1 12 8.8 3.2 3.2 0 0 1 15.2 12Z"
|
||||
android:fillColor="#000000" />
|
||||
<path
|
||||
android:pathData="M9 2L7.17 4 4 4C2.9 4 2 4.9 2 6l0 12c0 1.1 0.9 2 2 2l16 0c1.1 0 2 -0.9 2 -2L22 6C22 4.9 21.1 4 20 4L16.83 4 15 2 9 2Zm3 15C9.24 17 7 14.76 7 12 7 9.24 9.24 7 12 7c2.76 0 5 2.24 5 5 0 2.76 -2.24 5 -5 5z"
|
||||
android:fillColor="#000000" />
|
||||
</vector>
|
@ -326,6 +326,8 @@
|
||||
<string name="block_contents">Block contents of notifications</string>
|
||||
<string name="block_images">Block images in notifications</string>
|
||||
<string name="notification_channel_receivenotification">Notifications from other devices</string>
|
||||
<string name="take_picture">Take picture</string>
|
||||
<string name="plugin_photo_desc">Take a picture and send it to another device</string>
|
||||
|
||||
<string name="findmyphone_preference_key_ringtone" translatable="false">findmyphone_ringtone</string>
|
||||
</resources>
|
||||
|
@ -3,4 +3,5 @@
|
||||
<external-path
|
||||
name="external_files"
|
||||
path="." />
|
||||
<external-path name="images" path="Android/data/org.kde.kdeconnect_tp/files/Pictures" />
|
||||
</paths>
|
||||
|
@ -20,10 +20,18 @@
|
||||
|
||||
package org.kde.kdeconnect.Helpers;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class FilesHelper {
|
||||
|
||||
@ -100,4 +108,82 @@ public class FilesHelper {
|
||||
public static void LogOpenFileCount() {
|
||||
Log.e("KDE/FileCount", "" + GetOpenFileCount());
|
||||
}
|
||||
|
||||
|
||||
//Create the network package from the URI
|
||||
public static NetworkPacket uriToNetworkPacket(final Context context, final Uri uri, String type) {
|
||||
|
||||
try {
|
||||
|
||||
ContentResolver cr = context.getContentResolver();
|
||||
InputStream inputStream = cr.openInputStream(uri);
|
||||
|
||||
NetworkPacket np = new NetworkPacket(type);
|
||||
long size = -1;
|
||||
|
||||
if (uri.getScheme().equals("file")) {
|
||||
// file:// is a non media uri, so we cannot query the ContentProvider
|
||||
|
||||
np.set("filename", uri.getLastPathSegment());
|
||||
|
||||
try {
|
||||
size = new File(uri.getPath()).length();
|
||||
} catch (Exception e) {
|
||||
Log.e("SendFileActivity", "Could not obtain file size");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
} else {
|
||||
// Probably a content:// uri, so we query the Media content provider
|
||||
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
String[] proj = {MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.SIZE, MediaStore.MediaColumns.DISPLAY_NAME};
|
||||
cursor = cr.query(uri, proj, null, null, null);
|
||||
int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
|
||||
cursor.moveToFirst();
|
||||
String path = cursor.getString(column_index);
|
||||
np.set("filename", Uri.parse(path).getLastPathSegment());
|
||||
size = new File(path).length();
|
||||
} catch (Exception unused) {
|
||||
|
||||
Log.w("SendFileActivity", "Could not resolve media to a file, trying to get info as media");
|
||||
|
||||
try {
|
||||
int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME);
|
||||
cursor.moveToFirst();
|
||||
String name = cursor.getString(column_index);
|
||||
np.set("filename", name);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("SendFileActivity", "Could not obtain file name");
|
||||
}
|
||||
|
||||
try {
|
||||
int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.SIZE);
|
||||
cursor.moveToFirst();
|
||||
//For some reason this size can differ from the actual file size!
|
||||
size = cursor.getInt(column_index);
|
||||
} catch (Exception e) {
|
||||
Log.e("SendFileActivity", "Could not obtain file size");
|
||||
e.printStackTrace();
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
cursor.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
np.setPayload(new NetworkPacket.Payload(inputStream, size));
|
||||
|
||||
return np;
|
||||
} catch (Exception e) {
|
||||
Log.e("SendFileActivity", "Exception creating network packet", e);
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,72 @@
|
||||
package org.kde.kdeconnect.Plugins.PhotoPlugin;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
public class PhotoActivity extends AppCompatActivity {
|
||||
|
||||
private Uri photoURI;
|
||||
private PhotoPlugin plugin;
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
|
||||
BackgroundService.runWithPlugin(this, getIntent().getStringExtra("deviceId"), PhotoPlugin.class, plugin -> {
|
||||
this.plugin = plugin;
|
||||
});
|
||||
|
||||
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
|
||||
File photoFile = null;
|
||||
try {
|
||||
photoFile = createImageFile();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
// Continue only if the File was successfully created
|
||||
if (photoFile != null) {
|
||||
photoURI = FileProvider.getUriForFile(this,
|
||||
"org.kde.kdeconnect_tp.fileprovider",
|
||||
photoFile);
|
||||
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
|
||||
startActivityForResult(takePictureIntent, 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private File createImageFile() throws IOException {
|
||||
// Create an image file name
|
||||
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
|
||||
String imageFileName = "JPEG_" + timeStamp + "_";
|
||||
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
|
||||
return File.createTempFile(
|
||||
imageFileName, /* prefix */
|
||||
".jpg", /* suffix */
|
||||
storageDir /* directory */
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if (resultCode == -1) {
|
||||
plugin.sendPhoto(photoURI);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
}
|
90
src/org/kde/kdeconnect/Plugins/PhotoPlugin/PhotoPlugin.java
Normal file
90
src/org/kde/kdeconnect/Plugins/PhotoPlugin/PhotoPlugin.java
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2019 Nicolas Fella <nicolas.fella@gmx.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License or (at your option) version 3 or any later version
|
||||
* accepted by the membership of KDE e.V. (or its successor approved
|
||||
* by the membership of KDE e.V.), which shall act as a proxy
|
||||
* defined in Section 14 of version 3 of the license.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.kde.kdeconnect.Plugins.PhotoPlugin;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
|
||||
import org.kde.kdeconnect.Helpers.FilesHelper;
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
import org.kde.kdeconnect.Plugins.Plugin;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
|
||||
public class PhotoPlugin extends Plugin {
|
||||
|
||||
private final static String PACKET_TYPE_PHOTO = "kdeconnect.photo";
|
||||
private final static String PACKET_TYPE_PHOTO_REQUEST = "kdeconnect.photo.request";
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return context.getResources().getString(R.string.take_picture);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return context.getResources().getString(R.string.plugin_photo_desc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPacketReceived(NetworkPacket np) {
|
||||
Intent intent = new Intent(context, PhotoActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.putExtra("deviceId", device.getDeviceId());
|
||||
context.startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
|
||||
void sendPhoto(Uri uri) {
|
||||
NetworkPacket np = FilesHelper.uriToNetworkPacket(context, uri, PACKET_TYPE_PHOTO);
|
||||
if (np != null) {
|
||||
device.sendPacket(np);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMainActivity() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean displayInContextMenu() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedPacketTypes() {
|
||||
return new String[]{PACKET_TYPE_PHOTO_REQUEST};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getOutgoingPacketTypes() {
|
||||
return new String[]{PACKET_TYPE_PHOTO};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getIcon() {
|
||||
return ContextCompat.getDrawable(context, R.drawable.ic_camera);
|
||||
}
|
||||
}
|
@ -15,8 +15,8 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.kde.kdeconnect.Plugins.PingPlugin;
|
||||
|
||||
|
@ -27,7 +27,6 @@ import android.util.Log;
|
||||
import org.atteo.classindex.ClassIndex;
|
||||
import org.atteo.classindex.IndexAnnotated;
|
||||
import org.kde.kdeconnect.Device;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
@ -40,6 +40,7 @@ import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.kde.kdeconnect.Helpers.FilesHelper;
|
||||
import org.kde.kdeconnect.Helpers.NotificationHelper;
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
import org.kde.kdeconnect.Plugins.Plugin;
|
||||
@ -240,7 +241,7 @@ public class SharePlugin extends Plugin {
|
||||
//Read all the data early, as we only have permissions to do it while the activity is alive
|
||||
final ArrayList<NetworkPacket> toSend = new ArrayList<>();
|
||||
for (Uri uri : uriList) {
|
||||
NetworkPacket np = uriToNetworkPacket(context, uri);
|
||||
NetworkPacket np = FilesHelper.uriToNetworkPacket(context, uri, PACKET_TYPE_SHARE_REQUEST);
|
||||
|
||||
if (np != null) {
|
||||
toSend.add(np);
|
||||
@ -268,83 +269,6 @@ public class SharePlugin extends Plugin {
|
||||
|
||||
}
|
||||
|
||||
//Create the network package from the URI
|
||||
private static NetworkPacket uriToNetworkPacket(final Context context, final Uri uri) {
|
||||
|
||||
try {
|
||||
|
||||
ContentResolver cr = context.getContentResolver();
|
||||
InputStream inputStream = cr.openInputStream(uri);
|
||||
|
||||
NetworkPacket np = new NetworkPacket(PACKET_TYPE_SHARE_REQUEST);
|
||||
long size = -1;
|
||||
|
||||
if (uri.getScheme().equals("file")) {
|
||||
// file:// is a non media uri, so we cannot query the ContentProvider
|
||||
|
||||
np.set("filename", uri.getLastPathSegment());
|
||||
|
||||
try {
|
||||
size = new File(uri.getPath()).length();
|
||||
} catch (Exception e) {
|
||||
Log.e("SendFileActivity", "Could not obtain file size");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
} else {
|
||||
// Probably a content:// uri, so we query the Media content provider
|
||||
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
String[] proj = {MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.SIZE, MediaStore.MediaColumns.DISPLAY_NAME};
|
||||
cursor = cr.query(uri, proj, null, null, null);
|
||||
int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
|
||||
cursor.moveToFirst();
|
||||
String path = cursor.getString(column_index);
|
||||
np.set("filename", Uri.parse(path).getLastPathSegment());
|
||||
size = new File(path).length();
|
||||
} catch (Exception unused) {
|
||||
|
||||
Log.w("SendFileActivity", "Could not resolve media to a file, trying to get info as media");
|
||||
|
||||
try {
|
||||
int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME);
|
||||
cursor.moveToFirst();
|
||||
String name = cursor.getString(column_index);
|
||||
np.set("filename", name);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("SendFileActivity", "Could not obtain file name");
|
||||
}
|
||||
|
||||
try {
|
||||
int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.SIZE);
|
||||
cursor.moveToFirst();
|
||||
//For some reason this size can differ from the actual file size!
|
||||
size = cursor.getInt(column_index);
|
||||
} catch (Exception e) {
|
||||
Log.e("SendFileActivity", "Could not obtain file size");
|
||||
e.printStackTrace();
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
cursor.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
np.setPayload(new NetworkPacket.Payload(inputStream, size));
|
||||
|
||||
return np;
|
||||
} catch (Exception e) {
|
||||
Log.e("SendFileActivity", "Exception sending files");
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void share(Intent intent) {
|
||||
Bundle extras = intent.getExtras();
|
||||
if (extras != null) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user