From dc66b27c8ae3ad68538ce0920b48a248c4e8be94 Mon Sep 17 00:00:00 2001 From: LucasGGamerM Date: Sun, 11 Jun 2023 16:48:19 -0300 Subject: [PATCH 1/4] feat: add camera shortcut to compose screen --- mastodon/src/main/AndroidManifest.xml | 1 + .../android/fragments/ComposeFragment.java | 40 +++++++++++++++++-- .../drawable/ic_fluent_camera_24_regular.xml | 9 +++++ mastodon/src/main/res/menu/attach.xml | 4 ++ mastodon/src/main/res/values/strings_mo.xml | 1 + 5 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 mastodon/src/main/res/drawable/ic_fluent_camera_24_regular.xml diff --git a/mastodon/src/main/AndroidManifest.xml b/mastodon/src/main/AndroidManifest.xml index 54255565b..b648d1445 100644 --- a/mastodon/src/main/AndroidManifest.xml +++ b/mastodon/src/main/AndroidManifest.xml @@ -5,6 +5,7 @@ + diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java index fd9afde61..ccc58b6fc 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java @@ -1,7 +1,5 @@ package org.joinmastodon.android.fragments; -import static android.os.ext.SdkExtensions.getExtensionVersion; - import static org.joinmastodon.android.GlobalUserPreferences.recentLanguages; import static org.joinmastodon.android.api.requests.statuses.CreateStatus.DRAFTS_AFTER_INSTANT; import static org.joinmastodon.android.api.requests.statuses.CreateStatus.getDraftInstant; @@ -9,6 +7,7 @@ import static org.joinmastodon.android.ui.utils.UiUtils.isPhotoPickerAvailable; import static org.joinmastodon.android.utils.MastodonLanguage.allLanguages; import static org.joinmastodon.android.utils.MastodonLanguage.defaultRecentLanguages; +import android.Manifest; import android.animation.ObjectAnimator; import android.annotation.SuppressLint; import android.app.Activity; @@ -17,6 +16,7 @@ import android.app.TimePickerDialog; import android.content.ClipData; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.database.Cursor; import android.graphics.Bitmap; @@ -29,7 +29,6 @@ import android.graphics.drawable.LayerDrawable; import android.icu.text.BreakIterator; import android.media.MediaMetadataRetriever; import android.net.Uri; -import android.opengl.Visibility; import android.os.Build; import android.os.Bundle; import android.os.Parcelable; @@ -70,6 +69,8 @@ import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.NonNull; + import com.github.bottomSoftwareFoundation.bottom.Bottom; import com.twitter.twittertext.TwitterTextEmojiRegex; @@ -161,6 +162,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr private static final String GLITCH_LOCAL_ONLY_SUFFIX = "👁"; private static final Pattern GLITCH_LOCAL_ONLY_PATTERN = Pattern.compile("[\\s\\S]*" + GLITCH_LOCAL_ONLY_SUFFIX + "[\uFE00-\uFE0F]*"); private static final String TAG="ComposeFragment"; + private static final int CAMERA_PERMISSION_CODE = 626938; + private static final int CAMERA_PIC_REQUEST_CODE = 6242069; public static final Pattern MENTION_PATTERN=Pattern.compile("(^|[^\\/\\w])@(([a-z0-9_]+)@[a-z0-9\\.\\-]+[a-z0-9]+)", Pattern.CASE_INSENSITIVE); @@ -369,7 +372,17 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr PopupMenu attachPopup = new PopupMenu(getContext(), mediaBtn); attachPopup.inflate(R.menu.attach); attachPopup.setOnMenuItemClickListener(i -> { - openFilePicker(i.getItemId() == R.id.media); + if (i.getItemId() == R.id.camera){ + if (getContext().checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) { + Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + startActivityForResult(cameraIntent, CAMERA_PIC_REQUEST_CODE); + } else { + getActivity().requestPermissions(new String[] { Manifest.permission.CAMERA }, CAMERA_PERMISSION_CODE); + } + + } else { + openFilePicker(i.getItemId() == R.id.media); + } return true; }); UiUtils.enablePopupMenuIcons(getContext(), attachPopup); @@ -569,6 +582,19 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr if (scheduledStatus != null) outState.putParcelable("scheduledStatus", Parcels.wrap(scheduledStatus)); } + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + + if (requestCode == CAMERA_PERMISSION_CODE && (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)) { + Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + startActivityForResult(cameraIntent, CAMERA_PIC_REQUEST_CODE); + } else { + Toast.makeText(getContext(), R.string.permission_required, Toast.LENGTH_SHORT); + } + } + @Override public void onResume(){ super.onResume(); @@ -1427,6 +1453,12 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr } } } + + if(requestCode==CAMERA_PIC_REQUEST_CODE && resultCode==Activity.RESULT_OK){ + Bitmap image = (Bitmap) data.getExtras().get("data"); + String path = MediaStore.Images.Media.insertImage(getContext().getContentResolver(), image, null, null); + addMediaAttachment(Uri.parse(path), null); + } } private boolean addMediaAttachment(Uri uri, String description){ diff --git a/mastodon/src/main/res/drawable/ic_fluent_camera_24_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_camera_24_regular.xml new file mode 100644 index 000000000..f48869406 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_camera_24_regular.xml @@ -0,0 +1,9 @@ + + + diff --git a/mastodon/src/main/res/menu/attach.xml b/mastodon/src/main/res/menu/attach.xml index 6e3fa27a0..f1511e99d 100644 --- a/mastodon/src/main/res/menu/attach.xml +++ b/mastodon/src/main/res/menu/attach.xml @@ -8,4 +8,8 @@ android:id="@+id/file" android:title="@string/sk_attach_file" android:icon="@drawable/ic_fluent_document_24_regular" /> + \ No newline at end of file diff --git a/mastodon/src/main/res/values/strings_mo.xml b/mastodon/src/main/res/values/strings_mo.xml index 45f10ad35..b55cfe836 100644 --- a/mastodon/src/main/res/values/strings_mo.xml +++ b/mastodon/src/main/res/values/strings_mo.xml @@ -32,6 +32,7 @@ Reply as \'Unlisted\' by default Composer\'s Behavior Manage Notifications + Take picture Add new poll option From 986b9731a187769af70dee2eb361bd5b9af63900 Mon Sep 17 00:00:00 2001 From: LucasGGamerM Date: Sun, 11 Jun 2023 18:04:30 -0300 Subject: [PATCH 2/4] fix: fix attachment not adding only after accepting the permission prompt on camera shortcut --- .../joinmastodon/android/MainActivity.java | 29 +++++++++++++++++++ .../android/events/PictureTakenEvent.java | 13 +++++++++ .../android/fragments/ComposeFragment.java | 15 ++++++++-- 3 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 mastodon/src/main/java/org/joinmastodon/android/events/PictureTakenEvent.java diff --git a/mastodon/src/main/java/org/joinmastodon/android/MainActivity.java b/mastodon/src/main/java/org/joinmastodon/android/MainActivity.java index 7aea868fd..7e9bf6e34 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/MainActivity.java +++ b/mastodon/src/main/java/org/joinmastodon/android/MainActivity.java @@ -1,20 +1,28 @@ package org.joinmastodon.android; +import static org.joinmastodon.android.fragments.ComposeFragment.CAMERA_PERMISSION_CODE; +import static org.joinmastodon.android.fragments.ComposeFragment.CAMERA_PIC_REQUEST_CODE; + import android.Manifest; +import android.app.Activity; import android.app.Fragment; import android.app.assist.AssistContent; import android.content.Intent; import android.content.pm.PackageManager; +import android.graphics.Bitmap; import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.provider.MediaStore; import android.util.Log; import android.view.View; import android.widget.FrameLayout; +import android.widget.Toast; import org.joinmastodon.android.api.ObjectValidationException; import org.joinmastodon.android.api.session.AccountSession; import org.joinmastodon.android.api.session.AccountSessionManager; +import org.joinmastodon.android.events.PictureTakenEvent; import org.joinmastodon.android.fragments.ComposeFragment; import org.joinmastodon.android.fragments.HomeFragment; import org.joinmastodon.android.fragments.ProfileFragment; @@ -189,6 +197,27 @@ public class MainActivity extends FragmentStackActivity implements ProvidesAssis } } + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data){ + if(requestCode==CAMERA_PIC_REQUEST_CODE && resultCode== Activity.RESULT_OK){ + Bitmap image = (Bitmap) data.getExtras().get("data"); + String path = MediaStore.Images.Media.insertImage(this.getContentResolver(), image, null, null); + E.post(new PictureTakenEvent(Uri.parse(path))); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + + if (requestCode == CAMERA_PERMISSION_CODE && (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)) { + Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + startActivityForResult(cameraIntent, CAMERA_PIC_REQUEST_CODE); + } else { + Toast.makeText(this, R.string.permission_required, Toast.LENGTH_SHORT); + } + } + public Fragment getCurrentFragment() { for (int i = fragmentContainers.size() - 1; i >= 0; i--) { FrameLayout fl = fragmentContainers.get(i); diff --git a/mastodon/src/main/java/org/joinmastodon/android/events/PictureTakenEvent.java b/mastodon/src/main/java/org/joinmastodon/android/events/PictureTakenEvent.java new file mode 100644 index 000000000..80d1e1ce9 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/events/PictureTakenEvent.java @@ -0,0 +1,13 @@ +package org.joinmastodon.android.events; + +import android.net.Uri; + +import org.joinmastodon.android.model.Poll; + +public class PictureTakenEvent { + public Uri uri; + + public PictureTakenEvent(Uri uri){ + this.uri = uri; + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java index ccc58b6fc..b3431a33f 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java @@ -72,6 +72,7 @@ import android.widget.Toast; import androidx.annotation.NonNull; import com.github.bottomSoftwareFoundation.bottom.Bottom; +import com.squareup.otto.Subscribe; import com.twitter.twittertext.TwitterTextEmojiRegex; import org.joinmastodon.android.E; @@ -88,11 +89,14 @@ import org.joinmastodon.android.api.requests.statuses.GetAttachmentByID; import org.joinmastodon.android.api.requests.statuses.UploadAttachment; import org.joinmastodon.android.api.session.AccountSession; import org.joinmastodon.android.api.session.AccountSessionManager; +import org.joinmastodon.android.events.PictureTakenEvent; import org.joinmastodon.android.events.ScheduledStatusCreatedEvent; import org.joinmastodon.android.events.ScheduledStatusDeletedEvent; +import org.joinmastodon.android.events.SelfUpdateStateChangedEvent; import org.joinmastodon.android.events.StatusCountersUpdatedEvent; import org.joinmastodon.android.events.StatusCreatedEvent; import org.joinmastodon.android.events.StatusUpdatedEvent; +import org.joinmastodon.android.fragments.settings.SettingsBaseFragment; import org.joinmastodon.android.model.Account; import org.joinmastodon.android.model.Attachment; import org.joinmastodon.android.model.ContentType; @@ -114,6 +118,7 @@ import org.joinmastodon.android.ui.text.ComposeAutocompleteSpan; import org.joinmastodon.android.ui.text.ComposeHashtagOrMentionSpan; import org.joinmastodon.android.ui.text.HtmlParser; import org.joinmastodon.android.ui.utils.SimpleTextWatcher; +import org.joinmastodon.android.updater.GithubSelfUpdater; import org.joinmastodon.android.utils.TransferSpeedTracker; import org.joinmastodon.android.ui.utils.UiUtils; import org.joinmastodon.android.ui.views.ComposeEditText; @@ -162,8 +167,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr private static final String GLITCH_LOCAL_ONLY_SUFFIX = "👁"; private static final Pattern GLITCH_LOCAL_ONLY_PATTERN = Pattern.compile("[\\s\\S]*" + GLITCH_LOCAL_ONLY_SUFFIX + "[\uFE00-\uFE0F]*"); private static final String TAG="ComposeFragment"; - private static final int CAMERA_PERMISSION_CODE = 626938; - private static final int CAMERA_PIC_REQUEST_CODE = 6242069; + public static final int CAMERA_PERMISSION_CODE = 626938; + public static final int CAMERA_PIC_REQUEST_CODE = 6242069; public static final Pattern MENTION_PATTERN=Pattern.compile("(^|[^\\/\\w])@(([a-z0-9_]+)@[a-z0-9\\.\\-]+[a-z0-9]+)", Pattern.CASE_INSENSITIVE); @@ -249,6 +254,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); + E.register(this); setRetainInstance(true); navigationBarColorBefore = getActivity().getWindow().getNavigationBarColor(); getActivity().getWindow().setNavigationBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorBackgroundLightest)); @@ -1461,6 +1467,11 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr } } + @Subscribe + public void onPictureTaken(PictureTakenEvent ev){ + addMediaAttachment(ev.uri, null); + } + private boolean addMediaAttachment(Uri uri, String description){ if(getMediaAttachmentsCount()==MAX_ATTACHMENTS){ showMediaAttachmentError(getResources().getQuantityString(R.plurals.cant_add_more_than_x_attachments, MAX_ATTACHMENTS, MAX_ATTACHMENTS)); From 1675c8ab796f052275479d6c20dd9df4c6c484e7 Mon Sep 17 00:00:00 2001 From: LucasGGamerM Date: Sun, 11 Jun 2023 18:06:11 -0300 Subject: [PATCH 3/4] fix: unregister ComposeFragment for events on its destruction --- .../java/org/joinmastodon/android/fragments/ComposeFragment.java | 1 + 1 file changed, 1 insertion(+) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java index b3431a33f..e34c4c495 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java @@ -295,6 +295,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr @Override public void onDestroy(){ super.onDestroy(); + E.unregister(this); for(DraftMediaAttachment att:attachments){ if(att.isUploadingOrProcessing()) att.cancelUpload(); From b1cb75d45fc0637e35b35d65dd0edaef2bdbd5db Mon Sep 17 00:00:00 2001 From: LucasGGamerM Date: Sun, 11 Jun 2023 18:12:30 -0300 Subject: [PATCH 4/4] feat: still show the attach menu even if no PhotoPicker is avaiable This is due to the newly added camera shortcut, which makes the menu necessary even if no PhotoPicker is available --- .../android/fragments/ComposeFragment.java | 39 +++++++++---------- mastodon/src/main/res/menu/attach.xml | 1 + 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java index e34c4c495..c52d9fbfd 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java @@ -375,29 +375,28 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr view.findViewById(GlobalUserPreferences.replyLineAboveHeader ? R.id.reply_text_below : R.id.reply_text) .setVisibility(View.GONE); - if (isPhotoPickerAvailable()) { - PopupMenu attachPopup = new PopupMenu(getContext(), mediaBtn); - attachPopup.inflate(R.menu.attach); - attachPopup.setOnMenuItemClickListener(i -> { - if (i.getItemId() == R.id.camera){ - if (getContext().checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) { - Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - startActivityForResult(cameraIntent, CAMERA_PIC_REQUEST_CODE); - } else { - getActivity().requestPermissions(new String[] { Manifest.permission.CAMERA }, CAMERA_PERMISSION_CODE); - } + PopupMenu attachPopup = new PopupMenu(getContext(), mediaBtn); + attachPopup.inflate(R.menu.attach); + if(isPhotoPickerAvailable()) + attachPopup.getMenu().findItem(R.id.media).setVisible(true); + attachPopup.setOnMenuItemClickListener(i -> { + if (i.getItemId() == R.id.camera){ + if (getContext().checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) { + Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + startActivityForResult(cameraIntent, CAMERA_PIC_REQUEST_CODE); } else { - openFilePicker(i.getItemId() == R.id.media); + getActivity().requestPermissions(new String[] { Manifest.permission.CAMERA }, CAMERA_PERMISSION_CODE); } - return true; - }); - UiUtils.enablePopupMenuIcons(getContext(), attachPopup); - mediaBtn.setOnClickListener(v->attachPopup.show()); - mediaBtn.setOnTouchListener(attachPopup.getDragToOpenListener()); - } else { - mediaBtn.setOnClickListener(v -> openFilePicker(false)); - } + + } else { + openFilePicker(i.getItemId() == R.id.media); + } + return true; + }); + UiUtils.enablePopupMenuIcons(getContext(), attachPopup); + mediaBtn.setOnClickListener(v->attachPopup.show()); + mediaBtn.setOnTouchListener(attachPopup.getDragToOpenListener()); if (isInstancePixelfed()) pollBtn.setVisibility(View.GONE); pollBtn.setOnClickListener(v->togglePoll()); emojiBtn.setOnClickListener(v->emojiKeyboard.toggleKeyboardPopup(mainEditText)); diff --git a/mastodon/src/main/res/menu/attach.xml b/mastodon/src/main/res/menu/attach.xml index f1511e99d..6e8c92952 100644 --- a/mastodon/src/main/res/menu/attach.xml +++ b/mastodon/src/main/res/menu/attach.xml @@ -3,6 +3,7 @@