fixed update profile banner

This commit is contained in:
Mariotaku Lee 2015-06-18 22:15:29 +08:00
parent 6cde1e4877
commit 456e7ff1c5
4 changed files with 21 additions and 462 deletions

View File

@ -133,14 +133,14 @@ public interface UsersResources {
@POST("/account/update_profile_banner.json")
@Body(BodyType.MULTIPART)
ResponseCode updateProfileBannerImage(@Part("image") FileTypedData data, @Part("width") int width,
ResponseCode updateProfileBannerImage(@Part("banner") FileTypedData data, @Part("width") int width,
@Part("height") int height, @Part("offset_left") int offsetLeft,
@Part("offset_top") int offsetTop)
throws TwitterException;
@POST("/account/update_profile_banner.json")
@Body(BodyType.MULTIPART)
ResponseCode updateProfileBannerImage(@Part("image") FileTypedData data) throws TwitterException;
ResponseCode updateProfileBannerImage(@Part("banner") FileTypedData data) throws TwitterException;
@POST("/account/update_profile_image.json")
@Body(BodyType.MULTIPART)

View File

@ -1,434 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.ooxi.jdatauri;
import android.util.Base64;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
* A data URI parser
*
* @see https://tools.ietf.org/html/rfc2397
* @see http://shadow2531.com/opera/testcases/datauri/data_uri_rules.html
* @see https://en.wikipedia.org/wiki/Data_URI_scheme
*
* @author ooxi
*/
public class DataUri {
private static final String CHARSET_OPTION_NAME = "charset";
private static final String FILENAME_OPTION_NAME = "filename";
private static final String CONTENT_DISPOSITION_OPTION_NAME = "content-disposition";
private final String mime;
private final Charset charset;
private final String filename;
private final String contentDisposition;
private final byte[] data;
public DataUri(String mime, Charset charset, byte[] data) {
this(mime, charset, null, null, data);
}
public DataUri(String mime, Charset charset, String filename, String contentDisposition, byte[] data) {
this.mime = mime;
this.charset = charset;
this.filename = filename;
this.contentDisposition = contentDisposition;
this.data = data;
if (null == mime) {
throw new NullPointerException("`mime' must not be null");
}
if (null == data) {
throw new NullPointerException("`data' must not be null");
}
}
public String getMime() {
return mime;
}
/**
* @warning May be null
*/
public Charset getCharset() {
return charset;
}
/**
* @warning May be null
*/
public String getFilename() {
return filename;
}
/**
* @warning May be null
*/
public String getContentDisposition() {
return contentDisposition;
}
public byte[] getData() {
return data;
}
@Override
public int hashCode() {
int hash = 3;
hash = 23 * hash + (this.mime != null ? this.mime.hashCode() : 0);
hash = 23 * hash + (this.charset != null ? this.charset.hashCode() : 0);
hash = 23 * hash + (this.filename != null ? this.filename.hashCode() : 0);
hash = 23 * hash + (this.contentDisposition != null ? this.contentDisposition.hashCode() : 0);
hash = 23 * hash + Arrays.hashCode(this.data);
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final DataUri other = (DataUri) obj;
if ((this.mime == null) ? (other.mime != null) : !this.mime.equals(other.mime)) {
return false;
}
if (this.charset != other.charset && (this.charset == null || !this.charset.equals(other.charset))) {
return false;
}
if ((this.filename == null) ? (other.filename != null) : !this.filename.equals(other.filename)) {
return false;
}
if ((this.contentDisposition == null) ? (other.contentDisposition != null) : !this.contentDisposition.equals(other.contentDisposition)) {
return false;
}
if (!Arrays.equals(this.data, other.data)) {
return false;
}
return true;
}
/**
* Tries to parse a data URI described in RFC2397
*
* @param uri A string representing the data URI
* @param charset Charset to use when decoding percent encoded options
* like filename
*
* @return Parsed data URI
* @throws IllegalArgumentException iff an error occured during parse
* process
*/
public static DataUri parse(String uri, Charset charset) {
/* If URI does not start with a case-insensitive "data:":
* Throw a MALFORMED_URI exception.
*/
if (!uri.toLowerCase().startsWith("data:")) {
throw new IllegalArgumentException("URI must start with a case-insensitive `data:'");
}
/* If URI does not contain a ",":
* Throw a MALFORMED_URI exception.
*/
if (-1 == uri.indexOf(',')) {
throw new IllegalArgumentException("URI must contain a `,'");
}
/* Let supportedContentEncodings be an array of strings
* representing the supported content encodings. (["base64"] for
* example)
*/
Collection<String> supportedContentEncodings = Arrays.asList(
"base64"
);
/* Let mimeType be a string with the value "text/plain".
*/
String mimeType = "text/plain";
/* Let contentEncoding be an empy string.
*/
String contentEncoding = "";
/* Let contentEncodingAlreadySet be a boolean with a value of
* false.
*/
boolean contentEncodingAlreadySet = false;
/* Let supportedValues be a map of string:string pairs where the
* first string in each pair represents the name of the
* supported value and the second string in each pair represents
* an empty string or default string value. (Example: {"charset"
* : "", "filename" : "", "content-disposition" : ""})
*/
final Map<String, String> supportedValues = new HashMap<String, String>() {{
put(CHARSET_OPTION_NAME, "");
put(FILENAME_OPTION_NAME, "");
put(CONTENT_DISPOSITION_OPTION_NAME, "");
}};
/* Let supportedValueSetBits be a map of string:bool pairs
* representing each of the names in supportedValues with each
* name set to false.
*/
final Map<String, Boolean> supportedValueSetBits = new HashMap<String, Boolean>() {{
for (String key : supportedValues.keySet()) {
put(key, false);
}
}};
/* Let comma be the position of the first "," found in URI.
*/
int comma = uri.indexOf(',');
/* Let temp be the substring of URI from, and including,
* position 5 to, and excluding, the comma position. (between
* "data:" and first ",")
*/
String temp = uri.substring("data:".length(), comma);
/* Let headers be an array of strings returned by splitting temp
* by ";".
*/
String[] headers = temp.split(";");
/* For each string s in headers:
*/
for (int header = 0; header < headers.length; ++header) {
String s = headers[header];
/* Let s equal the lowercase version of s
*/
s = s.toLowerCase();
/* Let eq be the position result of searching for "=" in
* s.
*/
int eq = s.indexOf('=');
/* Let name and value be empty strings.
*/
String name;
String value = "";
/* If eq is not a valid position in s:
*/
if (-1 == eq) {
/* Let name equal the result of percent-decoding
* s.
*/
name = percentDecode(s, charset);
/* Let name equal the result of trimming leading
* and trailing white-space from name.
*/
name = name.trim();
/* Else:
*/
} else {
/* Let name equal the substring of s from
* position 0 to, but not including, position
* eq.
*/
name = s.substring(0, eq);
/* Let name equal the result of percent-decoding
* name.
*/
name = percentDecode(name, charset);
/* Let name equal the result of trimmnig leading
* and trailing white-space from name.
*/
name = name.trim();
/* Let value equal the substring of s from
* position eq + 1 to the end of s.
*/
value = s.substring(eq + 1);
/* Let value equal the result of precent-
* decoding value.
*/
value = percentDecode(value, charset);
/* Let value equal the result of trimming
* leading and trailing white-space from value.
*/
value = value.trim();
}
/* If s is the first element in headers and eq is not a
* valid position in s and the length of name is greater
* than 0:
*/
if ((0 == header) && (-1 == eq) && !name.isEmpty()) {
/* Let mimeType equal name.
*/
mimeType = name;
/* Else:
*/
} else {
/* If eq is not a valid position in s:
*/
if (-1 == eq) {
/* If name is found case-insensitively
* in supportedContentEncodings:
*/
final String nameCaseInsensitive = name.toLowerCase();
if (supportedContentEncodings.contains(nameCaseInsensitive)) {
/* If contentEncodingAlreadySet
* is false:
*/
if (!contentEncodingAlreadySet) {
/* Let contentEncoding
* equal name.
*/
contentEncoding = name;
/* Let contentEncodingAlreadySet
* equal true.
*/
contentEncodingAlreadySet = true;
}
}
/* Else:
*/
} else {
/* If the length of value is greater
* than 0 and name is found case-
* insensitively in supportedValues:
*/
final String nameCaseInsensitive = name.toLowerCase();
if (!value.isEmpty() && supportedValues.containsKey(nameCaseInsensitive)) {
/* If the corresponding value
* for name found (case-
* insensitivley) in
* supportedValueSetBits is
* false:
*/
boolean valueSet = supportedValueSetBits.get(nameCaseInsensitive);
if (!valueSet) {
/* Let the corresponding
* value for name found
* (case-insensitively)
* in supportedValues
* equal value.
*/
supportedValues.put(nameCaseInsensitive, value);
/* Let the corresponding
* value for name found
* (case-insensitively)
* in supportedValueSetBits
* equal true.
*/
supportedValueSetBits.put(nameCaseInsensitive, true);
}
}
}
}
}
/* Let data be the substring of URI from position comma + 1 to
* the end of URI.
*/
String data = uri.substring(comma + 1);
/* Let data be the result of percent-decoding data.
*/
data = percentDecode(data, charset);
/* Let dataURIObject be an object consisting of the mimeType,
* contentEncoding, data and supportedValues objects.
*/
final String finalMimeType = mimeType;
final Charset finalCharset = supportedValues.get(CHARSET_OPTION_NAME).isEmpty()
? null : Charset.forName(supportedValues.get(CHARSET_OPTION_NAME));
final String finalFilename = supportedValues.get(FILENAME_OPTION_NAME).isEmpty()
? null : supportedValues.get(FILENAME_OPTION_NAME);
final String finalContentDisposition = supportedValues.get(CONTENT_DISPOSITION_OPTION_NAME).isEmpty()
? null : supportedValues.get(CONTENT_DISPOSITION_OPTION_NAME);
final byte[] finalData = "base64".equalsIgnoreCase(contentEncoding)
? Base64.decode(data, Base64.DEFAULT) : data.getBytes(charset);
DataUri dataURIObject = new DataUri(
finalMimeType,
finalCharset,
finalFilename,
finalContentDisposition,
finalData
);
/* return dataURIObject.
*/
return dataURIObject;
}
/**
* @warning URLDecoder.decode does not do percentDecoding, but instead decodes
* application/x-www-form-urlencoded therefore the .replace hack
*/
private static String percentDecode(String s, Charset cs) {
try {
return URLDecoder.decode(s, cs.name()).replace(' ', '+');
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("Charset `"+ cs.name() +"' not supported", e);
}
}
}

View File

@ -30,6 +30,7 @@ import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.LoaderManager;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
@ -87,6 +88,7 @@ public class UserProfileEditorFragment extends BaseSupportFragment implements On
private static final int REQUEST_PICK_BACKGROUND_COLOR = 4;
private static final int RESULT_REMOVE_BANNER = 101;
private static final String UPDATE_PROFILE_DIALOG_FRAGMENT_TAG = "update_profile";
private MediaLoaderWrapper mLazyImageLoader;
private AsyncTaskManager mAsyncTaskManager;
@ -203,6 +205,14 @@ public class UserProfileEditorFragment extends BaseSupportFragment implements On
return super.onOptionsItemSelected(item);
}
@Override
public void onResume() {
super.onResume();
if (mTask != null && mTask.getStatus() == Status.PENDING) {
AsyncTaskUtils.executeTask(mTask);
}
}
@Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
@ -295,13 +305,11 @@ public class UserProfileEditorFragment extends BaseSupportFragment implements On
} else {
mTask = new UpdateProfileBannerImageTaskInternal(getActivity(), mAsyncTaskManager, mAccountId, data.getData(), true);
}
AsyncTaskUtils.executeTask(mTask);
break;
}
case REQUEST_UPLOAD_PROFILE_IMAGE: {
if (mTask != null && mTask.getStatus() == Status.RUNNING) return;
mTask = new UpdateProfileImageTaskInternal(getActivity(), mAsyncTaskManager, mAccountId, data.getData(), true);
AsyncTaskUtils.executeTask(mTask);
break;
}
case REQUEST_PICK_LINK_COLOR: {
@ -322,20 +330,6 @@ public class UserProfileEditorFragment extends BaseSupportFragment implements On
}
boolean isProfileChanged() {
final ParcelableUser user = mUser;
if (user == null) return true;
if (!stringEquals(mEditName.getText(), user.name)) return true;
if (!stringEquals(mEditDescription.getText(), user.description_expanded)) return true;
if (!stringEquals(mEditLocation.getText(), user.location)) return true;
if (!stringEquals(mEditUrl.getText(), isEmpty(user.url_expanded) ? user.url : user.url_expanded))
return true;
if (mLinkColor.getColor() != user.link_color) return true;
if (mBackgroundColor.getColor() != user.background_color) return true;
return false;
}
private void displayUser(final ParcelableUser user) {
if (!mGetUserInfoCalled) return;
mGetUserInfoCalled = false;
@ -373,15 +367,15 @@ public class UserProfileEditorFragment extends BaseSupportFragment implements On
}
private void setUpdateState(final boolean start) {
mEditName.setEnabled(!start);
mEditDescription.setEnabled(!start);
mEditLocation.setEnabled(!start);
mEditUrl.setEnabled(!start);
mProfileImageView.setEnabled(!start);
mProfileImageView.setOnClickListener(start ? null : this);
mProfileBannerView.setEnabled(!start);
mProfileBannerView.setOnClickListener(start ? null : this);
invalidateOptionsMenu();
final FragmentManager fm = getChildFragmentManager();
final Fragment f = fm.findFragmentByTag(UPDATE_PROFILE_DIALOG_FRAGMENT_TAG);
if (!start && f instanceof DialogFragment) {
((DialogFragment) f).dismiss();
} else if (start) {
SupportProgressDialogFragment df = new SupportProgressDialogFragment();
df.show(fm, UPDATE_PROFILE_DIALOG_FRAGMENT_TAG);
df.setCancelable(false);
}
}
private static boolean stringEquals(final CharSequence str1, final CharSequence str2) {

View File

@ -105,7 +105,6 @@
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/element_spacing_normal"
android:layout_marginStart="@dimen/element_spacing_normal"
android:paddingTop="@dimen/element_spacing_normal"
android:text="@string/profile_banner"
android:textAllCaps="true"
android:textAppearance="?android:textAppearanceSmall"