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.Map; | ||||
| import java.util.Set; | ||||
| import java.util.UUID; | ||||
| import java.util.concurrent.CopyOnWriteArrayList; | ||||
| 
 | ||||
| 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) { | ||||
| 		SQLiteDatabase db = this.getReadableDatabase(); | ||||
| 		String[] selectionArgs = {account.getUuid(), | ||||
|  | ||||
| @ -43,6 +43,7 @@ import java.security.DigestOutputStream; | ||||
| import java.security.MessageDigest; | ||||
| import java.security.NoSuchAlgorithmException; | ||||
| import java.text.SimpleDateFormat; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Date; | ||||
| import java.util.List; | ||||
| import java.util.Locale; | ||||
| @ -240,12 +241,12 @@ public class FileBackend { | ||||
|     } | ||||
| 
 | ||||
|     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(); | ||||
|         Bitmap bitmap = cache.get(attachment.getUuid().toString()); | ||||
|         Bitmap bitmap = cache.get(key); | ||||
|         if (bitmap != null || cacheOnly) { | ||||
|             return bitmap; | ||||
|         } | ||||
|         Log.d(Config.LOGTAG,"attachment mime="+attachment.getMime()); | ||||
|         if (attachment.getMime() != null && attachment.getMime().startsWith("video/")) { | ||||
|             bitmap = cropCenterSquareVideo(attachment.getUri(), size); | ||||
|             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; | ||||
|             } | ||||
|         } | ||||
|         cache.put(attachment.getUuid().toString(), bitmap); | ||||
|         cache.put(key, 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); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -106,7 +106,9 @@ import eu.siacs.conversations.persistance.FileBackend; | ||||
| import eu.siacs.conversations.ui.SettingsActivity; | ||||
| import eu.siacs.conversations.ui.UiCallback; | ||||
| 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.util.Attachment; | ||||
| import eu.siacs.conversations.utils.Compatibility; | ||||
| import eu.siacs.conversations.utils.ConversationsFileObserver; | ||||
| import eu.siacs.conversations.utils.CryptoHelper; | ||||
| @ -2418,6 +2420,16 @@ public class XmppConnectionService extends Service { | ||||
| 		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) { | ||||
| 		final Conversation conversation = self.getConversation(); | ||||
| 		Jid full = self.getFullJid(); | ||||
|  | ||||
| @ -11,13 +11,12 @@ import android.graphics.drawable.BitmapDrawable; | ||||
| import android.graphics.drawable.Drawable; | ||||
| import android.os.AsyncTask; | ||||
| import android.os.Bundle; | ||||
| import android.preference.PreferenceManager; | ||||
| import android.support.v4.content.ContextCompat; | ||||
| import android.support.v7.app.AlertDialog; | ||||
| import android.support.v7.widget.Toolbar; | ||||
| import android.text.Editable; | ||||
| import android.text.SpannableStringBuilder; | ||||
| import android.text.TextWatcher; | ||||
| import android.util.Log; | ||||
| import android.view.ContextMenu; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.Menu; | ||||
| @ -32,6 +31,7 @@ import org.openintents.openpgp.util.OpenPgpUtils; | ||||
| import java.lang.ref.WeakReference; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| import java.util.concurrent.RejectedExecutionException; | ||||
| 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.OnConversationUpdate; | ||||
| 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.MucDetailsContextMenuHelper; | ||||
| 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.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"; | ||||
| 
 | ||||
|     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 MediaAdapter mMediaAdapter; | ||||
|     private String uuid = null; | ||||
|     private User mSelectedUser = null; | ||||
| 
 | ||||
| @ -273,6 +278,9 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers | ||||
|         this.binding.mucEditTitle.addTextChangedListener(this); | ||||
|         this.binding.mucEditSubject.addTextChangedListener(this); | ||||
|         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 | ||||
| @ -442,6 +450,16 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers | ||||
|         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() { | ||||
|         xmppConnectionService.saveConversationAsBookmark(mConversation, mConversation.getMucOptions().getName()); | ||||
| @ -468,6 +486,8 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers | ||||
|         if (uuid != null) { | ||||
|             this.mConversation = xmppConnectionService.findConversationByUuid(uuid); | ||||
|             if (this.mConversation != null) { | ||||
|                 final int limit = GridManager.getCurrentColumnCount(this.binding.media); | ||||
|                 xmppConnectionService.getAttachments(this.mConversation, limit, this); | ||||
|                 updateView(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| package eu.siacs.conversations.ui; | ||||
| 
 | ||||
| import android.content.ActivityNotFoundException; | ||||
| import android.content.Context; | ||||
| import android.content.DialogInterface; | ||||
| import android.content.Intent; | ||||
| import android.content.SharedPreferences; | ||||
| @ -11,13 +12,18 @@ import android.preference.PreferenceManager; | ||||
| import android.provider.ContactsContract.CommonDataKinds; | ||||
| import android.provider.ContactsContract.Contacts; | ||||
| import android.provider.ContactsContract.Intents; | ||||
| import android.support.v4.content.ContextCompat; | ||||
| 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.util.Log; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.Menu; | ||||
| import android.view.MenuItem; | ||||
| import android.view.View; | ||||
| import android.view.View.OnClickListener; | ||||
| import android.view.ViewTreeObserver; | ||||
| import android.widget.CompoundButton; | ||||
| import android.widget.CompoundButton.OnCheckedChangeListener; | ||||
| import android.widget.TextView; | ||||
| @ -25,6 +31,7 @@ import android.widget.Toast; | ||||
| 
 | ||||
| import org.openintents.openpgp.util.OpenPgpUtils; | ||||
| 
 | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| 
 | ||||
| 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.services.XmppConnectionService.OnAccountUpdate; | ||||
| 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.utils.IrregularUnicodeDetector; | ||||
| import eu.siacs.conversations.utils.UIHelper; | ||||
| @ -48,9 +59,12 @@ import eu.siacs.conversations.xmpp.OnUpdateBlocklist; | ||||
| import eu.siacs.conversations.xmpp.XmppConnection; | ||||
| 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"; | ||||
|     ActivityContactDetailsBinding binding; | ||||
| 
 | ||||
|     private MediaAdapter mMediaAdapter; | ||||
| 
 | ||||
|     private Contact contact; | ||||
|     private DialogInterface.OnClickListener removeFromRoster = new DialogInterface.OnClickListener() { | ||||
| 
 | ||||
| @ -185,6 +199,10 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp | ||||
|             populateView(); | ||||
|         }); | ||||
|         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 | ||||
| @ -204,6 +222,7 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp | ||||
|             this.showDynamicTags = preferences.getBoolean(SettingsActivity.SHOW_DYNAMIC_TAGS, false); | ||||
|             this.showLastSeen = preferences.getBoolean("last_activity", false); | ||||
|         } | ||||
|         mMediaAdapter.setAttachments(Collections.emptyList()); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
| @ -466,6 +485,9 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp | ||||
|                 processFingerprintVerification(mPendingFingerprintVerificationUri); | ||||
|                 mPendingFingerprintVerificationUri = null; | ||||
|             } | ||||
| 
 | ||||
|             final int limit = GridManager.getCurrentColumnCount(this.binding.media); | ||||
|             xmppConnectionService.getAttachments(account, contact.getJid().asBareJid(), limit, this); | ||||
|             populateView(); | ||||
|         } | ||||
|     } | ||||
| @ -485,4 +507,14 @@ public class ContactDetailsActivity extends OmemoActivity implements OnAccountUp | ||||
|             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> { | ||||
| 
 | ||||
|     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 ConversationFragment conversationFragment; | ||||
| @ -64,34 +55,7 @@ public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapte | ||||
|             loadPreview(attachment, holder.binding.mediaPreview); | ||||
|         } else { | ||||
|             cancelPotentialWork(attachment, holder.binding.mediaPreview); | ||||
|             holder.binding.mediaPreview.setBackgroundColor(StyledAttributes.getColor(context, R.attr.color_background_tertiary)); | ||||
|             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)); | ||||
|             MediaAdapter.renderPreview(context, attachment, holder.binding.mediaPreview); | ||||
|         } | ||||
|         holder.binding.deleteButton.setOnClickListener(v -> { | ||||
|             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.QuoteSpan; | ||||
| 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.CopyTextView; | ||||
| 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(); | ||||
| 			return; | ||||
| 		} | ||||
| 		Intent openIntent = new Intent(Intent.ACTION_VIEW); | ||||
| 		String mime = file.getMimeType(); | ||||
| 		if (mime == null) { | ||||
| 			mime = "*/*"; | ||||
| 		} | ||||
| 		Uri uri; | ||||
| 		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(); | ||||
| 		} | ||||
| 		ViewUtil.view(activity, file, mime); | ||||
| 	} | ||||
| 
 | ||||
