implement direct sharing in android 6.0. fixes #1321

This commit is contained in:
Daniel Gultsch 2015-12-06 11:55:37 +01:00
parent 164d341915
commit 739a2d609d
5 changed files with 165 additions and 25 deletions

View file

@ -48,11 +48,11 @@ dependencies {
android { android {
compileSdkVersion 23 compileSdkVersion 23
buildToolsVersion "23.0.1" buildToolsVersion "23.0.2"
defaultConfig { defaultConfig {
minSdkVersion 14 minSdkVersion 14
targetSdkVersion 21 targetSdkVersion 23
versionCode 109 versionCode 109
versionName "1.8.0" versionName "1.8.0"
project.ext.set(archivesBaseName, archivesBaseName + "-" + versionName); project.ext.set(archivesBaseName, archivesBaseName + "-" + versionName);

View file

@ -136,6 +136,9 @@
<data android:mimeType="image/*"/> <data android:mimeType="image/*"/>
</intent-filter> </intent-filter>
<meta-data
android:name="android.service.chooser.chooser_target_service"
android:value=".services.ContactChooserTargetService" />
</activity> </activity>
<activity <activity
android:name=".ui.TrustKeysActivity" android:name=".ui.TrustKeysActivity"
@ -155,6 +158,12 @@
</activity> </activity>
<activity android:name="com.soundcloud.android.crop.CropImageActivity" /> <activity android:name="com.soundcloud.android.crop.CropImageActivity" />
<service android:name=".services.ExportLogsService"/> <service android:name=".services.ExportLogsService"/>
<service android:name=".services.ContactChooserTargetService"
android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE">
<intent-filter>
<action android:name="android.service.chooser.ChooserTargetService" />
</intent-filter>
</service>
</application> </application>
</manifest> </manifest>

View file

@ -0,0 +1,87 @@
package eu.siacs.conversations.services;
import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.SystemClock;
import android.service.chooser.ChooserTarget;
import android.service.chooser.ChooserTargetService;
import android.util.DisplayMetrics;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.ui.ShareWithActivity;
@TargetApi(Build.VERSION_CODES.M)
public class ContactChooserTargetService extends ChooserTargetService implements ServiceConnection {
private final Object lock = new Object();
private XmppConnectionService mXmppConnectionService;
private final int MAX_TARGETS = 5;
@Override
public List<ChooserTarget> onGetChooserTargets(ComponentName targetActivityName, IntentFilter matchedFilter) {
Intent intent = new Intent(this, XmppConnectionService.class);
intent.setAction("contact_chooser");
startService(intent);
bindService(intent, this, Context.BIND_AUTO_CREATE);
ArrayList<ChooserTarget> chooserTargets = new ArrayList<>();
try {
waitForService();
final ArrayList<Conversation> conversations = new ArrayList<>();
if (!mXmppConnectionService.areMessagesInitialized()) {
return chooserTargets;
}
mXmppConnectionService.populateWithOrderedConversations(conversations, false);
final ComponentName componentName = new ComponentName(this, ShareWithActivity.class);
final int pixel = (int) (48 * getResources().getDisplayMetrics().density);
for(int i = 0; i < Math.min(conversations.size(),MAX_TARGETS); ++i) {
final Conversation conversation = conversations.get(i);
final String name = conversation.getName();
final Icon icon = Icon.createWithBitmap(mXmppConnectionService.getAvatarService().get(conversation, pixel));
final float score = (1.0f / MAX_TARGETS) * i;
final Bundle extras = new Bundle();
extras.putString("uuid", conversation.getUuid());
chooserTargets.add(new ChooserTarget(name, icon, score, componentName, extras));
}
} catch (InterruptedException e) {
}
unbindService(this);
return chooserTargets;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
XmppConnectionService.XmppConnectionBinder binder = (XmppConnectionService.XmppConnectionBinder) service;
mXmppConnectionService = binder.getService();
synchronized (this.lock) {
lock.notifyAll();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mXmppConnectionService = null;
}
private void waitForService() throws InterruptedException {
if (mXmppConnectionService == null) {
synchronized (this.lock) {
lock.wait();
}
}
}
}

View file

@ -4,6 +4,7 @@ import android.app.PendingIntent;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
@ -17,6 +18,7 @@ import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
@ -33,6 +35,7 @@ public class ShareWithActivity extends XmppActivity {
public String account; public String account;
public String contact; public String contact;
public String text; public String text;
public String uuid;
} }
private Share share; private Share share;
@ -40,6 +43,7 @@ public class ShareWithActivity extends XmppActivity {
private static final int REQUEST_START_NEW_CONVERSATION = 0x0501; private static final int REQUEST_START_NEW_CONVERSATION = 0x0501;
private ListView mListView; private ListView mListView;
private List<Conversation> mConversations = new ArrayList<>(); private List<Conversation> mConversations = new ArrayList<>();
private Toast mToast;
private UiCallback<Message> attachFileCallback = new UiCallback<Message>() { private UiCallback<Message> attachFileCallback = new UiCallback<Message>() {
@ -50,8 +54,22 @@ public class ShareWithActivity extends XmppActivity {
} }
@Override @Override
public void success(Message message) { public void success(final Message message) {
xmppConnectionService.sendMessage(message); xmppConnectionService.sendMessage(message);
runOnUiThread(new Runnable() {
@Override
public void run() {
if (mToast != null) {
mToast.cancel();
}
if (share.uuid != null) {
mToast = Toast.makeText(getApplicationContext(),
getString(share.image ? R.string.shared_image_with_x : R.string.shared_file_with_x,message.getConversation().getName()),
Toast.LENGTH_SHORT);
mToast.show();
}
}
});
} }
@Override @Override
@ -128,6 +146,8 @@ public class ShareWithActivity extends XmppActivity {
return; return;
} }
final String type = intent.getType(); final String type = intent.getType();
Log.d(Config.LOGTAG, "action: "+intent.getAction()+ ", type:"+type);
share.uuid = intent.getStringExtra("uuid");
if (Intent.ACTION_SEND.equals(intent.getAction())) { if (Intent.ACTION_SEND.equals(intent.getAction())) {
final Uri uri = getIntent().getParcelableExtra(Intent.EXTRA_STREAM); final Uri uri = getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
if (type != null && uri != null && !type.equalsIgnoreCase("text/plain")) { if (type != null && uri != null && !type.equalsIgnoreCase("text/plain")) {
@ -146,7 +166,11 @@ public class ShareWithActivity extends XmppActivity {
this.share.uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); this.share.uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
} }
if (xmppConnectionServiceBound) { if (xmppConnectionServiceBound) {
xmppConnectionService.populateWithOrderedConversations(mConversations, this.share.uris.size() == 0); if (share.uuid != null) {
share();
} else {
xmppConnectionService.populateWithOrderedConversations(mConversations, this.share.uris.size() == 0);
}
} }
} }
@ -163,7 +187,7 @@ public class ShareWithActivity extends XmppActivity {
@Override @Override
void onBackendConnected() { void onBackendConnected() {
if (xmppConnectionServiceBound && share != null if (xmppConnectionServiceBound && share != null
&& share.contact != null && share.account != null) { && ((share.contact != null && share.account != null) || share.uuid != null)) {
share(); share();
return; return;
} }
@ -172,28 +196,41 @@ public class ShareWithActivity extends XmppActivity {
} }
private void share() { private void share() {
Account account;
try {
account = xmppConnectionService.findAccountByJid(Jid.fromString(share.account));
} catch (final InvalidJidException e) {
account = null;
}
if (account == null) {
return;
}
final Conversation conversation; final Conversation conversation;
try { if (share.uuid != null) {
conversation = xmppConnectionService conversation = xmppConnectionService.findConversationByUuid(share.uuid);
.findOrCreateConversation(account, Jid.fromString(share.contact), false); if (conversation == null) {
} catch (final InvalidJidException e) { return;
return; }
}else{
Account account;
try {
account = xmppConnectionService.findAccountByJid(Jid.fromString(share.account));
} catch (final InvalidJidException e) {
account = null;
}
if (account == null) {
return;
}
try {
conversation = xmppConnectionService
.findOrCreateConversation(account, Jid.fromString(share.contact), false);
} catch (final InvalidJidException e) {
return;
}
} }
share(conversation); share(conversation);
} }
private void share(final Conversation conversation) { private void share(final Conversation conversation) {
if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP && !hasPgp()) { if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP && !hasPgp()) {
showInstallPgpDialog(); if (share.uuid == null) {
showInstallPgpDialog();
} else {
Toast.makeText(this,R.string.openkeychain_not_installed,Toast.LENGTH_SHORT).show();
finish();
}
return; return;
} }
if (share.uris.size() != 0) { if (share.uris.size() != 0) {
@ -201,23 +238,27 @@ public class ShareWithActivity extends XmppActivity {
@Override @Override
public void onPresenceSelected() { public void onPresenceSelected() {
if (share.image) { if (share.image) {
Toast.makeText(getApplicationContext(), mToast = Toast.makeText(getApplicationContext(),
getText(R.string.preparing_image), getText(R.string.preparing_image),
Toast.LENGTH_LONG).show(); Toast.LENGTH_LONG);
mToast.show();
for (Iterator<Uri> i = share.uris.iterator(); i.hasNext(); i.remove()) { for (Iterator<Uri> i = share.uris.iterator(); i.hasNext(); i.remove()) {
ShareWithActivity.this.xmppConnectionService ShareWithActivity.this.xmppConnectionService
.attachImageToConversation(conversation, i.next(), .attachImageToConversation(conversation, i.next(),
attachFileCallback); attachFileCallback);
} }
} else { } else {
Toast.makeText(getApplicationContext(), mToast = Toast.makeText(getApplicationContext(),
getText(R.string.preparing_file), getText(R.string.preparing_file),
Toast.LENGTH_LONG).show(); Toast.LENGTH_LONG);
mToast.show();
ShareWithActivity.this.xmppConnectionService ShareWithActivity.this.xmppConnectionService
.attachFileToConversation(conversation, share.uris.get(0), .attachFileToConversation(conversation, share.uris.get(0),
attachFileCallback); attachFileCallback);
} }
switchToConversation(conversation, null, true); if (share.uuid == null) {
switchToConversation(conversation, null, true);
}
finish(); finish();
} }
}; };

View file

@ -91,6 +91,7 @@
<string name="openkeychain_required_long">Conversations utilizes a third party app called <b>OpenKeychain</b> to encrypt and decrypt messages and to manage your public keys.\n\nOpenKeychain is licensed under GPLv3 and available on F-Droid and Google Play.\n\n<small>(Please restart Conversations afterwards.)</small></string> <string name="openkeychain_required_long">Conversations utilizes a third party app called <b>OpenKeychain</b> to encrypt and decrypt messages and to manage your public keys.\n\nOpenKeychain is licensed under GPLv3 and available on F-Droid and Google Play.\n\n<small>(Please restart Conversations afterwards.)</small></string>
<string name="restart">Restart</string> <string name="restart">Restart</string>
<string name="install">Install</string> <string name="install">Install</string>
<string name="openkeychain_not_installed">Please install OpenKeychain</string>
<string name="offering">offering…</string> <string name="offering">offering…</string>
<string name="waiting">waiting…</string> <string name="waiting">waiting…</string>
<string name="no_pgp_key">No OpenPGP Key found</string> <string name="no_pgp_key">No OpenPGP Key found</string>
@ -554,4 +555,6 @@
<item quantity="one">%d message</item> <item quantity="one">%d message</item>
<item quantity="other">%d messages</item> <item quantity="other">%d messages</item>
</plurals> </plurals>
<string name="shared_file_with_x">Shared file with %s</string>
<string name="shared_image_with_x">Shared image with %s</string>
</resources> </resources>