/* Copyright 2017 Andrew Dawson * * This file is part of Tusky. * * Tusky 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. * * Tusky 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 Tusky. If not, see * . */ package com.keylesspalace.tusky; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.DialogFragment; import android.support.v4.app.FragmentTransaction; import android.support.v7.widget.PopupMenu; import android.support.v7.widget.RecyclerView; import android.text.Spanned; import android.view.MenuItem; import android.view.View; import com.keylesspalace.tusky.entity.Relationship; import com.keylesspalace.tusky.entity.Status; import java.util.ArrayList; import java.util.List; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.Callback; /* Note from Andrew on Jan. 22, 2017: This class is a design problem for me, so I left it with an * awkward name. TimelineFragment and NotificationFragment have significant overlap but the nature * of that is complicated by how they're coupled with Status and Notification and the corresponding * adapters. I feel like the profile pages and thread viewer, which I haven't made yet, will also * overlap functionality. So, I'm momentarily leaving it and hopefully working on those will clear * up what needs to be where. */ public class SFragment extends BaseFragment { protected String loggedInAccountId; protected String loggedInUsername; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); SharedPreferences preferences = getContext().getSharedPreferences( getString(R.string.preferences_file_key), Context.MODE_PRIVATE); loggedInAccountId = preferences.getString("loggedInAccountId", null); loggedInUsername = preferences.getString("loggedInAccountUsername", null); } public MastodonAPI getApi() { return ((BaseActivity) getActivity()).mastodonAPI; } protected void reply(Status status) { String inReplyToId = status.getActionableId(); Status.Mention[] mentions = status.mentions; List mentionedUsernames = new ArrayList<>(); for (Status.Mention mention : mentions) { mentionedUsernames.add(mention.username); } mentionedUsernames.add(status.account.username); mentionedUsernames.remove(loggedInUsername); Intent intent = new Intent(getContext(), ComposeActivity.class); intent.putExtra("in_reply_to_id", inReplyToId); intent.putExtra("reply_visibility", status.getVisibility().toString().toLowerCase()); intent.putExtra("mentioned_usernames", mentionedUsernames.toArray(new String[0])); startActivity(intent); } protected void reblog(final Status status, final boolean reblog, final RecyclerView.Adapter adapter, final int position) { String id = status.getActionableId(); Callback cb = new Callback() { @Override public void onResponse(Call call, retrofit2.Response response) { if (response.isSuccessful()) { status.reblogged = reblog; if (status.reblog != null) { status.reblog.reblogged = reblog; } adapter.notifyItemChanged(position); } } @Override public void onFailure(Call call, Throwable t) { } }; Call call; if (reblog) { call = getApi().reblogStatus(id); } else { call = getApi().unreblogStatus(id); } call.enqueue(cb); callList.add(call); } protected void favourite(final Status status, final boolean favourite, final RecyclerView.Adapter adapter, final int position) { String id = status.getActionableId(); Callback cb = new Callback() { @Override public void onResponse(Call call, retrofit2.Response response) { if (response.isSuccessful()) { status.favourited = favourite; if (status.reblog != null) { status.reblog.favourited = favourite; } adapter.notifyItemChanged(position); } } @Override public void onFailure(Call call, Throwable t) { } }; Call call; if (favourite) { call = getApi().favouriteStatus(id); } else { call = getApi().unfavouriteStatus(id); } call.enqueue(cb); callList.add(call); } private void block(String id) { Call call = getApi().blockAccount(id); call.enqueue(new Callback() { @Override public void onResponse(Call call, retrofit2.Response response) { } @Override public void onFailure(Call call, Throwable t) { } }); callList.add(call); } private void delete(String id) { Call call = getApi().deleteStatus(id); call.enqueue(new Callback() { @Override public void onResponse(Call call, retrofit2.Response response) { } @Override public void onFailure(Call call, Throwable t) { } }); callList.add(call); } protected void more(Status status, View view, final AdapterItemRemover adapter, final int position) { final String id = status.getActionableId(); final String accountId = status.getActionableStatus().account.id; final String accountUsename = status.getActionableStatus().account.username; final Spanned content = status.getActionableStatus().content; final String statusUrl = status.getActionableStatus().url; PopupMenu popup = new PopupMenu(getContext(), view); // Give a different menu depending on whether this is the user's own toot or not. if (loggedInAccountId == null || !loggedInAccountId.equals(accountId)) { popup.inflate(R.menu.status_more); } else { popup.inflate(R.menu.status_more_for_user); } popup.setOnMenuItemClickListener( new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case R.id.status_share: { Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); sendIntent.putExtra(Intent.EXTRA_TEXT, statusUrl); sendIntent.setType("text/plain"); startActivity(Intent.createChooser(sendIntent, getResources().getText(R.string.send_status_to))); return true; } case R.id.status_block: { block(accountId); return true; } case R.id.status_report: { openReportPage(accountId, accountUsename, id, content); return true; } case R.id.status_delete: { delete(id); adapter.removeItem(position); return true; } } return false; } }); popup.show(); } protected void viewMedia(String url, Status.MediaAttachment.Type type) { switch (type) { case IMAGE: { DialogFragment newFragment = ViewMediaFragment.newInstance(url); FragmentTransaction ft = getFragmentManager().beginTransaction(); newFragment.show(ft, "view_media"); break; } case GIFV: case VIDEO: { Intent intent = new Intent(getContext(), ViewVideoActivity.class); intent.putExtra("url", url); startActivity(intent); break; } case UNKNOWN: { /* Intentionally do nothing. This case is here is to handle when new attachment * types are added to the API before code is added here to handle them. So, the * best fallback is to just show the preview and ignore requests to view them. */ break; } } } protected void viewThread(Status status) { Intent intent = new Intent(getContext(), ViewThreadActivity.class); intent.putExtra("id", status.id); startActivity(intent); } protected void viewTag(String tag) { Intent intent = new Intent(getContext(), ViewTagActivity.class); intent.putExtra("hashtag", tag); startActivity(intent); } protected void viewAccount(String id) { Intent intent = new Intent(getContext(), AccountActivity.class); intent.putExtra("id", id); startActivity(intent); } @Override public void startActivity(Intent intent) { super.startActivity(intent); getActivity().overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left); } protected void openReportPage(String accountId, String accountUsername, String statusId, Spanned statusContent) { Intent intent = new Intent(getContext(), ReportActivity.class); intent.putExtra("account_id", accountId); intent.putExtra("account_username", accountUsername); intent.putExtra("status_id", statusId); intent.putExtra("status_content", HtmlUtils.toHtml(statusContent)); startActivity(intent); } }