| 	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.util.Log; | ||||
| 
 | ||||
| import java.io.File; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| @ -44,7 +45,6 @@ import java.util.UUID; | ||||
| 
 | ||||
| import eu.siacs.conversations.Config; | ||||
| import eu.siacs.conversations.utils.MimeUtils; | ||||
| import eu.siacs.conversations.xmpp.stanzas.IqPacket; | ||||
| 
 | ||||
| public class Attachment implements Parcelable { | ||||
| 
 | ||||
| @ -97,6 +97,13 @@ public class Attachment implements Parcelable { | ||||
|     private final UUID uuid; | ||||
|     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) { | ||||
|         this.uri = uri; | ||||
|         this.type = type; | ||||
| @ -118,6 +125,10 @@ public class Attachment implements Parcelable { | ||||
|         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) { | ||||
|         List<Attachment> uris = new ArrayList<>(); | ||||
|         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"?> | ||||
| <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 | ||||
|         android:layout_width="match_parent" | ||||
| @ -10,7 +11,7 @@ | ||||
| 
 | ||||
|         <include | ||||
|             android:id="@+id/toolbar" | ||||
|             layout="@layout/toolbar"/> | ||||
|             layout="@layout/toolbar" /> | ||||
| 
 | ||||
|         <ScrollView | ||||
|             android:layout_width="fill_parent" | ||||
| @ -40,7 +41,7 @@ | ||||
|                             android:layout_width="@dimen/avatar_on_details_screen_size" | ||||
|                             android:layout_height="@dimen/avatar_on_details_screen_size" | ||||
|                             android:layout_alignParentTop="true" | ||||
|                             android:scaleType="centerCrop"/> | ||||
|                             android:scaleType="centerCrop" /> | ||||
| 
 | ||||
|                         <LinearLayout | ||||
|                             android:id="@+id/details_jidbox" | ||||
| @ -55,7 +56,7 @@ | ||||
|                                 android:layout_width="wrap_content" | ||||
|                                 android:layout_height="wrap_content" | ||||
|                                 android:text="@string/account_settings_example_jabber_id" | ||||
|                                 android:textAppearance="@style/TextAppearance.Conversations.Title"/> | ||||
|                                 android:textAppearance="@style/TextAppearance.Conversations.Title" /> | ||||
| 
 | ||||
|                             <com.wefika.flowlayout.FlowLayout | ||||
|                                 android:id="@+id/tags" | ||||
| @ -64,29 +65,28 @@ | ||||
|                                 android:layout_marginBottom="4dp" | ||||
|                                 android:layout_marginLeft="-2dp" | ||||
|                                 android:layout_marginTop="4dp" | ||||
|                                 android:orientation="horizontal"> | ||||
|                             </com.wefika.flowlayout.FlowLayout> | ||||
|                                 android:orientation="horizontal"></com.wefika.flowlayout.FlowLayout> | ||||
| 
 | ||||
|                             <TextView | ||||
|                                 android:id="@+id/details_lastseen" | ||||
|                                 android:layout_width="wrap_content" | ||||
|                                 android:layout_height="wrap_content" | ||||
|                                 android:layout_marginTop="4dp" | ||||
|                                 android:textAppearance="@style/TextAppearance.Conversations.Subhead"/> | ||||
|                                 android:textAppearance="@style/TextAppearance.Conversations.Subhead" /> | ||||
| 
 | ||||
|                             <TextView | ||||
|                                 android:id="@+id/status_message" | ||||
|                                 android:layout_width="wrap_content" | ||||
|                                 android:layout_height="wrap_content" | ||||
|                                 android:layout_marginTop="8dp" | ||||
|                                 android:textAppearance="@style/TextAppearance.Conversations.Body1"/> | ||||
|                                 android:textAppearance="@style/TextAppearance.Conversations.Body1" /> | ||||
| 
 | ||||
|                             <Button | ||||
|                                 android:id="@+id/add_contact_button" | ||||
|                                 android:layout_width="wrap_content" | ||||
|                                 android:layout_height="wrap_content" | ||||
|                                 android:layout_marginTop="8dp" | ||||
|                                 android:text="@string/add_contact"/> | ||||
|                                 android:text="@string/add_contact" /> | ||||
| 
 | ||||
|                             <CheckBox | ||||
|                                 android:id="@+id/details_send_presence" | ||||
| @ -95,7 +95,7 @@ | ||||
|                                 android:layout_height="wrap_content" | ||||
|                                 android:layout_marginTop="8dp" | ||||
|                                 android:text="@string/send_presence_updates" | ||||
|                                 android:textAppearance="@style/TextAppearance.Conversations.Body1"/> | ||||
|                                 android:textAppearance="@style/TextAppearance.Conversations.Body1" /> | ||||
| 
 | ||||
|                             <CheckBox | ||||
|                                 android:id="@+id/details_receive_presence" | ||||
| @ -103,7 +103,7 @@ | ||||
|                                 android:layout_width="wrap_content" | ||||
|                                 android:layout_height="wrap_content" | ||||
|                                 android:text="@string/receive_presence_updates" | ||||
|                                 android:textAppearance="@style/TextAppearance.Conversations.Body1"/> | ||||
|                                 android:textAppearance="@style/TextAppearance.Conversations.Body1" /> | ||||
|                         </LinearLayout> | ||||
| 
 | ||||
|                         <TextView | ||||
| @ -114,10 +114,56 @@ | ||||
|                             android:layout_below="@+id/details_jidbox" | ||||
|                             android:layout_marginTop="32dp" | ||||
|                             android:text="@string/using_account" | ||||
|                             android:textAppearance="@style/TextAppearance.Conversations.Caption"/> | ||||
|                             android:textAppearance="@style/TextAppearance.Conversations.Caption" /> | ||||
|                     </RelativeLayout> | ||||
|                 </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:id="@+id/keys_wrapper" | ||||
|                     android:layout_width="fill_parent" | ||||
| @ -137,8 +183,7 @@ | ||||
|                             android:layout_width="match_parent" | ||||
|                             android:layout_height="wrap_content" | ||||
|                             android:orientation="vertical" | ||||
|                             android:padding="@dimen/card_padding_list"> | ||||
|                         </LinearLayout> | ||||
|                             android:padding="@dimen/card_padding_list"/> | ||||
| 
 | ||||
|                         <LinearLayout | ||||
|                             android:layout_width="wrap_content" | ||||
| @ -156,7 +201,7 @@ | ||||
|                                 android:paddingLeft="16dp" | ||||
|                                 android:paddingRight="16dp" | ||||
|                                 android:text="@string/scan_qr_code" | ||||
|                                 android:textColor="?attr/colorAccent"/> | ||||
|                                 android:textColor="?attr/colorAccent" /> | ||||
| 
 | ||||
|                             <Button | ||||
|                                 android:id="@+id/show_inactive_devices" | ||||
| @ -167,7 +212,7 @@ | ||||
|                                 android:paddingLeft="16dp" | ||||
|                                 android:paddingRight="16dp" | ||||
|                                 android:text="@string/show_inactive_devices" | ||||
|                                 android:textColor="?attr/colorAccent"/> | ||||
|                                 android:textColor="?attr/colorAccent" /> | ||||
|                         </LinearLayout> | ||||
|                     </LinearLayout> | ||||
|                 </android.support.v7.widget.CardView> | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <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 | ||||
|         android:layout_width="match_parent" | ||||
| @ -207,6 +208,52 @@ | ||||
|                     </LinearLayout> | ||||
|                 </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:layout_width="match_parent" | ||||
|                     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="media_preview_size">80dp</dimen> | ||||
| 	<dimen name="media_size">64dp</dimen> | ||||
| 	<dimen name="toolbar_elevation">4dp</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_summary">Importance, Sound, Vibrate</string> | ||||
|     <string name="video_compression_channel_name">Video compression</string> | ||||
|     <string name="show_media">Show media</string> | ||||
| </resources> | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Daniel Gultsch
						Daniel Gultsch