Commit 499f10ea authored by Thomas's avatar Thomas
Browse files

Share feature

parent 1a370632
Loading
Loading
Loading
Loading
+20 −3
Original line number Diff line number Diff line
@@ -26,13 +26,30 @@
        <activity
            android:name=".activities.MainActivity"
            android:configChanges="orientation|screenSize"
            android:exported="true"
            >
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.SEND" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="text/plain" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.SEND" />
                <category android:name="android.intent.category.DEFAULT" />

                <data android:mimeType="image/*" />
                <data android:mimeType="video/*" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.SEND_MULTIPLE" />
                <category android:name="android.intent.category.DEFAULT" />

                <data android:mimeType="image/*" />
                <data android:mimeType="video/*" />
            </intent-filter>
        </activity>

        <activity
+152 −0
Original line number Diff line number Diff line
@@ -28,11 +28,15 @@ import android.content.SharedPreferences;
import android.graphics.PorterDuff;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.Editable;
import android.text.Html;
import android.text.TextWatcher;
import android.util.Patterns;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -72,8 +76,12 @@ import com.google.android.material.snackbar.Snackbar;
import com.jaredrummler.cyanea.Cyanea;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import app.fedilab.android.activities.AboutActivity;
@@ -115,6 +123,7 @@ import app.fedilab.android.client.entities.app.PinnedTimeline;
import app.fedilab.android.databinding.ActivityMainBinding;
import app.fedilab.android.databinding.NavHeaderMainBinding;
import app.fedilab.android.exception.DBException;
import app.fedilab.android.helper.CrossActionHelper;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.helper.PinnedTimelineHelper;
@@ -128,6 +137,11 @@ import app.fedilab.android.viewmodel.mastodon.InstancesVM;
import app.fedilab.android.viewmodel.mastodon.TimelinesVM;
import app.fedilab.android.viewmodel.mastodon.TopBarVM;
import es.dmoral.toasty.Toasty;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public abstract class BaseMainActivity extends BaseActivity implements NetworkStateReceiver.NetworkStateReceiverListener {

@@ -234,6 +248,8 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
    private void mamageNewIntent(Intent intent) {
        if (intent == null)
            return;
        String action = intent.getAction();
        String type = intent.getType();
        Bundle extras = intent.getExtras();
        String userIdIntent, instanceIntent;
        if (extras != null && extras.containsKey(Helper.INTENT_ACTION)) {
@@ -270,6 +286,142 @@ public abstract class BaseMainActivity extends BaseActivity implements NetworkSt
                intent.removeExtra(Helper.INTENT_ACTION);

            }
        } else if (Intent.ACTION_SEND.equals(action) && type != null) {
            if ("text/plain".equals(type)) {
                final String[] url = {null};
                String sharedSubject = intent.getStringExtra(Intent.EXTRA_SUBJECT);
                String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
                //SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(BaseMainActivity.this);
                //boolean shouldRetrieveMetaData = sharedpreferences.getBoolean(getString(R.string.SET_RETRIEVE_METADATA_IF_URL_FROM_EXTERAL), true);
                if (sharedText != null) {
                    /* Some apps don't send the URL as the first part of the EXTRA_TEXT,
                        the BBC News app being one such, in this case find where the URL
                        is and strip that out into sharedText.
                     */
                    Matcher matcher;
                    matcher = Patterns.WEB_URL.matcher(sharedText);
                    while (matcher.find()) {
                        int matchStart = matcher.start(1);
                        int matchEnd = matcher.end();
                        if (matchStart < matchEnd && sharedText.length() >= matchEnd)
                            url[0] = sharedText.substring(matchStart, matchEnd);
                    }
                    new Thread(() -> {
                        if (url[0].startsWith("www."))
                            url[0] = "http://" + url[0];
                        Matcher matcherPattern = Patterns.WEB_URL.matcher(url[0]);
                        String potentialUrl = null;
                        while (matcherPattern.find()) {
                            int matchStart = matcherPattern.start(1);
                            int matchEnd = matcherPattern.end();
                            if (matchStart < matchEnd && url[0].length() >= matchEnd)
                                potentialUrl = url[0].substring(matchStart, matchEnd);
                        }
                        // If we actually have a URL then make use of it.
                        if (potentialUrl != null && potentialUrl.length() > 0) {
                            Pattern titlePattern = Pattern.compile("meta[ a-zA-Z=\"'-]+property=[\"']og:title[\"']\\s+content=[\"']([^>]*)[\"']");
                            Pattern descriptionPattern = Pattern.compile("meta[ a-zA-Z=\"'-]+property=[\"']og:description[\"']\\s+content=[\"']([^>]*)[\"']");
                            Pattern imagePattern = Pattern.compile("meta[ a-zA-Z=\"'-]+property=[\"']og:image[\"']\\s+content=[\"']([^>]*)[\"']");
                            try {
                                OkHttpClient client = new OkHttpClient.Builder()
                                        .connectTimeout(10, TimeUnit.SECONDS)
                                        .writeTimeout(10, TimeUnit.SECONDS)
                                        .proxy(Helper.getProxy(getApplication().getApplicationContext()))
                                        .readTimeout(10, TimeUnit.SECONDS).build();
                                Request request = new Request.Builder()
                                        .url(potentialUrl)
                                        .build();
                                client.newCall(request).enqueue(new Callback() {
                                    @Override
                                    public void onFailure(@NonNull Call call, @NonNull IOException e) {
                                        e.printStackTrace();
                                        runOnUiThread(() -> Toasty.warning(BaseMainActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show());
                                    }

                                    @Override
                                    public void onResponse(@NonNull Call call, @NonNull final Response response) {
                                        if (response.isSuccessful()) {
                                            try {
                                                String data = response.body().string();
                                                Matcher matcherTitle;
                                                matcherTitle = titlePattern.matcher(data);
                                                Matcher matcherDescription = descriptionPattern.matcher(data);
                                                Matcher matcherImage = imagePattern.matcher(data);
                                                String titleEncoded = null;
                                                String descriptionEncoded = null;
                                                while (matcherTitle.find())
                                                    titleEncoded = matcherTitle.group(1);
                                                while (matcherDescription.find())
                                                    descriptionEncoded = matcherDescription.group(1);
                                                String image = null;
                                                while (matcherImage.find())
                                                    image = matcherImage.group(1);
                                                String title = null;
                                                String description = null;
                                                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                                                    if (titleEncoded != null)
                                                        title = Html.fromHtml(titleEncoded, Html.FROM_HTML_MODE_LEGACY).toString();
                                                    if (descriptionEncoded != null)
                                                        description = Html.fromHtml(descriptionEncoded, Html.FROM_HTML_MODE_LEGACY).toString();
                                                } else {
                                                    if (titleEncoded != null)
                                                        title = Html.fromHtml(titleEncoded).toString();
                                                    if (descriptionEncoded != null)
                                                        description = Html.fromHtml(descriptionEncoded).toString();
                                                }
                                                String finalImage = image;
                                                String finalTitle = title;
                                                String finalDescription = description;
                                                runOnUiThread(() -> {


                                                    Bundle b = new Bundle();
                                                    b.putString(Helper.ARG_SHARE_URL, url[0]);
                                                    b.putString(Helper.ARG_SHARE_URL_MEDIA, finalImage);
                                                    b.putString(Helper.ARG_SHARE_TITLE, finalTitle);
                                                    b.putString(Helper.ARG_SHARE_DESCRIPTION, finalDescription);
                                                    b.putString(Helper.ARG_SHARE_SUBJECT, sharedSubject);
                                                    b.putString(Helper.ARG_SHARE_CONTENT, sharedText);
                                                    CrossActionHelper.doCrossShare(BaseMainActivity.this, b);
                                                });
                                            } catch (Exception e) {
                                                e.printStackTrace();
                                            }
                                        } else {
                                            runOnUiThread(() -> Toasty.warning(BaseMainActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show());
                                        }
                                    }
                                });
                            } catch (IndexOutOfBoundsException e) {
                                Toasty.warning(BaseMainActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
                            }

                        }
                    }).start();

                }
            } else if (type.startsWith("image/") || type.startsWith("video/")) {
                Uri imageUri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
                if (imageUri != null) {
                    intent = new Intent(BaseMainActivity.this, ComposeActivity.class);
                    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
                    intent.putExtra(Helper.ARG_SHARE_URI, imageUri.toString());
                    startActivity(intent);
                } else {
                    Toasty.warning(BaseMainActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
                }
            }
        } else if (Intent.ACTION_SEND_MULTIPLE.equals(action) && type != null) {
            if (type.startsWith("image/") || type.startsWith("video/")) {
                ArrayList<Uri> imageList = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
                if (imageList != null) {
                    Bundle b = new Bundle();
                    b.putParcelableArrayList(Helper.ARG_SHARE_URI_LIST, imageList);
                    CrossActionHelper.doCrossShare(BaseMainActivity.this, b);
                } else {
                    Toasty.warning(BaseMainActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
                }
            }
        }

    }
+38 −2
Original line number Diff line number Diff line
@@ -72,7 +72,6 @@ import app.fedilab.android.client.entities.api.EmojiInstance;
import app.fedilab.android.client.entities.api.Mention;
import app.fedilab.android.client.entities.api.ScheduledStatus;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.app.Account;
import app.fedilab.android.client.entities.app.BaseAccount;
import app.fedilab.android.client.entities.app.StatusDraft;
import app.fedilab.android.databinding.ActivityPaginationBinding;
@@ -84,6 +83,7 @@ import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.helper.MediaHelper;
import app.fedilab.android.helper.SpannableHelper;
import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.interfaces.OnDownloadInterface;
import app.fedilab.android.jobs.ScheduleThreadWorker;
import app.fedilab.android.services.PostMessageService;
import app.fedilab.android.services.ThreadMessageService;
@@ -134,6 +134,9 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
    private app.fedilab.android.client.entities.api.Account accountMention;
    private String statusReplyId;
    private app.fedilab.android.client.entities.api.Account mentionBooster;
    private ArrayList<Uri> sharedUriList = new ArrayList<>();
    private Uri sharedUri;
    private String sharedSubject, sharedContent, sharedTitle, sharedDescription, shareURL, sharedUrlMedia;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
@@ -160,13 +163,24 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
            scheduledStatus = (ScheduledStatus) b.getSerializable(Helper.ARG_STATUS_SCHEDULED);
            statusReplyId = b.getString(Helper.ARG_STATUS_REPLY_ID);
            statusMention = (Status) b.getSerializable(Helper.ARG_STATUS_MENTION);
            account = (Account) b.getSerializable(Helper.ARG_ACCOUNT);
            account = (BaseAccount) b.getSerializable(Helper.ARG_ACCOUNT);
            instance = b.getString(Helper.ARG_INSTANCE, null);
            token = b.getString(Helper.ARG_TOKEN, null);
            visibility = b.getString(Helper.ARG_VISIBILITY, null);
            mentionBooster = (app.fedilab.android.client.entities.api.Account) b.getSerializable(Helper.ARG_MENTION_BOOSTER);
            accountMention = (app.fedilab.android.client.entities.api.Account) b.getSerializable(Helper.ARG_ACCOUNT_MENTION);
            //Shared elements
            sharedUriList = b.getParcelableArrayList(Helper.ARG_SHARE_URI_LIST);
            sharedUri = b.getParcelable(Helper.ARG_SHARE_URI);
            sharedUrlMedia = b.getString(Helper.ARG_SHARE_URL_MEDIA);
            sharedSubject = b.getString(Helper.ARG_SHARE_SUBJECT, null);
            sharedContent = b.getString(Helper.ARG_SHARE_CONTENT, null);
            sharedTitle = b.getString(Helper.ARG_SHARE_TITLE, null);
            sharedDescription = b.getString(Helper.ARG_SHARE_DESCRIPTION, null);
            shareURL = b.getString(Helper.ARG_SHARE_URL, null);
        }


        binding.toolbar.setPopupTheme(Helper.popupStyle());
        //Edit a scheduled status from server
        if (scheduledStatus != null) {
@@ -342,6 +356,28 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
                storeDraft(false);
            }
        }, 0, 10000);

        if (sharedUriList != null && sharedUriList.size() > 0) {
            List<Uri> uris = new ArrayList<>(sharedUriList);
            composeAdapter.addAttachment(-1, uris);
        } else if (sharedUri != null && !sharedUri.toString().startsWith("http")) {
            List<Uri> uris = new ArrayList<>();
            uris.add(sharedUri);
            composeAdapter.addAttachment(-1, uris);
        } else if (shareURL != null) {
            Helper.download(ComposeActivity.this, sharedUrlMedia, new OnDownloadInterface() {
                @Override
                public void onDownloaded(String saveFilePath, String downloadUrl, Error error) {
                    composeAdapter.addSharing(shareURL, sharedTitle, sharedDescription, sharedSubject, sharedContent, saveFilePath);
                }

                @Override
                public void onUpdateProgress(int progress) {

                }
            });

        }
    }

    @Override
