diff --git a/app/src/main/java/acr/browser/lightning/app/AppComponent.java b/app/src/main/java/acr/browser/lightning/app/AppComponent.java index 8ca9d5c..c47e3f4 100644 --- a/app/src/main/java/acr/browser/lightning/app/AppComponent.java +++ b/app/src/main/java/acr/browser/lightning/app/AppComponent.java @@ -14,6 +14,7 @@ import acr.browser.lightning.constant.HistoryPage; import acr.browser.lightning.constant.StartPage; import acr.browser.lightning.database.history.HistoryDatabase; import acr.browser.lightning.dialog.LightningDialogBuilder; +import acr.browser.lightning.download.DownloadHandler; import acr.browser.lightning.download.LightningDownloadListener; import acr.browser.lightning.fragment.BookmarkSettingsFragment; import acr.browser.lightning.fragment.BookmarksFragment; @@ -80,6 +81,8 @@ public interface AppComponent { void inject(LightningChromeClient chromeClient); + void inject(DownloadHandler downloadHandler); + HistoryDatabase historyDatabase(); } diff --git a/app/src/main/java/acr/browser/lightning/app/AppModule.java b/app/src/main/java/acr/browser/lightning/app/AppModule.java index 9ae607a..32fbf0c 100644 --- a/app/src/main/java/acr/browser/lightning/app/AppModule.java +++ b/app/src/main/java/acr/browser/lightning/app/AppModule.java @@ -14,6 +14,7 @@ import acr.browser.lightning.database.downloads.DownloadsDatabase; import acr.browser.lightning.database.downloads.DownloadsModel; import acr.browser.lightning.database.history.HistoryDatabase; import acr.browser.lightning.database.history.HistoryModel; +import acr.browser.lightning.download.DownloadHandler; import dagger.Module; import dagger.Provides; @@ -56,6 +57,13 @@ public class AppModule { return new HistoryDatabase(mApp); } + @NonNull + @Provides + @Singleton + public DownloadHandler provideDownloadHandler() { + return new DownloadHandler(); + } + @NonNull @Provides @Singleton diff --git a/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java b/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java index 26d9ed2..7d7d8eb 100644 --- a/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java +++ b/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java @@ -8,9 +8,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.app.AlertDialog; import android.text.TextUtils; -import android.util.Log; import android.view.View; -import android.webkit.URLUtil; import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; import android.widget.EditText; @@ -31,13 +29,12 @@ import acr.browser.lightning.constant.Constants; import acr.browser.lightning.controller.UIController; import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.database.bookmark.BookmarkModel; -import acr.browser.lightning.database.downloads.DownloadItem; import acr.browser.lightning.database.downloads.DownloadsModel; import acr.browser.lightning.database.history.HistoryModel; +import acr.browser.lightning.download.DownloadHandler; import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.utils.IntentUtils; import acr.browser.lightning.utils.Preconditions; -import acr.browser.lightning.utils.Utils; /** * TODO Rename this class it doesn't build dialogs only for bookmarks @@ -57,6 +54,7 @@ public class LightningDialogBuilder { @Inject DownloadsModel mDownloadsModel; @Inject HistoryModel mHistoryModel; @Inject PreferenceManager mPreferenceManager; + @Inject DownloadHandler mDownloadHandler; @Inject public LightningDialogBuilder() { @@ -385,16 +383,7 @@ public class LightningDialogBuilder { new BrowserDialog.Item(R.string.dialog_download_image) { @Override public void onClick() { - Utils.downloadFile(activity, mPreferenceManager, url, userAgent, "attachment"); - - mDownloadsModel.addDownloadIfNotExists(new DownloadItem(url, URLUtil.guessFileName(url, null, null), "")) - .subscribe(new SingleOnSubscribe() { - @Override - public void onItem(@Nullable Boolean item) { - if (item != null && !item) - Log.i(TAG, "error saving download to database"); - } - }); + mDownloadHandler.onDownloadStart(activity, mPreferenceManager, url, userAgent, "attachment", null, ""); } }); } diff --git a/app/src/main/java/acr/browser/lightning/download/DownloadHandler.java b/app/src/main/java/acr/browser/lightning/download/DownloadHandler.java index 6f02b92..2bc9344 100644 --- a/app/src/main/java/acr/browser/lightning/download/DownloadHandler.java +++ b/app/src/main/java/acr/browser/lightning/download/DownloadHandler.java @@ -23,16 +23,26 @@ import android.webkit.CookieManager; import android.webkit.MimeTypeMap; import android.webkit.URLUtil; +import com.anthonycr.bonsai.SingleOnSubscribe; + import java.io.File; import java.io.IOException; +import javax.inject.Inject; + import acr.browser.lightning.BuildConfig; import acr.browser.lightning.R; +import acr.browser.lightning.activity.BrowserActivity; import acr.browser.lightning.activity.MainActivity; +import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.constant.Constants; +import acr.browser.lightning.database.downloads.DownloadItem; +import acr.browser.lightning.database.downloads.DownloadsModel; import acr.browser.lightning.dialog.BrowserDialog; import acr.browser.lightning.preference.PreferenceManager; +import acr.browser.lightning.utils.FileUtils; import acr.browser.lightning.utils.Utils; +import acr.browser.lightning.view.LightningView; /** * Handle download requests @@ -43,9 +53,11 @@ public class DownloadHandler { private static final String COOKIE_REQUEST_HEADER = "Cookie"; - public static final String DEFAULT_DOWNLOAD_PATH = - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) - .getPath(); + @Inject DownloadsModel downloadsModel; + + public DownloadHandler() { + BrowserApp.getAppComponent().inject(this); + } /** * Notify the host application a download should be done, or that the data @@ -56,9 +68,10 @@ public class DownloadHandler { * @param userAgent User agent of the downloading application. * @param contentDisposition Content-disposition http header, if present. * @param mimetype The mimetype of the content reported by the server + * @param contentSize The size of the content */ - public static void onDownloadStart(@NonNull Activity context, @NonNull PreferenceManager manager, String url, String userAgent, - @Nullable String contentDisposition, String mimetype) { + public void onDownloadStart(@NonNull Activity context, @NonNull PreferenceManager manager, String url, String userAgent, + @Nullable String contentDisposition, String mimetype, String contentSize) { Log.d(TAG, "DOWNLOAD: Trying to download from URL: " + url); Log.d(TAG, "DOWNLOAD: Content disposition: " + contentDisposition); @@ -68,7 +81,7 @@ public class DownloadHandler { // if we're dealing wih A/V content that's not explicitly marked // for download, check if it's streamable. if (contentDisposition == null - || !contentDisposition.regionMatches(true, 0, "attachment", 0, 10)) { + || !contentDisposition.regionMatches(true, 0, "attachment", 0, 10)) { // query the package manager to see if there's a registered handler // that matches. Intent intent = new Intent(Intent.ACTION_VIEW); @@ -80,12 +93,12 @@ public class DownloadHandler { intent.setSelector(null); } ResolveInfo info = context.getPackageManager().resolveActivity(intent, - PackageManager.MATCH_DEFAULT_ONLY); + PackageManager.MATCH_DEFAULT_ONLY); if (info != null) { // If we resolved to ourselves, we don't want to attempt to // load the url only to try and download it again. if (BuildConfig.APPLICATION_ID.equals(info.activityInfo.packageName) - || MainActivity.class.getName().equals(info.activityInfo.name)) { + || MainActivity.class.getName().equals(info.activityInfo.name)) { // someone (other than us) knows how to handle this mime // type with this scheme, don't download. try { @@ -98,7 +111,7 @@ public class DownloadHandler { } } } - onDownloadStartNoStream(context, manager, url, userAgent, contentDisposition, mimetype); + onDownloadStartNoStream(context, manager, url, userAgent, contentDisposition, mimetype, contentSize); } // This is to work around the fact that java.net.URI throws Exceptions @@ -141,11 +154,12 @@ public class DownloadHandler { * @param userAgent User agent of the downloading application. * @param contentDisposition Content-disposition http header, if present. * @param mimetype The mimetype of the content reported by the server + * @param contentSize The size of the content */ /* package */ - private static void onDownloadStartNoStream(@NonNull final Activity context, @NonNull PreferenceManager preferences, - String url, String userAgent, - String contentDisposition, @Nullable String mimetype) { + private void onDownloadStartNoStream(@NonNull final Activity context, @NonNull PreferenceManager preferences, + String url, String userAgent, + String contentDisposition, @Nullable String mimetype, String contentSize) { final String filename = URLUtil.guessFileName(url, contentDisposition, mimetype); // Check to see if we have an SDCard @@ -164,8 +178,8 @@ public class DownloadHandler { } Dialog dialog = new AlertDialog.Builder(context).setTitle(title) - .setIcon(android.R.drawable.ic_dialog_alert).setMessage(msg) - .setPositiveButton(R.string.action_ok, null).show(); + .setIcon(android.R.drawable.ic_dialog_alert).setMessage(msg) + .setPositiveButton(R.string.action_ok, null).show(); BrowserDialog.setDialogSize(context, dialog); return; } @@ -198,16 +212,8 @@ public class DownloadHandler { // or, should it be set to one of several Environment.DIRECTORY* dirs // depending on mimetype? String location = preferences.getDownloadDirectory(); - Uri downloadFolder; - location = addNecessarySlashes(location); - downloadFolder = Uri.parse(location); - - File dir = new File(downloadFolder.getPath()); - if (!dir.isDirectory() && !dir.mkdirs()) { - // Cannot make the directory - Utils.showSnackbar(context, R.string.problem_location_download); - return; - } + location = FileUtils.addNecessarySlashes(location); + Uri downloadFolder = Uri.parse(location); if (!isWriteAccessAvailable(downloadFolder)) { Utils.showSnackbar(context, R.string.problem_location_download); @@ -240,7 +246,7 @@ public class DownloadHandler { } else { Log.d(TAG, "Valid mimetype, attempting to download"); final DownloadManager manager = (DownloadManager) context - .getSystemService(Context.DOWNLOAD_SERVICE); + .getSystemService(Context.DOWNLOAD_SERVICE); try { manager.enqueue(request); } catch (IllegalArgumentException e) { @@ -254,80 +260,30 @@ public class DownloadHandler { } Utils.showSnackbar(context, context.getString(R.string.download_pending) + ' ' + filename); } - } - - private static final String sFileName = "test"; - private static final String sFileExtension = ".txt"; - /** - * Determine whether there is write access in the given directory. Returns false if a - * file cannot be created in the directory or if the directory does not exist. - * - * @param directory the directory to check for write access - * @return returns true if the directory can be written to or is in a directory that can - * be written to. false if there is no write access. - */ - public static boolean isWriteAccessAvailable(@Nullable String directory) { - if (directory == null || directory.isEmpty()) { - return false; - } - String dir = addNecessarySlashes(directory); - dir = getFirstRealParentDirectory(dir); - File file = new File(dir + sFileName + sFileExtension); - for (int n = 0; n < 100; n++) { - if (!file.exists()) { - try { - if (file.createNewFile()) { - //noinspection ResultOfMethodCallIgnored - file.delete(); - } - return true; - } catch (IOException ignored) { - return false; - } - } else { - file = new File(dir + sFileName + '-' + n + sFileExtension); - } - } - return file.canWrite(); - } + // save download in database + BrowserActivity browserActivity = (BrowserActivity) context; + LightningView view = browserActivity.getTabModel().getCurrentTab(); - /** - * Returns the first parent directory of a directory that exists. This is useful - * for subdirectories that do not exist but their parents do. - * - * @param directory the directory to find the first existent parent - * @return the first existent parent - */ - @Nullable - private static String getFirstRealParentDirectory(@Nullable String directory) { - while (true) { - if (directory == null || directory.isEmpty()) { - return "/"; - } - directory = addNecessarySlashes(directory); - File file = new File(directory); - if (!file.isDirectory()) { - int indexSlash = directory.lastIndexOf('/'); - if (indexSlash > 0) { - String parent = directory.substring(0, indexSlash); - int previousIndex = parent.lastIndexOf('/'); - if (previousIndex > 0) { - directory = parent.substring(0, previousIndex); - } else { - return "/"; - } - } else { - return "/"; - } - } else { - return directory; - } + if (view != null && !view.isIncognito()) { + downloadsModel.addDownloadIfNotExists(new DownloadItem(url, filename, contentSize)) + .subscribe(new SingleOnSubscribe() { + @Override + public void onItem(@Nullable Boolean item) { + if (item != null && !item) + Log.i(TAG, "error saving download to database"); + } + }); } } private static boolean isWriteAccessAvailable(@NonNull Uri fileUri) { File file = new File(fileUri.getPath()); + + if (!file.isDirectory() && !file.mkdirs()) { + return false; + } + try { if (file.createNewFile()) { //noinspection ResultOfMethodCallIgnored @@ -338,19 +294,4 @@ public class DownloadHandler { return false; } } - - @NonNull - public static String addNecessarySlashes(@Nullable String originalPath) { - if (originalPath == null || originalPath.length() == 0) { - return "/"; - } - if (originalPath.charAt(originalPath.length() - 1) != '/') { - originalPath = originalPath + '/'; - } - if (originalPath.charAt(0) != '/') { - originalPath = '/' + originalPath; - } - return originalPath; - } - -} +} \ No newline at end of file diff --git a/app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java b/app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java index a38bb93..febdb08 100644 --- a/app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java +++ b/app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java @@ -7,7 +7,6 @@ import android.Manifest; import android.app.Activity; import android.app.Dialog; import android.content.DialogInterface; -import android.support.annotation.Nullable; import android.support.v7.app.AlertDialog; import android.text.format.Formatter; import android.util.Log; @@ -16,12 +15,10 @@ import android.webkit.URLUtil; import acr.browser.lightning.R; import acr.browser.lightning.app.BrowserApp; -import acr.browser.lightning.database.downloads.DownloadItem; import acr.browser.lightning.database.downloads.DownloadsModel; import acr.browser.lightning.dialog.BrowserDialog; import acr.browser.lightning.preference.PreferenceManager; -import com.anthonycr.bonsai.SingleOnSubscribe; import com.anthonycr.grant.PermissionsManager; import com.anthonycr.grant.PermissionsResultAction; @@ -34,7 +31,7 @@ public class LightningDownloadListener implements DownloadListener { private final Activity mActivity; @Inject PreferenceManager mPreferenceManager; - + @Inject DownloadHandler mDownloadHandler; @Inject DownloadsModel downloadsModel; public LightningDownloadListener(Activity context) { @@ -64,17 +61,7 @@ public class LightningDownloadListener implements DownloadListener { public void onClick(DialogInterface dialog, int which) { switch (which) { case DialogInterface.BUTTON_POSITIVE: - DownloadHandler.onDownloadStart(mActivity, mPreferenceManager, url, userAgent, - contentDisposition, mimetype); - - downloadsModel.addDownloadIfNotExists(new DownloadItem(url, fileName, downloadSize)) - .subscribe(new SingleOnSubscribe() { - @Override - public void onItem(@Nullable Boolean item) { - if (item != null && !item) - Log.i(TAG, "error saving download to database"); - } - }); + mDownloadHandler.onDownloadStart(mActivity, mPreferenceManager, url, userAgent, "attachment", null, ""); break; case DialogInterface.BUTTON_NEGATIVE: break; diff --git a/app/src/main/java/acr/browser/lightning/fragment/GeneralSettingsFragment.java b/app/src/main/java/acr/browser/lightning/fragment/GeneralSettingsFragment.java index 3d11cd1..70db8dc 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/GeneralSettingsFragment.java +++ b/app/src/main/java/acr/browser/lightning/fragment/GeneralSettingsFragment.java @@ -25,7 +25,7 @@ import acr.browser.lightning.BuildConfig; import acr.browser.lightning.R; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.dialog.BrowserDialog; -import acr.browser.lightning.download.DownloadHandler; +import acr.browser.lightning.utils.FileUtils; import acr.browser.lightning.utils.ProxyUtils; import acr.browser.lightning.utils.ThemeUtils; import acr.browser.lightning.utils.Utils; @@ -459,8 +459,8 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme public void onClick(DialogInterface dialog, int which) { switch (which) { case 0: - mPreferenceManager.setDownloadDirectory(DownloadHandler.DEFAULT_DOWNLOAD_PATH); - downloadloc.setSummary(DownloadHandler.DEFAULT_DOWNLOAD_PATH); + mPreferenceManager.setDownloadDirectory(FileUtils.DEFAULT_DOWNLOAD_PATH); + downloadloc.setSummary(FileUtils.DEFAULT_DOWNLOAD_PATH); break; case 1: downPicker(); @@ -539,7 +539,7 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme @Override public void onClick(DialogInterface dialog, int which) { String text = getDownload.getText().toString(); - text = DownloadHandler.addNecessarySlashes(text); + text = FileUtils.addNecessarySlashes(text); mPreferenceManager.setDownloadDirectory(text); downloadloc.setSummary(text); } @@ -667,7 +667,7 @@ public class GeneralSettingsFragment extends LightningPreferenceFragment impleme @Override public void afterTextChanged(@NonNull Editable s) { - if (!DownloadHandler.isWriteAccessAvailable(s.toString())) { + if (!FileUtils.isWriteAccessAvailable(s.toString())) { this.getDownload.setTextColor(this.errorColor); } else { this.getDownload.setTextColor(this.regularColor); diff --git a/app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java b/app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java index 8be0ac5..d052eac 100644 --- a/app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java +++ b/app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java @@ -9,7 +9,7 @@ import javax.inject.Inject; import javax.inject.Singleton; import acr.browser.lightning.constant.Constants; -import acr.browser.lightning.download.DownloadHandler; +import acr.browser.lightning.utils.FileUtils; @Singleton public class PreferenceManager { @@ -150,7 +150,7 @@ public class PreferenceManager { @NonNull public String getDownloadDirectory() { - return mPrefs.getString(Name.DOWNLOAD_DIRECTORY, DownloadHandler.DEFAULT_DOWNLOAD_PATH); + return mPrefs.getString(Name.DOWNLOAD_DIRECTORY, FileUtils.DEFAULT_DOWNLOAD_PATH); } public int getFlashSupport() { diff --git a/app/src/main/java/acr/browser/lightning/utils/FileUtils.java b/app/src/main/java/acr/browser/lightning/utils/FileUtils.java index 0ac8135..d272c43 100644 --- a/app/src/main/java/acr/browser/lightning/utils/FileUtils.java +++ b/app/src/main/java/acr/browser/lightning/utils/FileUtils.java @@ -28,6 +28,9 @@ public class FileUtils { private static final String TAG = "FileUtils"; + public static final String DEFAULT_DOWNLOAD_PATH = + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath(); + /** * Writes a bundle to persistent storage in the files directory * using the specified file name. This method is a blocking @@ -160,4 +163,87 @@ public class FileUtils { return megaBytes * 1024 * 1024; } + /** + * Determine whether there is write access in the given directory. Returns false if a + * file cannot be created in the directory or if the directory does not exist. + * + * @param directory the directory to check for write access + * @return returns true if the directory can be written to or is in a directory that can + * be written to. false if there is no write access. + */ + public static boolean isWriteAccessAvailable(@Nullable String directory) { + if (directory == null || directory.isEmpty()) { + return false; + } + + final String sFileName = "test"; + final String sFileExtension = ".txt"; + String dir = addNecessarySlashes(directory); + dir = getFirstRealParentDirectory(dir); + File file = new File(dir + sFileName + sFileExtension); + for (int n = 0; n < 100; n++) { + if (!file.exists()) { + try { + if (file.createNewFile()) { + //noinspection ResultOfMethodCallIgnored + file.delete(); + } + return true; + } catch (IOException ignored) { + return false; + } + } else { + file = new File(dir + sFileName + '-' + n + sFileExtension); + } + } + return file.canWrite(); + } + + /** + * Returns the first parent directory of a directory that exists. This is useful + * for subdirectories that do not exist but their parents do. + * + * @param directory the directory to find the first existent parent + * @return the first existent parent + */ + @Nullable + private static String getFirstRealParentDirectory(@Nullable String directory) { + while (true) { + if (directory == null || directory.isEmpty()) { + return "/"; + } + directory = addNecessarySlashes(directory); + File file = new File(directory); + if (!file.isDirectory()) { + int indexSlash = directory.lastIndexOf('/'); + if (indexSlash > 0) { + String parent = directory.substring(0, indexSlash); + int previousIndex = parent.lastIndexOf('/'); + if (previousIndex > 0) { + directory = parent.substring(0, previousIndex); + } else { + return "/"; + } + } else { + return "/"; + } + } else { + return directory; + } + } + } + + @NonNull + public static String addNecessarySlashes(@Nullable String originalPath) { + if (originalPath == null || originalPath.length() == 0) { + return "/"; + } + if (originalPath.charAt(originalPath.length() - 1) != '/') { + originalPath = originalPath + '/'; + } + if (originalPath.charAt(0) != '/') { + originalPath = '/' + originalPath; + } + return originalPath; + } } diff --git a/app/src/main/java/acr/browser/lightning/utils/Utils.java b/app/src/main/java/acr/browser/lightning/utils/Utils.java index b626582..8bcbe3d 100644 --- a/app/src/main/java/acr/browser/lightning/utils/Utils.java +++ b/app/src/main/java/acr/browser/lightning/utils/Utils.java @@ -3,7 +3,6 @@ */ package acr.browser.lightning.utils; -import android.Manifest; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; @@ -33,12 +32,8 @@ import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Log; import android.view.View; -import android.webkit.URLUtil; import android.widget.Toast; -import com.anthonycr.grant.PermissionsManager; -import com.anthonycr.grant.PermissionsResultAction; - import java.io.Closeable; import java.io.File; import java.io.IOException; @@ -52,8 +47,6 @@ import acr.browser.lightning.activity.MainActivity; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.dialog.BrowserDialog; -import acr.browser.lightning.download.DownloadHandler; -import acr.browser.lightning.preference.PreferenceManager; public final class Utils { @@ -63,35 +56,6 @@ public final class Utils { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; } - /** - * Downloads a file from the specified URL. Handles permissions - * requests, and creates all the necessary dialogs that must be - * showed to the user. - * - * @param activity activity needed to created dialogs. - * @param url url to download from. - * @param userAgent the user agent of the browser. - * @param contentDisposition the content description of the file. - */ - public static void downloadFile(@NonNull final Activity activity, @NonNull final PreferenceManager manager, final String url, - final String userAgent, final String contentDisposition) { - PermissionsManager.getInstance().requestPermissionsIfNecessaryForResult(activity, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.WRITE_EXTERNAL_STORAGE}, new PermissionsResultAction() { - @Override - public void onGranted() { - String fileName = URLUtil.guessFileName(url, null, null); - DownloadHandler.onDownloadStart(activity, manager, url, userAgent, contentDisposition, null); - Log.i(TAG, "Downloading: " + fileName); - } - - @Override - public void onDenied(String permission) { - // TODO Show Message - } - }); - - } - /** * Creates a new intent that can launch the email * app with a subject, address, body, and cc. It