From 1e63a8291579043659f86dbb46d4d63d3225c471 Mon Sep 17 00:00:00 2001 From: Christian Schneppe Date: Sun, 23 Sep 2018 16:42:56 +0200 Subject: preview media before sending them and allow sharing and attaching of multiple files --- .../pixart/messenger/ui/ConversationFragment.java | 224 ++++++---------- .../pixart/messenger/ui/ConversationsActivity.java | 15 +- .../de/pixart/messenger/ui/SearchActivity.java | 8 +- .../de/pixart/messenger/ui/SettingsActivity.java | 3 +- .../de/pixart/messenger/ui/ShareWithActivity.java | 297 +++------------------ .../messenger/ui/adapter/AccountAdapter.java | 8 +- .../messenger/ui/adapter/ConversationAdapter.java | 10 +- .../messenger/ui/adapter/ListItemAdapter.java | 8 +- .../messenger/ui/adapter/MediaPreviewAdapter.java | 211 +++++++++++++++ .../messenger/ui/forms/FormFieldWrapper.java | 8 +- .../de/pixart/messenger/ui/util/Attachment.java | 120 +++++++++ .../pixart/messenger/ui/util/AttachmentTool.java | 60 ----- .../java/de/pixart/messenger/ui/util/Color.java | 48 ---- .../java/de/pixart/messenger/ui/util/Drawable.java | 43 --- .../pixart/messenger/ui/util/StyledAttributes.java | 60 +++++ 15 files changed, 545 insertions(+), 578 deletions(-) create mode 100644 src/main/java/de/pixart/messenger/ui/adapter/MediaPreviewAdapter.java create mode 100644 src/main/java/de/pixart/messenger/ui/util/Attachment.java delete mode 100644 src/main/java/de/pixart/messenger/ui/util/AttachmentTool.java delete mode 100644 src/main/java/de/pixart/messenger/ui/util/Color.java delete mode 100644 src/main/java/de/pixart/messenger/ui/util/Drawable.java create mode 100644 src/main/java/de/pixart/messenger/ui/util/StyledAttributes.java (limited to 'src/main/java/de/pixart/messenger/ui') diff --git a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java index def4b8a69..451ac6bde 100644 --- a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java +++ b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java @@ -15,8 +15,6 @@ import android.content.IntentSender.SendIntentException; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.databinding.DataBindingUtil; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -27,7 +25,6 @@ import android.provider.MediaStore; import android.support.annotation.IdRes; import android.support.annotation.NonNull; import android.support.annotation.StringRes; -import android.support.media.ExifInterface; import android.support.v13.view.inputmethod.InputConnectionCompat; import android.support.v13.view.inputmethod.InputContentInfoCompat; import android.support.v7.app.AlertDialog; @@ -54,16 +51,12 @@ import android.widget.AbsListView.OnScrollListener; import android.widget.AdapterView; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.CheckBox; -import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView.OnEditorActionListener; import android.widget.Toast; import net.java.otr4j.session.SessionStatus; -import java.io.File; -import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; @@ -98,9 +91,10 @@ import de.pixart.messenger.http.HttpDownloadConnection; import de.pixart.messenger.persistance.FileBackend; import de.pixart.messenger.services.MessageArchiveService; import de.pixart.messenger.services.XmppConnectionService; +import de.pixart.messenger.ui.adapter.MediaPreviewAdapter; import de.pixart.messenger.ui.adapter.MessageAdapter; import de.pixart.messenger.ui.util.ActivityResult; -import de.pixart.messenger.ui.util.AttachmentTool; +import de.pixart.messenger.ui.util.Attachment; import de.pixart.messenger.ui.util.ConversationMenuConfigurator; import de.pixart.messenger.ui.util.DateSeparator; import de.pixart.messenger.ui.util.EditMessageActionModeCallback; @@ -113,7 +107,6 @@ import de.pixart.messenger.ui.util.SendButtonAction; import de.pixart.messenger.ui.util.SendButtonTool; import de.pixart.messenger.ui.util.ShareUtil; import de.pixart.messenger.ui.widget.EditMessage; -import de.pixart.messenger.utils.FileUtils; import de.pixart.messenger.utils.MenuDoubleTabUtil; import de.pixart.messenger.utils.MessageUtils; import de.pixart.messenger.utils.NickValidityChecker; @@ -129,7 +122,6 @@ import rocks.xmpp.addr.Jid; import static de.pixart.messenger.ui.XmppActivity.EXTRA_ACCOUNT; import static de.pixart.messenger.ui.XmppActivity.REQUEST_INVITE_TO_CONVERSATION; -import static de.pixart.messenger.ui.util.SendButtonAction.TEXT; import static de.pixart.messenger.ui.util.SoftKeyboardUtils.hideSoftKeyboard; import static de.pixart.messenger.xmpp.Patches.ENCRYPTION_EXCEPTIONS; @@ -179,6 +171,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke private Toast messageLoaderToast; private ConversationsActivity activity; private boolean reInitRequiredOnStart = true; + private MediaPreviewAdapter mediaPreviewAdapter; private SimpleFingerGestures gesturesDetector = new SimpleFingerGestures(); @@ -851,111 +844,19 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke } public void attachEditorContentToConversation(Uri uri) { - this.attachFileToConversation(conversation, uri, null); + mediaPreviewAdapter.addMediaPreviews(Attachment.of(getActivity(), uri, Attachment.Type.FILE)); + toggleInputMethod(); } - private void attachImageToConversation(Conversation conversation, Uri uri, boolean sendAsIs) { + private void attachImageToConversation(Conversation conversation, Uri uri) { if (conversation == null) { return; } - if (sendAsIs) { - sendImage(conversation, uri); - return; - } - final Conversation conversation_preview = conversation; - final Uri uri_preview = uri; - Bitmap bitmap = null; - try { - bitmap = BitmapFactory.decodeFile(FileUtils.getPath(activity, uri)); - } catch (Exception e) { - e.printStackTrace(); - } - File file = null; - ExifInterface exif = null; - int orientation = 0; - try { - file = new File(FileUtils.getPath(activity, uri)); - } catch (Exception e) { - e.printStackTrace(); - } - if (file != null) { - try { - exif = new ExifInterface(file.toString()); - } catch (IOException e) { - e.printStackTrace(); - } - orientation = exif != null ? exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL) : 0; - } - Log.d(Config.LOGTAG, "EXIF: " + orientation); - Bitmap rotated_image = null; - Log.d(Config.LOGTAG, "Rotate image"); - try { - rotated_image = FileBackend.rotateBitmap(file, bitmap, orientation); - } catch (Exception e) { - e.printStackTrace(); - } - if (rotated_image != null) { - int scaleSize = 600; - int originalWidth = rotated_image.getWidth(); - int originalHeight = rotated_image.getHeight(); - int newWidth = -1; - int newHeight = -1; - float multFactor; - if (originalHeight > originalWidth) { - newHeight = scaleSize; - multFactor = (float) originalWidth / (float) originalHeight; - newWidth = (int) (newHeight * multFactor); - } else if (originalWidth > originalHeight) { - newWidth = scaleSize; - multFactor = (float) originalHeight / (float) originalWidth; - newHeight = (int) (newWidth * multFactor); - } else if (originalHeight == originalWidth) { - newHeight = scaleSize; - newWidth = scaleSize; - } - Log.d(Config.LOGTAG, "Scaling preview image from " + originalHeight + "px x " + originalWidth + "px to " + newHeight + "px x " + newWidth + "px"); - Bitmap preview = null; - - try { - preview = Bitmap.createScaledBitmap(rotated_image, newWidth, newHeight, false); - } catch (Exception e) { - e.printStackTrace(); - } - if (preview != null) { - ImageView ImagePreview = new ImageView(activity); - LinearLayout.LayoutParams vp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); - ImagePreview.setLayoutParams(vp); - ImagePreview.setMaxWidth(newWidth); - ImagePreview.setMaxHeight(newHeight); - ImagePreview.setPadding(5, 5, 5, 5); - ImagePreview.setImageBitmap(preview); - getActivity().runOnUiThread(() -> { - AlertDialog.Builder builder = new AlertDialog.Builder(activity); - builder.setView(ImagePreview); - builder.setTitle(R.string.send_image); - builder.setPositiveButton(R.string.ok, (dialog, which) -> sendImage(conversation_preview, uri_preview)); - builder.setOnCancelListener(dialog -> mPendingImageUris.clear()); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - builder.setOnDismissListener(dialog -> mPendingImageUris.clear()); - } - AlertDialog alertDialog = builder.create(); - alertDialog.show(); - }); - } else { - getActivity().runOnUiThread(() -> Toast.makeText(getActivity(), getText(R.string.error_file_not_found), Toast.LENGTH_LONG).show()); - } - } else { - getActivity().runOnUiThread(() -> Toast.makeText(getActivity(), getText(R.string.error_file_not_found), Toast.LENGTH_LONG).show()); - } - } - - private void sendImage(Conversation conversation, Uri uri) { final Toast prepareFileToast = Toast.makeText(getActivity(), getText(R.string.preparing_image), Toast.LENGTH_LONG); prepareFileToast.show(); activity.delegateUriPermissionsToService(uri); activity.xmppConnectionService.attachImageToConversation(conversation, uri, new UiCallback() { - @Override public void userInputRequried(PendingIntent pi, Message object) { hidePrepareFileToast(prepareFileToast); @@ -981,6 +882,10 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke } private void sendMessage() { + if (mediaPreviewAdapter.hasAttachments()) { + commitAttachments(); + return; + } final String body = binding.textinput.getText().toString(); final Conversation conversation = this.conversation; if (body.length() == 0 || conversation == null) { @@ -1086,7 +991,6 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke } private void handlePositiveActivityResult(int requestCode, final Intent data) { - final String type = data == null ? null : data.getType(); switch (requestCode) { case REQUEST_TRUST_KEYS_TEXT: final String body = binding.textinput.getText().toString(); @@ -1098,51 +1002,32 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke selectPresenceToAttachFile(choice); break; case ATTACHMENT_CHOICE_CHOOSE_IMAGE: - final List imageUris = AttachmentTool.extractUriFromIntent(data); - final int ImageUrisCount = imageUris.size(); - Log.d(Config.LOGTAG, "ConversationsActivity.onActivityResult() - attaching image - number of uris: " + ImageUrisCount); - if (ImageUrisCount == 1) { - Uri uri = imageUris.get(0); - Log.d(Config.LOGTAG, "ConversationsActivity.onActivityResult() - attaching image to conversations. CHOOSE_IMAGE"); - attachImageToConversation(conversation, uri, false); - } else { - for (Iterator i = imageUris.iterator(); i.hasNext(); i.remove()) { - Log.d(Config.LOGTAG, "ConversationsActivity.onActivityResult() - attaching images to conversations. CHOOSE_IMAGES"); - attachImagesToConversation(conversation, i.next()); - } - } + final List imageUris = Attachment.extractAttachments(getActivity(), data, Attachment.Type.IMAGE); + mediaPreviewAdapter.addMediaPreviews(imageUris); + toggleInputMethod(); break; case ATTACHMENT_CHOICE_TAKE_FROM_CAMERA: final Uri takePhotoUri = pendingTakePhotoUri.pop(); - final Uri takeVideoUri = pendingTakeVideoUri.pop(); if (takePhotoUri != null) { - attachPhotoToConversation(conversation, takePhotoUri); - } else if (takeVideoUri != null) { - attachFileToConversation(conversation, takeVideoUri, type); + mediaPreviewAdapter.addMediaPreviews(Attachment.of(getActivity(), takePhotoUri, Attachment.Type.IMAGE)); + toggleInputMethod(); } else { - Log.d(Config.LOGTAG, "lost take uri. unable to to attach"); + Log.d(Config.LOGTAG, "lost take photo uri. unable to to attach"); } break; case ATTACHMENT_CHOICE_CHOOSE_FILE: case ATTACHMENT_CHOICE_RECORD_VOICE: - final List fileUris = AttachmentTool.extractUriFromIntent(data); - final PresenceSelector.OnPresenceSelected callback = () -> { - for (Iterator i = fileUris.iterator(); i.hasNext(); i.remove()) { - Log.d(Config.LOGTAG, "ConversationsActivity.onActivityResult() - attaching file to conversations. CHOOSE_FILE/RECORD_VOICE"); - attachFileToConversation(conversation, i.next(), type); - } - }; - if (conversation == null || conversation.getMode() == Conversation.MODE_MULTI || FileBackend.allFilesUnderSize(getActivity(), fileUris, getMaxHttpUploadSize(conversation))) { - callback.onPresenceSelected(); - } else { - activity.selectPresence(conversation, callback); - } + final Attachment.Type type = requestCode == ATTACHMENT_CHOICE_RECORD_VOICE ? Attachment.Type.RECORDING : Attachment.Type.FILE; + final List fileUris = Attachment.extractAttachments(getActivity(), data, type); + mediaPreviewAdapter.addMediaPreviews(fileUris); + toggleInputMethod(); break; case ATTACHMENT_CHOICE_LOCATION: double latitude = data.getDoubleExtra("latitude", 0); double longitude = data.getDoubleExtra("longitude", 0); Uri geo = Uri.parse("geo:" + String.valueOf(latitude) + "," + String.valueOf(longitude)); - attachLocationToConversation(conversation, geo); + mediaPreviewAdapter.addMediaPreviews(Attachment.of(getActivity(), geo, Attachment.Type.LOCATION)); + toggleInputMethod(); break; case REQUEST_INVITE_TO_CONVERSATION: XmppActivity.ConferenceInvite invite = XmppActivity.ConferenceInvite.parse(data); @@ -1156,6 +1041,38 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke } } + private void commitAttachments() { + final List attachments = mediaPreviewAdapter.getAttachments(); + final PresenceSelector.OnPresenceSelected callback = () -> { + for (Iterator i = attachments.iterator(); i.hasNext(); i.remove()) { + final Attachment attachment = i.next(); + if (attachment.getType() == Attachment.Type.LOCATION) { + attachLocationToConversation(conversation, attachment.getUri()); + } else if (attachment.getType() == Attachment.Type.IMAGE) { + Log.d(Config.LOGTAG, "ConversationsActivity.commitAttachments() - attaching image to conversations. CHOOSE_IMAGE"); + attachImageToConversation(conversation, attachment.getUri()); + } else { + Log.d(Config.LOGTAG, "ConversationsActivity.commitAttachments() - attaching file to conversations. CHOOSE_FILE/RECORD_VOICE/RECORD_VIDEO"); + attachFileToConversation(conversation, attachment.getUri(), attachment.getMime()); + } + } + mediaPreviewAdapter.notifyDataSetChanged(); + toggleInputMethod(); + }; + if (conversation == null || conversation.getMode() == Conversation.MODE_MULTI || FileBackend.allFilesUnderSize(getActivity(), attachments, getMaxHttpUploadSize(conversation))) { + callback.onPresenceSelected(); + } else { + activity.selectPresence(conversation, callback); + } + } + + public void toggleInputMethod() { + boolean hasAttachments = mediaPreviewAdapter.hasAttachments(); + binding.textinput.setVisibility(hasAttachments ? View.GONE : View.VISIBLE); + binding.mediaPreview.setVisibility(hasAttachments ? View.VISIBLE : View.GONE); + updateSendButton(); + } + private void handleNegativeActivityResult(int requestCode) { switch (requestCode) { //nothing to do for now @@ -1253,6 +1170,8 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke binding.messagesView.setOnScrollListener(mOnScrollListener); binding.messagesView.setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL); + mediaPreviewAdapter = new MediaPreviewAdapter(this); + binding.mediaPreview.setAdapter(mediaPreviewAdapter); messageListAdapter = new MessageAdapter((XmppActivity) getActivity(), this.messageList); messageListAdapter.setOnContactPictureClicked(message -> { String fingerprint; @@ -1871,6 +1790,7 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke case ATTACHMENT_CHOICE_CHOOSE_FILE: chooser = true; intent.setType("*/*"); + intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setAction(Intent.ACTION_GET_CONTENT); break; @@ -2361,6 +2281,12 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke final String nick = extras.getString(ConversationsActivity.EXTRA_NICK); final boolean asQuote = extras.getBoolean(ConversationsActivity.EXTRA_AS_QUOTE); final boolean pm = extras.getBoolean(ConversationsActivity.EXTRA_IS_PRIVATE_MESSAGE, false); + final List uris = extractUris(extras); + if (uris != null && uris.size() > 0) { + mediaPreviewAdapter.addMediaPreviews(Attachment.of(getActivity(), uris)); + toggleInputMethod(); + return; + } if (nick != null) { if (pm) { Jid jid = conversation.getJid(); @@ -2389,6 +2315,19 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke } } + private List extractUris(Bundle extras) { + final List uris = extras.getParcelableArrayList(Intent.EXTRA_STREAM); + if (uris != null) { + return uris; + } + final Uri uri = extras.getParcelable(Intent.EXTRA_STREAM); + if (uri != null) { + return Collections.singletonList(uri); + } else { + return null; + } + } + private boolean showBlockSubmenu(View view) { final Jid jid = conversation.getJid(); if (jid.getLocal() == null) { @@ -2603,11 +2542,17 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke } public void updateSendButton() { + boolean hasAttachments = mediaPreviewAdapter != null && mediaPreviewAdapter.hasAttachments(); boolean useSendButtonToIndicateStatus = PreferenceManager.getDefaultSharedPreferences(getActivity()).getBoolean("send_button_status", getResources().getBoolean(R.bool.send_button_status)); final Conversation c = this.conversation; final Presence.Status status; final String text = this.binding.textinput == null ? "" : this.binding.textinput.getText().toString(); - SendButtonAction action = SendButtonTool.getAction(getActivity(), c, text); + final SendButtonAction action; + if (hasAttachments) { + action = SendButtonAction.TEXT; + } else { + action = SendButtonTool.getAction(getActivity(), c, text); + } if (useSendButtonToIndicateStatus && c.getAccount().getStatus() == Account.State.ONLINE) { if (activity.xmppConnectionService != null && activity.xmppConnectionService.getMessageArchiveService().isCatchingUp(c)) { status = Presence.Status.OFFLINE; @@ -2619,9 +2564,6 @@ public class ConversationFragment extends XmppFragment implements EditMessage.Ke } else { status = Presence.Status.OFFLINE; } - if (action.toString().equals("CHOOSE_ATTACHMENT") && !activity.xmppConnectionService.getAttachmentChoicePreference()) { - action = TEXT; - } this.binding.textSendButton.setTag(action); this.binding.textSendButton.setImageResource(SendButtonTool.getSendButtonImageResource(getActivity(), action, status)); } diff --git a/src/main/java/de/pixart/messenger/ui/ConversationsActivity.java b/src/main/java/de/pixart/messenger/ui/ConversationsActivity.java index 11d4cc31f..05cfe3ab4 100644 --- a/src/main/java/de/pixart/messenger/ui/ConversationsActivity.java +++ b/src/main/java/de/pixart/messenger/ui/ConversationsActivity.java @@ -63,6 +63,7 @@ import net.java.otr4j.session.SessionStatus; import org.openintents.openpgp.util.OpenPgpApi; +import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -111,6 +112,11 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio public static final String ACTION_DESTROY_MUC = "de.pixart.messenger.DESTROY_MUC"; public static final int REQUEST_OPEN_MESSAGE = 0x9876; public static final int REQUEST_PLAY_PAUSE = 0x5432; + private static List VIEW_AND_SHARE_ACTIONS = Arrays.asList( + ACTION_VIEW_CONVERSATION, + Intent.ACTION_SEND, + Intent.ACTION_SEND_MULTIPLE + ); private boolean showLastSeen = false; @@ -126,8 +132,9 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio private boolean mActivityPaused = true; private AtomicBoolean mRedirectInProcess = new AtomicBoolean(false); - private static boolean isViewIntent(Intent i) { - return i != null && ACTION_VIEW_CONVERSATION.equals(i.getAction()) && i.hasExtra(EXTRA_CONVERSATION); + private static boolean isViewOrShareIntent(Intent i) { + Log.d(Config.LOGTAG, "action: " + (i == null ? null : i.getAction())); + return i != null && VIEW_AND_SHARE_ACTIONS.contains(i.getAction()) && i.hasExtra(EXTRA_CONVERSATION); } private static Intent createLauncherIntent(Context context) { @@ -451,7 +458,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio } else { intent = savedInstanceState.getParcelable("intent"); } - if (isViewIntent(intent)) { + if (isViewOrShareIntent(intent)) { pendingViewIntent.push(intent); setIntent(createLauncherIntent(this)); } @@ -626,7 +633,7 @@ public class ConversationsActivity extends XmppActivity implements OnConversatio @Override protected void onNewIntent(final Intent intent) { - if (isViewIntent(intent)) { + if (isViewOrShareIntent(intent)) { if (xmppConnectionService != null) { processViewIntent(intent); } else { diff --git a/src/main/java/de/pixart/messenger/ui/SearchActivity.java b/src/main/java/de/pixart/messenger/ui/SearchActivity.java index cc3172670..9ea9e7592 100644 --- a/src/main/java/de/pixart/messenger/ui/SearchActivity.java +++ b/src/main/java/de/pixart/messenger/ui/SearchActivity.java @@ -56,7 +56,7 @@ import de.pixart.messenger.services.MessageSearchTask; import de.pixart.messenger.ui.adapter.MessageAdapter; import de.pixart.messenger.ui.interfaces.OnSearchResultsAvailable; import de.pixart.messenger.ui.util.ChangeWatcher; -import de.pixart.messenger.ui.util.Color; +import de.pixart.messenger.ui.util.StyledAttributes; import de.pixart.messenger.ui.util.DateSeparator; import de.pixart.messenger.ui.util.Drawable; import de.pixart.messenger.ui.util.ListViewUtils; @@ -213,12 +213,12 @@ public class SearchActivity extends XmppActivity implements TextWatcher, OnSearc private void changeBackground(boolean hasSearch, boolean hasResults) { if (hasSearch) { if (hasResults) { - binding.searchResults.setBackgroundColor(Color.get(this, R.attr.color_background_secondary)); + binding.searchResults.setBackgroundColor(StyledAttributes.getColor(this, R.attr.color_background_secondary)); } else { - binding.searchResults.setBackground(Drawable.get(this, R.attr.activity_background_no_results)); + binding.searchResults.setBackground(StyledAttributes.getDrawable(this, R.attr.activity_background_no_results)); } } else { - binding.searchResults.setBackground(Drawable.get(this, R.attr.activity_background_search)); + binding.searchResults.setBackground(StyledAttributes.getDrawable(this, R.attr.activity_background_search)); } } diff --git a/src/main/java/de/pixart/messenger/ui/SettingsActivity.java b/src/main/java/de/pixart/messenger/ui/SettingsActivity.java index 0e4b5d391..c9c6006de 100644 --- a/src/main/java/de/pixart/messenger/ui/SettingsActivity.java +++ b/src/main/java/de/pixart/messenger/ui/SettingsActivity.java @@ -38,7 +38,6 @@ import de.pixart.messenger.crypto.OmemoSetting; import de.pixart.messenger.entities.Account; import de.pixart.messenger.services.ExportLogsService; import de.pixart.messenger.services.MemorizingTrustManager; -import de.pixart.messenger.ui.util.Color; import de.pixart.messenger.utils.TimeframeUtils; import rocks.xmpp.addr.Jid; @@ -85,7 +84,7 @@ public class SettingsActivity extends XmppActivity implements fm.beginTransaction().replace(R.id.settings_content, mSettingsFragment).commit(); } mSettingsFragment.setActivityIntent(getIntent()); - getWindow().getDecorView().setBackgroundColor(Color.get(this, R.attr.color_background_secondary)); + getWindow().getDecorView().setBackgroundColor(StyledAttributes.getColor(this, R.attr.color_background_secondary)); setSupportActionBar(findViewById(R.id.toolbar)); configureActionBar(getSupportActionBar()); } diff --git a/src/main/java/de/pixart/messenger/ui/ShareWithActivity.java b/src/main/java/de/pixart/messenger/ui/ShareWithActivity.java index a820378a3..da3d2ff8f 100644 --- a/src/main/java/de/pixart/messenger/ui/ShareWithActivity.java +++ b/src/main/java/de/pixart/messenger/ui/ShareWithActivity.java @@ -1,6 +1,5 @@ package de.pixart.messenger.ui; -import android.app.PendingIntent; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; @@ -12,116 +11,32 @@ import android.view.Menu; import android.view.MenuItem; import android.widget.Toast; -import java.net.URLConnection; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; import de.pixart.messenger.Config; import de.pixart.messenger.R; import de.pixart.messenger.entities.Account; import de.pixart.messenger.entities.Conversation; -import de.pixart.messenger.entities.Message; -import de.pixart.messenger.persistance.FileBackend; import de.pixart.messenger.services.EmojiService; import de.pixart.messenger.services.XmppConnectionService; import de.pixart.messenger.ui.adapter.ConversationAdapter; -import de.pixart.messenger.ui.util.PresenceSelector; -import de.pixart.messenger.xmpp.XmppConnection; import rocks.xmpp.addr.Jid; import static de.pixart.messenger.ui.SettingsActivity.USE_BUNDLED_EMOJIS; -import static java.lang.String.format; public class ShareWithActivity extends XmppActivity implements XmppConnectionService.OnConversationUpdate { private static final int REQUEST_STORAGE_PERMISSION = 0x733f32; - private boolean mReturnToPrevious = false; + private static final int REQUEST_START_NEW_CONVERSATION = 0x0501; private Conversation mPendingConversation = null; - - @Override - public void onConversationUpdate() { - refreshUi(); - } - - private class Share { - public List uris = new ArrayList<>(); - public boolean image; - public String account; - public String contact; - public String text; - public String uuid; - public boolean multiple = false; - public String type; - } - private Share share; - - private static final int REQUEST_START_NEW_CONVERSATION = 0x0501; - private RecyclerView mListView; private ConversationAdapter mAdapter; private List mConversations = new ArrayList<>(); - private Toast mToast; - private AtomicInteger attachmentCounter = new AtomicInteger(0); - - private UiInformableCallback attachFileCallback = new UiInformableCallback() { - - @Override - public void inform(final String text) { - runOnUiThread(() -> replaceToast(text)); - } - - @Override - public void userInputRequried(PendingIntent pi, Message object) { - // TODO Auto-generated method stub - - } - - @Override - public void success(final Message message) { - runOnUiThread(() -> { - if (attachmentCounter.decrementAndGet() <= 0) { - int resId; - if (share.image && share.multiple) { - resId = R.string.shared_images_with_x; - } else if (share.image) { - resId = R.string.shared_image_with_x; - } else { - resId = R.string.shared_file_with_x; - } - Conversation conversation = (Conversation) message.getConversation(); - replaceToast(getString(resId, conversation.getName())); - if (mReturnToPrevious) { - finish(); - } else { - switchToConversation(conversation); - } - } - }); - } - - @Override - public void error(final int errorCode, Message object) { - runOnUiThread(() -> { - replaceToast(getString(errorCode)); - if (attachmentCounter.decrementAndGet() <= 0) { - finish(); - } - }); - } - }; - - protected void hideToast() { - if (mToast != null) { - mToast.cancel(); - } - } - protected void replaceToast(String msg) { - hideToast(); - mToast = Toast.makeText(this, msg, Toast.LENGTH_LONG); - mToast.show(); + @Override + public void onConversationUpdate() { + refreshUi(); } protected void onActivityResult(int requestCode, int resultCode, final Intent data) { @@ -160,20 +75,16 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer super.onCreate(savedInstanceState); boolean useBundledEmoji = getPreferences().getBoolean(USE_BUNDLED_EMOJIS, getResources().getBoolean(R.bool.use_bundled_emoji)); new EmojiService(this).init(useBundledEmoji); - setContentView(R.layout.activity_share_with); - setSupportActionBar(findViewById(R.id.toolbar)); if (getSupportActionBar() != null) { getSupportActionBar().setDisplayHomeAsUpEnabled(false); getSupportActionBar().setHomeButtonEnabled(false); } - setTitle(getString(R.string.title_activity_sharewith)); - - mListView = findViewById(R.id.choose_conversation_list); + RecyclerView mListView = findViewById(R.id.choose_conversation_list); mAdapter = new ConversationAdapter(this, this.mConversations); - mListView.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false)); + mListView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)); mListView.setAdapter(mAdapter); mAdapter.setConversationClickListener((view, conversation) -> share(conversation)); this.share = new Share(); @@ -203,57 +114,28 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer if (intent == null) { return; } - this.mReturnToPrevious = getBooleanPreference("return_to_previous", R.bool.return_to_previous); final String type = intent.getType(); final String action = intent.getAction(); - Log.d(Config.LOGTAG, "action: " + action + ", type:" + type); - share.uuid = intent.getStringExtra("uuid"); if (Intent.ACTION_SEND.equals(action)) { - final String subject = intent.getStringExtra(Intent.EXTRA_SUBJECT); final String text = intent.getStringExtra(Intent.EXTRA_TEXT); final Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM); if (type != null && uri != null && (text == null || !type.equals("text/plain"))) { this.share.uris.clear(); this.share.uris.add(uri); - this.share.image = type.startsWith("image/") || isImage(uri); - this.share.type = type; } else { - if (subject != null) { - this.share.text = format("[%s]%n%s", subject, text); - } else { - this.share.text = text; - } + this.share.text = text; } } else if (Intent.ACTION_SEND_MULTIPLE.equals(action)) { - this.share.image = type != null && type.startsWith("image/"); - if (!this.share.image) { - return; - } this.share.uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); } if (xmppConnectionServiceBound) { - if (share.uuid != null) { - share(); - } else { - xmppConnectionService.populateWithOrderedConversations(mConversations, this.share.uris.size() == 0); - } - } - - } - - protected boolean isImage(Uri uri) { - try { - String guess = URLConnection.guessContentTypeFromName(uri.toString()); - return (guess != null && guess.startsWith("image/")); - } catch (final StringIndexOutOfBoundsException ignored) { - return false; + xmppConnectionService.populateWithOrderedConversations(mConversations, this.share.uris.size() == 0); } } @Override void onBackendConnected() { - if (xmppConnectionServiceBound && share != null - && ((share.contact != null && share.account != null) || share.uuid != null)) { + if (xmppConnectionServiceBound && share != null && ((share.contact != null && share.account != null))) { share(); return; } @@ -262,28 +144,19 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer private void share() { final Conversation conversation; - if (share.uuid != null) { - conversation = xmppConnectionService.findConversationByUuid(share.uuid); - if (conversation == null) { - return; - } - } else { - Account account; - try { - account = xmppConnectionService.findAccountByJid(Jid.of(share.account)); - } catch (final IllegalArgumentException e) { - account = null; - } - if (account == null) { - return; - } - - try { - conversation = xmppConnectionService - .findOrCreateConversation(account, Jid.of(share.contact), false, true); - } catch (final IllegalArgumentException e) { - return; - } + Account account; + try { + account = xmppConnectionService.findAccountByJid(Jid.of(share.account)); + } catch (final IllegalArgumentException e) { + account = null; + } + if (account == null) { + return; + } + try { + conversation = xmppConnectionService.findOrCreateConversation(account, Jid.of(share.contact), false, true); + } catch (final IllegalArgumentException e) { + return; } share(conversation); } @@ -293,110 +166,18 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer mPendingConversation = conversation; return; } - final Account account = conversation.getAccount(); - final XmppConnection connection = account.getXmppConnection(); - final long max = connection == null ? -1 : connection.getFeatures().getMaxHttpUploadSize(); - mListView.setEnabled(false); - if (conversation.getNextEncryption() == Message.ENCRYPTION_PGP && !hasPgp()) { - if (share.uuid == null) { - showInstallPgpDialog(); - } else { - Toast.makeText(this, R.string.openkeychain_not_installed, Toast.LENGTH_SHORT).show(); - finish(); - } - return; + Intent intent = new Intent(this, ConversationsActivity.class); + intent.putExtra(ConversationsActivity.EXTRA_CONVERSATION, conversation.getUuid()); + if (share.uris.size() > 0) { + intent.setAction(Intent.ACTION_SEND_MULTIPLE); + intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, share.uris); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + } else if (share.text != null) { + intent.setAction(ConversationsActivity.ACTION_VIEW_CONVERSATION); + intent.putExtra(ConversationsActivity.EXTRA_TEXT, share.text); } - if (share.uris.size() != 0) { - PresenceSelector.OnPresenceSelected callback = () -> { - attachmentCounter.set(share.uris.size()); - if (share.image) { - Log.d(Config.LOGTAG, "ShareWithActivity share() image " + share.uris.size() + " uri(s) " + share.uris.toString()); - share.multiple = share.uris.size() > 1; - replaceToast(getString(share.multiple ? R.string.preparing_images : R.string.preparing_image)); - for (Iterator i = share.uris.iterator(); i.hasNext(); i.remove()) { - final Uri uri = i.next(); - delegateUriPermissionsToService(uri); - xmppConnectionService.attachImageToConversation(conversation, uri, attachFileCallback); - } - } else { - Log.d(Config.LOGTAG, "ShareWithActivity share() file " + share.uris.size() + " uri(s) " + share.uris.toString()); - replaceToast(getString(R.string.preparing_file)); - final Uri uri = share.uris.get(0); - delegateUriPermissionsToService(uri); - xmppConnectionService.attachFileToConversation(conversation, uri, share.type, attachFileCallback); - finish(); - } - }; - if (account.httpUploadAvailable() - && ((share.image && !neverCompressPictures()) - || conversation.getMode() == Conversation.MODE_MULTI - || FileBackend.allFilesUnderSize(this, share.uris, max)) - && conversation.getNextEncryption() != Message.ENCRYPTION_OTR) { - callback.onPresenceSelected(); - } else { - selectPresence(conversation, callback); - } - } else { - if (mReturnToPrevious && this.share.text != null && !this.share.text.isEmpty()) { - final PresenceSelector.OnPresenceSelected callback = new PresenceSelector.OnPresenceSelected() { - - private void finishAndSend(Message message) { - replaceToast(getString(R.string.shared_text_with_x, conversation.getName())); - finish(); - } - - private UiCallback messageEncryptionCallback = new UiCallback() { - @Override - public void success(final Message message) { - runOnUiThread(() -> finishAndSend(message)); - } - - @Override - public void error(final int errorCode, Message object) { - runOnUiThread(() -> { - replaceToast(getString(errorCode)); - finish(); - }); - } - - @Override - public void userInputRequried(PendingIntent pi, Message object) { - finish(); - } - }; - - @Override - public void onPresenceSelected() { - - final int encryption = conversation.getNextEncryption(); - - Message message = new Message(conversation,share.text, encryption); - - Log.d(Config.LOGTAG,"on presence selected encrpytion="+encryption); - - if (encryption == Message.ENCRYPTION_PGP) { - replaceToast(getString(R.string.encrypting_message)); - xmppConnectionService.getPgpEngine().encrypt(message,messageEncryptionCallback); - return; - } - - if (encryption == Message.ENCRYPTION_OTR) { - message.setCounterpart(conversation.getNextCounterpart()); - } - xmppConnectionService.sendMessage(message); - finishAndSend(message); - } - }; - if (conversation.getNextEncryption() == Message.ENCRYPTION_OTR) { - selectPresence(conversation, callback); - } else { - callback.onPresenceSelected(); - } - } else { - switchToConversation(conversation, this.share.text, true); - } - } - + startActivity(intent); + finish(); } public void refreshUiReal() { @@ -404,12 +185,10 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer mAdapter.notifyDataSetChanged(); } - @Override - public void onBackPressed() { - if (attachmentCounter.get() >= 1) { - replaceToast(getString(R.string.sharing_files_please_wait)); - } else { - super.onBackPressed(); - } + private class Share { + public String account; + public String contact; + public String text; + ArrayList uris = new ArrayList<>(); } } \ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/ui/adapter/AccountAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/AccountAdapter.java index 01b432d65..e37ea6121 100644 --- a/src/main/java/de/pixart/messenger/ui/adapter/AccountAdapter.java +++ b/src/main/java/de/pixart/messenger/ui/adapter/AccountAdapter.java @@ -24,7 +24,7 @@ import de.pixart.messenger.R; import de.pixart.messenger.entities.Account; import de.pixart.messenger.ui.ManageAccountActivity; import de.pixart.messenger.ui.XmppActivity; -import de.pixart.messenger.ui.util.Color; +import de.pixart.messenger.ui.util.StyledAttributes; import de.pixart.messenger.utils.UIHelper; public class AccountAdapter extends ArrayAdapter { @@ -65,14 +65,14 @@ public class AccountAdapter extends ArrayAdapter { statusView.setText(getContext().getString(account.getStatus().getReadableId())); switch (account.getStatus()) { case ONLINE: - statusView.setTextColor(Color.get(activity, R.attr.TextColorOnline)); + statusView.setTextColor(StyledAttributes.getColor(activity, R.attr.TextColorOnline)); break; case DISABLED: case CONNECTING: - statusView.setTextColor(Color.get(activity, android.R.attr.textColorSecondary)); + statusView.setTextColor(StyledAttributes.getColor(activity, android.R.attr.textColorSecondary)); break; default: - statusView.setTextColor(Color.get(activity, R.attr.TextColorError)); + statusView.setTextColor(StyledAttributes.getColor(activity, R.attr.TextColorError)); break; } final SwitchCompat tglAccountState = view.findViewById(R.id.tgl_account_status); diff --git a/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java index 36cae0eb3..55dd6d65f 100644 --- a/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java +++ b/src/main/java/de/pixart/messenger/ui/adapter/ConversationAdapter.java @@ -31,7 +31,7 @@ import de.pixart.messenger.entities.MucOptions; import de.pixart.messenger.entities.Transferable; import de.pixart.messenger.ui.ConversationFragment; import de.pixart.messenger.ui.XmppActivity; -import de.pixart.messenger.ui.util.Color; +import de.pixart.messenger.ui.util.StyledAttributes; import de.pixart.messenger.ui.widget.UnreadCountCustomView; import de.pixart.messenger.utils.EmojiWrapper; import de.pixart.messenger.utils.IrregularUnicodeDetector; @@ -97,9 +97,9 @@ public class ConversationAdapter extends RecyclerView.Adapter { } } if (offline) { - viewHolder.name.setTextColor(Color.get(activity, R.attr.text_Color_Main)); + viewHolder.name.setTextColor(StyledAttributes.get(activity, R.attr.text_Color_Main)); viewHolder.name.setAlpha(INACTIVE_ALPHA); viewHolder.jid.setAlpha(INACTIVE_ALPHA); viewHolder.avatar.setAlpha(INACTIVE_ALPHA); viewHolder.tags.setAlpha(INACTIVE_ALPHA); } else { if (ShowPresenceColoredNames()) { - viewHolder.name.setTextColor(color != 0 ? color : Color.get(activity, R.attr.text_Color_Main)); + viewHolder.name.setTextColor(color != 0 ? color : StyledAttributes.get(activity, R.attr.text_Color_Main)); } else { - viewHolder.name.setTextColor(Color.get(activity, R.attr.text_Color_Main)); + viewHolder.name.setTextColor(StyledAttributes.get(activity, R.attr.text_Color_Main)); } viewHolder.name.setAlpha(ACTIVE_ALPHA); viewHolder.jid.setAlpha(ACTIVE_ALPHA); diff --git a/src/main/java/de/pixart/messenger/ui/adapter/MediaPreviewAdapter.java b/src/main/java/de/pixart/messenger/ui/adapter/MediaPreviewAdapter.java new file mode 100644 index 000000000..bbcaa5dc6 --- /dev/null +++ b/src/main/java/de/pixart/messenger/ui/adapter/MediaPreviewAdapter.java @@ -0,0 +1,211 @@ +package de.pixart.messenger.ui.adapter; + +import android.app.Activity; +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.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.List; +import java.util.concurrent.RejectedExecutionException; + +import de.pixart.messenger.R; +import de.pixart.messenger.databinding.MediaPreviewBinding; +import de.pixart.messenger.ui.ConversationFragment; +import de.pixart.messenger.ui.XmppActivity; +import de.pixart.messenger.ui.util.Attachment; +import de.pixart.messenger.ui.util.StyledAttributes; + +public class MediaPreviewAdapter extends RecyclerView.Adapter { + + private final List mediaPreviews = new ArrayList<>(); + + private final ConversationFragment conversationFragment; + + public MediaPreviewAdapter(ConversationFragment fragment) { + this.conversationFragment = fragment; + } + + 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 MediaPreviewViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); + MediaPreviewBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.media_preview, parent, false); + return new MediaPreviewViewHolder(binding); + } + + @Override + public void onBindViewHolder(@NonNull MediaPreviewViewHolder holder, int position) { + final Context context = conversationFragment.getActivity(); + final Attachment attachment = mediaPreviews.get(position); + if (attachment.renderThumbnail()) { + holder.binding.mediaPreview.setImageAlpha(255); + 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_file; + } 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 { + attr = R.attr.media_preview_file; + } + } + holder.binding.mediaPreview.setImageDrawable(StyledAttributes.getDrawable(context, attr)); + } + holder.binding.deleteButton.setOnClickListener(v -> { + int pos = mediaPreviews.indexOf(attachment); + mediaPreviews.remove(pos); + notifyItemRemoved(pos); + conversationFragment.toggleInputMethod(); + }); + } + + public void addMediaPreviews(List attachments) { + this.mediaPreviews.addAll(attachments); + notifyDataSetChanged(); + } + + private void loadPreview(Attachment attachment, ImageView imageView) { + if (cancelPotentialWork(attachment, imageView)) { + XmppActivity activity = (XmppActivity) conversationFragment.getActivity(); + final Bitmap bm = activity.xmppConnectionService.getFileBackend().getPreviewForUri(attachment, Math.round(activity.getResources().getDimension(R.dimen.media_preview_size)), 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(conversationFragment.getActivity().getResources(), null, task); + imageView.setImageDrawable(asyncDrawable); + try { + task.execute(attachment); + } catch (final RejectedExecutionException ignored) { + } + } + } + } + + @Override + public int getItemCount() { + return mediaPreviews.size(); + } + + public boolean hasAttachments() { + return mediaPreviews.size() > 0; + } + + public List getAttachments() { + return mediaPreviews; + } + + static class AsyncDrawable extends BitmapDrawable { + private final WeakReference bitmapWorkerTaskReference; + + AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) { + super(res, bitmap); + bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask); + } + + BitmapWorkerTask getBitmapWorkerTask() { + return bitmapWorkerTaskReference.get(); + } + } + + class MediaPreviewViewHolder extends RecyclerView.ViewHolder { + + private final MediaPreviewBinding binding; + + MediaPreviewViewHolder(MediaPreviewBinding binding) { + super(binding.getRoot()); + this.binding = binding; + } + } + + class BitmapWorkerTask extends AsyncTask { + private final WeakReference imageViewReference; + private Attachment attachment = null; + + BitmapWorkerTask(ImageView imageView) { + imageViewReference = new WeakReference<>(imageView); + } + + @Override + protected Bitmap doInBackground(Attachment... params) { + Activity activity = conversationFragment.getActivity(); + if (activity instanceof XmppActivity) { + final XmppActivity xmppActivity = (XmppActivity) activity; + this.attachment = params[0]; + return xmppActivity.xmppConnectionService.getFileBackend().getPreviewForUri(this.attachment, Math.round(xmppActivity.getResources().getDimension(R.dimen.media_preview_size)), false); + } else { + return null; + } + } + + @Override + protected void onPostExecute(Bitmap bitmap) { + if (bitmap != null && !isCancelled()) { + final ImageView imageView = imageViewReference.get(); + if (imageView != null) { + imageView.setImageBitmap(bitmap); + imageView.setBackgroundColor(0x00000000); + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/ui/forms/FormFieldWrapper.java b/src/main/java/de/pixart/messenger/ui/forms/FormFieldWrapper.java index adcc11809..0dfd86327 100644 --- a/src/main/java/de/pixart/messenger/ui/forms/FormFieldWrapper.java +++ b/src/main/java/de/pixart/messenger/ui/forms/FormFieldWrapper.java @@ -1,7 +1,6 @@ package de.pixart.messenger.ui.forms; import android.content.Context; -import android.support.v4.content.ContextCompat; import android.text.SpannableString; import android.text.style.ForegroundColorSpan; import android.text.style.StyleSpan; @@ -11,6 +10,7 @@ import android.view.View; import java.util.List; import de.pixart.messenger.R; +import de.pixart.messenger.ui.util.StyledAttributes; import de.pixart.messenger.xmpp.forms.Field; public abstract class FormFieldWrapper { @@ -18,9 +18,9 @@ public abstract class FormFieldWrapper { protected final Context context; protected final Field field; protected final View view; - protected OnFormFieldValuesEdited onFormFieldValuesEditedListener; + OnFormFieldValuesEdited onFormFieldValuesEditedListener; - protected FormFieldWrapper(Context context, Field field) { + FormFieldWrapper(Context context, Field field) { this.context = context; this.field = field; LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); @@ -58,7 +58,7 @@ public abstract class FormFieldWrapper { int start = label.length(); int end = label.length() + 2; spannableString.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), start, end, 0); - spannableString.setSpan(new ForegroundColorSpan(ContextCompat.getColor(context, R.color.accent)), start, end, 0); + spannableString.setSpan(new ForegroundColorSpan(StyledAttributes.getColor(context, R.color.accent)), start, end, 0); } return spannableString; } diff --git a/src/main/java/de/pixart/messenger/ui/util/Attachment.java b/src/main/java/de/pixart/messenger/ui/util/Attachment.java new file mode 100644 index 000000000..8c1d0c75e --- /dev/null +++ b/src/main/java/de/pixart/messenger/ui/util/Attachment.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2018, Daniel Gultsch All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package de.pixart.messenger.ui.util; + +import android.content.ClipData; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +import de.pixart.messenger.Config; +import de.pixart.messenger.utils.MimeUtils; + +public class Attachment { + private final Uri uri; + private final Type type; + private final UUID uuid; + private final String mime; + private Attachment(Uri uri, Type type, String mime) { + this.uri = uri; + this.type = type; + this.mime = mime; + this.uuid = UUID.randomUUID(); + } + + public static List of(final Context context, Uri uri, Type type) { + final String mime = type == Type.LOCATION ? null : MimeUtils.guessMimeTypeFromUri(context, uri); + return Collections.singletonList(new Attachment(uri, type, mime)); + } + + public static List of(final Context context, List uris) { + List attachments = new ArrayList<>(); + for (Uri uri : uris) { + final String mime = MimeUtils.guessMimeTypeFromUri(context, uri); + attachments.add(new Attachment(uri, mime != null && mime.startsWith("image/") ? Type.IMAGE : Type.FILE, mime)); + } + return attachments; + } + + public static List extractAttachments(final Context context, final Intent intent, Type type) { + List uris = new ArrayList<>(); + if (intent == null) { + return uris; + } + final String contentType = intent.getType(); + final Uri data = intent.getData(); + if (data == null) { + final ClipData clipData = intent.getClipData(); + if (clipData != null) { + for (int i = 0; i < clipData.getItemCount(); ++i) { + final Uri uri = clipData.getItemAt(i).getUri(); + Log.d(Config.LOGTAG, "uri=" + uri + " contentType=" + contentType); + final String mime = contentType != null ? contentType : MimeUtils.guessMimeTypeFromUri(context, uri); + Log.d(Config.LOGTAG, "mime=" + mime); + uris.add(new Attachment(uri, type, mime)); + } + } + } else { + final String mime = contentType != null ? contentType : MimeUtils.guessMimeTypeFromUri(context, data); + uris.add(new Attachment(data, type, mime)); + } + return uris; + } + + public String getMime() { + return mime; + } + + public Type getType() { + return type; + } + + public boolean renderThumbnail() { + return type == Type.IMAGE || (type == Type.FILE && mime != null && (mime.startsWith("video/") || mime.startsWith("image/"))); + } + + public Uri getUri() { + return uri; + } + + public UUID getUuid() { + return uuid; + } + + public enum Type { + FILE, IMAGE, LOCATION, RECORDING + } +} \ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/ui/util/AttachmentTool.java b/src/main/java/de/pixart/messenger/ui/util/AttachmentTool.java deleted file mode 100644 index c56f27ebf..000000000 --- a/src/main/java/de/pixart/messenger/ui/util/AttachmentTool.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2018, Daniel Gultsch All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation and/or - * other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package de.pixart.messenger.ui.util; - -import android.annotation.SuppressLint; -import android.content.ClipData; -import android.content.Intent; -import android.net.Uri; - -import java.util.ArrayList; -import java.util.List; - -public class AttachmentTool { - @SuppressLint("NewApi") - public static List extractUriFromIntent(final Intent intent) { - List uris = new ArrayList<>(); - if (intent == null) { - return uris; - } - final Uri uri = intent.getData(); - if (uri == null) { - final ClipData clipData = intent.getClipData(); - if (clipData != null) { - for (int i = 0; i < clipData.getItemCount(); ++i) { - uris.add(clipData.getItemAt(i).getUri()); - } - } - } else { - uris.add(uri); - } - return uris; - } -} \ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/ui/util/Color.java b/src/main/java/de/pixart/messenger/ui/util/Color.java deleted file mode 100644 index 58ce6a465..000000000 --- a/src/main/java/de/pixart/messenger/ui/util/Color.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2018, Daniel Gultsch All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation and/or - * other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package de.pixart.messenger.ui.util; - - -import android.content.Context; -import android.content.res.TypedArray; -import android.support.annotation.AttrRes; -import android.support.annotation.ColorInt; - -public class Color { - - public static @ColorInt - int get(Context context, @AttrRes int attr) { - TypedArray typedArray = context.obtainStyledAttributes(new int[]{attr}); - int color = typedArray.getColor(0, 0); - typedArray.recycle(); - return color; - } - -} \ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/ui/util/Drawable.java b/src/main/java/de/pixart/messenger/ui/util/Drawable.java deleted file mode 100644 index 5e0c770d4..000000000 --- a/src/main/java/de/pixart/messenger/ui/util/Drawable.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2018, Daniel Gultsch All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation and/or - * other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package de.pixart.messenger.ui.util; - -import android.content.Context; -import android.content.res.TypedArray; -import android.support.annotation.AttrRes; - -public class Drawable { - public static android.graphics.drawable.Drawable get(Context context, @AttrRes int id) { - TypedArray typedArray = context.obtainStyledAttributes(new int[]{id}); - android.graphics.drawable.Drawable drawable = typedArray.getDrawable(0); - typedArray.recycle(); - return drawable; - } -} \ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/ui/util/StyledAttributes.java b/src/main/java/de/pixart/messenger/ui/util/StyledAttributes.java new file mode 100644 index 000000000..cd4ad3e49 --- /dev/null +++ b/src/main/java/de/pixart/messenger/ui/util/StyledAttributes.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018, Daniel Gultsch All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package de.pixart.messenger.ui.util; + + +import android.content.Context; +import android.content.res.TypedArray; +import android.support.annotation.AttrRes; +import android.support.annotation.ColorInt; + +public class StyledAttributes { + public static android.graphics.drawable.Drawable getDrawable(Context context, @AttrRes int id) { + TypedArray typedArray = context.obtainStyledAttributes(new int[]{id}); + android.graphics.drawable.Drawable drawable = typedArray.getDrawable(0); + typedArray.recycle(); + return drawable; + } + + public static float getFloat(Context context, @AttrRes int id) { + TypedArray typedArray = context.obtainStyledAttributes(new int[]{id}); + float value = typedArray.getFloat(0, 0f); + typedArray.recycle(); + return value; + } + + public static @ColorInt + int getColor(Context context, @AttrRes int attr) { + TypedArray typedArray = context.obtainStyledAttributes(new int[]{attr}); + int color = typedArray.getColor(0, 0); + typedArray.recycle(); + return color; + } +} \ No newline at end of file -- cgit v1.2.3