added poll vote support

This commit is contained in:
nuclearfog 2023-02-20 22:42:15 +01:00
parent bcc9740f3d
commit cdbf7aff60
No known key found for this signature in database
GPG Key ID: 03488A185C476379
9 changed files with 142 additions and 94 deletions

View File

@ -8,7 +8,6 @@ import org.nuclearfog.twidda.backend.helper.StatusUpdate;
import org.nuclearfog.twidda.backend.helper.UserListUpdate;
import org.nuclearfog.twidda.backend.helper.UserLists;
import org.nuclearfog.twidda.backend.helper.Users;
import org.nuclearfog.twidda.backend.helper.VoteUpdate;
import org.nuclearfog.twidda.model.Account;
import org.nuclearfog.twidda.model.Emoji;
import org.nuclearfog.twidda.model.Location;
@ -526,9 +525,11 @@ public interface Connection {
/**
* send a vote to a poll
*
* @param poll poll tovote
* @param selection selected poll choices
* @return updated poll
*/
Poll vote(VoteUpdate update) throws ConnectionException;
Poll vote(Poll poll, int[] selection) throws ConnectionException;
/**
* returns a list of blocked user IDs

View File

@ -29,7 +29,6 @@ import org.nuclearfog.twidda.backend.helper.StatusUpdate;
import org.nuclearfog.twidda.backend.helper.UserListUpdate;
import org.nuclearfog.twidda.backend.helper.UserLists;
import org.nuclearfog.twidda.backend.helper.Users;
import org.nuclearfog.twidda.backend.helper.VoteUpdate;
import org.nuclearfog.twidda.backend.utils.ConnectionBuilder;
import org.nuclearfog.twidda.backend.utils.StringTools;
import org.nuclearfog.twidda.config.GlobalSettings;
@ -762,13 +761,13 @@ public class Mastodon implements Connection {
@Override
public Poll vote(VoteUpdate update) throws ConnectionException {
public Poll vote(Poll poll, int[] selection) throws ConnectionException {
List<String> params = new ArrayList<>();
for (int choice : update.getSelected()) {
for (int choice : selection) {
params.add("choices[]=" + choice);
}
try {
Response response = post(ENDPOINT_POLL + update.getPollId() + "/votes", params);
Response response = post(ENDPOINT_POLL + poll.getId() + "/votes", params);
ResponseBody body = response.body();
if (response.code() == 200 && body != null) {
JSONObject json = new JSONObject(body.string());

View File

@ -22,7 +22,6 @@ import org.nuclearfog.twidda.backend.helper.StatusUpdate;
import org.nuclearfog.twidda.backend.helper.UserListUpdate;
import org.nuclearfog.twidda.backend.helper.UserLists;
import org.nuclearfog.twidda.backend.helper.Users;
import org.nuclearfog.twidda.backend.helper.VoteUpdate;
import org.nuclearfog.twidda.backend.utils.ConnectionBuilder;
import org.nuclearfog.twidda.backend.utils.StringTools;
import org.nuclearfog.twidda.config.GlobalSettings;
@ -928,7 +927,7 @@ public class TwitterV1 implements Connection {
@Override
public Poll vote(VoteUpdate update) throws ConnectionException {
public Poll vote(Poll poll, int[] choices) throws ConnectionException {
throw new TwitterException("not supported!");
}

View File

@ -0,0 +1,76 @@
package org.nuclearfog.twidda.backend.async;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.nuclearfog.twidda.backend.api.Connection;
import org.nuclearfog.twidda.backend.api.ConnectionException;
import org.nuclearfog.twidda.backend.api.ConnectionManager;
import org.nuclearfog.twidda.backend.utils.AsyncExecutor;
import org.nuclearfog.twidda.model.Poll;
import java.util.Arrays;
/**
* Asynctask to update a poll vote
*
* @author nuclearfog
*/
public class VoteUpdater extends AsyncExecutor<VoteUpdater.VoteParam, VoteUpdater.VoteResult> {
private Connection connection;
/**
*
*/
public VoteUpdater(Context context) {
connection = ConnectionManager.get(context);
}
@NonNull
@Override
protected VoteResult doInBackground(VoteParam param) {
try {
Poll poll = connection.vote(param.poll, param.selection);
return new VoteResult(poll, null);
} catch (ConnectionException exception) {
return new VoteResult(null, exception);
} catch (Exception e) {
e.printStackTrace();
}
return new VoteResult(null, null);
}
/**
*
*/
public static class VoteParam {
public final Poll poll;
public final int[] selection;
public VoteParam(Poll poll, int[] selection) {
this.poll = poll;
this.selection = Arrays.copyOf(selection, selection.length);
}
}
/**
*
*/
public static class VoteResult {
@Nullable
public final Poll poll;
@Nullable
public final ConnectionException exception;
VoteResult(@Nullable Poll poll, @Nullable ConnectionException exception) {
this.poll = poll;
this.exception = exception;
}
}
}

View File

@ -1,71 +0,0 @@
package org.nuclearfog.twidda.backend.helper;
import androidx.annotation.NonNull;
import org.nuclearfog.twidda.model.Poll;
import java.util.Set;
import java.util.TreeSet;
/**
* This class is used to send a vote to a poll
*
* @author nuclearfog
*/
public class VoteUpdate {
private long id;
private boolean multipleChoice;
private Set<Integer> choices;
/**
* @param poll poll to vote
*/
public VoteUpdate(Poll poll) {
id = poll.getId();
multipleChoice = poll.getLimit() > 1;
choices = new TreeSet<>();
}
/**
* select index of the selected vote option, starting with "0"
*
* @param index index of the option
*/
public void setChoice(int index) {
if (!multipleChoice || choices.isEmpty()) {
choices.add(index);
}
}
/**
* ID of the poll
*
* @return poll ID
*/
public long getPollId() {
return id;
}
/**
* get all selected vote option
*
* @return array of selected option index
*/
public int[] getSelected() {
int i = 0;
int[] result = new int[choices.size()];
for (Integer choice : choices) {
result[i] = choice;
i++;
}
return result;
}
@NonNull
@Override
public String toString() {
return "id=" + id + " choices=" + choices.size();
}
}

View File

@ -56,8 +56,10 @@ import org.nuclearfog.twidda.backend.api.ConnectionException;
import org.nuclearfog.twidda.backend.async.StatusAction;
import org.nuclearfog.twidda.backend.async.StatusAction.StatusParam;
import org.nuclearfog.twidda.backend.async.StatusAction.StatusResult;
import org.nuclearfog.twidda.backend.async.VoteUpdater;
import org.nuclearfog.twidda.backend.async.VoteUpdater.VoteParam;
import org.nuclearfog.twidda.backend.async.VoteUpdater.VoteResult;
import org.nuclearfog.twidda.backend.utils.AppStyles;
import org.nuclearfog.twidda.backend.utils.AsyncExecutor.AsyncCallback;
import org.nuclearfog.twidda.backend.utils.ErrorHandler;
import org.nuclearfog.twidda.backend.utils.PicassoBuilder;
import org.nuclearfog.twidda.backend.utils.StringTools;
@ -87,7 +89,7 @@ import jp.wasabeef.picasso.transformations.RoundedCornersTransformation;
*
* @author nuclearfog
*/
public class StatusActivity extends AppCompatActivity implements OnClickListener, OnScrollChangeListener, AsyncCallback<StatusResult>,
public class StatusActivity extends AppCompatActivity implements OnClickListener, OnScrollChangeListener,
OnLongClickListener, OnTagClickListener, OnConfirmListener, OnCardClickListener {
/**
@ -140,8 +142,11 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
@Nullable
private ClipboardManager clip;
private GlobalSettings settings;
@Nullable
private StatusAction statusAsync;
@Nullable
private VoteUpdater voteAsync;
private GlobalSettings settings;
private Picasso picasso;
private PreviewAdapter adapter;
@ -272,12 +277,12 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
if (status != null) {
setStatus(status);
StatusParam param = new StatusParam(StatusParam.ONLINE, status.getId());
statusAsync.execute(param, this);
statusAsync.execute(param, this::onStatusResult);
}
// Load status from database first if no status is defined
else {
StatusParam param = new StatusParam(StatusParam.ONLINE, id);
statusAsync.execute(param, this);
statusAsync.execute(param, this::onStatusResult);
}
}
}
@ -380,7 +385,7 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
param = new StatusParam(StatusParam.HIDE, status.getId());
}
statusAsync = new StatusAction(this);
statusAsync.execute(param, this);
statusAsync.execute(param, this::onStatusResult);
}
// get status link
else if (item.getItemId() == R.id.menu_status_browser) {
@ -505,7 +510,7 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
param = new StatusParam(StatusParam.REPOST, status.getId());
}
statusAsync = new StatusAction(this);
statusAsync.execute(param, this);
statusAsync.execute(param, this::onStatusResult);
Toast.makeText(getApplicationContext(), R.string.info_loading, LENGTH_SHORT).show();
return true;
}
@ -518,7 +523,7 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
param = new StatusParam(StatusParam.FAVORITE, status.getId());
}
statusAsync = new StatusAction(this);
statusAsync.execute(param, this);
statusAsync.execute(param, this::onStatusResult);
Toast.makeText(getApplicationContext(), R.string.info_loading, LENGTH_SHORT).show();
return true;
}
@ -556,7 +561,7 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
param = new StatusParam(StatusParam.BOOKMARK, status.getId());
}
statusAsync = new StatusAction(this);
statusAsync.execute(param, this);
statusAsync.execute(param, this::onStatusResult);
Toast.makeText(getApplicationContext(), R.string.info_loading, LENGTH_SHORT).show();
return true;
}
@ -586,7 +591,7 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
if (type == ConfirmDialog.DELETE_STATUS) {
StatusParam param = new StatusParam(StatusParam.DELETE, status.getId());
statusAsync = new StatusAction(this);
statusAsync.execute(param, this);
statusAsync.execute(param, this::onStatusResult);
}
// confirm playing video without proxy
else if (type == ConfirmDialog.PROXY_CONFIRM) {
@ -692,7 +697,11 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
@Override
public void onVoteClick(Poll poll, int[] selection) {
// todo add implementation
if (voteAsync == null || voteAsync.isIdle()) {
VoteParam param = new VoteParam(poll, selection);
voteAsync = new VoteUpdater(this);
voteAsync.execute(param, this::setPollResult);
}
}
/**
@ -818,16 +827,18 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
}
}
@Override
public void onResult(StatusResult result) {
/**
*
*/
public void onStatusResult(StatusResult result) {
if (result.status != null) {
setStatus(result.status);
}
switch (result.mode) {
case StatusResult.DATABASE: // update database status
StatusParam param = new StatusParam(StatusParam.ONLINE, id);
statusAsync.execute(param, this);
statusAsync = new StatusAction(this);
statusAsync.execute(param, this::onStatusResult);
break;
case StatusResult.REPOST:
@ -901,4 +912,19 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
break;
}
}
/**
* update status poll
*
* @param result poll result
*/
public void setPollResult(VoteResult result) {
if (result.poll != null) {
adapter.updatePoll(result.poll);
Toast.makeText(getApplicationContext(), R.string.info_poll_voted, Toast.LENGTH_SHORT).show();
} else {
String message = ErrorHandler.getErrorMessage(this, result.exception);
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
}
}
}

View File

@ -174,6 +174,21 @@ public class PreviewAdapter extends RecyclerView.Adapter<ViewHolder> implements
notifyDataSetChanged();
}
/**
* update existing poll item
*
* @param poll updated poll item
*/
public void updatePoll(Poll poll) {
for (int i = 0; i < items.size(); i++) {
if (items.get(i) instanceof Poll && ((Poll)items.get(i)).getId() == poll.getId()) {
items.set(i, poll);
notifyItemChanged(i);
break;
}
}
}
/**
* item click listener
*/

