/* Copyright 2017 Thomas Schneider * * This file is a part of Fedilab * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 3 of the * License, or (at your option) any later version. * * Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. * * You should have received a copy of the GNU General Public License along with Fedilab; if not, * see . */ package app.fedilab.android.activities; import android.content.BroadcastReceiver; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.database.sqlite.SQLiteDatabase; import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.view.View; import android.widget.Toast; import androidx.core.content.ContextCompat; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import org.json.JSONException; import org.json.JSONObject; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import app.fedilab.android.R; import app.fedilab.android.asynctasks.RetrieveContextAsyncTask; import app.fedilab.android.asynctasks.RetrieveFeedsAsyncTask; import app.fedilab.android.asynctasks.UpdateAccountInfoAsyncTask; import app.fedilab.android.client.APIResponse; import app.fedilab.android.client.Entities.Account; import app.fedilab.android.client.Entities.Status; import app.fedilab.android.client.Entities.StatusDrawerParams; import app.fedilab.android.client.PixelfedAPI; import app.fedilab.android.databinding.ActivityShowConversationBinding; import app.fedilab.android.drawers.PixelfedListAdapter; import app.fedilab.android.drawers.StatusListAdapter; import app.fedilab.android.helper.Helper; import app.fedilab.android.interfaces.OnRetrieveContextInterface; import app.fedilab.android.sqlite.AccountDAO; import app.fedilab.android.sqlite.Sqlite; import es.dmoral.toasty.Toasty; /** * Created by Thomas on 04/05/2017. * Show conversation activity class */ public class ShowConversationActivity extends BaseActivity implements OnRetrieveContextInterface { private Status initialStatus; private Status detailsStatus; private SwipeRefreshLayout swipeRefreshLayout; private List statuses; private StatusListAdapter statusListAdapter; private PixelfedListAdapter pixelfedListAdapter; private boolean expanded; private BroadcastReceiver receive_action; private String conversationId; private boolean spoilerShown, spoilerBehaviour; private ActivityShowConversationBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK); switch (theme) { case Helper.THEME_LIGHT: setTheme(R.style.AppTheme_NoActionBar_Fedilab); break; case Helper.THEME_BLACK: setTheme(R.style.AppThemeBlack_NoActionBar); break; default: setTheme(R.style.AppThemeDark_NoActionBar); } binding = ActivityShowConversationBinding.inflate(getLayoutInflater()); View viewRoot = binding.getRoot(); setContentView(viewRoot); spoilerShown = spoilerBehaviour = sharedpreferences.getBoolean(Helper.SET_EXPAND_CW, false); Bundle b = getIntent().getExtras(); statuses = new ArrayList<>(); if (b != null) { detailsStatus = b.getParcelable("status"); expanded = b.getBoolean("expanded", false); initialStatus = b.getParcelable("initialStatus"); conversationId = b.getString("conversationId", null); } if (detailsStatus == null || detailsStatus.getId() == null) finish(); binding.loader.setVisibility(View.VISIBLE); detailsStatus.setFocused(true); //Some spannable Status.fillSpan(new WeakReference<>(ShowConversationActivity.this), detailsStatus); if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.MASTODON) { if (receive_action != null) LocalBroadcastManager.getInstance(ShowConversationActivity.this).unregisterReceiver(receive_action); receive_action = new BroadcastReceiver() { @Override public void onReceive(android.content.Context context, Intent intent) { Bundle b = intent.getExtras(); assert b != null; Status status = b.getParcelable("status"); if (status != null && statusListAdapter != null) { statusListAdapter.notifyStatusChanged(status); } else if (status != null && pixelfedListAdapter != null) { pixelfedListAdapter.notifyStatusChanged(status); } } }; LocalBroadcastManager.getInstance(ShowConversationActivity.this).registerReceiver(receive_action, new IntentFilter(Helper.RECEIVE_ACTION)); } binding.incToolbar.toolbar.setBackground(new ColorDrawable(ContextCompat.getColor(ShowConversationActivity.this, R.color.cyanea_primary))); binding.incToolbar.toolbarTitle.setText(R.string.conversation); if (expanded) binding.incToolbar.actionExpand.setImageResource(R.drawable.ic_expand_less); else binding.incToolbar.actionExpand.setImageResource(R.drawable.ic_expand_more); //Scroll to top binding.incToolbar.toolbarTitle.setOnClickListener(view -> { if (statusListAdapter != null) { binding.lvStatus.setAdapter(statusListAdapter); } else if (pixelfedListAdapter != null) { binding.lvStatus.setAdapter(pixelfedListAdapter); } }); binding.incToolbar.closeConversation.setOnClickListener(v -> finish()); binding.incToolbar.actionRefresh.setOnClickListener(view -> { Intent intent = new Intent(ShowConversationActivity.this, ShowConversationActivity.class); Bundle b1 = new Bundle(); b1.putParcelable("status", detailsStatus); b1.putBoolean("expanded", expanded); if (expanded && statuses != null && statuses.size() > 0) b1.putParcelable("initialStatus", statuses.get(0)); intent.putExtras(b1); finish(); startActivity(intent); overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); }); binding.incToolbar.actionUnhide.setOnClickListener(v -> { if (statuses != null && statuses.size() > 0) { spoilerShown = !spoilerShown; for (Status status : statuses) { status.setAutoHiddenCW(spoilerBehaviour && !status.isSpoilerShown()); status.setSpoilerShown(spoilerShown); status.setShowSpoiler(spoilerShown); } if (statusListAdapter != null) { statusListAdapter.notifyItemRangeChanged(0, statuses.size()); } else if (pixelfedListAdapter != null) { pixelfedListAdapter.notifyItemRangeChanged(0, statuses.size()); } } }); binding.incToolbar.actionExpand.setOnClickListener(v -> { expanded = !expanded; Intent intent = new Intent(ShowConversationActivity.this, ShowConversationActivity.class); Bundle b12 = new Bundle(); b12.putParcelable("status", detailsStatus); b12.putBoolean("expanded", expanded); if (expanded && statuses != null && statuses.size() > 0) b12.putParcelable("initialStatus", statuses.get(0)); intent.putExtras(b12); finish(); startActivity(intent); overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); }); SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); String instance = sharedpreferences.getString(Helper.PREF_INSTANCE, null); Account account = new AccountDAO(ShowConversationActivity.this, db).getUniqAccount(userId, instance); if (account.getAvatar() == null) { Toasty.error(ShowConversationActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show(); finish(); } Helper.loadGiF(ShowConversationActivity.this, account, binding.incToolbar.ppActionBar); swipeRefreshLayout = findViewById(R.id.swipeContainer); int c1 = getResources().getColor(R.color.cyanea_accent); int c2 = getResources().getColor(R.color.cyanea_primary_dark); int c3 = getResources().getColor(R.color.cyanea_primary); swipeRefreshLayout.setProgressBackgroundColorSchemeColor(c3); swipeRefreshLayout.setColorSchemeColors( c1, c2, c1 ); boolean isOnWifi = Helper.isOnWIFI(ShowConversationActivity.this); if (initialStatus != null) statuses.add(initialStatus); else statuses.add(detailsStatus); StatusDrawerParams statusDrawerParams = new StatusDrawerParams(); statusDrawerParams.setPosition(0); statusDrawerParams.setTargetedId(null); statusDrawerParams.setOnWifi(isOnWifi); statusDrawerParams.setStatuses(statuses); statusDrawerParams.setType(RetrieveFeedsAsyncTask.Type.CONTEXT); final LinearLayoutManager mLayoutManager; mLayoutManager = new LinearLayoutManager(this); binding.lvStatus.setLayoutManager(mLayoutManager); if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PIXELFED) { pixelfedListAdapter = new PixelfedListAdapter(statusDrawerParams); binding.lvStatus.setAdapter(pixelfedListAdapter); } else { statusListAdapter = new StatusListAdapter(statusDrawerParams); binding.lvStatus.setAdapter(statusListAdapter); } String statusIdToFetch = null; if (initialStatus != null) statusIdToFetch = initialStatus.getId(); else if (detailsStatus != null) statusIdToFetch = detailsStatus.getId(); if (statusIdToFetch == null) finish(); if (conversationId != null) statusIdToFetch = conversationId; new RetrieveContextAsyncTask(ShowConversationActivity.this, expanded, detailsStatus.getVisibility().equals("direct"), detailsStatus.getAccount().getId(), statusIdToFetch, ShowConversationActivity.this); swipeRefreshLayout.setDistanceToTriggerSync(500); swipeRefreshLayout.setOnRefreshListener(() -> { Intent intent = new Intent(ShowConversationActivity.this, ShowConversationActivity.class); Bundle b13 = new Bundle(); b13.putParcelable("status", detailsStatus); b13.putBoolean("expanded", expanded); if (expanded && statuses != null && statuses.size() > 0) b13.putParcelable("initialStatus", statuses.get(0)); b13.putParcelable("status", detailsStatus); intent.putExtras(b13); finish(); startActivity(intent); overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); }); //Searching some pixelfed info (bookmarked?) if (MainActivity.social == UpdateAccountInfoAsyncTask.SOCIAL.PIXELFED) { new Thread(() -> { JSONObject result = new PixelfedAPI(ShowConversationActivity.this).getPostDetails(detailsStatus.getAccount().getUsername(), detailsStatus.getId()); if (result != null && result.has("bookmarked")) { try { boolean bookmarked = result.getBoolean("bookmarked"); statuses.get(0).setBookmarked(bookmarked); runOnUiThread(() -> { pixelfedListAdapter.notifyItemChanged(0); }); } catch (JSONException e) { e.printStackTrace(); } } }).start(); } } public void addStatuses(Status status) { if (status != null && status.getIn_reply_to_id() != null && this.statuses != null) { int position = 0; for (Status s : this.statuses) { if (status.getIn_reply_to_id().equals(s.getId())) { this.statuses.add(position + 1, status); if (statusListAdapter != null) { statusListAdapter.notifyItemInserted(position + 1); } else if (pixelfedListAdapter != null) { pixelfedListAdapter.notifyItemInserted(position + 1); } break; } position++; } } } @Override public void onStop() { super.onStop(); if (statusListAdapter != null) { statusListAdapter.storeToot(); } } @Override public void onDestroy() { super.onDestroy(); if (receive_action != null) LocalBroadcastManager.getInstance(ShowConversationActivity.this).unregisterReceiver(receive_action); detailsStatus = null; } @Override public void onRetrieveContext(APIResponse apiResponse) { swipeRefreshLayout.setRefreshing(false); binding.loader.setVisibility(View.GONE); if (apiResponse.getError() != null) { if (apiResponse.getError().getError() != null) { Toasty.error(ShowConversationActivity.this, apiResponse.getError().getError(), Toast.LENGTH_LONG).show(); } else { Toasty.error(ShowConversationActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show(); } return; } if (apiResponse.getContext() == null || apiResponse.getContext().getAncestors() == null) { return; } if (BaseMainActivity.social != UpdateAccountInfoAsyncTask.SOCIAL.GNU && BaseMainActivity.social != UpdateAccountInfoAsyncTask.SOCIAL.FRIENDICA) { if (statusListAdapter != null) { statusListAdapter.setConversationPosition(apiResponse.getContext().getAncestors().size()); } if (!expanded) { if (apiResponse.getContext().getAncestors() != null && apiResponse.getContext().getAncestors().size() > 0) { if (statusListAdapter != null) { statuses.addAll(0, apiResponse.getContext().getAncestors()); statusListAdapter.notifyItemRangeInserted(0, apiResponse.getContext().getAncestors().size()); } else if (pixelfedListAdapter != null) { statuses.addAll(1, apiResponse.getContext().getAncestors()); pixelfedListAdapter.notifyItemRangeInserted(1, apiResponse.getContext().getAncestors().size()); } } int targetedPosition = statuses.size() - 1; if (apiResponse.getContext().getDescendants() != null && apiResponse.getContext().getDescendants().size() > 0) { statuses.addAll(apiResponse.getContext().getAncestors().size() + 1, apiResponse.getContext().getDescendants()); if (statusListAdapter != null) { statusListAdapter.notifyItemRangeChanged(apiResponse.getContext().getAncestors().size() + 1, apiResponse.getContext().getDescendants().size()); } else if (pixelfedListAdapter != null) { pixelfedListAdapter.notifyItemRangeChanged(apiResponse.getContext().getAncestors().size() + 1, apiResponse.getContext().getDescendants().size()); } } decorate(targetedPosition); } else { List statusesTemp = apiResponse.getContext().getDescendants(); int i = 1; int position = 0; for (Status status : statusesTemp) { statuses.add(status); if (status.getId().equals(detailsStatus.getId())) { if (statusListAdapter != null) { statusListAdapter.setConversationPosition(i); } detailsStatus = status; position = i; } i++; } decorate(position); if (statusListAdapter != null) { statusListAdapter.notifyItemRangeChanged(1, apiResponse.getContext().getDescendants().size()); } else if (pixelfedListAdapter != null) { pixelfedListAdapter.notifyItemRangeChanged(1, apiResponse.getContext().getDescendants().size()); } binding.lvStatus.scrollToPosition(position); } } else { int i = 0; if (apiResponse.getContext().getAncestors() != null && apiResponse.getContext().getAncestors().size() > 1) { statuses = new ArrayList<>(); statuses.clear(); for (Status status : apiResponse.getContext().getAncestors()) { if (detailsStatus != null && detailsStatus.equals(status)) { break; } i++; } boolean isOnWifi = Helper.isOnWIFI(ShowConversationActivity.this); for (Status status : apiResponse.getContext().getAncestors()) { statuses.add(0, status); } StatusDrawerParams statusDrawerParams = new StatusDrawerParams(); statusDrawerParams.setPosition((statuses.size() - 1 - i)); statusDrawerParams.setTargetedId(null); statusDrawerParams.setOnWifi(isOnWifi); statusDrawerParams.setStatuses(statuses); statusDrawerParams.setType(RetrieveFeedsAsyncTask.Type.CONTEXT); statusListAdapter = new StatusListAdapter(statusDrawerParams); statusListAdapter.setConversationPosition((statuses.size() - 1 - i)); decorate(0); final LinearLayoutManager mLayoutManager; mLayoutManager = new LinearLayoutManager(this); binding.lvStatus.setLayoutManager(mLayoutManager); binding.lvStatus.setAdapter(statusListAdapter); } } } //Add lines and offset to messages private void decorate(int targetedPosition) { for (int i = 0; i < statuses.size(); i++) { if (i == targetedPosition) { if (targetedPosition < statuses.size() - 1) statuses.get(targetedPosition).setShowBottomLine(true); if (targetedPosition > 0 && statuses.get(targetedPosition).getIn_reply_to_id() != null && statuses.get(targetedPosition).getIn_reply_to_id().compareTo(statuses.get(targetedPosition - 1).getId()) == 0) { statuses.get(targetedPosition - 1).setShowBottomLine(true); statuses.get(targetedPosition).setShowTopLine(true); } } else if (0 < i && i <= statuses.size() - 1) { if (statuses.get(i).getIn_reply_to_id() != null && statuses.get(i - 1).getId().compareTo(statuses.get(i).getIn_reply_to_id()) == 0) { statuses.get(i - 1).setShowBottomLine(true); statuses.get(i).setShowTopLine(true); } } } if (statusListAdapter != null) { statusListAdapter.notifyItemRangeChanged(0, statuses.size()); } else if (pixelfedListAdapter != null) { pixelfedListAdapter.notifyItemRangeChanged(0, statuses.size()); } } }