mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-08-22 09:58:08 +00:00
Direct Share
Summary: Support Android 6 direct share. BUG:362266 BackgroundService is a SIngleton now. This should only be used if strictly necessary. Test Plan: Pick a random app with a share button, look for device entries in Sharing options (might not be there because other sharing options have higher priority) Reviewers: #kde_connect, albertvaka Reviewed By: #kde_connect, albertvaka Subscribers: albertvaka, #kde_connect Tags: #kde_connect Differential Revision: https://phabricator.kde.org/D6743
This commit is contained in:
parent
b68c159275
commit
bea90521c5
3
.arcconfig
Normal file
3
.arcconfig
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"phabricator.uri" : "https://phabricator.kde.org/project/profile/159/"
|
||||
}
|
@ -35,8 +35,8 @@
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@drawable/icon"
|
||||
android:supportsRtl="true"
|
||||
android:label="KDE Connect"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/KdeConnectTheme"
|
||||
>
|
||||
|
||||
@ -140,9 +140,9 @@
|
||||
|
||||
<activity
|
||||
android:name="org.kde.kdeconnect.Plugins.FindMyPhonePlugin.FindMyPhoneActivity"
|
||||
android:label="@string/findmyphone_title"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:excludeFromRecents="true"
|
||||
android:label="@string/findmyphone_title"
|
||||
android:launchMode="singleInstance">
|
||||
</activity>
|
||||
|
||||
@ -194,6 +194,9 @@
|
||||
|
||||
<data android:mimeType="*/*" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.service.chooser.chooser_target_service"
|
||||
android:value="org.kde.kdeconnect.Plugins.SharePlugin.ShareChooserTargetService" />
|
||||
</activity>
|
||||
<service
|
||||
android:name="org.kde.kdeconnect.Plugins.NotificationsPlugin.NotificationReceiver"
|
||||
@ -202,6 +205,13 @@
|
||||
<action android:name="android.service.notification.NotificationListenerService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<service
|
||||
android:name="org.kde.kdeconnect.Plugins.SharePlugin.ShareChooserTargetService"
|
||||
android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.chooser.ChooserTargetService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<activity
|
||||
android:name="org.kde.kdeconnect.Plugins.NotificationsPlugin.NotificationFilterActivity"
|
||||
|
@ -46,6 +46,8 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class BackgroundService extends Service {
|
||||
|
||||
private static BackgroundService instance;
|
||||
|
||||
public interface DeviceListChangedCallback {
|
||||
void onDeviceListChanged();
|
||||
}
|
||||
@ -58,6 +60,10 @@ public class BackgroundService extends Service {
|
||||
|
||||
private final HashSet<Object> discoveryModeAcquisitions = new HashSet<>();
|
||||
|
||||
public static BackgroundService getInstance(){
|
||||
return instance;
|
||||
}
|
||||
|
||||
public boolean acquireDiscoveryMode(Object key) {
|
||||
boolean wasEmpty = discoveryModeAcquisitions.isEmpty();
|
||||
discoveryModeAcquisitions.add(key);
|
||||
@ -250,6 +256,8 @@ public class BackgroundService extends Service {
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
instance = this;
|
||||
|
||||
// Register screen on listener
|
||||
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
|
||||
registerReceiver(new KdeConnectBroadcastReceiver(), filter);
|
||||
|
@ -33,6 +33,8 @@ import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.NetworkPackage;
|
||||
@ -130,57 +132,7 @@ public class ShareActivity extends AppCompatActivity {
|
||||
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
|
||||
Device device = devicesList.get(i - 1); //NOTE: -1 because of the title!
|
||||
|
||||
Bundle extras = intent.getExtras();
|
||||
if (extras != null) {
|
||||
if (extras.containsKey(Intent.EXTRA_STREAM)) {
|
||||
|
||||
try {
|
||||
|
||||
ArrayList<Uri> uriList;
|
||||
if (!Intent.ACTION_SEND.equals(intent.getAction())) {
|
||||
uriList = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
|
||||
} else {
|
||||
Uri uri = extras.getParcelable(Intent.EXTRA_STREAM);
|
||||
uriList = new ArrayList<>();
|
||||
uriList.add(uri);
|
||||
}
|
||||
|
||||
SharePlugin.queuedSendUriList(getApplicationContext(), device, uriList);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e("ShareActivity", "Exception");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
} else if (extras.containsKey(Intent.EXTRA_TEXT)) {
|
||||
String text = extras.getString(Intent.EXTRA_TEXT);
|
||||
String subject = extras.getString(Intent.EXTRA_SUBJECT);
|
||||
|
||||
//Hack: Detect shared youtube videos, so we can open them in the browser instead of as text
|
||||
if (subject != null && subject.endsWith("YouTube")) {
|
||||
int index = text.indexOf(": http://youtu.be/");
|
||||
if (index > 0) {
|
||||
text = text.substring(index + 2); //Skip ": "
|
||||
}
|
||||
}
|
||||
|
||||
boolean isUrl;
|
||||
try {
|
||||
new URL(text);
|
||||
isUrl = true;
|
||||
} catch (Exception e) {
|
||||
isUrl = false;
|
||||
}
|
||||
NetworkPackage np = new NetworkPackage(SharePlugin.PACKAGE_TYPE_SHARE_REQUEST);
|
||||
if (isUrl) {
|
||||
np.set("url", text);
|
||||
} else {
|
||||
np.set("text", text);
|
||||
}
|
||||
device.sendPackage(np);
|
||||
}
|
||||
}
|
||||
SharePlugin.share(intent, device);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
@ -215,22 +167,44 @@ public class ShareActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
BackgroundService.addGuiInUseCounter(this);
|
||||
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
|
||||
@Override
|
||||
public void onServiceStart(BackgroundService service) {
|
||||
service.onNetworkChange();
|
||||
service.addDeviceListChangedCallback("ShareActivity", new BackgroundService.DeviceListChangedCallback() {
|
||||
@Override
|
||||
public void onDeviceListChanged() {
|
||||
updateComputerList();
|
||||
|
||||
final Intent intent = getIntent();
|
||||
final String deviceId = intent.getStringExtra("deviceId");
|
||||
|
||||
if (deviceId!=null) {
|
||||
|
||||
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback(){
|
||||
|
||||
@Override
|
||||
public void onServiceStart(BackgroundService service) {
|
||||
Log.d("DirectShare", "sharing to "+service.getDevice(deviceId).getName());
|
||||
Device device = service.getDevice(deviceId);
|
||||
if (device.isReachable() && device.isPaired()) {
|
||||
SharePlugin.share(intent, device);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
updateComputerList();
|
||||
finish();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
||||
BackgroundService.addGuiInUseCounter(this);
|
||||
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
|
||||
@Override
|
||||
public void onServiceStart(BackgroundService service) {
|
||||
service.onNetworkChange();
|
||||
service.addDeviceListChangedCallback("ShareActivity", new BackgroundService.DeviceListChangedCallback() {
|
||||
@Override
|
||||
public void onDeviceListChanged() {
|
||||
updateComputerList();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
updateComputerList();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
|
||||
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2017 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.SharePlugin;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.ComponentName;
|
||||
import android.content.IntentFilter;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.Bundle;
|
||||
import android.service.chooser.ChooserTarget;
|
||||
import android.service.chooser.ChooserTargetService;
|
||||
import android.util.Log;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@TargetApi(23)
|
||||
public class ShareChooserTargetService extends ChooserTargetService {
|
||||
@Override
|
||||
public List<ChooserTarget> onGetChooserTargets(ComponentName targetActivityName, IntentFilter matchedFilter) {
|
||||
Log.d("DirectShare", "invoked");
|
||||
final List<ChooserTarget> targets = new ArrayList<>();
|
||||
for(Device d: BackgroundService.getInstance().getDevices().values()){
|
||||
if(d.isReachable() && d.isPaired()) {
|
||||
Log.d("DirectShare", d.getName());
|
||||
final String targetName = d.getName();
|
||||
final Icon targetIcon = Icon.createWithResource(this, R.drawable.icon);
|
||||
final float targetRanking = 1;
|
||||
final ComponentName targetComponentName = new ComponentName(getPackageName(),
|
||||
ShareActivity.class.getCanonicalName());
|
||||
final Bundle targetExtras = new Bundle();
|
||||
targetExtras.putString("deviceId", d.getDeviceId());
|
||||
targets.add(new ChooserTarget(
|
||||
targetName, targetIcon, targetRanking, targetComponentName, targetExtras
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return targets;
|
||||
}
|
||||
}
|
@ -35,6 +35,7 @@ import android.database.Cursor;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.MediaStore;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.app.TaskStackBuilder;
|
||||
@ -56,6 +57,7 @@ import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class SharePlugin extends Plugin {
|
||||
@ -399,6 +401,59 @@ public class SharePlugin extends Plugin {
|
||||
}
|
||||
}
|
||||
|
||||
public static void share(Intent intent, Device device){
|
||||
Bundle extras = intent.getExtras();
|
||||
if (extras != null) {
|
||||
if (extras.containsKey(Intent.EXTRA_STREAM)) {
|
||||
|
||||
try {
|
||||
|
||||
ArrayList<Uri> uriList;
|
||||
if (!Intent.ACTION_SEND.equals(intent.getAction())) {
|
||||
uriList = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
|
||||
} else {
|
||||
Uri uri = extras.getParcelable(Intent.EXTRA_STREAM);
|
||||
uriList = new ArrayList<>();
|
||||
uriList.add(uri);
|
||||
}
|
||||
|
||||
SharePlugin.queuedSendUriList(device.getContext(), device, uriList);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e("ShareActivity", "Exception");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
} else if (extras.containsKey(Intent.EXTRA_TEXT)) {
|
||||
String text = extras.getString(Intent.EXTRA_TEXT);
|
||||
String subject = extras.getString(Intent.EXTRA_SUBJECT);
|
||||
|
||||
//Hack: Detect shared youtube videos, so we can open them in the browser instead of as text
|
||||
if (subject != null && subject.endsWith("YouTube")) {
|
||||
int index = text.indexOf(": http://youtu.be/");
|
||||
if (index > 0) {
|
||||
text = text.substring(index + 2); //Skip ": "
|
||||
}
|
||||
}
|
||||
|
||||
boolean isUrl;
|
||||
try {
|
||||
new URL(text);
|
||||
isUrl = true;
|
||||
} catch (Exception e) {
|
||||
isUrl = false;
|
||||
}
|
||||
NetworkPackage np = new NetworkPackage(SharePlugin.PACKAGE_TYPE_SHARE_REQUEST);
|
||||
if (isUrl) {
|
||||
np.set("url", text);
|
||||
} else {
|
||||
np.set("text", text);
|
||||
}
|
||||
device.sendPackage(np);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedPackageTypes() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user