View File

@ -123,6 +123,7 @@
<string name="item_load_more">Mehr laden</string>
<string name="item_image_save">Bild speichern</string>
<string name="error_cant_load_video">Video kann nicht abgespielt werden!</string>
<string name="item_poll_option_vote">abstimmen</string>
<string name="settings_enable_proxy">Proxy aktivieren</string>
<string name="settings_enable_proxy_auth">Proxy Authentifizierung aktivieren</string>
<string name="error_wrong_connection_settings">Falsche Proxykonfiguration! Änderung verwerfen?</string>
@ -268,4 +269,5 @@
<string name="info_tweet_bookmarked">Status zu Lesezeichen hinzugefügt</string>
<string name="info_tweet_unbookmarked">Status aus Lesezeichen entfernt</string>
<string name="settings_bookmark_color">Lesezeichen symbol</string>
<string name="info_poll_voted">Umfrage abgestimmt!</string>
</resources>

View File

@ -56,6 +56,7 @@
<string name="info_tweet_text_copied">Status text copied</string>
<string name="info_tweet_location_copied">Location coordinates copied</string>
<string name="info_tweet_medialink_copied">Media link copied</string>
<string name="info_poll_voted">vote sent!</string>
<string name="info_account_selected">%1$s selected</string>
<string name="info_user_favorited">%1$s favorited your status</string>
<string name="info_user_follow">%1$s followed you</string>