show conversation media in contact/conference details
This commit is contained in:
parent
63f203c1d1
commit
06972ec95c
|
@ -34,6 +34,7 @@ import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
|
@ -774,6 +775,28 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<FilePath> getRelativeFilePaths(Account account, Jid jid, int limit) {
|
||||||
|
SQLiteDatabase db = this.getReadableDatabase();
|
||||||
|
final String SQL = "select uuid,relativeFilePath from messages where type in (1,2) and conversationUuid=(select uuid from conversations where accountUuid=? and (contactJid=? or contactJid like ?)) order by timeSent desc";
|
||||||
|
final String[] args = {account.getUuid(), jid.asBareJid().toEscapedString(), jid.asBareJid().toEscapedString()+"/%"};
|
||||||
|
Cursor cursor = db.rawQuery(SQL+(limit > 0 ? " limit "+String.valueOf(limit) : ""), args);
|
||||||
|
List<FilePath> filesPaths = new ArrayList<>();
|
||||||
|
while(cursor.moveToNext()) {
|
||||||
|
filesPaths.add(new FilePath(cursor.getString(0),cursor.getString(1)));
|
||||||
|
}
|
||||||
|
cursor.close();
|
||||||
|
return filesPaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class FilePath {
|
||||||
|
public final UUID uuid;
|
||||||
|
public final String path;
|
||||||
|
private FilePath(String uuid, String path) {
|
||||||
|
this.uuid = UUID.fromString(uuid);
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Conversation findConversation(final Account account, final Jid contactJid) {
|
public Conversation findConversation(final Account account, final Jid contactJid) {
|
||||||
SQLiteDatabase db = this.getReadableDatabase();
|
SQLiteDatabase db = this.getReadableDatabase();
|
||||||
String[] selectionArgs = {account.getUuid(),
|
String[] selectionArgs = {account.getUuid(),
|
||||||
|
|
|
@ -43,6 +43,7 @@ import java.security.DigestOutputStream;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
@ -240,12 +241,12 @@ public class FileBackend {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bitmap getPreviewForUri(Attachment attachment, int size, boolean cacheOnly) {
|
public Bitmap getPreviewForUri(Attachment attachment, int size, boolean cacheOnly) {
|
||||||
|
final String key = "attachment_"+attachment.getUuid().toString()+"_"+String.valueOf(size);
|
||||||
final LruCache<String, Bitmap> cache = mXmppConnectionService.getBitmapCache();
|
final LruCache<String, Bitmap> cache = mXmppConnectionService.getBitmapCache();
|
||||||
Bitmap bitmap = cache.get(attachment.getUuid().toString());
|
Bitmap bitmap = cache.get(key);
|
||||||
if (bitmap != null || cacheOnly) {
|
if (bitmap != null || cacheOnly) {
|
||||||
return bitmap;
|
return bitmap;
|
||||||
}
|
}
|
||||||
Log.d(Config.LOGTAG,"attachment mime="+attachment.getMime());
|
|
||||||
if (attachment.getMime() != null && attachment.getMime().startsWith("video/")) {
|
if (attachment.getMime() != null && attachment.getMime().startsWith("video/")) {
|
||||||
bitmap = cropCenterSquareVideo(attachment.getUri(), size);
|
bitmap = cropCenterSquareVideo(attachment.getUri(), size);
|
||||||
drawOverlay(bitmap, paintOverlayBlack(bitmap) ? R.drawable.play_video_black : R.drawable.play_video_white, 0.75f);
|
drawOverlay(bitmap, paintOverlayBlack(bitmap) ? R.drawable.play_video_black : R.drawable.play_video_white, 0.75f);
|
||||||
|
@ -258,7 +259,7 @@ public class FileBackend {
|
||||||
bitmap = withGifOverlay;
|
bitmap = withGifOverlay;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cache.put(attachment.getUuid().toString(), bitmap);
|
cache.put(key, bitmap);
|
||||||
return bitmap;
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -452,7 +453,22 @@ public class FileBackend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getConversationsDirectory(final String type) {
|
public List<Attachment> convertToAttachments(List<DatabaseBackend.FilePath> relativeFilePaths) {
|
||||||
|
List<Attachment> attachments = new ArrayList<>();
|
||||||
|
for(DatabaseBackend.FilePath relativeFilePath : relativeFilePaths) {
|
||||||
|
final String mime = MimeUtils.guessMimeTypeFromExtension(MimeUtils.extractRelevantExtension(relativeFilePath.path));
|
||||||
|
Log.d(Config.LOGTAG,"mime="+mime);
|
||||||
|
File file = getFileForPath(relativeFilePath.path, mime);
|
||||||
|
if (file.exists()) {
|
||||||
|
attachments.add(Attachment.of(relativeFilePath.uuid, file,mime));
|
||||||
|
} else {
|
||||||
|
Log.d(Config.LOGTAG,"file "+file.getAbsolutePath()+" doesnt exist");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return attachments;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getConversationsDirectory(final String type) {
|
||||||
return getConversationsDirectory(mXmppConnectionService, type);
|
return getConversationsDirectory(mXmppConnectionService, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -106,7 +106,9 @@ import eu.siacs.conversations.persistance.FileBackend;
|
||||||
import eu.siacs.conversations.ui.SettingsActivity;
|
import eu.siacs.conversations.ui.SettingsActivity;
|
||||||
import eu.siacs.conversations.ui.UiCallback;
|
import eu.siacs.conversations.ui.UiCallback;
|
||||||
import eu.siacs.conversations.ui.interfaces.OnAvatarPublication;
|
import eu.siacs.conversations.ui.interfaces.OnAvatarPublication;
|
||||||
|
import eu.siacs.conversations.ui.interfaces.OnMediaLoaded;
|
||||||
import eu.siacs.conversations.ui.interfaces.OnSearchResultsAvailable;
|
import eu.siacs.conversations.ui.interfaces.OnSearchResultsAvailable;
|
||||||
|
import eu.siacs.conversations.ui.util.Attachment;
|
||||||
import eu.siacs.conversations.utils.Compatibility;
|
import eu.siacs.conversations.utils.Compatibility;
|
||||||
import eu.siacs.conversations.utils.ConversationsFileObserver;
|
import eu.siacs.conversations.utils.ConversationsFileObserver;
|
||||||
import eu.siacs.conversations.utils.CryptoHelper;
|
import eu.siacs.conversations.utils.CryptoHelper;
|
||||||
|
@ -2418,6 +2420,16 @@ public class XmppConnectionService extends Service {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void getAttachments(final Conversation conversation, int limit, final OnMediaLoaded onMediaLoaded) {
|
||||||
|
getAttachments(conversation.getAccount(), conversation.getJid().asBareJid(), limit, onMediaLoaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void getAttachments(final Account account, final Jid jid, final int limit, final OnMediaLoaded onMediaLoaded) {
|
||||||
|
new Thread(() -> onMediaLoaded.onMediaLoaded(fileBackend.convertToAttachments(databaseBackend.getRelativeFilePaths(account, jid, limit)))).start();
|
||||||
|
}
|
||||||
|
|
||||||
public void persistSelfNick(MucOptions.User self) {
|
public void persistSelfNick(MucOptions.User self) {
|
||||||
final Conversation conversation = self.getConversation();
|
final Conversation conversation = self.getConversation();
|
||||||
Jid full = self.getFullJid();
|
Jid full = self.getFullJid();
|
||||||
|
|
|
@ -11,13 +11,12 @@ import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.ContextMenu;
|
import android.view.ContextMenu;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
|
@ -32,6 +31,7 @@ import org.openintents.openpgp.util.OpenPgpUtils;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.RejectedExecutionException;
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
@ -49,6 +49,10 @@ import eu.siacs.conversations.entities.MucOptions.User;
|
||||||
import eu.siacs.conversations.services.XmppConnectionService;
|
import eu.siacs.conversations.services.XmppConnectionService;
|
||||||
import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate;
|
import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate;
|
||||||
import eu.siacs.conversations.services.XmppConnectionService.OnMucRosterUpdate;
|
import eu.siacs.conversations.services.XmppConnectionService.OnMucRosterUpdate;
|
||||||
|
import eu.siacs.conversations.ui.adapter.MediaAdapter;
|
||||||
|
import eu.siacs.conversations.ui.interfaces.OnMediaLoaded;
|
||||||
|
import eu.siacs.conversations.ui.util.Attachment;
|
||||||
|
import eu.siacs.conversations.ui.util.GridManager;
|
||||||
import eu.siacs.conversations.ui.util.MenuDoubleTabUtil;
|
import eu.siacs.conversations.ui.util.MenuDoubleTabUtil;
|
||||||
import eu.siacs.conversations.ui.util.MucDetailsContextMenuHelper;
|
import eu.siacs.conversations.ui.util.MucDetailsContextMenuHelper;
|
||||||
import eu.siacs.conversations.ui.util.MyLinkify;
|
import eu.siacs.conversations.ui.util.MyLinkify;
|
||||||
|
@ -63,7 +67,7 @@ import rocks.xmpp.addr.Jid;
|
||||||
import static eu.siacs.conversations.entities.Bookmark.printableValue;
|
import static eu.siacs.conversations.entities.Bookmark.printableValue;
|
||||||
import static eu.siacs.conversations.utils.StringUtils.changed;
|
import static eu.siacs.conversations.utils.StringUtils.changed;
|
||||||
|
|
||||||
public class ConferenceDetailsActivity extends XmppActivity implements OnConversationUpdate, OnMucRosterUpdate, XmppConnectionService.OnAffiliationChanged, XmppConnectionService.OnRoleChanged, XmppConnectionService.OnConfigurationPushed, TextWatcher {
|
public class ConferenceDetailsActivity extends XmppActivity implements OnConversationUpdate, OnMucRosterUpdate, XmppConnectionService.OnAffiliationChanged, XmppConnectionService.OnRoleChanged, XmppConnectionService.OnConfigurationPushed, TextWatcher, OnMediaLoaded {
|
||||||
public static final String ACTION_VIEW_MUC = "view_muc";
|
public static final String ACTION_VIEW_MUC = "view_muc";
|
||||||
|
|
||||||
private static final float INACTIVE_ALPHA = 0.4684f; //compromise between dark and light theme
|
private static final float INACTIVE_ALPHA = 0.4684f; //compromise between dark and light theme
|
||||||
|
@ -77,6 +81,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private ActivityMucDetailsBinding binding;
|
private ActivityMucDetailsBinding binding;
|
||||||
|
private MediaAdapter mMediaAdapter;
|
||||||
private String uuid = null;
|
private String uuid = null;
|
||||||
private User mSelectedUser = null;
|
private User mSelectedUser = null;
|
||||||
|
|
||||||
|
@ -273,6 +278,9 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
|
||||||
this.binding.mucEditTitle.addTextChangedListener(this);
|
this.binding.mucEditTitle.addTextChangedListener(this);
|
||||||
this.binding.mucEditSubject.addTextChangedListener(this);
|
this.binding.mucEditSubject.addTextChangedListener(this);
|
||||||
this.binding.mucEditSubject.addTextChangedListener(new StylingHelper.MessageEditorStyler(this.binding.mucEditSubject));
|
this.binding.mucEditSubject.addTextChangedListener(new StylingHelper.MessageEditorStyler(this.binding.mucEditSubject));
|
||||||
|
mMediaAdapter = new MediaAdapter(this,R.dimen.media_size);
|
||||||
|
this.binding.media.setAdapter(mMediaAdapter);
|
||||||
|
GridManager.setupLayoutManager(this, this.binding.media, R.dimen.media_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -442,6 +450,16 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMediaLoaded(List<Attachment> attachments) {
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
int limit = GridManager.getCurrentColumnCount(binding.media);
|
||||||
|
mMediaAdapter.setAttachments(attachments.subList(0, Math.min(limit,attachments.size())));
|
||||||
|
binding.mediaWrapper.setVisibility(attachments.size() > 0 ? View.VISIBLE : View.GONE);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
protected void saveAsBookmark() {
|
protected void saveAsBookmark() {
|
||||||
xmppConnectionService.saveConversationAsBookmark(mConversation, mConversation.getMucOptions().getName());
|
xmppConnectionService.saveConversationAsBookmark(mConversation, mConversation.getMucOptions().getName());
|
||||||
|
@ -468,6 +486,8 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
|
||||||
if (uuid != null) {
|
if (uuid != null) {
|
||||||
this.mConversation = xmppConnectionService.findConversationByUuid(uuid);
|
this.mConversation = xmppConnectionService.findConversationByUuid(uuid);
|
||||||
if (this.mConversation != null) {
|
if (this.mConversation != null) {
|
||||||
|
final int limit = GridManager.getCurrentColumnCount(this.binding.media);
|
||||||
|
xmppConnectionService.getAttachments(this.mConversation, limit, this);
|
||||||
updateView();
|
updateView();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package eu.siacs.conversations.ui;
|
package eu.siacs.conversations.ui;
|
||||||
|
|
||||||
import android.content.ActivityNotFoundException;
|
import android.content.ActivityNotFoundException;
|
||||||
|
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;
|
||||||
|
@ -11,13 +12,18 @@ import android.preference.PreferenceManager;
|
||||||
import android.provider.ContactsContract.CommonDataKinds;
|
import android.provider.ContactsContract.CommonDataKinds;
|
||||||
import android.provider.ContactsContract.Contacts;
|
import android.provider.ContactsContract.Contacts;
|
||||||
import android.provider.ContactsContract.Intents;
|
import android.provider.ContactsContract.Intents;
|
||||||
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.support.v7.widget.GridLayoutManager;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
|
import android.view.ViewTreeObserver;
|
||||||
import android.widget.CompoundButton;
|
import android.widget.CompoundButton;
|
||||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
@ -25,6 +31,7 @@ import android.widget.Toast;
|
||||||
|
|
||||||
import org.openintents.openpgp.util.OpenPgpUtils;
|
import org.openintents.openpgp.util.OpenPgpUtils;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import eu.siacs.conversations.Config;
|
import eu.siacs.conversations.Config;
|
||||||
|
@ -38,6 +45,10 @@ import eu.siacs.conversations.entities.Contact;
|
||||||
import eu.siacs.conversations.entities.ListItem;
|
import eu.siacs.conversations.entities.ListItem;
|
||||||
import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
|
import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
|
||||||
import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
|
import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
|
||||||
|
import eu.siacs.conversations.ui.adapter.MediaAdapter;
|
||||||
|
import eu.siacs.conversations.ui.interfaces.OnMediaLoaded;
|
||||||
|
import eu.siacs.conversations.ui.util.Attachment;
|
||||||
|
import eu.siacs.conversations.ui.util.GridManager;
|
||||||
import eu.siacs.conversations.ui.util.MenuDoubleTabUtil;
|
import eu.siacs.conversations.ui.util.MenuDoubleTabUtil;
|
||||||
import eu.siacs.conversations.utils.IrregularUnicodeDetector;
|
import eu.siacs.conversations.utils.IrregularUnicodeDetector;
|
||||||
import eu.siacs.conversations.utils.UIHelper;
|
import eu.siacs.conversations.utils.UIHelper;
|
||||||
|
@ -48,9 +59,12 @@ import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
|
||||||
import eu.siacs.conversations.xmpp.XmppConnection;
|
import eu.siacs.conversations.xmpp.XmppConnection;
|
||||||
import rocks.xmpp.addr.Jid;
|
import rocks.xmpp.addr.Jid;
|
||||||
|
|
||||||
public class ContactDetailsActivity extends OmemoActivity implements OnAccountUpdate, OnRosterUpdate, OnUpdateBlocklist, OnKeyStatusUpdated {
|
public class ContactDetailsActivity extends OmemoActivity implements OnAccountUpdate, OnRosterUpdate, OnUpdateBlocklist, OnKeyStatusUpdated, OnMediaLoaded {
|
||||||
public static final String ACTION_VIEW_CONTACT = "view_contact";
|
public static final String ACTION_VIEW_CONTACT = "view_contact";
|
||||||
ActivityContactDetailsBinding binding;
|
ActivityContactDetailsBinding binding;
|
||||||
|
|
||||||
|
private MediaAdapter mMediaAdapter;
|
||||||
|
|
||||||
private Contact contact;
|
private Contact contact;
|
||||||
private DialogInterface.OnClickListener removeFromRoster = new DialogInterface.OnClickListener() {
|
private DialogInterface.OnClickListener removeFromRoster = new DialogInterface.OnClickListener() {
|
||||||
|
|
||||||
|
@ -185,6 +199,10 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
|
||||||
populateView();
|
populateView();
|
||||||
});
|
});
|
||||||
binding.addContactButton.setOnClickListener(v -> showAddToRosterDialog(contact));
|
binding.addContactButton.setOnClickListener(v -> showAddToRosterDialog(contact));
|
||||||
|
|
||||||
|
mMediaAdapter = new MediaAdapter(this,R.dimen.media_size);
|
||||||
|
this.binding.media.setAdapter(mMediaAdapter);
|
||||||
|
GridManager.setupLayoutManager(this, this.binding.media, R.dimen.media_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -204,6 +222,7 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
|
||||||
this.showDynamicTags = preferences.getBoolean(SettingsActivity.SHOW_DYNAMIC_TAGS, false);
|
this.showDynamicTags = preferences.getBoolean(SettingsActivity.SHOW_DYNAMIC_TAGS, false);
|
||||||
this.showLastSeen = preferences.getBoolean("last_activity", false);
|
this.showLastSeen = preferences.getBoolean("last_activity", false);
|
||||||
}
|
}
|
||||||
|
mMediaAdapter.setAttachments(Collections.emptyList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -466,6 +485,9 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
|
||||||
processFingerprintVerification(mPendingFingerprintVerificationUri);
|
processFingerprintVerification(mPendingFingerprintVerificationUri);
|
||||||
mPendingFingerprintVerificationUri = null;
|
mPendingFingerprintVerificationUri = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final int limit = GridManager.getCurrentColumnCount(this.binding.media);
|
||||||
|
xmppConnectionService.getAttachments(account, contact.getJid().asBareJid(), limit, this);
|
||||||
populateView();
|
populateView();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -485,4 +507,14 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp
|
||||||
Toast.makeText(this, R.string.invalid_barcode, Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, R.string.invalid_barcode, Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMediaLoaded(List<Attachment> attachments) {
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
int limit = GridManager.getCurrentColumnCount(binding.media);
|
||||||
|
mMediaAdapter.setAttachments(attachments.subList(0, Math.min(limit,attachments.size())));
|
||||||
|
binding.mediaWrapper.setVisibility(attachments.size() > 0 ? View.VISIBLE : View.GONE);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,225 @@
|
||||||
|
package eu.siacs.conversations.ui.adapter;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.databinding.DataBindingUtil;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.support.annotation.AttrRes;
|
||||||
|
import android.support.annotation.DimenRes;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.R;
|
||||||
|
import eu.siacs.conversations.databinding.MediaBinding;
|
||||||
|
import eu.siacs.conversations.ui.XmppActivity;
|
||||||
|
import eu.siacs.conversations.ui.util.Attachment;
|
||||||
|
import eu.siacs.conversations.ui.util.StyledAttributes;
|
||||||
|
import eu.siacs.conversations.ui.util.ViewUtil;
|
||||||
|
|
||||||
|
public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHolder> {
|
||||||
|
|
||||||
|
private static final List<String> DOCUMENT_MIMES = Arrays.asList(
|
||||||
|
"application/pdf",
|
||||||
|
"application/vnd.oasis.opendocument.text",
|
||||||
|
"application/msword",
|
||||||
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||||
|
"text/x-tex",
|
||||||
|
"text/plain"
|
||||||
|
);
|
||||||
|
|
||||||
|
private final ArrayList<Attachment> attachments = new ArrayList<>();
|
||||||
|
|
||||||
|
private final XmppActivity activity;
|
||||||
|
|
||||||
|
private int mediaSize = 0;
|
||||||
|
|
||||||
|
public MediaAdapter(XmppActivity activity, @DimenRes int mediaSize) {
|
||||||
|
this.activity = activity;
|
||||||
|
this.mediaSize = Math.round(activity.getResources().getDimension(mediaSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setMediaSize(RecyclerView recyclerView, int mediaSize) {
|
||||||
|
RecyclerView.Adapter adapter = recyclerView.getAdapter();
|
||||||
|
if (adapter instanceof MediaAdapter) {
|
||||||
|
((MediaAdapter) adapter).setMediaSize(mediaSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @AttrRes int getImageAttr(Attachment attachment) {
|
||||||
|
final @AttrRes int attr;
|
||||||
|
if (attachment.getType() == Attachment.Type.LOCATION) {
|
||||||
|
attr = R.attr.media_preview_location;
|
||||||
|
} else if (attachment.getType() == Attachment.Type.RECORDING) {
|
||||||
|
attr = R.attr.media_preview_recording;
|
||||||
|
} else {
|
||||||
|
final String mime = attachment.getMime();
|
||||||
|
if (mime == null) {
|
||||||
|
attr = R.attr.media_preview_unknown;
|
||||||
|
} else if (mime.startsWith("audio/")) {
|
||||||
|
attr = R.attr.media_preview_audio;
|
||||||
|
} else if (mime.equals("text/calendar") || (mime.equals("text/x-vcalendar"))) {
|
||||||
|
attr = R.attr.media_preview_calendar;
|
||||||
|
} else if (mime.equals("text/x-vcard")) {
|
||||||
|
attr = R.attr.media_preview_contact;
|
||||||
|
} else if (mime.equals("application/vnd.android.package-archive")) {
|
||||||
|
attr = R.attr.media_preview_app;
|
||||||
|
} else if (mime.equals("application/zip") || mime.equals("application/rar")) {
|
||||||
|
attr = R.attr.media_preview_archive;
|
||||||
|
} else if (DOCUMENT_MIMES.contains(mime)) {
|
||||||
|
attr = R.attr.media_preview_document;
|
||||||
|
} else {
|
||||||
|
attr = R.attr.media_preview_unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return attr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void renderPreview(Context context, Attachment attachment, ImageView imageView) {
|
||||||
|
imageView.setBackgroundColor(StyledAttributes.getColor(context, R.attr.color_background_tertiary));
|
||||||
|
imageView.setImageAlpha(Math.round(StyledAttributes.getFloat(context, R.attr.icon_alpha) * 255));
|
||||||
|
imageView.setImageDrawable(StyledAttributes.getDrawable(context, getImageAttr(attachment)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean cancelPotentialWork(Attachment attachment, ImageView imageView) {
|
||||||
|
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
|
||||||
|
|
||||||
|
if (bitmapWorkerTask != null) {
|
||||||
|
final Attachment oldAttachment = bitmapWorkerTask.attachment;
|
||||||
|
if (oldAttachment == null || !oldAttachment.equals(attachment)) {
|
||||||
|
bitmapWorkerTask.cancel(true);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
|
||||||
|
if (imageView != null) {
|
||||||
|
final Drawable drawable = imageView.getDrawable();
|
||||||
|
if (drawable instanceof AsyncDrawable) {
|
||||||
|
final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
|
||||||
|
return asyncDrawable.getBitmapWorkerTask();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public MediaViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
|
||||||
|
MediaBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.media, parent, false);
|
||||||
|
return new MediaViewHolder(binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull MediaViewHolder holder, int position) {
|
||||||
|
final Attachment attachment = attachments.get(position);
|
||||||
|
if (attachment.renderThumbnail()) {
|
||||||
|
holder.binding.media.setImageAlpha(255);
|
||||||
|
loadPreview(attachment, holder.binding.media);
|
||||||
|
} else {
|
||||||
|
cancelPotentialWork(attachment, holder.binding.media);
|
||||||
|
renderPreview(activity, attachment, holder.binding.media);
|
||||||
|
}
|
||||||
|
holder.binding.media.setOnClickListener(v -> ViewUtil.view(activity, attachment));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttachments(List<Attachment> attachments) {
|
||||||
|
this.attachments.clear();
|
||||||
|
this.attachments.addAll(attachments);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setMediaSize(int mediaSize) {
|
||||||
|
this.mediaSize = mediaSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadPreview(Attachment attachment, ImageView imageView) {
|
||||||
|
if (cancelPotentialWork(attachment, imageView)) {
|
||||||
|
final Bitmap bm = activity.xmppConnectionService.getFileBackend().getPreviewForUri(attachment,mediaSize,true);
|
||||||
|
if (bm != null) {
|
||||||
|
cancelPotentialWork(attachment, imageView);
|
||||||
|
imageView.setImageBitmap(bm);
|
||||||
|
imageView.setBackgroundColor(0x00000000);
|
||||||
|
} else {
|
||||||
|
imageView.setBackgroundColor(0xff333333);
|
||||||
|
imageView.setImageDrawable(null);
|
||||||
|
final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
|
||||||
|
final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task);
|
||||||
|
imageView.setImageDrawable(asyncDrawable);
|
||||||
|
try {
|
||||||
|
task.execute(attachment);
|
||||||
|
} catch (final RejectedExecutionException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return attachments.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static class AsyncDrawable extends BitmapDrawable {
|
||||||
|
private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
|
||||||
|
|
||||||
|
AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
|
||||||
|
super(res, bitmap);
|
||||||
|
bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
BitmapWorkerTask getBitmapWorkerTask() {
|
||||||
|
return bitmapWorkerTaskReference.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MediaViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
|
||||||
|
private final MediaBinding binding;
|
||||||
|
|
||||||
|
MediaViewHolder(MediaBinding binding) {
|
||||||
|
super(binding.getRoot());
|
||||||
|
this.binding = binding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BitmapWorkerTask extends AsyncTask<Attachment, Void, Bitmap> {
|
||||||
|
private final WeakReference<ImageView> imageViewReference;
|
||||||
|
private Attachment attachment = null;
|
||||||
|
|
||||||
|
BitmapWorkerTask(ImageView imageView) {
|
||||||
|
imageViewReference = new WeakReference<>(imageView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Bitmap doInBackground(Attachment... params) {
|
||||||
|
this.attachment = params[0];
|
||||||
|
return activity.xmppConnectionService.getFileBackend().getPreviewForUri(this.attachment, mediaSize, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Bitmap bitmap) {
|
||||||
|
if (bitmap != null && !isCancelled()) {
|
||||||
|
final ImageView imageView = imageViewReference.get();
|
||||||
|
if (imageView != null) {
|
||||||
|
imageView.setImageBitmap(bitmap);
|
||||||
|
imageView.setBackgroundColor(0x00000000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,15 +30,6 @@ import eu.siacs.conversations.ui.util.StyledAttributes;
|
||||||
|
|
||||||
public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapter.MediaPreviewViewHolder> {
|
public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapter.MediaPreviewViewHolder> {
|
||||||
|
|
||||||
private static final List<String> DOCUMENT_MIMES = Arrays.asList(
|
|
||||||
"application/pdf",
|
|
||||||
"application/vnd.oasis.opendocument.text",
|
|
||||||
"application/msword",
|
|
||||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
||||||
"text/x-tex",
|
|
||||||
"text/plain"
|
|
||||||
);
|
|
||||||
|
|
||||||
private final ArrayList<Attachment> mediaPreviews = new ArrayList<>();
|
private final ArrayList<Attachment> mediaPreviews = new ArrayList<>();
|
||||||
|
|
||||||
private final ConversationFragment conversationFragment;
|
private final ConversationFragment conversationFragment;
|
||||||
|
@ -64,34 +55,7 @@ public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapte
|
||||||
loadPreview(attachment, holder.binding.mediaPreview);
|
loadPreview(attachment, holder.binding.mediaPreview);
|
||||||
} else {
|
} else {
|
||||||
cancelPotentialWork(attachment, holder.binding.mediaPreview);
|
cancelPotentialWork(attachment, holder.binding.mediaPreview);
|
||||||
holder.binding.mediaPreview.setBackgroundColor(StyledAttributes.getColor(context, R.attr.color_background_tertiary));
|
MediaAdapter.renderPreview(context, attachment, holder.binding.mediaPreview);
|
||||||
holder.binding.mediaPreview.setImageAlpha(Math.round(StyledAttributes.getFloat(context, R.attr.icon_alpha) * 255));
|
|
||||||
final @AttrRes int attr;
|
|
||||||
if (attachment.getType() == Attachment.Type.LOCATION) {
|
|
||||||
attr = R.attr.media_preview_location;
|
|
||||||
} else if (attachment.getType() == Attachment.Type.RECORDING) {
|
|
||||||
attr = R.attr.media_preview_recording;
|
|
||||||
} else {
|
|
||||||
final String mime = attachment.getMime();
|
|
||||||
if (mime == null) {
|
|
||||||
attr = R.attr.media_preview_unknown;
|
|
||||||
} else if (mime.startsWith("audio/")) {
|
|
||||||
attr = R.attr.media_preview_audio;
|
|
||||||
} else if (mime.equals("text/calendar") || (mime.equals("text/x-vcalendar"))) {
|
|
||||||
attr = R.attr.media_preview_calendar;
|
|
||||||
} else if (mime.equals("text/x-vcard")) {
|
|
||||||
attr = R.attr.media_preview_contact;
|
|
||||||
} else if (mime.equals("application/vnd.android.package-archive")) {
|
|
||||||
attr = R.attr.media_preview_app;
|
|
||||||
} else if (mime.equals("application/zip") || mime.equals("application/rar")) {
|
|
||||||
attr = R.attr.media_preview_archive;
|
|
||||||
} else if (DOCUMENT_MIMES.contains(mime)) {
|
|
||||||
attr = R.attr.media_preview_document;
|
|
||||||
} else {
|
|
||||||
attr = R.attr.media_preview_unknown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
holder.binding.mediaPreview.setImageDrawable(StyledAttributes.getDrawable(context, attr));
|
|
||||||
}
|
}
|
||||||
holder.binding.deleteButton.setOnClickListener(v -> {
|
holder.binding.deleteButton.setOnClickListener(v -> {
|
||||||
int pos = mediaPreviews.indexOf(attachment);
|
int pos = mediaPreviews.indexOf(attachment);
|
||||||
|
|
|
@ -71,6 +71,7 @@ import eu.siacs.conversations.ui.service.AudioPlayer;
|
||||||
import eu.siacs.conversations.ui.text.DividerSpan;
|
import eu.siacs.conversations.ui.text.DividerSpan;
|
||||||
import eu.siacs.conversations.ui.text.QuoteSpan;
|
import eu.siacs.conversations.ui.text.QuoteSpan;
|
||||||
import eu.siacs.conversations.ui.util.MyLinkify;
|
import eu.siacs.conversations.ui.util.MyLinkify;
|
||||||
|
import eu.siacs.conversations.ui.util.ViewUtil;
|
||||||
import eu.siacs.conversations.ui.widget.ClickableMovementMethod;
|
import eu.siacs.conversations.ui.widget.ClickableMovementMethod;
|
||||||
import eu.siacs.conversations.ui.widget.CopyTextView;
|
import eu.siacs.conversations.ui.widget.CopyTextView;
|
||||||
import eu.siacs.conversations.ui.widget.ListSelectionManager;
|
import eu.siacs.conversations.ui.widget.ListSelectionManager;
|
||||||
|
@ -896,31 +897,11 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
|
||||||
Toast.makeText(activity, R.string.file_deleted, Toast.LENGTH_SHORT).show();
|
Toast.makeText(activity, R.string.file_deleted, Toast.LENGTH_SHORT).show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Intent openIntent = new Intent(Intent.ACTION_VIEW);
|
|
||||||
String mime = file.getMimeType();
|
String mime = file.getMimeType();
|
||||||
if (mime == null) {
|
if (mime == null) {
|
||||||
mime = "*/*";
|
mime = "*/*";
|
||||||
}
|
}
|
||||||
Uri uri;
|
ViewUtil.view(activity, file, mime);
|
||||||
try {
|
|
||||||
uri = FileBackend.getUriForFile(activity, file);
|
|
||||||
} catch (SecurityException e) {
|
|
||||||
Log.d(Config.LOGTAG, "No permission to access " + file.getAbsolutePath(), e);
|
|
||||||
Toast.makeText(activity, activity.getString(R.string.no_permission_to_access_x, file.getAbsolutePath()), Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
openIntent.setDataAndType(uri, mime);
|
|
||||||
openIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
||||||
PackageManager manager = activity.getPackageManager();
|
|
||||||
List<ResolveInfo> info = manager.queryIntentActivities(openIntent, 0);
|
|
||||||
if (info.size() == 0) {
|
|
||||||
openIntent.setDataAndType(uri, "*/*");
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
getContext().startActivity(openIntent);
|
|
||||||
} catch (ActivityNotFoundException e) {
|
|
||||||
Toast.makeText(activity, R.string.no_application_found_to_open_file, Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showLocation(Message message) {
|
public void showLocation(Message message) {
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
package eu.siacs.conversations.ui.interfaces;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.ui.util.Attachment;
|
||||||
|
|
||||||
|
public interface OnMediaLoaded {
|
||||||
|
|
||||||
|
void onMediaLoaded(List<Attachment> attachments);
|
||||||
|
}
|
|
@ -37,6 +37,7 @@ import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -44,7 +45,6 @@ import java.util.UUID;
|
||||||
|
|
||||||
import eu.siacs.conversations.Config;
|
import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.utils.MimeUtils;
|
import eu.siacs.conversations.utils.MimeUtils;
|
||||||
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
|
|
||||||
|
|
||||||
public class Attachment implements Parcelable {
|
public class Attachment implements Parcelable {
|
||||||
|
|
||||||
|
@ -97,6 +97,13 @@ public class Attachment implements Parcelable {
|
||||||
private final UUID uuid;
|
private final UUID uuid;
|
||||||
private final String mime;
|
private final String mime;
|
||||||
|
|
||||||
|
private Attachment(UUID uuid, Uri uri, Type type, String mime) {
|
||||||
|
this.uri = uri;
|
||||||
|
this.type = type;
|
||||||
|
this.mime = mime;
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
private Attachment(Uri uri, Type type, String mime) {
|
private Attachment(Uri uri, Type type, String mime) {
|
||||||
this.uri = uri;
|
this.uri = uri;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
@ -118,6 +125,10 @@ public class Attachment implements Parcelable {
|
||||||
return attachments;
|
return attachments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Attachment of(UUID uuid, final File file, String mime) {
|
||||||
|
return new Attachment(uuid, Uri.fromFile(file),mime != null && (mime.startsWith("image/") || mime.startsWith("video/")) ? Type.IMAGE : Type.FILE, mime);
|
||||||
|
}
|
||||||
|
|
||||||
public static List<Attachment> extractAttachments(final Context context, final Intent intent, Type type) {
|
public static List<Attachment> extractAttachments(final Context context, final Intent intent, Type type) {
|
||||||
List<Attachment> uris = new ArrayList<>();
|
List<Attachment> uris = new ArrayList<>();
|
||||||
if (intent == null) {
|
if (intent == null) {
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
package eu.siacs.conversations.ui.util;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.DimenRes;
|
||||||
|
import android.support.v7.widget.GridLayoutManager;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.ViewTreeObserver;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.Config;
|
||||||
|
import eu.siacs.conversations.ui.adapter.MediaAdapter;
|
||||||
|
|
||||||
|
public class GridManager {
|
||||||
|
|
||||||
|
public static void setupLayoutManager(final Context context, RecyclerView recyclerView, @DimenRes int desiredSize) {
|
||||||
|
int maxWidth = context.getResources().getDisplayMetrics().widthPixels;
|
||||||
|
ColumnInfo columnInfo = calculateColumnCount(context, maxWidth, desiredSize);
|
||||||
|
Log.d(Config.LOGTAG, "preliminary count=" + columnInfo.count);
|
||||||
|
MediaAdapter.setMediaSize(recyclerView, columnInfo.width);
|
||||||
|
recyclerView.setLayoutManager(new GridLayoutManager(context, columnInfo.count));
|
||||||
|
recyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||||
|
@Override
|
||||||
|
public void onGlobalLayout() {
|
||||||
|
recyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||||
|
final ColumnInfo columnInfo = calculateColumnCount(context, recyclerView.getMeasuredWidth(), desiredSize);
|
||||||
|
Log.d(Config.LOGTAG, "final count " + columnInfo.count);
|
||||||
|
if (recyclerView.getAdapter().getItemCount() != 0) {
|
||||||
|
Log.e(Config.LOGTAG, "adapter already has items; just go with it now");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setupLayoutManagerInternal(recyclerView, columnInfo);
|
||||||
|
MediaAdapter.setMediaSize(recyclerView, columnInfo.width);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setupLayoutManagerInternal(RecyclerView recyclerView, final ColumnInfo columnInfo) {
|
||||||
|
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
|
||||||
|
if (layoutManager instanceof GridLayoutManager) {
|
||||||
|
((GridLayoutManager) layoutManager).setSpanCount(columnInfo.count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ColumnInfo calculateColumnCount(Context context, int availableWidth, @DimenRes int desiredSize) {
|
||||||
|
final float desiredWidth = context.getResources().getDimension(desiredSize);
|
||||||
|
final int columns = Math.round(availableWidth / desiredWidth);
|
||||||
|
final int realWidth = availableWidth / columns;
|
||||||
|
Log.d(Config.LOGTAG, "desired=" + desiredWidth + " real=" + realWidth);
|
||||||
|
return new ColumnInfo(columns, realWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getCurrentColumnCount(RecyclerView recyclerView) {
|
||||||
|
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
|
||||||
|
if (layoutManager instanceof GridLayoutManager) {
|
||||||
|
return ((GridLayoutManager) layoutManager).getSpanCount();
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ColumnInfo {
|
||||||
|
private final int count;
|
||||||
|
private final int width;
|
||||||
|
|
||||||
|
private ColumnInfo(int count, int width) {
|
||||||
|
this.count = count;
|
||||||
|
this.width = width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
51
src/main/java/eu/siacs/conversations/ui/util/ViewUtil.java
Normal file
51
src/main/java/eu/siacs/conversations/ui/util/ViewUtil.java
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package eu.siacs.conversations.ui.util;
|
||||||
|
|
||||||
|
import android.content.ActivityNotFoundException;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.ResolveInfo;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.Config;
|
||||||
|
import eu.siacs.conversations.R;
|
||||||
|
import eu.siacs.conversations.persistance.FileBackend;
|
||||||
|
|
||||||
|
public class ViewUtil {
|
||||||
|
|
||||||
|
public static void view(Context context, Attachment attachment) {
|
||||||
|
File file = new File(attachment.getUri().getPath());
|
||||||
|
final String mime = attachment.getMime() == null ? "*/*" : attachment.getMime();
|
||||||
|
view(context, file, mime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void view(Context context, File file, String mime) {
|
||||||
|
Intent openIntent = new Intent(Intent.ACTION_VIEW);
|
||||||
|
Uri uri;
|
||||||
|
try {
|
||||||
|
uri = FileBackend.getUriForFile(context, file);
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
Log.d(Config.LOGTAG, "No permission to access " + file.getAbsolutePath(), e);
|
||||||
|
Toast.makeText(context, context.getString(R.string.no_permission_to_access_x, file.getAbsolutePath()), Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
openIntent.setDataAndType(uri, mime);
|
||||||
|
openIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
PackageManager manager = context.getPackageManager();
|
||||||
|
List<ResolveInfo> info = manager.queryIntentActivities(openIntent, 0);
|
||||||
|
if (info.size() == 0) {
|
||||||
|
openIntent.setDataAndType(uri, "*/*");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
context.startActivity(openIntent);
|
||||||
|
} catch (ActivityNotFoundException e) {
|
||||||
|
Toast.makeText(context, R.string.no_application_found_to_open_file, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package eu.siacs.conversations.ui.widget;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
|
public class SquareFrameLayout extends FrameLayout {
|
||||||
|
public SquareFrameLayout(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SquareFrameLayout(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SquareFrameLayout(Context context, AttributeSet attrs, int defStyle) {
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
//noinspection SuspiciousNameCombination
|
||||||
|
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -64,8 +65,7 @@
|
||||||
android:layout_marginBottom="4dp"
|
android:layout_marginBottom="4dp"
|
||||||
android:layout_marginLeft="-2dp"
|
android:layout_marginLeft="-2dp"
|
||||||
android:layout_marginTop="4dp"
|
android:layout_marginTop="4dp"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal"></com.wefika.flowlayout.FlowLayout>
|
||||||
</com.wefika.flowlayout.FlowLayout>
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/details_lastseen"
|
android:id="@+id/details_lastseen"
|
||||||
|
@ -118,6 +118,52 @@
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
</android.support.v7.widget.CardView>
|
</android.support.v7.widget.CardView>
|
||||||
|
|
||||||
|
<android.support.v7.widget.CardView
|
||||||
|
android:id="@+id/media_wrapper"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="@dimen/activity_vertical_margin"
|
||||||
|
android:layout_marginLeft="@dimen/activity_horizontal_margin"
|
||||||
|
android:layout_marginRight="@dimen/activity_horizontal_margin"
|
||||||
|
android:layout_marginTop="@dimen/activity_vertical_margin">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<android.support.v7.widget.RecyclerView
|
||||||
|
android:id="@+id/media"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingEnd="@dimen/card_padding_regular"
|
||||||
|
android:paddingStart="@dimen/card_padding_regular"
|
||||||
|
android:paddingTop="@dimen/card_padding_regular"
|
||||||
|
android:paddingBottom="@dimen/card_padding_list"
|
||||||
|
android:layout_marginStart="-2dp"
|
||||||
|
android:layout_marginEnd="-2dp"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_gravity="end">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/show_media"
|
||||||
|
style="@style/Widget.Conversations.Button.Borderless"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minWidth="0dp"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
|
android:text="@string/show_media"
|
||||||
|
android:textColor="?attr/colorAccent" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</android.support.v7.widget.CardView>
|
||||||
|
|
||||||
<android.support.v7.widget.CardView
|
<android.support.v7.widget.CardView
|
||||||
android:id="@+id/keys_wrapper"
|
android:id="@+id/keys_wrapper"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
|
@ -137,8 +183,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="@dimen/card_padding_list">
|
android:padding="@dimen/card_padding_list"/>
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -207,6 +208,52 @@
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</android.support.v7.widget.CardView>
|
</android.support.v7.widget.CardView>
|
||||||
|
|
||||||
|
<android.support.v7.widget.CardView
|
||||||
|
android:id="@+id/media_wrapper"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="@dimen/activity_vertical_margin"
|
||||||
|
android:layout_marginLeft="@dimen/activity_horizontal_margin"
|
||||||
|
android:layout_marginRight="@dimen/activity_horizontal_margin"
|
||||||
|
android:layout_marginTop="@dimen/activity_vertical_margin">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<android.support.v7.widget.RecyclerView
|
||||||
|
android:id="@+id/media"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingEnd="@dimen/card_padding_regular"
|
||||||
|
android:paddingStart="@dimen/card_padding_regular"
|
||||||
|
android:paddingTop="@dimen/card_padding_regular"
|
||||||
|
android:paddingBottom="@dimen/card_padding_list"
|
||||||
|
android:layout_marginStart="-2dp"
|
||||||
|
android:layout_marginEnd="-2dp"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_gravity="end">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/show_media"
|
||||||
|
style="@style/Widget.Conversations.Button.Borderless"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:minWidth="0dp"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
|
android:text="@string/show_media"
|
||||||
|
android:textColor="?attr/colorAccent" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</android.support.v7.widget.CardView>
|
||||||
|
|
||||||
<android.support.v7.widget.CardView
|
<android.support.v7.widget.CardView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
14
src/main/res/layout/media.xml
Normal file
14
src/main/res/layout/media.xml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<eu.siacs.conversations.ui.widget.SquareFrameLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="2dp">
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/media"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/black54"
|
||||||
|
android:scaleType="centerInside"/>
|
||||||
|
</eu.siacs.conversations.ui.widget.SquareFrameLayout>
|
||||||
|
</layout>
|
|
@ -12,6 +12,7 @@
|
||||||
<dimen name="avatar_item_distance">16dp</dimen>
|
<dimen name="avatar_item_distance">16dp</dimen>
|
||||||
|
|
||||||
<dimen name="media_preview_size">80dp</dimen>
|
<dimen name="media_preview_size">80dp</dimen>
|
||||||
|
<dimen name="media_size">64dp</dimen>
|
||||||
<dimen name="toolbar_elevation">4dp</dimen>
|
<dimen name="toolbar_elevation">4dp</dimen>
|
||||||
|
|
||||||
<dimen name="publish_avatar_top_margin">8dp</dimen>
|
<dimen name="publish_avatar_top_margin">8dp</dimen>
|
||||||
|
|
|
@ -734,4 +734,5 @@
|
||||||
<string name="pref_more_notification_settings">Notification Settings</string>
|
<string name="pref_more_notification_settings">Notification Settings</string>
|
||||||
<string name="pref_more_notification_settings_summary">Importance, Sound, Vibrate</string>
|
<string name="pref_more_notification_settings_summary">Importance, Sound, Vibrate</string>
|
||||||
<string name="video_compression_channel_name">Video compression</string>
|
<string name="video_compression_channel_name">Video compression</string>
|
||||||
|
<string name="show_media">Show media</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in a new issue