+45 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ package app.fedilab.android.helper;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;

@@ -34,6 +35,7 @@ import java.util.concurrent.TimeUnit;

import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.activities.BaseActivity;
import app.fedilab.android.activities.ComposeActivity;
import app.fedilab.android.client.endpoints.MastodonSearchService;
import app.fedilab.android.client.entities.api.Results;
@@ -399,6 +401,49 @@ public class CrossActionHelper {
        }).start();
    }

    public static void doCrossShare(final Context context, final Bundle bundle) {
        List<BaseAccount> accounts;
        try {
            accounts = new Account(context).getAll();
            List<app.fedilab.android.client.entities.api.Account> accountList = new ArrayList<>();
            for (BaseAccount account : accounts) {
                accountList.add(account.mastodon_account);
            }
            if (accounts.size() == 1) {
                Intent intentToot = new Intent(context, ComposeActivity.class);
                intentToot.putExtras(bundle);
                context.startActivity(intentToot);
                ((BaseActivity) context).finish();
            } else {
                AlertDialog.Builder builderSingle = new AlertDialog.Builder(context, Helper.dialogStyle());
                builderSingle.setTitle(context.getString(R.string.choose_accounts));
                final AccountsSearchAdapter accountsSearchAdapter = new AccountsSearchAdapter(context, accountList);
                final BaseAccount[] accountArray = new BaseAccount[accounts.size()];
                int i = 0;
                for (BaseAccount account : accounts) {
                    accountArray[i] = account;
                    i++;
                }
                builderSingle.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
                builderSingle.setAdapter(accountsSearchAdapter, (dialog, which) -> {
                    final BaseAccount account = accountArray[which];
                    Intent intentToot = new Intent(context, ComposeActivity.class);
                    bundle.putSerializable(Helper.ARG_ACCOUNT, account);
                    intentToot.putExtras(bundle);
                    context.startActivity(intentToot);
                    ((BaseActivity) context).finish();
                    dialog.dismiss();
                });
                builderSingle.show();
            }

        } catch (DBException e) {
            e.printStackTrace();
        }


    }


    public enum TypeOfCrossAction {
        FOLLOW_ACTION,
+30 −0
Original line number Diff line number Diff line
package app.fedilab.android.helper;

import java.util.Arrays;

/**
 * Work from https://stackoverflow.com/a/26420820
 */

public class FileNameCleaner {
    private final static int[] illegalChars = {34, 60, 62, 124, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 58, 42, 63, 92, 47};

    static {
        Arrays.sort(illegalChars);
    }

    public static String cleanFileName(String badFileName) {
        StringBuilder cleanName = new StringBuilder();
        if (badFileName.contains("?")) {
            badFileName = badFileName.split("\\?")[0];
        }
        int len = badFileName.codePointCount(0, badFileName.length());
        for (int i = 0; i < len; i++) {
            int c = badFileName.codePointAt(i);
            if (Arrays.binarySearch(illegalChars, c) < 0) {
                cleanName.appendCodePoint(c);
            }
        }
        return cleanName.toString();
    }
}
 No newline at end of file
Loading