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/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 fd9afde61..c52d9fbfd 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,7 +69,10 @@ import android.widget.ScrollView; import android.widget.TextView; 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; @@ -87,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; @@ -113,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; @@ -161,6 +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"; + 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); @@ -246,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)); @@ -286,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(); @@ -365,19 +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 -> { + 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 { + getActivity().requestPermissions(new String[] { Manifest.permission.CAMERA }, CAMERA_PERMISSION_CODE); + } + + } else { openFilePicker(i.getItemId() == R.id.media); - return true; - }); - UiUtils.enablePopupMenuIcons(getContext(), attachPopup); - mediaBtn.setOnClickListener(v->attachPopup.show()); - mediaBtn.setOnTouchListener(attachPopup.getDragToOpenListener()); - } else { - mediaBtn.setOnClickListener(v -> openFilePicker(false)); - } + } + 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)); @@ -569,6 +588,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 +1459,17 @@ 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); + } + } + + @Subscribe + public void onPictureTaken(PictureTakenEvent ev){ + addMediaAttachment(ev.uri, 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..6e8c92952 100644 --- a/mastodon/src/main/res/menu/attach.xml +++ b/mastodon/src/main/res/menu/attach.xml @@ -3,9 +3,14 @@ + \ 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