aboutsummaryrefslogtreecommitdiff
path: root/app/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main')
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/services/EditProfileService.java97
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/utils/EditProfileUtils.java315
-rw-r--r--app/src/main/res/values/strings.xml2
3 files changed, 196 insertions, 218 deletions
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/services/EditProfileService.java b/app/src/main/java/ml/docilealligator/infinityforreddit/services/EditProfileService.java
index 9049c950..d1e2e81f 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/services/EditProfileService.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/services/EditProfileService.java
@@ -16,6 +16,7 @@ import android.os.PersistableBundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
import androidx.core.app.NotificationChannelCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
@@ -152,17 +153,17 @@ public class EditProfileService extends JobService {
mExecutor.execute(() -> {
switch (postType) {
case EXTRA_POST_TYPE_CHANGE_BANNER:
- submitChangeBanner(params, accessToken,
+ submitChangeBannerSync(params, accessToken,
Uri.parse(bundle.getString(EXTRA_MEDIA_URI)),
accountName);
break;
case EXTRA_POST_TYPE_CHANGE_AVATAR:
- submitChangeAvatar(params, accessToken,
+ submitChangeAvatarSync(params, accessToken,
Uri.parse(bundle.getString(EXTRA_MEDIA_URI)),
accountName);
break;
case EXTRA_POST_TYPE_SAVE_EDIT_PROFILE:
- submitSaveEditProfile(
+ submitSaveEditProfileSync(
params,
accessToken,
accountName,
@@ -181,80 +182,68 @@ public class EditProfileService extends JobService {
return false;
}
- private void submitChangeBanner(JobParameters parameters, String accessToken, Uri mediaUri, String accountName) {
+ @WorkerThread
+ private void submitChangeBannerSync(JobParameters parameters, String accessToken, Uri mediaUri, String accountName) {
try {
final int width = getWidthBanner(mediaUri);
final int height = Math.round(width * 3 / 10f); // ratio 10:3
CropTransformation bannerCrop = new CropTransformation(width, height, CropTransformation.CropType.CENTER);
Bitmap resource = Glide.with(this).asBitmap().skipMemoryCache(true)
.load(mediaUri).transform(bannerCrop).submit().get();
- EditProfileUtils.uploadBanner(mOauthRetrofit, accessToken, accountName, resource, new EditProfileUtils.EditProfileUtilsListener() {
- @Override
- public void success() {
- handler.post(() -> EventBus.getDefault().post(new SubmitChangeBannerEvent(true, "")));
- jobFinished(parameters, false);
- }
-
- @Override
- public void failed(String message) {
- handler.post(() -> EventBus.getDefault().post(new SubmitChangeBannerEvent(false, message)));
- jobFinished(parameters, false);
- }
- });
+ String potentialError = EditProfileUtils.uploadBannerSync(mOauthRetrofit, accessToken, accountName, resource);
+ if (potentialError == null) {
+ //Successful
+ handler.post(() -> EventBus.getDefault().post(new SubmitChangeBannerEvent(true, "")));
+ jobFinished(parameters, false);
+ } else {
+ handler.post(() -> EventBus.getDefault().post(new SubmitChangeBannerEvent(false, potentialError)));
+ jobFinished(parameters, false);
+ }
} catch (InterruptedException | ExecutionException | FileNotFoundException e) {
e.printStackTrace();
+ handler.post(() -> EventBus.getDefault().post(new SubmitChangeBannerEvent(false, e.getLocalizedMessage())));
jobFinished(parameters, false);
}
}
- private void submitChangeAvatar(JobParameters parameters, String accessToken, Uri mediaUri, String accountName) {
+ @WorkerThread
+ private void submitChangeAvatarSync(JobParameters parameters, String accessToken, Uri mediaUri, String accountName) {
try {
final CropTransformation avatarCrop = new CropTransformation(AVATAR_SIZE, AVATAR_SIZE, CropTransformation.CropType.CENTER);
final Bitmap resource = Glide.with(this).asBitmap().skipMemoryCache(true)
.load(mediaUri).transform(avatarCrop).submit().get();
- EditProfileUtils.uploadAvatar(mOauthRetrofit, accessToken, accountName, resource, new EditProfileUtils.EditProfileUtilsListener() {
- @Override
- public void success() {
- handler.post(() -> EventBus.getDefault().post(new SubmitChangeAvatarEvent(true, "")));
- jobFinished(parameters, false);
- }
-
- @Override
- public void failed(String message) {
- handler.post(() -> EventBus.getDefault().post(new SubmitChangeAvatarEvent(false, message)));
- jobFinished(parameters, false);
- }
- });
+ String potentialError = EditProfileUtils.uploadAvatarSync(mOauthRetrofit, accessToken, accountName, resource);
+ if (potentialError == null) {
+ //Successful
+ handler.post(() -> EventBus.getDefault().post(new SubmitChangeAvatarEvent(true, "")));
+ jobFinished(parameters, false);
+ } else {
+ handler.post(() -> EventBus.getDefault().post(new SubmitChangeAvatarEvent(false, potentialError)));
+ jobFinished(parameters, false);
+ }
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
+ handler.post(() -> EventBus.getDefault().post(new SubmitChangeAvatarEvent(false, e.getLocalizedMessage())));
jobFinished(parameters, false);
}
}
- private void submitSaveEditProfile(JobParameters parameters, @Nullable String accessToken,
- @NonNull String accountName,
- String displayName,
- String publicDesc
+ @WorkerThread
+ private void submitSaveEditProfileSync(JobParameters parameters, @Nullable String accessToken,
+ @NonNull String accountName,
+ String displayName,
+ String publicDesc
) {
- EditProfileUtils.updateProfile(mOauthRetrofit,
- accessToken,
- accountName,
- displayName,
- publicDesc,
- new EditProfileUtils.EditProfileUtilsListener() {
- @Override
- public void success() {
- handler.post(() -> EventBus.getDefault().post(new SubmitSaveProfileEvent(true, "")));
- jobFinished(parameters, false);
- }
-
- @Override
- public void failed(String message) {
- handler.post(() -> EventBus.getDefault().post(new SubmitSaveProfileEvent(false, message)));
- jobFinished(parameters, false);
- }
- });
-
+ String potentialError = EditProfileUtils.updateProfileSync(mOauthRetrofit, accessToken, accountName,
+ displayName, publicDesc);
+ if (potentialError == null) {
+ //Successful
+ handler.post(() -> EventBus.getDefault().post(new SubmitSaveProfileEvent(true, "")));
+ jobFinished(parameters, false);
+ } else {
+ handler.post(() -> EventBus.getDefault().post(new SubmitSaveProfileEvent(false, potentialError)));
+ jobFinished(parameters, false);
+ }
}
private Notification createNotification(int stringResId) {
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/utils/EditProfileUtils.java b/app/src/main/java/ml/docilealligator/infinityforreddit/utils/EditProfileUtils.java
index 0eab01ce..13faa90a 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/utils/EditProfileUtils.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/utils/EditProfileUtils.java
@@ -4,6 +4,7 @@ import android.graphics.Bitmap;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
import com.google.gson.Gson;
@@ -11,6 +12,7 @@ import org.json.JSONException;
import org.json.JSONObject;
import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@@ -26,189 +28,176 @@ import retrofit2.Retrofit;
public final class EditProfileUtils {
- public static void updateProfile(Retrofit oauthRetrofit,
- @Nullable String accessToken,
- @NonNull String accountName,
- String displayName,
- String publicDesc,
- EditProfileUtilsListener listener) {
+ /**
+ * @return the error String. Null indicates no error.
+ */
+ @WorkerThread
+ public static String updateProfileSync(Retrofit oauthRetrofit,
+ @Nullable String accessToken,
+ @NonNull String accountName,
+ String displayName,
+ String publicDesc) {
final Map<String, String> oauthHeader = APIUtils.getOAuthHeader(accessToken);
final RedditAPI api = oauthRetrofit.create(RedditAPI.class);
final String name = "u_" + accountName;
- api.getSubredditSetting(oauthHeader, name).enqueue(new Callback<>() {
- @Override
- public void onResponse(@NonNull Call<String> call, @NonNull Response<String> response) {
- if (response.isSuccessful()) {
- try {
- final String json = response.body();
- if (json == null) {
- listener.failed("Something happen.");
- return;
- }
- final JSONObject resBody = new JSONObject(json);
- final SubredditSettingData data = new Gson().fromJson(resBody.getString("data"), SubredditSettingData.class);
+ try {
+ Response<String> response = api.getSubredditSetting(oauthHeader, name).execute();
+ if (response.isSuccessful()) {
+ final String json = response.body();
+ if (json == null) {
+ return "Something happened.";
+ }
- if (data.getPublicDescription().equals(publicDesc)
- && data.getTitle().equals(displayName)) {
- // no-op
- listener.success();
- return;
- }
+ final JSONObject resBody = new JSONObject(json);
+ final SubredditSettingData data = new Gson().fromJson(resBody.getString("data"), SubredditSettingData.class);
- final Map<String, String> params = new HashMap<>();
+ if (data.getPublicDescription().equals(publicDesc)
+ && data.getTitle().equals(displayName)) {
+ // no-op
+ return null;
+ }
- params.put("api_type", "json");
- params.put("sr", data.getSubredditId());
- params.put("name", name);
- params.put("type", data.getSubredditType());
- // Only this 2 param
- params.put("public_description", publicDesc);
- params.put("title", displayName);
- // Official Reddit app have this 2 params
- // 1 = disable; 0 = enable || Active in communities visibility || Show which communities I am active in on my profile.
- params.put("toxicity_threshold_chat_level", String.valueOf(data.getToxicityThresholdChatLevel()));
- // Content visibility || Posts to this profile can appear in r/all and your profile can be discovered in /users
- params.put("default_set", String.valueOf(data.isDefaultSet()));
+ final Map<String, String> params = new HashMap<>();
- // Allow people to follow you || Followers will be notified about posts you make to your profile and see them in their home feed.
- params.put("accept_followers", String.valueOf(data.isAcceptFollowers()));
+ params.put("api_type", "json");
+ params.put("sr", data.getSubredditId());
+ params.put("name", name);
+ params.put("type", data.getSubredditType());
+ // Only this 2 param
+ params.put("public_description", publicDesc);
+ params.put("title", displayName);
+ // Official Reddit app have this 2 params
+ // 1 = disable; 0 = enable || Active in communities visibility || Show which communities I am active in on my profile.
+ params.put("toxicity_threshold_chat_level", String.valueOf(data.getToxicityThresholdChatLevel()));
+ // Content visibility || Posts to this profile can appear in r/all and your profile can be discovered in /users
+ params.put("default_set", String.valueOf(data.isDefaultSet()));
- params.put("allow_top", String.valueOf(data.isPublicTraffic())); //
- params.put("link_type", String.valueOf(data.getContentOptions())); //
- //
- params.put("original_content_tag_enabled", String.valueOf(data.isOriginalContentTagEnabled()));
- params.put("new_pinned_post_pns_enabled", String.valueOf(data.isNewPinnedPostPnsEnabled()));
- params.put("prediction_leaderboard_entry_type", String.valueOf(data.getPredictionLeaderboardEntryType()));
- params.put("restrict_commenting", String.valueOf(data.isRestrictCommenting()));
- params.put("restrict_posting", String.valueOf(data.isRestrictPosting()));
- params.put("should_archive_posts", String.valueOf(data.isShouldArchivePosts()));
- params.put("show_media", String.valueOf(data.isShowMedia()));
- params.put("show_media_preview", String.valueOf(data.isShowMediaPreview()));
- params.put("spam_comments", data.getSpamComments());
- params.put("spam_links", data.getSpamLinks());
- params.put("spam_selfposts", data.getSpamSelfPosts());
- params.put("spoilers_enabled", String.valueOf(data.isSpoilersEnabled()));
- params.put("submit_link_label", data.getSubmitLinkLabel());
- params.put("submit_text", data.getSubmitText());
- params.put("submit_text_label", data.getSubmitTextLabel());
- params.put("user_flair_pns_enabled", String.valueOf(data.isUserFlairPnsEnabled()));
- params.put("all_original_content", String.valueOf(data.isAllOriginalContent()));
- params.put("allow_chat_post_creation", String.valueOf(data.isAllowChatPostCreation()));
- params.put("allow_discovery", String.valueOf(data.isAllowDiscovery()));
- params.put("allow_galleries", String.valueOf(data.isAllowGalleries()));
- params.put("allow_images", String.valueOf(data.isAllowImages()));
- params.put("allow_polls", String.valueOf(data.isAllowPolls()));
- params.put("allow_post_crossposts", String.valueOf(data.isAllowPostCrossPosts()));
- params.put("allow_prediction_contributors", String.valueOf(data.isAllowPredictionContributors()));
- params.put("allow_predictions", String.valueOf(data.isAllowPredictions()));
- params.put("allow_predictions_tournament", String.valueOf(data.isAllowPredictionsTournament()));
- params.put("allow_videos", String.valueOf(data.isAllowVideos()));
- params.put("collapse_deleted_comments", String.valueOf(data.isCollapseDeletedComments()));
- params.put("comment_score_hide_mins", String.valueOf(data.getCommentScoreHideMins()));
- params.put("crowd_control_chat_level", String.valueOf(data.getCrowdControlChatLevel()));
- params.put("crowd_control_filter", String.valueOf(data.getCrowdControlChatLevel()));
- params.put("crowd_control_level", String.valueOf(data.getCrowdControlLevel()));
- params.put("crowd_control_mode", String.valueOf(data.isCrowdControlMode()));
- params.put("description", data.getDescription());
- params.put("disable_contributor_requests", String.valueOf(data.isDisableContributorRequests()));
- params.put("exclude_banned_modqueue", String.valueOf(data.isExcludeBannedModQueue()));
- params.put("free_form_reports", String.valueOf(data.isFreeFormReports()));
- params.put("header-title", data.getHeaderHoverText());
- params.put("hide_ads", String.valueOf(data.isHideAds()));
- params.put("key_color", data.getKeyColor());
- params.put("lang", data.getLanguage());
- params.put("over_18", String.valueOf(data.isOver18()));
- params.put("suggested_comment_sort", data.getSuggestedCommentSort());
- params.put("welcome_message_enabled", String.valueOf(data.isWelcomeMessageEnabled()));
- params.put("welcome_message_text", String.valueOf(data.getWelcomeMessageText()));
- params.put("wiki_edit_age", String.valueOf(data.getWikiEditAge()));
- params.put("wiki_edit_karma", String.valueOf(data.getWikiEditKarma()));
- params.put("wikimode", data.getWikiMode());
+ // Allow people to follow you || Followers will be notified about posts you make to your profile and see them in their home feed.
+ params.put("accept_followers", String.valueOf(data.isAcceptFollowers()));
- api.postSiteAdmin(oauthHeader, params)
- .enqueue(new Callback<>() {
- @Override
- public void onResponse(@NonNull Call<String> call, @NonNull Response<String> response) {
- if (response.isSuccessful()) listener.success();
- else listener.failed(response.message());
- }
+ params.put("allow_top", String.valueOf(data.isPublicTraffic())); //
+ params.put("link_type", String.valueOf(data.getContentOptions())); //
+ //
+ params.put("original_content_tag_enabled", String.valueOf(data.isOriginalContentTagEnabled()));
+ params.put("new_pinned_post_pns_enabled", String.valueOf(data.isNewPinnedPostPnsEnabled()));
+ params.put("prediction_leaderboard_entry_type", String.valueOf(data.getPredictionLeaderboardEntryType()));
+ params.put("restrict_commenting", String.valueOf(data.isRestrictCommenting()));
+ params.put("restrict_posting", String.valueOf(data.isRestrictPosting()));
+ params.put("should_archive_posts", String.valueOf(data.isShouldArchivePosts()));
+ params.put("show_media", String.valueOf(data.isShowMedia()));
+ params.put("show_media_preview", String.valueOf(data.isShowMediaPreview()));
+ params.put("spam_comments", data.getSpamComments());
+ params.put("spam_links", data.getSpamLinks());
+ params.put("spam_selfposts", data.getSpamSelfPosts());
+ params.put("spoilers_enabled", String.valueOf(data.isSpoilersEnabled()));
+ params.put("submit_link_label", data.getSubmitLinkLabel());
+ params.put("submit_text", data.getSubmitText());
+ params.put("submit_text_label", data.getSubmitTextLabel());
+ params.put("user_flair_pns_enabled", String.valueOf(data.isUserFlairPnsEnabled()));
+ params.put("all_original_content", String.valueOf(data.isAllOriginalContent()));
+ params.put("allow_chat_post_creation", String.valueOf(data.isAllowChatPostCreation()));
+ params.put("allow_discovery", String.valueOf(data.isAllowDiscovery()));
+ params.put("allow_galleries", String.valueOf(data.isAllowGalleries()));
+ params.put("allow_images", String.valueOf(data.isAllowImages()));
+ params.put("allow_polls", String.valueOf(data.isAllowPolls()));
+ params.put("allow_post_crossposts", String.valueOf(data.isAllowPostCrossPosts()));
+ params.put("allow_prediction_contributors", String.valueOf(data.isAllowPredictionContributors()));
+ params.put("allow_predictions", String.valueOf(data.isAllowPredictions()));
+ params.put("allow_predictions_tournament", String.valueOf(data.isAllowPredictionsTournament()));
+ params.put("allow_videos", String.valueOf(data.isAllowVideos()));
+ params.put("collapse_deleted_comments", String.valueOf(data.isCollapseDeletedComments()));
+ params.put("comment_score_hide_mins", String.valueOf(data.getCommentScoreHideMins()));
+ params.put("crowd_control_chat_level", String.valueOf(data.getCrowdControlChatLevel()));
+ params.put("crowd_control_filter", String.valueOf(data.getCrowdControlChatLevel()));
+ params.put("crowd_control_level", String.valueOf(data.getCrowdControlLevel()));
+ params.put("crowd_control_mode", String.valueOf(data.isCrowdControlMode()));
+ params.put("description", data.getDescription());
+ params.put("disable_contributor_requests", String.valueOf(data.isDisableContributorRequests()));
+ params.put("exclude_banned_modqueue", String.valueOf(data.isExcludeBannedModQueue()));
+ params.put("free_form_reports", String.valueOf(data.isFreeFormReports()));
+ params.put("header-title", data.getHeaderHoverText());
+ params.put("hide_ads", String.valueOf(data.isHideAds()));
+ params.put("key_color", data.getKeyColor());
+ params.put("lang", data.getLanguage());
+ params.put("over_18", String.valueOf(data.isOver18()));
+ params.put("suggested_comment_sort", data.getSuggestedCommentSort());
+ params.put("welcome_message_enabled", String.valueOf(data.isWelcomeMessageEnabled()));
+ params.put("welcome_message_text", String.valueOf(data.getWelcomeMessageText()));
+ params.put("wiki_edit_age", String.valueOf(data.getWikiEditAge()));
+ params.put("wiki_edit_karma", String.valueOf(data.getWikiEditKarma()));
+ params.put("wikimode", data.getWikiMode());
- @Override
- public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) {
- t.printStackTrace();
- listener.failed(t.getLocalizedMessage());
- }
- });
- } catch (JSONException e) {
- listener.failed(e.getLocalizedMessage());
- }
+ Response<String> postSiteAdminResponse = api.postSiteAdmin(oauthHeader, params).execute();
+ if (postSiteAdminResponse.isSuccessful()) {
+ return null;
} else {
- listener.failed(response.message());
+ return postSiteAdminResponse.message();
}
+ } else {
+ return response.message();
}
-
- @Override
- public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) {
- t.printStackTrace();
- listener.failed(t.getLocalizedMessage());
- }
- });
-
+ } catch (IOException | JSONException e) {
+ e.printStackTrace();
+ return e.getLocalizedMessage();
+ }
}
- public static void uploadAvatar(Retrofit oauthRetrofit,
- @Nullable String accessToken,
- @NonNull String accountName,
- Bitmap image,
- EditProfileUtilsListener listener) {
- oauthRetrofit.create(RedditAPI.class)
- .uploadSrImg(
- APIUtils.getOAuthHeader(accessToken),
- "u_" + accountName,
- requestBodyUploadSr("icon"),
- fileToUpload(image, accountName + "-icon"))
- .enqueue(new Callback<>() {
- @Override
- public void onResponse(@NonNull Call<String> call,
- @NonNull Response<String> response) {
- if (response.isSuccessful()) listener.success();
- else listener.failed(response.message());
- }
+ /**
+ *
+ * @return the error String. Null indicates no error.
+ */
+ @WorkerThread
+ public static String uploadAvatarSync(Retrofit oauthRetrofit,
+ @Nullable String accessToken,
+ @NonNull String accountName,
+ Bitmap image) {
+ try {
+ Response<String> response = oauthRetrofit.create(RedditAPI.class)
+ .uploadSrImg(
+ APIUtils.getOAuthHeader(accessToken),
+ "u_" + accountName,
+ requestBodyUploadSr("icon"),
+ fileToUpload(image, accountName + "-icon"))
+ .execute();
- @Override
- public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) {
- t.printStackTrace();
- listener.failed(t.getLocalizedMessage());
- }
- });
+ if (response.isSuccessful()) {
+ return null;
+ } else {
+ return response.message();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ return e.getLocalizedMessage();
+ }
}
- public static void uploadBanner(Retrofit oauthRetrofit,
- @Nullable String accessToken,
- @NonNull String accountName,
- Bitmap image,
- EditProfileUtilsListener listener) {
- oauthRetrofit.create(RedditAPI.class)
- .uploadSrImg(
- APIUtils.getOAuthHeader(accessToken),
- "u_" + accountName,
- requestBodyUploadSr("banner"),
- fileToUpload(image, accountName + "-banner"))
- .enqueue(new Callback<>() {
- @Override
- public void onResponse(@NonNull Call<String> call,
- @NonNull Response<String> response) {
- if (response.isSuccessful()) listener.success();
- else listener.failed(response.message());
- }
+ /**
+ @return the error String. Null indicates no error.
+ */
+ @WorkerThread
+ public static String uploadBannerSync(Retrofit oauthRetrofit,
+ @Nullable String accessToken,
+ @NonNull String accountName,
+ Bitmap image) {
+ try {
+ Response<String> response = oauthRetrofit.create(RedditAPI.class)
+ .uploadSrImg(
+ APIUtils.getOAuthHeader(accessToken),
+ "u_" + accountName,
+ requestBodyUploadSr("banner"),
+ fileToUpload(image, accountName + "-banner"))
+ .execute();
- @Override
- public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) {
- t.printStackTrace();
- listener.failed(t.getLocalizedMessage());
- }
- });
+ if (response.isSuccessful()) {
+ return null;
+ } else {
+ return response.message();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ return e.getLocalizedMessage();
+ }
}
public static void deleteAvatar(Retrofit oauthRetrofit,
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 1bc250e2..1b301887 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1294,7 +1294,7 @@
<string name="message_remove_banner_failed">Failed to remove banner %s</string>
<string name="message_change_avatar_success">Change avatar successfully</string>
<string name="message_change_avatar_failed">Failed to change avatar %s</string>
- <string name="message_change_banner_success">Changing banner successfully</string>
+ <string name="message_change_banner_success">Change banner successfully</string>
<string name="message_change_banner_failed">Failed to change banner %s</string>
<string name="message_save_profile_success">Save profile successfully</string>
<string name="message_save_profile_failed">Failed to save profile %s</string>