diff --git a/app/src/main/java/com/todoroo/astrid/activity/TaskListFragment.kt b/app/src/main/java/com/todoroo/astrid/activity/TaskListFragment.kt index 9ed9a236f..c490a2b41 100644 --- a/app/src/main/java/com/todoroo/astrid/activity/TaskListFragment.kt +++ b/app/src/main/java/com/todoroo/astrid/activity/TaskListFragment.kt @@ -66,6 +66,7 @@ import org.tasks.db.SuspendDbUtils.chunkedMap import org.tasks.dialogs.DateTimePicker.Companion.newDateTimePicker import org.tasks.dialogs.DialogBuilder import org.tasks.dialogs.SortDialog +import org.tasks.extensions.safeStartActivityForResult import org.tasks.filters.PlaceFilter import org.tasks.intents.TaskIntents import org.tasks.locale.Locale @@ -308,16 +309,23 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL override fun onMenuItemClick(item: MenuItem): Boolean { return when (item.itemId) { R.id.menu_voice_add -> { - val recognition = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH) - locale.languageOverride?.let { - recognition.putExtra(RecognizerIntent.EXTRA_LANGUAGE, it) - } - recognition.putExtra( - RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM) - recognition.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 1) - recognition.putExtra( - RecognizerIntent.EXTRA_PROMPT, getString(R.string.voice_create_prompt)) - startActivityForResult(recognition, VOICE_RECOGNITION_REQUEST_CODE) + safeStartActivityForResult( + Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).apply { + locale.languageOverride?.let { + putExtra(RecognizerIntent.EXTRA_LANGUAGE, it) + } + putExtra( + RecognizerIntent.EXTRA_LANGUAGE_MODEL, + RecognizerIntent.LANGUAGE_MODEL_FREE_FORM + ) + putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 1) + putExtra( + RecognizerIntent.EXTRA_PROMPT, + getString(R.string.voice_create_prompt) + ) + }, + VOICE_RECOGNITION_REQUEST_CODE + ) true } R.id.menu_sort -> { diff --git a/app/src/main/java/org/tasks/data/Place.kt b/app/src/main/java/org/tasks/data/Place.kt index 65f941426..36868471c 100644 --- a/app/src/main/java/org/tasks/data/Place.kt +++ b/app/src/main/java/org/tasks/data/Place.kt @@ -6,7 +6,6 @@ import android.location.Location import android.net.Uri import android.os.Parcel import android.os.Parcelable -import android.widget.Toast import androidx.room.* import com.mapbox.api.geocoding.v5.GeocodingCriteria import com.mapbox.api.geocoding.v5.models.CarmenFeature @@ -14,8 +13,8 @@ import com.todoroo.andlib.data.Table import com.todoroo.astrid.api.FilterListItem.NO_ORDER import com.todoroo.astrid.helper.UUIDHelper import net.fortuna.ical4j.model.property.Geo -import org.tasks.R import org.tasks.Strings +import org.tasks.extensions.safeStartActivity import org.tasks.location.MapPosition import org.tasks.themes.CustomIcons.PLACE import java.io.Serializable @@ -115,18 +114,12 @@ class Place : Serializable, Parcelable { get() = if (Strings.isNullOrEmpty(address)) null else address!!.replace("$name, ", "") fun open(context: Context?) { - if (context == null) { - return - } - val intent = Intent(Intent.ACTION_VIEW) - intent.data = Uri.parse("geo:$latitude,$longitude?q=${Uri.encode(displayName)}") - val pm = context.packageManager - val resolveInfos = pm.queryIntentActivities(intent, 0) - if (resolveInfos.isEmpty()) { - Toast.makeText(context, R.string.no_application_found_link, Toast.LENGTH_SHORT).show() - } else { - context.startActivity(intent) - } + context?.safeStartActivity( + Intent( + Intent.ACTION_VIEW, + Uri.parse("geo:$latitude,$longitude?q=${Uri.encode(displayName)}") + ) + ) } val mapPosition: MapPosition diff --git a/app/src/main/java/org/tasks/dialogs/AddAttachmentDialog.kt b/app/src/main/java/org/tasks/dialogs/AddAttachmentDialog.kt index 915a71248..c07b8f547 100644 --- a/app/src/main/java/org/tasks/dialogs/AddAttachmentDialog.kt +++ b/app/src/main/java/org/tasks/dialogs/AddAttachmentDialog.kt @@ -10,6 +10,7 @@ import com.todoroo.astrid.files.FilesControlSet import dagger.hilt.android.AndroidEntryPoint import org.tasks.R import org.tasks.activities.CameraActivity +import org.tasks.extensions.safeStartActivityForResult import org.tasks.files.FileHelper.newFilePickerIntent import org.tasks.preferences.Device import java.util.* @@ -28,12 +29,12 @@ class AddAttachmentDialog : DialogFragment() { entries.add(getString(R.string.take_a_picture)) actions.add(Runnable { takePicture() }) } - entries.add(getString(R.string.premium_record_audio)) - actions.add(Runnable { recordNote() }) - if (device.hasGallery()) { - entries.add(getString(R.string.pick_from_gallery)) - actions.add(Runnable { pickFromGallery() }) + if (device.hasMicrophone()) { + entries.add(getString(R.string.premium_record_audio)) + actions.add(Runnable { recordNote() }) } + entries.add(getString(R.string.pick_from_gallery)) + actions.add(Runnable { pickFromGallery() }) entries.add(getString(R.string.pick_from_storage)) actions.add(Runnable { pickFromStorage() }) return dialogBuilder @@ -50,20 +51,24 @@ class AddAttachmentDialog : DialogFragment() { } private fun recordNote() { - RecordAudioDialog.newRecordAudioDialog(targetFragment as FilesControlSet?, REQUEST_AUDIO) + RecordAudioDialog.newRecordAudioDialog(targetFragment, REQUEST_AUDIO) .show(parentFragmentManager, FRAG_TAG_RECORD_AUDIO) } private fun pickFromGallery() { - val intent = Intent(Intent.ACTION_PICK) - intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*") - if (intent.resolveActivity(context.packageManager) != null) { - targetFragment?.startActivityForResult(intent, REQUEST_GALLERY) - } + targetFragment?.safeStartActivityForResult( + Intent(Intent.ACTION_PICK).apply { + setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*") + }, + REQUEST_GALLERY + ) } private fun pickFromStorage() { - targetFragment?.startActivityForResult(newFilePickerIntent(activity, null), REQUEST_STORAGE) + targetFragment?.safeStartActivityForResult( + newFilePickerIntent(activity, null), + REQUEST_STORAGE + ) } companion object { diff --git a/app/src/main/java/org/tasks/dialogs/RecordAudioDialog.java b/app/src/main/java/org/tasks/dialogs/RecordAudioDialog.java index a8bf839db..4820efb3e 100644 --- a/app/src/main/java/org/tasks/dialogs/RecordAudioDialog.java +++ b/app/src/main/java/org/tasks/dialogs/RecordAudioDialog.java @@ -18,7 +18,6 @@ import androidx.lifecycle.ViewModelProvider; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; -import com.todoroo.astrid.files.FilesControlSet; import com.todoroo.astrid.voice.AACRecorder; import dagger.hilt.android.AndroidEntryPoint; import java.io.IOException; @@ -44,7 +43,7 @@ public class RecordAudioDialog extends DialogFragment implements AACRecorder.AAC private AACRecorder recorder; - static RecordAudioDialog newRecordAudioDialog(FilesControlSet target, int requestCode) { + static RecordAudioDialog newRecordAudioDialog(Fragment target, int requestCode) { RecordAudioDialog dialog = new RecordAudioDialog(); dialog.setTargetFragment(target, requestCode); return dialog; diff --git a/app/src/main/java/org/tasks/extensions/ContextExtensions.kt b/app/src/main/java/org/tasks/extensions/ContextExtensions.kt new file mode 100644 index 000000000..acaf696f5 --- /dev/null +++ b/app/src/main/java/org/tasks/extensions/ContextExtensions.kt @@ -0,0 +1,30 @@ +package org.tasks.extensions + +import android.content.ActivityNotFoundException +import android.content.Context +import android.content.Intent +import android.widget.Toast +import androidx.fragment.app.Fragment +import org.tasks.R + +fun Context.safeStartActivity(intent: Intent) { + try { + startActivity(intent) + } catch (e: ActivityNotFoundException) { + toast(this) + } +} + +fun Fragment.safeStartActivityForResult(intent: Intent, rc: Int) { + try { + startActivityForResult(intent, rc) + } catch(e: ActivityNotFoundException) { + toast(context) + } +} + +private fun toast(context: Context?) { + context?.let { + Toast.makeText(it, R.string.no_application_found, Toast.LENGTH_LONG).show() + } +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/files/FileHelper.kt b/app/src/main/java/org/tasks/files/FileHelper.kt index 2262e22e0..24f910faa 100644 --- a/app/src/main/java/org/tasks/files/FileHelper.kt +++ b/app/src/main/java/org/tasks/files/FileHelper.kt @@ -10,7 +10,6 @@ import android.os.Build import android.provider.DocumentsContract import android.provider.OpenableColumns import android.webkit.MimeTypeMap -import android.widget.Toast import androidx.core.content.FileProvider import androidx.documentfile.provider.DocumentFile import androidx.fragment.app.Fragment @@ -18,8 +17,8 @@ import com.google.common.collect.Iterables import com.google.common.io.ByteStreams import com.google.common.io.Files import com.todoroo.astrid.utility.Constants -import org.tasks.R import org.tasks.Strings.isNullOrEmpty +import org.tasks.extensions.safeStartActivity import timber.log.Timber import java.io.File import java.io.FileNotFoundException @@ -147,17 +146,8 @@ object FileHelper { } val share = FileProvider.getUriForFile(context, Constants.FILE_PROVIDER_AUTHORITY, File(uri.path)) intent.setDataAndType(share, mimeType) - grantReadPermissions(intent) - val packageManager = context.packageManager - if (intent.resolveActivity(packageManager) != null) { - context.startActivity(intent) - } else { - Toast.makeText(context, R.string.no_application_found, Toast.LENGTH_SHORT).show() - } - } - - private fun grantReadPermissions(intent: Intent) { intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + context.safeStartActivity(intent) } @JvmStatic diff --git a/app/src/main/java/org/tasks/preferences/Device.java b/app/src/main/java/org/tasks/preferences/Device.java index dd0fdda05..0b81a150c 100644 --- a/app/src/main/java/org/tasks/preferences/Device.java +++ b/app/src/main/java/org/tasks/preferences/Device.java @@ -7,7 +7,6 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.Build; -import android.provider.MediaStore; import android.speech.RecognizerIntent; import com.google.common.base.Joiner; import dagger.hilt.android.qualifiers.ApplicationContext; @@ -30,17 +29,11 @@ public class Device { } public boolean hasCamera() { - return context - .getPackageManager() - .queryIntentActivities(new Intent(MediaStore.ACTION_IMAGE_CAPTURE), 0) - .size() - > 0; + return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA); } - public boolean hasGallery() { - Intent intent = new Intent(Intent.ACTION_PICK); - intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*"); - return intent.resolveActivity(context.getPackageManager()) != null; + public boolean hasMicrophone() { + return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE); } public boolean supportsGeofences() {