diff --git a/app/src/main/java/app/fedilab/android/activities/ContextActivity.java b/app/src/main/java/app/fedilab/android/activities/ContextActivity.java index 243e90b0..e486e14d 100644 --- a/app/src/main/java/app/fedilab/android/activities/ContextActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/ContextActivity.java @@ -18,11 +18,13 @@ package app.fedilab.android.activities; import static app.fedilab.android.BaseMainActivity.currentAccount; import static app.fedilab.android.ui.drawer.StatusAdapter.sendAction; +import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Resources; import android.os.Bundle; import android.os.Handler; import android.os.Looper; +import android.util.Log; import android.util.TypedValue; import android.view.Menu; import android.view.MenuItem; @@ -35,27 +37,32 @@ import androidx.preference.PreferenceManager; import app.fedilab.android.BaseMainActivity; import app.fedilab.android.R; +import app.fedilab.android.client.entities.api.Account; import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.client.entities.app.StatusCache; import app.fedilab.android.databinding.ActivityConversationBinding; 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.ui.fragment.timeline.FragmentMastodonContext; import app.fedilab.android.viewmodel.mastodon.StatusesVM; +import es.dmoral.toasty.Toasty; -public class ContextActivity extends BaseActivity { +public class ContextActivity extends BaseActivity implements FragmentMastodonContext.FirstMessage { public static boolean expand; public static boolean displayCW; public static Resources.Theme theme; Fragment currentFragment; + private Status firstMessage; + private String remote_instance; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - app.fedilab.android.databinding.ActivityConversationBinding binding = ActivityConversationBinding.inflate(getLayoutInflater()); + ActivityConversationBinding binding = ActivityConversationBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); setSupportActionBar(binding.toolbar); ActionBar actionBar = getSupportActionBar(); @@ -75,8 +82,10 @@ public class ContextActivity extends BaseActivity { Bundle b = getIntent().getExtras(); displayCW = sharedpreferences.getBoolean(getString(R.string.SET_EXPAND_CW), false); Status focusedStatus = null; // or other values - if (b != null) + if (b != null) { focusedStatus = (Status) b.getSerializable(Helper.ARG_STATUS); + remote_instance = b.getString(Helper.ARG_REMOTE_INSTANCE, null); + } if (focusedStatus == null || currentAccount == null || currentAccount.mastodon_account == null) { finish(); return; @@ -84,29 +93,34 @@ public class ContextActivity extends BaseActivity { MastodonHelper.loadPPMastodon(binding.profilePicture, currentAccount.mastodon_account); Bundle bundle = new Bundle(); bundle.putSerializable(Helper.ARG_STATUS, focusedStatus); - currentFragment = Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_content_main, new FragmentMastodonContext(), bundle, null, null); - StatusesVM timelinesVM = new ViewModelProvider(ContextActivity.this).get(StatusesVM.class); - timelinesVM.getStatus(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, focusedStatus.id).observe(ContextActivity.this, status -> { - if (status != null) { - StatusCache statusCache = new StatusCache(); - statusCache.instance = BaseMainActivity.currentInstance; - statusCache.user_id = BaseMainActivity.currentUserID; - statusCache.status = status; - statusCache.status_id = status.id; - //Update cache - new Thread(() -> { - try { - new StatusCache(getApplication()).updateIfExists(statusCache); - Handler mainHandler = new Handler(Looper.getMainLooper()); - //Update UI - Runnable myRunnable = () -> sendAction(ContextActivity.this, Helper.ARG_STATUS_ACTION, status, null); - mainHandler.post(myRunnable); - } catch (DBException e) { - e.printStackTrace(); - } - }).start(); - } - }); + bundle.putString(Helper.ARG_REMOTE_INSTANCE, remote_instance); + FragmentMastodonContext fragmentMastodonContext = new FragmentMastodonContext(); + fragmentMastodonContext.firstMessage = this; + currentFragment = Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_content_main, fragmentMastodonContext, bundle, null, null); + if (remote_instance == null) { + StatusesVM timelinesVM = new ViewModelProvider(ContextActivity.this).get(StatusesVM.class); + timelinesVM.getStatus(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, focusedStatus.id).observe(ContextActivity.this, status -> { + if (status != null) { + StatusCache statusCache = new StatusCache(); + statusCache.instance = BaseMainActivity.currentInstance; + statusCache.user_id = BaseMainActivity.currentUserID; + statusCache.status = status; + statusCache.status_id = status.id; + //Update cache + new Thread(() -> { + try { + new StatusCache(getApplication()).updateIfExists(statusCache); + Handler mainHandler = new Handler(Looper.getMainLooper()); + //Update UI + Runnable myRunnable = () -> sendAction(ContextActivity.this, Helper.ARG_STATUS_ACTION, status, null); + mainHandler.post(myRunnable); + } catch (DBException e) { + e.printStackTrace(); + } + }).start(); + } + }); + } } @@ -126,6 +140,10 @@ public class ContextActivity extends BaseActivity { } else { itemDisplayCW.setIcon(R.drawable.ic_outline_remove_red_eye_24); } + MenuItem action_remote = menu.findItem(R.id.action_remote); + if (remote_instance != null) { + action_remote.setVisible(false); + } return true; } @@ -151,8 +169,52 @@ public class ContextActivity extends BaseActivity { ((FragmentMastodonContext) currentFragment).refresh(); } invalidateOptionsMenu(); + } else if (item.getItemId() == R.id.action_remote) { + + if (firstMessage == null) { + Toasty.warning(ContextActivity.this, getString(R.string.toast_try_later), Toasty.LENGTH_SHORT).show(); + return true; + } + if (firstMessage.account.acct != null) { + String[] splitAcct = firstMessage.account.acct.split("@"); + String instance; + if (splitAcct.length > 1) { + instance = splitAcct[1]; + } else { + Toasty.info(ContextActivity.this, getString(R.string.toast_on_your_instance), Toasty.LENGTH_SHORT).show(); + return true; + } + Log.v(Helper.TAG, "firstMessage.uri: " + firstMessage.uri); + Log.v(Helper.TAG, "instance: " + instance); + CrossActionHelper.fetchStatusInRemoteInstance(ContextActivity.this, firstMessage.uri, instance, new CrossActionHelper.Callback() { + @Override + public void federatedStatus(Status status) { + Log.v(Helper.TAG, ">status: " + status); + if (status != null) { + Intent intentContext = new Intent(ContextActivity.this, ContextActivity.class); + intentContext.putExtra(Helper.ARG_STATUS, status); + intentContext.putExtra(Helper.ARG_REMOTE_INSTANCE, true); + intentContext.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intentContext); + } else { + Toasty.warning(ContextActivity.this, getString(R.string.toast_error_fetch_message), Toasty.LENGTH_SHORT).show(); + } + } + + @Override + public void federatedAccount(Account account) { + Log.v(Helper.TAG, ">account: " + account); + } + }); + } else { + Toasty.warning(ContextActivity.this, getString(R.string.toast_error_fetch_message), Toasty.LENGTH_SHORT).show(); + } } return true; } + @Override + public void get(Status status) { + firstMessage = status; + } } \ No newline at end of file diff --git a/app/src/main/java/app/fedilab/android/helper/CrossActionHelper.java b/app/src/main/java/app/fedilab/android/helper/CrossActionHelper.java index 40e62117..be38d26c 100644 --- a/app/src/main/java/app/fedilab/android/helper/CrossActionHelper.java +++ b/app/src/main/java/app/fedilab/android/helper/CrossActionHelper.java @@ -20,6 +20,7 @@ import android.content.SharedPreferences; import android.os.Bundle; import android.os.Handler; import android.os.Looper; +import android.util.Log; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; @@ -439,6 +440,48 @@ public class CrossActionHelper { } + /** + * Fetch and federate the remote status + */ + public static void fetchStatusInRemoteInstance(@NonNull Context context, String url, String instance, Callback callback) { + + MastodonSearchService mastodonSearchService = init(context, instance); + new Thread(() -> { + Call resultsCall = mastodonSearchService.search(null, url, null, "statuses", null, null, null, null, null, null, null); + Results results = null; + Log.v(Helper.TAG, ">request: " + resultsCall.request()); + if (resultsCall != null) { + try { + Response resultsResponse = resultsCall.execute(); + if (resultsResponse.isSuccessful()) { + + results = resultsResponse.body(); + if (results != null) { + if (results.statuses == null) { + results.statuses = new ArrayList<>(); + } + } + } else { + Log.v(Helper.TAG, ">err: " + resultsResponse.errorBody().string()); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + Handler mainHandler = new Handler(Looper.getMainLooper()); + Results finalResults = results; + Runnable myRunnable = () -> { + Log.v(Helper.TAG, ">finalResults.statuses " + finalResults.statuses); + if (finalResults != null && finalResults.statuses != null && finalResults.statuses.size() > 0) { + callback.federatedStatus(finalResults.statuses.get(0)); + } + }; + mainHandler.post(myRunnable); + + }).start(); + } + + /** * Fetch and federate the remote status */ diff --git a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonContext.java b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonContext.java index b3fd472d..33dc10c8 100644 --- a/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonContext.java +++ b/app/src/main/java/app/fedilab/android/ui/fragment/timeline/FragmentMastodonContext.java @@ -34,9 +34,9 @@ import androidx.recyclerview.widget.LinearLayoutManager; import java.util.ArrayList; import java.util.List; -import app.fedilab.android.BaseMainActivity; import app.fedilab.android.R; import app.fedilab.android.activities.ContextActivity; +import app.fedilab.android.activities.MainActivity; import app.fedilab.android.client.entities.api.Context; import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.client.entities.app.Timeline; @@ -54,6 +54,8 @@ public class FragmentMastodonContext extends Fragment { private StatusesVM statusesVM; private List statuses; private StatusAdapter statusAdapter; + public FirstMessage firstMessage; + //Handle actions that can be done in other fragments private final BroadcastReceiver receive_action = new BroadcastReceiver() { @Override @@ -116,8 +118,10 @@ public class FragmentMastodonContext extends Fragment { } }; private Status focusedStatus; + private String remote_instance; private Status firstStatus; private boolean pullToRefresh; + private String user_token, user_instance; /** * Return the position of the status in the ArrayList @@ -145,17 +149,26 @@ public class FragmentMastodonContext extends Fragment { pullToRefresh = false; if (getArguments() != null) { focusedStatus = (Status) getArguments().getSerializable(Helper.ARG_STATUS); + remote_instance = getArguments().getString(Helper.ARG_REMOTE_INSTANCE, null); + } + if (remote_instance != null) { + user_instance = remote_instance; + user_token = null; + } else { + user_instance = MainActivity.currentInstance; + user_token = MainActivity.currentToken; } if (focusedStatus == null) { getChildFragmentManager().beginTransaction().remove(this).commit(); } + binding = FragmentPaginationBinding.inflate(inflater, container, false); statusesVM = new ViewModelProvider(FragmentMastodonContext.this).get(StatusesVM.class); binding.recyclerView.setNestedScrollingEnabled(true); this.statuses = new ArrayList<>(); focusedStatus.isFocused = true; this.statuses.add(focusedStatus); - statusAdapter = new StatusAdapter(this.statuses, Timeline.TimeLineEnum.UNKNOWN, false, true, false); + statusAdapter = new StatusAdapter(this.statuses, Timeline.TimeLineEnum.UNKNOWN, false, true, remote_instance != null); binding.swipeContainer.setRefreshing(false); LinearLayoutManager mLayoutManager = new LinearLayoutManager(requireActivity()); binding.recyclerView.setLayoutManager(mLayoutManager); @@ -164,12 +177,12 @@ public class FragmentMastodonContext extends Fragment { if (this.statuses.size() > 0) { binding.swipeContainer.setRefreshing(true); pullToRefresh = true; - statusesVM.getContext(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, focusedStatus.id) + statusesVM.getContext(user_instance, user_token, focusedStatus.id) .observe(getViewLifecycleOwner(), this::initializeContextView); } }); if (focusedStatus != null) { - statusesVM.getContext(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, focusedStatus.id) + statusesVM.getContext(user_instance, user_token, focusedStatus.id) .observe(getViewLifecycleOwner(), this::initializeContextView); } LocalBroadcastManager.getInstance(requireActivity()).registerReceiver(receive_action, new IntentFilter(Helper.RECEIVE_STATUS_ACTION)); @@ -196,7 +209,7 @@ public class FragmentMastodonContext extends Fragment { } else { id = focusedStatus.id; } - statusesVM.getContext(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, id) + statusesVM.getContext(user_instance, user_token, id) .observe(FragmentMastodonContext.this, this::initializeContextView); } } @@ -228,6 +241,10 @@ public class FragmentMastodonContext extends Fragment { } else { firstStatus = statuses.get(0); } + if (firstMessage != null) { + firstMessage.get(firstStatus); + } + int statusPosition = context.ancestors.size(); //Build the array of statuses statuses.addAll(0, context.ancestors); @@ -250,4 +267,8 @@ public class FragmentMastodonContext extends Fragment { super.onDestroyView(); } + + public interface FirstMessage { + void get(Status status); + } } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_baseline_location_searching_24.xml b/app/src/main/res/drawable/ic_baseline_location_searching_24.xml new file mode 100644 index 00000000..a32cb664 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_location_searching_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/menu/menu_context.xml b/app/src/main/res/menu/menu_context.xml index 31b56e66..63aabec8 100644 --- a/app/src/main/res/menu/menu_context.xml +++ b/app/src/main/res/menu/menu_context.xml @@ -6,9 +6,15 @@ android:icon="@drawable/ic_baseline_expand_more_24" android:title="@string/expand_conversation" app:showAsAction="ifRoom" /> + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 259a36f5..4dadf67f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2048,4 +2048,8 @@ Set custom colors Light - Custom colors Dark - Custom colors + Display remote conversation + Please, try again later. + The conversation started on your instance! + The app didn\'t find the remote message. \ No newline at end of file