added new profile preferences, bug fix

This commit is contained in:
nuclearfog 2023-10-08 21:19:18 +02:00
parent ce0464ef79
commit a24ba241eb
No known key found for this signature in database
GPG Key ID: 03488A185C476379
12 changed files with 271 additions and 84 deletions

View File

@ -1154,6 +1154,7 @@ public class Mastodon implements Connection {
params.add("display_name=" + StringUtils.encode(update.getName()));
params.add("note=" + StringUtils.encode(update.getDescription()));
params.add("locked=" + update.privacyEnabled());
if (update.getProfileImageMedia() != null) {
streams.add(update.getProfileImageMedia().getStream());
keys.add("avatar");
@ -1162,6 +1163,18 @@ public class Mastodon implements Connection {
streams.add(update.getBannerImageMedia().getStream());
keys.add("header");
}
if (!update.getLanguageCode().isEmpty()) {
params.add("source[language]=" + update.getLanguageCode());
}
if (update.getStatusVisibility() == Status.VISIBLE_PUBLIC) {
params.add("source[privacy]=public");
} else if (update.getStatusVisibility() == Status.VISIBLE_PRIVATE) {
params.add("source[privacy]=private");
} else if (update.getStatusVisibility() == Status.VISIBLE_UNLISTED) {
params.add("source[privacy]=unlisted");
} else if (update.getStatusVisibility() == Status.VISIBLE_DIRECT) {
params.add("source[privacy]=direct");
}
try {
Response response = patch(ENDPOINT_UPDATE_CREDENTIALS, params, streams, keys);
ResponseBody body = response.body();

View File

@ -3,7 +3,7 @@ package org.nuclearfog.twidda.backend.api.mastodon.impl;
import org.json.JSONException;
import org.json.JSONObject;
import org.nuclearfog.twidda.model.Credentials;
import org.nuclearfog.twidda.model.User.Field;
import org.nuclearfog.twidda.model.Status;
/**
* Mastodon implementation of {@link Credentials}
@ -20,6 +20,7 @@ public class MastodonCredentials implements Credentials {
private String language;
private int visibility;
private boolean sensitive;
private boolean locked;
/**
* @param json Credentials json format
@ -32,26 +33,27 @@ public class MastodonCredentials implements Credentials {
description = sourceJson.getString("note");
language = sourceJson.getString("language");
sensitive = sourceJson.getBoolean("sensitive");
locked = json.optBoolean("locked");
switch(visStr) {
case "public":
visibility = PUBLIC;
visibility = Status.VISIBLE_PUBLIC;
break;
case "private":
visibility = PRIVATE;
visibility = Status.VISIBLE_PRIVATE;
break;
case "direct":
visibility = DIRECT;
visibility = Status.VISIBLE_DIRECT;
break;
case "unlisted":
visibility = UNLISTED;
visibility = Status.VISIBLE_UNLISTED;
break;
default:
visibility = DEFAULT;
visibility = Status.VISIBLE_DEFAULT;
break;
}
try {
@ -99,7 +101,7 @@ public class MastodonCredentials implements Credentials {
@Override
public Field[] getFields() {
return new Field[0];
public boolean isLocked() {
return locked;
}
}

View File

@ -39,16 +39,6 @@ public class MastodonUser implements User {
private Emoji[] emojis = {};
private Field[] fields = {};
/**
* constructor used to create an user instance of the current user
*
* @param json json object used by Mastodon API
*/
public MastodonUser(JSONObject json) throws JSONException {
this(json, 0L);
isCurrentUser = true;
}
/**
* default constructor for all user instances
*

View File

@ -6,6 +6,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.nuclearfog.twidda.backend.helper.MediaStatus;
import org.nuclearfog.twidda.model.Status;
import java.io.Closeable;
@ -22,6 +23,11 @@ public class UserUpdate implements Closeable {
private String description = "";
private String location = "";
private int statusVisibility = Status.VISIBLE_DEFAULT;
private boolean sensitiveContent = false;
private boolean privacy = false;
private String langCode = "";
/**
* close all image streams
@ -63,6 +69,40 @@ public class UserUpdate implements Closeable {
this.bannerImage = bannerImage;
}
/**
* set default status visibiltiy
*
* @param statusVisibility default status visibility {@link Status#VISIBLE_PUBLIC,Status#VISIBLE_DEFAULT,Status#VISIBLE_PRIVATE,Status#VISIBLE_UNLISTED}
*/
public void setStatusVisibility(int statusVisibility) {
this.statusVisibility = statusVisibility;
}
/**
* set default status sensitive flag
*
* @param sensitiveContent true to set sensitive flag by default
*/
public void setContentSensitive(boolean sensitiveContent) {
this.sensitiveContent = sensitiveContent;
}
/**
* set default language for posts
*
* @param langCode lang code
*/
public void setLanguageCode(@NonNull String langCode) {
this.langCode = langCode;
}
/**
* enable/disable follow confirmation
*/
public void setPrivacy(boolean privacy) {
this.privacy = privacy;
}
/**
* @return screen name of the user
*/
@ -77,13 +117,6 @@ public class UserUpdate implements Closeable {
return description;
}
/**
* @return true if any image is added
*/
public boolean imageAdded() {
return profileImage != null || bannerImage != null;
}
/**
* @return profile image media instance or null if not added
*/
@ -100,6 +133,40 @@ public class UserUpdate implements Closeable {
return bannerImage;
}
/**
* get default status visibility
*
* @return status visibility constant {@link Status#VISIBLE_PUBLIC,Status#VISIBLE_DEFAULT,Status#VISIBLE_PRIVATE,Status#VISIBLE_UNLISTED}
*/
public int getStatusVisibility() {
return statusVisibility;
}
/**
* @return true if user's status should be marked as sensitive by default
*/
public boolean getContentSensitive() {
return sensitiveContent;
}
/**
* get default language code used for posts
*
* @return language code
*/
public String getLanguageCode() {
return langCode;
}
/**
* get profile privacy preference
*
* @return true to ask user to confirm new followers
*/
public boolean privacyEnabled() {
return privacy;
}
/**
* initialize input streams of the image files
* streams must be closed calling {@link #close()}

View File

@ -2,8 +2,6 @@ package org.nuclearfog.twidda.model;
import java.io.Serializable;
import org.nuclearfog.twidda.model.User.Field;
/**
* represents credentials of the current user
*
@ -11,16 +9,6 @@ import org.nuclearfog.twidda.model.User.Field;
*/
public interface Credentials extends Serializable {
int DEFAULT = 0;
int PUBLIC = 10;
int PRIVATE = 11;
int DIRECT = 12;
int UNLISTED = 13;
/**
* get user ID
*
@ -48,7 +36,7 @@ public interface Credentials extends Serializable {
/**
* get default visibility of the user's status
*
* @return {@link #PUBLIC,#PRIVATE,#DIRECT,#UNLISTED}
* @return {@link Status#VISIBLE_PUBLIC,Status#VISIBLE_PRIVATE,Status#VISIBLE_DIRECT,Status#VISIBLE_UNLISTED}
*/
int getVisibility();
@ -58,7 +46,7 @@ public interface Credentials extends Serializable {
boolean isSensitive();
/**
* get user fields
* check if account requires follow request
*/
Field[] getFields();
boolean isLocked();
}

View File

@ -14,6 +14,7 @@ import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;
@ -44,6 +45,7 @@ import org.nuclearfog.twidda.model.User;
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog;
import org.nuclearfog.twidda.ui.dialogs.ConfirmDialog.OnConfirmListener;
import org.nuclearfog.twidda.ui.dialogs.ProgressDialog;
import org.nuclearfog.twidda.ui.dialogs.StatusPreferenceDialog;
import java.io.FileNotFoundException;
@ -72,11 +74,13 @@ public class ProfileEditor extends MediaActivity implements OnClickListener, Asy
private Picasso picasso;
private ProgressDialog progressDialog;
private StatusPreferenceDialog prefDialog;
private ConfirmDialog confirmDialog;
private ImageView profile_image, profile_banner, toolbar_background, changeBannerBtn;
private EditText username, profileUrl, profileLocation, userDescription;
private Button addBannerBtn;
private CompoundButton privacy;
@Nullable
private User user;
@ -100,6 +104,7 @@ public class ProfileEditor extends MediaActivity implements OnClickListener, Asy
ConstraintLayout root = findViewById(R.id.page_edit);
View profileLocationLabel = findViewById(R.id.profile_edit_change_location_label);
View profileUrlLabel = findViewById(R.id.profile_edit_change_url_label);
View statusPrefBtn = findViewById(R.id.profile_edit_status_pref);
profile_image = findViewById(R.id.edit_profile_image);
profile_banner = findViewById(R.id.profile_edit_banner);
addBannerBtn = findViewById(R.id.profile_edit_add_banner);
@ -109,7 +114,9 @@ public class ProfileEditor extends MediaActivity implements OnClickListener, Asy
profileUrl = findViewById(R.id.profile_edit_change_url);
profileLocation = findViewById(R.id.profile_edit_change_location);
userDescription = findViewById(R.id.profile_edit_change_description);
privacy = findViewById(R.id.profile_edit_privacy);
prefDialog = new StatusPreferenceDialog(this, userUpdate);
progressDialog = new ProgressDialog(this, null);
confirmDialog = new ConfirmDialog(this, this);
credentialAction = new CredentialsAction(this);
@ -152,6 +159,7 @@ public class ProfileEditor extends MediaActivity implements OnClickListener, Asy
profile_image.setOnClickListener(this);
profile_banner.setOnClickListener(this);
addBannerBtn.setOnClickListener(this);
statusPrefBtn.setOnClickListener(this);
}
@ -238,6 +246,10 @@ public class ProfileEditor extends MediaActivity implements OnClickListener, Asy
else if (v.getId() == R.id.profile_edit_add_banner || v.getId() == R.id.profile_edit_banner) {
getMedia(REQUEST_BANNER);
}
//
else if (v.getId() == R.id.profile_edit_status_pref) {
prefDialog.show();
}
}
@ -315,6 +327,7 @@ public class ProfileEditor extends MediaActivity implements OnClickListener, Asy
this.username.setError(errMsg);
} else {
userUpdate.setProfile(username, userBio, userLoc);
userUpdate.setPrivacy(privacy.isChecked());
if (userUpdate.prepare(getContentResolver())) {
CredentialsAction.Param param = new CredentialsAction.Param(CredentialsAction.Param.UPDATE, userUpdate);
credentialAction.execute(param, this);
@ -357,6 +370,8 @@ public class ProfileEditor extends MediaActivity implements OnClickListener, Asy
* set current user's credentials
*/
private void setCredentials() {
if (credentials != null) {
privacy.setChecked(credentials.isLocked());
}
}
}

View File

@ -50,7 +50,7 @@ public class ScheduleActivity extends AppCompatActivity implements OnClickListen
@Override
public void onClick(View v) {
if (v.getId() == R.id.page_tab_view_post_button) {
if (v.getId() == R.id.page_fragment_floating_button) {
Intent intent = new Intent(this, StatusEditor.class);
startActivity(intent);
}

View File

@ -14,10 +14,14 @@ import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.Spinner;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.kyleduo.switchbutton.SwitchButton;
import org.nuclearfog.twidda.R;
import org.nuclearfog.twidda.backend.helper.update.StatusUpdate;
import org.nuclearfog.twidda.backend.helper.update.UserUpdate;
import org.nuclearfog.twidda.backend.utils.AppStyles;
import org.nuclearfog.twidda.config.GlobalSettings;
import org.nuclearfog.twidda.model.Status;
@ -42,21 +46,42 @@ public class StatusPreferenceDialog extends Dialog implements OnCheckedChangeLis
private DropdownAdapter visibility_adapter, language_adapter;
private TimePickerDialog timePicker;
private GlobalSettings settings;
private StatusUpdate statusUpdate;
private String[] languageCodes;
@Nullable
private StatusUpdate statusUpdate;
@Nullable
private UserUpdate userUpdate;
/**
* @param statusUpdate status information from status editor
* create dialog to set user preferences
*
* @param userUpdate user update holder
*/
public StatusPreferenceDialog(Activity activity, StatusUpdate statusUpdate) {
super(activity, R.style.DefaultDialog);
public StatusPreferenceDialog(Activity activity, @NonNull UserUpdate userUpdate) {
this(activity);
this.userUpdate = userUpdate;
}
/**
* create dialog to set status preferences
*
* @param statusUpdate status update holder
*/
public StatusPreferenceDialog(Activity activity, @NonNull StatusUpdate statusUpdate) {
this(activity);
this.statusUpdate = statusUpdate;
}
/**
*
*/
private StatusPreferenceDialog(Activity activity) {
super(activity, R.style.DefaultDialog);
visibility_adapter = new DropdownAdapter(activity.getApplicationContext());
language_adapter = new DropdownAdapter(activity.getApplicationContext());
timePicker = new TimePickerDialog(activity, this);
settings = GlobalSettings.get(getContext());
// initialize language selector
Map<String, String> languages = new TreeMap<>();
languages.put("", "");
@ -99,6 +124,11 @@ public class StatusPreferenceDialog extends Dialog implements OnCheckedChangeLis
if (!settings.getLogin().getConfiguration().statusSpoilerSupported()) {
statusSpoiler.setVisibility(View.GONE);
}
if (userUpdate != null) {
scheduleText.setVisibility(View.GONE);
timePicker.setVisibility(View.GONE);
statusSpoiler.setVisibility(View.GONE);
}
sensitiveCheck.setOnCheckedChangeListener(this);
spoilerCheck.setOnCheckedChangeListener(this);
languageSelector.setOnItemSelectedListener(this);
@ -109,21 +139,41 @@ public class StatusPreferenceDialog extends Dialog implements OnCheckedChangeLis
@Override
protected void onStart() {
if (statusUpdate.getVisibility() == Status.VISIBLE_PUBLIC) {
visibilitySelector.setSelection(0, false);
} else if (statusUpdate.getVisibility() == Status.VISIBLE_PRIVATE) {
visibilitySelector.setSelection(1, false);
} else if (statusUpdate.getVisibility() == Status.VISIBLE_DIRECT) {
visibilitySelector.setSelection(2, false);
} else if (statusUpdate.getVisibility() == Status.VISIBLE_UNLISTED) {
visibilitySelector.setSelection(3, false);
}
sensitiveCheck.setCheckedImmediately(statusUpdate.isSensitive());
spoilerCheck.setCheckedImmediately(statusUpdate.isSpoiler());
if (!statusUpdate.getLanguageCode().isEmpty()) {
for (int i = 0; i < languageCodes.length; i++) {
if (languageCodes[i].equals(statusUpdate.getLanguageCode())) {
languageSelector.setSelection(i);
if (statusUpdate != null) {
if (statusUpdate.getVisibility() == Status.VISIBLE_PUBLIC) {
visibilitySelector.setSelection(0, false);
} else if (statusUpdate.getVisibility() == Status.VISIBLE_PRIVATE) {
visibilitySelector.setSelection(1, false);
} else if (statusUpdate.getVisibility() == Status.VISIBLE_DIRECT) {
visibilitySelector.setSelection(2, false);
} else if (statusUpdate.getVisibility() == Status.VISIBLE_UNLISTED) {
visibilitySelector.setSelection(3, false);
}
sensitiveCheck.setCheckedImmediately(statusUpdate.isSensitive());
spoilerCheck.setCheckedImmediately(statusUpdate.isSpoiler());
if (!statusUpdate.getLanguageCode().isEmpty()) {
for (int i = 0; i < languageCodes.length; i++) {
if (languageCodes[i].equals(statusUpdate.getLanguageCode())) {
languageSelector.setSelection(i);
}
}
}
} else if (userUpdate != null) {
if (userUpdate.getStatusVisibility() == Status.VISIBLE_PUBLIC) {
visibilitySelector.setSelection(0, false);
} else if (userUpdate.getStatusVisibility() == Status.VISIBLE_PRIVATE) {
visibilitySelector.setSelection(1, false);
} else if (userUpdate.getStatusVisibility() == Status.VISIBLE_DIRECT) {
visibilitySelector.setSelection(2, false);
} else if (userUpdate.getStatusVisibility() == Status.VISIBLE_UNLISTED) {
visibilitySelector.setSelection(3, false);
}
sensitiveCheck.setCheckedImmediately(userUpdate.getContentSensitive());
if (!userUpdate.getLanguageCode().isEmpty()) {
for (int i = 0; i < languageCodes.length; i++) {
if (languageCodes[i].equals(userUpdate.getLanguageCode())) {
languageSelector.setSelection(i);
}
}
}
}
@ -150,7 +200,9 @@ public class StatusPreferenceDialog extends Dialog implements OnCheckedChangeLis
@Override
public void onClick(View v) {
if (v.getId() == R.id.dialog_status_time_picker) {
timePicker.show(statusUpdate.getScheduleTime());
if (statusUpdate != null) {
timePicker.show(statusUpdate.getScheduleTime());
}
}
}
@ -158,9 +210,15 @@ public class StatusPreferenceDialog extends Dialog implements OnCheckedChangeLis
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (buttonView.getId() == R.id.dialog_status_sensitive) {
statusUpdate.setSensitive(isChecked);
if (statusUpdate != null) {
statusUpdate.setSensitive(isChecked);
} else if (userUpdate != null) {
userUpdate.setContentSensitive(isChecked);
}
} else if (buttonView.getId() == R.id.dialog_status_spoiler) {
statusUpdate.setSpoiler(isChecked);
if (statusUpdate != null) {
statusUpdate.setSpoiler(isChecked);
}
}
}
@ -168,30 +226,38 @@ public class StatusPreferenceDialog extends Dialog implements OnCheckedChangeLis
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (parent.getId() == R.id.dialog_status_visibility) {
switch (position) {
case 0:
if (statusUpdate != null) {
if (position == 0) {
statusUpdate.setVisibility(Status.VISIBLE_DEFAULT);
break;
case 1:
} else if (position == 1) {
statusUpdate.setVisibility(Status.VISIBLE_PUBLIC);
break;
case 2:
} else if (position == 2) {
statusUpdate.setVisibility(Status.VISIBLE_PRIVATE);
break;
case 3:
} else if (position == 3) {
statusUpdate.setVisibility(Status.VISIBLE_DIRECT);
break;
case 4:
} else if (position == 4) {
statusUpdate.setVisibility(Status.VISIBLE_UNLISTED);
break;
}
} else if (userUpdate != null) {
if (position == 0) {
userUpdate.setStatusVisibility(Status.VISIBLE_DEFAULT);
} else if (position == 1) {
userUpdate.setStatusVisibility(Status.VISIBLE_PUBLIC);
} else if (position == 2) {
userUpdate.setStatusVisibility(Status.VISIBLE_PRIVATE);
} else if (position == 3) {
userUpdate.setStatusVisibility(Status.VISIBLE_DIRECT);
} else if (position == 4) {
userUpdate.setStatusVisibility(Status.VISIBLE_UNLISTED);
}
}
} else if (parent.getId() == R.id.dialog_status_language) {
if (position > 0) {
statusUpdate.addLanguage(languageCodes[position]);
if (statusUpdate != null) {
statusUpdate.addLanguage(languageCodes[position]);
} else if (userUpdate != null) {
userUpdate.setLanguageCode(languageCodes[position]);
}
}
}
}
@ -204,7 +270,9 @@ public class StatusPreferenceDialog extends Dialog implements OnCheckedChangeLis
@Override
public void onTimeSelected(long time) {
statusUpdate.setScheduleTime(time);
if (statusUpdate != null) {
statusUpdate.setScheduleTime(time);
}
if (time != 0L) {
scheduleText.setText(new Date(time).toString());
} else {

View File

@ -169,6 +169,43 @@
android:minHeight="@dimen/editprofile_bio_min_height"
style="@style/TextInput" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/editprofile_edittext_padding"
android:text="@string/profile_other" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<com.kyleduo.switchbutton.SwitchButton
android:id="@+id/profile_edit_privacy"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/editprofile_button_margin" />
<TextView
android:id="@+id/profile_edit_privacy_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/profile_privacy"
android:lines="1" />
</LinearLayout>
<Button
android:id="@+id/profile_edit_status_pref"
android:layout_width="wrap_content"
android:layout_height="@dimen/editprofile_button_height"
android:paddingLeft="@dimen/editprofile_button_padding"
android:paddingRight="@dimen/editprofile_button_padding"
android:layout_margin="@dimen/editprofile_button_margin"
android:text="@string/profile_status_pref"
style="@style/FeedbackButton" />
</LinearLayout>
</ScrollView>

View File

@ -80,6 +80,9 @@
<string name="menu_edit_save">speichern</string>
<string name="info_profile_updated">Profile aktualisiert!</string>
<string name="error_empty_name">Name leer!</string>
<string name="profile_other">weitere Einstellungen</string>
<string name="profile_privacy">aktiviere Follower-Bestätigung</string>
<string name="profile_status_pref">Standardeinstellungen für Posts</string>
<string name="connection_discard">verwerfen</string>
<string name="confirm_discard">Änderungen verwerfen?</string>
<string name="user_data">Nutzerdaten</string>

View File

@ -65,7 +65,8 @@
<dimen name="editprofile_layout_margin">5dp</dimen>
<dimen name="editprofile_layout_padding">20dp</dimen>
<dimen name="editprofile_button_padding">5dp</dimen>
<dimen name="editprofile_button_height">20sp</dimen>
<dimen name="editprofile_button_margin">8dp</dimen>
<dimen name="editprofile_button_height">24sp</dimen>
<dimen name="editprofile_edittext_padding">5dp</dimen>
<dimen name="editprofile_bio_min_height">150dp</dimen>
<dimen name="editprofile_image">@dimen/profile_image_size</dimen>

View File

@ -231,6 +231,9 @@
<string name="profile_link">Link</string>
<string name="profile_location">Location</string>
<string name="profile_bio">Bio</string>
<string name="profile_other">Other settings</string>
<string name="profile_privacy">enable follower approval</string>
<string name="profile_status_pref">set default status preferences</string>
<string name="connection_discard">discard</string>
<string name="dialog_poll_title">create poll</string>
<string name="dialog_poll_button_create">create</string>