implementing twitter card support

This commit is contained in:
Mariotaku Lee 2015-01-01 18:38:34 +08:00
parent 5209ae8db6
commit 5c355a1b12
52 changed files with 2362 additions and 647 deletions

View File

@ -7,7 +7,7 @@ android {
defaultConfig {
applicationId "org.mariotaku.twidere"
minSdkVersion 14
targetSdkVersion 21
targetSdkVersion 19
versionCode 98
versionName "0.3.0-dev"
@ -101,5 +101,6 @@ dependencies {
compile project(':MenuComponent')
compile project(':MessageBubbleView')
compile project(':twidere.nyan')
compile fileTree(dir: 'libs', include: ['*.jar'])
compile fileTree(dir: 'libs/main', include: ['*.jar'])
googleCompile fileTree(dir: 'libs/google', include: ['*.jar'])
}

Binary file not shown.

View File

@ -0,0 +1,50 @@
/*
* 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 org.mariotaku.jsonserializer;
import org.json.JSONArray;
import org.mariotaku.jsonserializer.JSONParcelable.Creator;
/**
* Created by mariotaku on 15/1/1.
*/
public final class JSONArrayParcel {
private final JSONArray jsonArray;
JSONArrayParcel(JSONArray json) {
if (json == null) throw new NullPointerException();
jsonArray = json;
}
public String readString(int index) {
return jsonArray.optString(index);
}
public <T extends JSONParcelable> T readParcelable(int index, Creator<T> creator) {
final JSONParcel parcel = new JSONParcel(jsonArray.optJSONObject(index));
return creator.createFromParcel(parcel);
}
public int size() {
return jsonArray.length();
}
}

View File

@ -0,0 +1,377 @@
/*
* 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 org.mariotaku.jsonserializer;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.List;
public final class JSONParcel {
private final JSONObject jsonObject;
JSONParcel() {
this(new JSONObject());
}
JSONParcel(final JSONObject json) {
if (json == null) throw new NullPointerException();
jsonObject = json;
}
public boolean contains(final String key) {
return jsonObject.has(key);
}
public JSONObject getJSON() {
return jsonObject;
}
public boolean isNull(String key) {
return jsonObject.isNull(key);
}
public boolean readBoolean(final String key) {
return jsonObject.optBoolean(key);
}
public boolean readBoolean(final String key, final boolean def) {
return jsonObject.optBoolean(key, def);
}
public double readDouble(final String key) {
return jsonObject.optDouble(key);
}
public double readDouble(final String key, final double def) {
return jsonObject.optDouble(key, def);
}
public float readFloat(final String key, final float def) {
return (float) readDouble(key, def);
}
public int readInt(final String key) {
return jsonObject.optInt(key);
}
public int readInt(final String key, final int def) {
return jsonObject.optInt(key, def);
}
public JSONObject readJSONObject(final String key) {
return jsonObject.optJSONObject(key);
}
public JSONArray readJSONArray(final String key) {
return jsonObject.optJSONArray(key);
}
public JSONArrayParcel readJSONArrayParcel(final String key) {
if (jsonObject.isNull(key)) return null;
return new JSONArrayParcel(readJSONArray(key));
}
public String[] readStringArray(final String key) {
if (jsonObject.isNull(key)) return null;
final JSONArray array = jsonObject.optJSONArray(key);
final String[] stringArray = new String[array.length()];
for (int i = 0, j = array.length(); i < j; i++) {
try {
stringArray[i] = array.getString(i);
} catch (JSONException e) {
return null;
}
}
return stringArray;
}
public long readLong(final String key) {
return jsonObject.optLong(key);
}
public long readLong(final String key, final long def) {
return jsonObject.optLong(key, def);
}
public Object readObject(final String key) {
return jsonObject.opt(key);
}
public <T extends JSONParcelable> T readParcelable(final String key, final JSONParcelable.Creator<T> creator) {
return JSONSerializer.createObject(creator, jsonObject.optJSONObject(key));
}
public <T extends JSONParcelable> T[] readParcelableArray(final String key, final JSONParcelable.Creator<T> creator) {
return JSONSerializer.createArray(creator, jsonObject.optJSONArray(key));
}
public String readString(final String key) {
return readString(key, null);
}
public String readString(final String key, final String def) {
return jsonObject.optString(key, def);
}
public void writeBoolean(final String key, final boolean value) {
try {
jsonObject.put(key, value);
} catch (final JSONException e) {
e.printStackTrace();
}
}
public void writeBooleanArray(final String key, final boolean[] value) {
if (key == null) return;
try {
if (value == null) {
jsonObject.put(key, JSONObject.NULL);
return;
}
final JSONArray array = new JSONArray();
for (final boolean item : value) {
array.put(item);
}
jsonObject.put(key, array);
} catch (final JSONException e) {
e.printStackTrace();
}
}
public void writeDouble(final String key, final double value) {
try {
jsonObject.put(key, value);
} catch (final JSONException e) {
e.printStackTrace();
}
}
public void writeDoubleArray(final String key, final double[] value) {
if (key == null) return;
try {
if (value == null) {
jsonObject.put(key, JSONObject.NULL);
return;
}
final JSONArray array = new JSONArray();
for (final double item : value) {
array.put(item);
}
jsonObject.put(key, array);
} catch (final JSONException e) {
e.printStackTrace();
}
}
public void writeFloat(final String key, final float value) {
writeDouble(key, value);
}
public void writeFloatArray(final String key, final float[] value) {
try {
if (value == null) {
jsonObject.put(key, JSONObject.NULL);
return;
}
final JSONArray array = new JSONArray();
for (final float item : value) {
array.put(item);
}
jsonObject.put(key, array);
} catch (final JSONException e) {
e.printStackTrace();
}
}
public void writeInt(final String key, final int value) {
try {
jsonObject.put(key, value);
} catch (final JSONException e) {
e.printStackTrace();
}
}
public void writeIntArray(final String key, final int[] value) {
if (key == null) return;
try {
if (value == null) {
jsonObject.put(key, JSONObject.NULL);
return;
}
final JSONArray array = new JSONArray();
for (final int item : value) {
array.put(item);
}
jsonObject.put(key, array);
} catch (final JSONException e) {
e.printStackTrace();
}
}
public void writeJSONArray(final String key, final JSONArray value) {
try {
jsonObject.put(key, value);
} catch (final JSONException e) {
e.printStackTrace();
}
}
public void writeJSONObject(final String key, final JSONObject value) {
try {
jsonObject.put(key, value);
} catch (final JSONException e) {
e.printStackTrace();
}
}
public void writeLong(final String key, final long value) {
try {
jsonObject.put(key, value);
} catch (final JSONException e) {
e.printStackTrace();
}
}
public void writeLongArray(final String key, final long[] value) {
if (key == null) return;
try {
if (value == null) {
jsonObject.put(key, JSONObject.NULL);
return;
}
final JSONArray array = new JSONArray();
for (final long item : value) {
array.put(item);
}
jsonObject.put(key, array);
} catch (final JSONException e) {
e.printStackTrace();
}
}
public void writeObject(final String key, final Object value) {
if (value instanceof JSONParcelable) {
writeParcelable(key, (JSONParcelable) value);
return;
}
try {
jsonObject.put(key, value);
} catch (final JSONException e) {
e.printStackTrace();
}
}
public void writeObjectArray(final String key, final Object[] value) {
if (key == null) return;
try {
if (value == null) {
jsonObject.put(key, JSONObject.NULL);
return;
}
final JSONArray array = new JSONArray();
for (final Object item : value) {
if (item instanceof JSONParcelable) {
final JSONObject json = JSONSerializer.toJSONObject((JSONParcelable) item);
array.put(json);
} else {
array.put(item);
}
}
jsonObject.put(key, array);
} catch (final JSONException e) {
e.printStackTrace();
}
}
public void writeObjectList(final String key, final List<Object> value) {
if (key == null) return;
writeObjectArray(key, value.toArray());
}
public <T extends JSONParcelable> void writeParcelable(final String key, final T value) {
if (key == null) return;
try {
if (value == null) {
jsonObject.put(key, JSONObject.NULL);
return;
}
final JSONObject json = JSONSerializer.toJSONObject(value);
jsonObject.put(key, json);
} catch (final JSONException e) {
e.printStackTrace();
}
}
public <T extends JSONParcelable> void writeParcelableArray(final String key, final T[] value) {
if (key == null) return;
try {
if (value == null) {
jsonObject.put(key, JSONObject.NULL);
return;
}
final JSONArray array = new JSONArray();
for (final T item : value) {
final JSONObject json = JSONSerializer.toJSONObject(item);
array.put(json != null ? json : JSONObject.NULL);
}
jsonObject.put(key, array);
} catch (final JSONException e) {
e.printStackTrace();
}
}
public void writeString(final String key, final String value) {
if (key == null) return;
try {
jsonObject.put(key, value);
} catch (final JSONException e) {
e.printStackTrace();
}
}
public void writeStringArray(final String key, final String[] value) {
if (key == null) return;
try {
if (value == null) {
jsonObject.put(key, JSONObject.NULL);
return;
}
final JSONArray array = new JSONArray();
for (final String item : value) {
array.put(item);
}
jsonObject.put(key, array);
} catch (final JSONException e) {
e.printStackTrace();
}
}
public void writeValue(final String key, final Object value) {
if (key == null) return;
try {
jsonObject.put(key, value);
} catch (final JSONException e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,40 @@
/**
* This is free and unencumbered software released into the public domain.
*
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*
* In jurisdictions that recognize copyright laws, the author or authors
* of this software dedicate any and all copyright interest in the
* software to the public domain. We make this dedication for the benefit
* of the public at large and to the detriment of our heirs and
* successors. We intend this dedication to be an overt act of
* relinquishment in perpetuity of all present and future rights to this
* software under copyright law.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* For more information, please refer to <http://unlicense.org/>
*/
package org.mariotaku.jsonserializer;
public interface JSONParcelable {
public void writeToParcel(JSONParcel out);
public interface Creator<T extends JSONParcelable> {
public T createFromParcel(JSONParcel in);
public T[] newArray(int size);
}
}

View File

@ -0,0 +1,148 @@
/*
* 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 org.mariotaku.jsonserializer;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
public class JSONSerializer {
private static boolean debugMode = false;
public static <T extends JSONParcelable> T[] createArray(final JSONParcelable.Creator<T> creator,
final JSONArray json) {
if (creator == null) throw new NullPointerException("JSON_CREATOR must not be null!");
if (json == null) return null;
final int size = json.length();
final T[] array = creator.newArray(size);
for (int i = 0; i < size; i++) {
array[i] = creator.createFromParcel(new JSONParcel(json.optJSONObject(i)));
}
return array;
}
public static <T extends JSONParcelable> ArrayList<T> createArrayList(final JSONParcelable.Creator<T> creator,
final JSONArray json) {
if (creator == null) throw new NullPointerException("JSON_CREATOR must not be null!");
if (json == null) return null;
final int size = json.length();
final ArrayList<T> list = new ArrayList<T>(size);
for (int i = 0; i < size; i++) {
list.add(creator.createFromParcel(new JSONParcel(json.optJSONObject(i))));
}
return list;
}
public static <T extends JSONParcelable> T createObject(final JSONParcelable.Creator<T> creator,
final JSONObject json) {
if (creator == null) throw new NullPointerException("JSON_CREATOR must not be null!");
if (json == null) return null;
return creator.createFromParcel(new JSONParcel(json));
}
public static <T extends JSONParcelable> byte[] getByteArray(final T parcelable) {
final JSONObject json = toJSONObject(parcelable);
final String string = jsonToString(json);
if (string == null) return null;
return string.getBytes(Charset.defaultCharset());
}
public static <T extends JSONParcelable> byte[] getByteArray(final T[] array) {
final JSONArray json = toJSONArray(array);
final String string = jsonToString(json);
if (string == null) return null;
return string.getBytes(Charset.defaultCharset());
}
public static boolean isDebugMode() {
return debugMode;
}
public static void setIsDebugMode(final boolean debug) {
debugMode = debug;
}
public static <T extends JSONParcelable> byte[] toByteArray(final T parcelable) throws IOException {
final String jsonString = jsonToString(toJSONObject(parcelable));
if (jsonString == null) return null;
return jsonString.getBytes(Charset.defaultCharset());
}
public static <T extends JSONParcelable> byte[] toByteArray(final T[] array) throws IOException {
final String jsonString = jsonToString(toJSONArray(array));
if (jsonString == null) return null;
return jsonString.getBytes(Charset.defaultCharset());
}
public static <T extends JSONParcelable> JSONArray toJSONArray(final T[] array) {
if (array == null) return null;
final JSONArray json = new JSONArray();
for (final T parcelable : array) {
json.put(toJSONObject(parcelable));
}
return json;
}
public static <T extends JSONParcelable> String toJSONArrayString(final T[] array) {
return jsonToString(toJSONArray(array));
}
public static <T extends JSONParcelable> JSONObject toJSONObject(final T parcelable) {
if (parcelable == null) return null;
final JSONObject json = new JSONObject();
parcelable.writeToParcel(new JSONParcel(json));
return json;
}
public static <T extends JSONParcelable> String toJSONObjectString(final T parcelable) {
return jsonToString(toJSONObject(parcelable));
}
static String jsonToString(final JSONArray json) {
if (json == null) return null;
if (debugMode) {
try {
return json.toString(4);
} catch (final JSONException e) {
e.printStackTrace();
}
return json.toString();
} else
return json.toString();
}
static String jsonToString(final JSONObject json) {
if (json == null) return null;
if (debugMode) {
try {
return json.toString(4);
} catch (final JSONException e) {
e.printStackTrace();
}
return json.toString();
} else
return json.toString();
}
}

View File

@ -28,7 +28,7 @@ package org.mariotaku.twidere;
public interface Constants extends TwidereConstants {
public static final String DATABASES_NAME = "twidere.sqlite";
public static final int DATABASES_VERSION = 75;
public static final int DATABASES_VERSION = 76;
public static final int MENU_GROUP_STATUS_EXTENSION = 10;
public static final int MENU_GROUP_COMPOSE_EXTENSION = 11;

View File

@ -1208,8 +1208,13 @@ public class ComposeActivity extends BaseSupportDialogActivity implements TextWa
final ImageLoaderWrapper loader = application.getImageLoaderWrapper();
final ImageLoadingHandler handler = new ImageLoadingHandler(R.id.media_preview_progress);
final AsyncTwitterWrapper twitter = getTwitterWrapper();
final SharedPreferencesWrapper preferences = SharedPreferencesWrapper.getInstance(activity,
SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
final ParcelableStatus status = args.getParcelable(EXTRA_STATUS);
mHolder.displayStatus(activity, loader, handler, twitter, status);
final int profileImageStyle = Utils.getProfileImageStyle(preferences.getString(KEY_PROFILE_IMAGE_STYLE, null));
final int mediaPreviewStyle = Utils.getMediaPreviewStyle(preferences.getString(KEY_MEDIA_PREVIEW_STYLE, null));
mHolder.displayStatus(activity, loader, handler, twitter, profileImageStyle,
mediaPreviewStyle, status, null);
mStatusContainer.findViewById(R.id.item_menu).setVisibility(View.GONE);
mStatusContainer.findViewById(R.id.action_buttons).setVisibility(View.GONE);
mStatusContainer.findViewById(R.id.reply_retweet_status).setVisibility(View.GONE);

View File

@ -45,9 +45,15 @@ public abstract class AbsStatusesAdapter<D> extends Adapter<ViewHolder> implemen
private final AsyncTwitterWrapper mTwitterWrapper;
private final int mCardBackgroundColor;
private final int mTextSize;
private final int mProfileImageStyle, mMediaPreviewStyle;
private boolean mLoadMoreIndicatorEnabled;
private StatusAdapterListener mStatusAdapterListener;
@Override
public int getMediaPreviewStyle() {
return mMediaPreviewStyle;
}
public AbsStatusesAdapter(Context context, boolean compact) {
mContext = context;
final TwidereApplication app = TwidereApplication.getInstance(context);
@ -56,13 +62,21 @@ public abstract class AbsStatusesAdapter<D> extends Adapter<ViewHolder> implemen
mImageLoader = app.getImageLoaderWrapper();
mLoadingHandler = new ImageLoadingHandler(R.id.media_preview_progress);
mTwitterWrapper = app.getTwitterWrapper();
final SharedPreferencesWrapper preferences = SharedPreferencesWrapper.getInstance(context, SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
final SharedPreferencesWrapper preferences = SharedPreferencesWrapper.getInstance(context,
SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
mTextSize = preferences.getInt(KEY_TEXT_SIZE, context.getResources().getInteger(R.integer.default_text_size));
if (compact) {
mCardLayoutResource = R.layout.card_item_status_compat;
} else {
mCardLayoutResource = R.layout.card_item_status;
}
mProfileImageStyle = Utils.getProfileImageStyle(preferences.getString(KEY_PROFILE_IMAGE_STYLE, null));
mMediaPreviewStyle = Utils.getMediaPreviewStyle(preferences.getString(KEY_MEDIA_PREVIEW_STYLE, null));
}
@Override
public int getProfileImageStyle() {
return mProfileImageStyle;
}
public abstract D getData();

View File

@ -102,7 +102,7 @@ public class BaseArrayAdapter<T> extends ArrayAdapter<T> implements IBaseAdapter
@Override
public void onSharedPreferenceChanged(final SharedPreferences preferences, final String key) {
if (KEY_NICKNAME_ONLY.equals(key) || KEY_DISPLAY_PROFILE_IMAGE.equals(key)
|| KEY_DISPLAY_IMAGE_PREVIEW.equals(key) || KEY_DISPLAY_SENSITIVE_CONTENTS.equals(key)) {
|| KEY_MEDIA_PREVIEW_STYLE.equals(key) || KEY_DISPLAY_SENSITIVE_CONTENTS.equals(key)) {
notifyDataSetChanged();
}
}

View File

@ -3,6 +3,7 @@ package org.mariotaku.twidere.adapter;
import android.content.Context;
import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.view.holder.StatusViewHolder;
import java.util.List;
@ -40,6 +41,11 @@ public class ParcelableStatusesAdapter extends AbsStatusesAdapter<List<Parcelabl
return mData.size();
}
@Override
public int getMediaPreviewStyle() {
return 0;
}
public void setData(List<ParcelableStatus> data) {
mData = data;
notifyDataSetChanged();

View File

@ -23,6 +23,10 @@ public interface IStatusesAdapter<Data> extends IGapSupportedAdapter, ICardSuppo
int getStatusCount();
int getProfileImageStyle();
int getMediaPreviewStyle();
void onStatusClick(StatusViewHolder holder, int position);
void onUserProfileClick(StatusViewHolder holder, int position);

View File

@ -69,6 +69,7 @@ public interface IntentConstants {
public static final String INTENT_ACTION_UPDATE_STATUS = INTENT_PACKAGE_PREFIX + "UPDATE_STATUS";
public static final String INTENT_ACTION_SEND_DIRECT_MESSAGE = INTENT_PACKAGE_PREFIX + "SEND_DIRECT_MESSAGE";
public static final String INTENT_ACTION_DISCARD_DRAFT = INTENT_PACKAGE_PREFIX + "DISCARD_DRAFT";
public static final String INTENT_ACTION_PICK_ACTIVITY = "org.mariotaku.twidere.PICK_ACTIVITY";
public static final String BROADCAST_NOTIFICATION_DELETED = INTENT_PACKAGE_PREFIX + "NOTIFICATION_DELETED";

View File

@ -19,6 +19,7 @@
package org.mariotaku.twidere.constant;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.TwidereConstants;
import org.mariotaku.twidere.annotation.Preference;
import org.mariotaku.twidere.provider.TweetStore.Accounts;
@ -46,6 +47,14 @@ public interface SharedPreferenceConstants {
public static final int VALUE_LINK_HIGHLIGHT_OPTION_CODE_BOTH = VALUE_LINK_HIGHLIGHT_OPTION_CODE_HIGHLIGHT
| VALUE_LINK_HIGHLIGHT_OPTION_CODE_UNDERLINE;
public static final String VALUE_MEDIA_PREVIEW_STYLE_CROP = "crop";
public static final String VALUE_MEDIA_PREVIEW_STYLE_SCALE = "scale";
public static final String VALUE_MEDIA_PREVIEW_STYLE_NONE = VALUE_NONE;
public static final int VALUE_MEDIA_PREVIEW_STYLE_CODE_CROP = 1;
public static final int VALUE_MEDIA_PREVIEW_STYLE_CODE_SCALE = 2;
public static final int VALUE_MEDIA_PREVIEW_STYLE_CODE_NONE = 0;
public static final String VALUE_THEME_FONT_FAMILY_REGULAR = "sans-serif";
public static final String VALUE_THEME_FONT_FAMILY_CONDENSED = "sans-serif-condensed";
public static final String VALUE_THEME_FONT_FAMILY_LIGHT = "sans-serif-light";
@ -76,6 +85,9 @@ public interface SharedPreferenceConstants {
public static final String VALUE_THEME_NAME_DARK = "dark";
public static final String VALUE_THEME_NAME_LIGHT = "light";
public static final String VALUE_PROFILE_IMAGE_STYLE_ROUND = "round";
public static final String VALUE_PROFILE_IMAGE_STYLE_SQUARE = "square";
public static final String VALUE_COMPOSE_NOW_ACTION_COMPOSE = "compose";
public static final String VALUE_COMPOSE_NOW_ACTION_TAKE_PHOTO = "take_photo";
public static final String VALUE_COMPOSE_NOW_ACTION_PICK_IMAGE = "pick_image";
@ -122,7 +134,7 @@ public interface SharedPreferenceConstants {
public static final String KEY_DATABASE_ITEM_LIMIT = "database_item_limit";
@Preference(type = INT, hasDefault = true, defaultInt = DEFAULT_LOAD_ITEM_LIMIT)
public static final String KEY_LOAD_ITEM_LIMIT = "load_item_limit";
@Preference(type = INT)
@Preference(type = INT, hasDefault = true, defaultResource = R.integer.default_text_size)
public static final String KEY_TEXT_SIZE = "text_size_int";
@Preference(type = STRING, hasDefault = true, defaultString = DEFAULT_THEME)
public static final String KEY_THEME = "theme";
@ -138,8 +150,6 @@ public interface SharedPreferenceConstants {
public static final String KEY_THEME_FONT_FAMILY = "theme_font_family";
@Preference(type = BOOLEAN, hasDefault = true, defaultBoolean = true)
public static final String KEY_DISPLAY_PROFILE_IMAGE = "display_profile_image";
@Preference(type = BOOLEAN, hasDefault = true, defaultBoolean = false)
public static final String KEY_DISPLAY_IMAGE_PREVIEW = "display_image_preview";
@Preference(type = BOOLEAN)
public static final String KEY_LEFTSIDE_COMPOSE_BUTTON = "leftside_compose_button";
@Preference(type = BOOLEAN)
@ -274,10 +284,13 @@ public interface SharedPreferenceConstants {
@Preference(type = STRING, hasDefault = true, defaultString = VALUE_COMPOSE_NOW_ACTION_COMPOSE)
public static final String KEY_COMPOSE_NOW_ACTION = "compose_now_action";
public static final String KEY_FALLBACK_TWITTER_LINK_HANDLER = "fallback_twitter_link_handler";
@Preference(type = STRING, hasDefault = true, defaultString = "CENTER_CROP")
public static final String KEY_IMAGE_PREVIEW_SCALE_TYPE = "image_preview_scale_type";
@Preference(type = STRING, hasDefault = true, defaultString = VALUE_MEDIA_PREVIEW_STYLE_CROP)
public static final String KEY_MEDIA_PREVIEW_STYLE = "media_preview_style";
@Preference(type = BOOLEAN, hasDefault = true, defaultBoolean = false)
public static final String KEY_SORT_TIMELINE_BY_ID = "sort_timeline_by_id";
@Preference(type = STRING, hasDefault = true)
public static final String KEY_PROFILE_IMAGE_STYLE = "profile_image_style";
public static final String KEY_QUICK_MENU_EXPANDED = "quick_menu_expanded";

View File

@ -32,14 +32,14 @@ import org.mariotaku.twidere.util.webkit.DefaultWebViewClient;
@SuppressLint("SetJavaScriptEnabled")
public class BaseWebViewFragment extends WebViewFragment implements Constants {
@Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
final WebView view = getWebView();
view.setWebViewClient(new DefaultWebViewClient(getActivity()));
final WebSettings settings = view.getSettings();
settings.setBuiltInZoomControls(true);
settings.setJavaScriptEnabled(true);
WebSettingsAccessor.setAllowUniversalAccessFromFileURLs(settings, true);
}
@Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
final WebView view = getWebView();
view.setWebViewClient(new DefaultWebViewClient(getActivity()));
final WebSettings settings = view.getSettings();
settings.setBuiltInZoomControls(true);
settings.setJavaScriptEnabled(true);
WebSettingsAccessor.setAllowUniversalAccessFromFileURLs(settings, true);
}
}

View File

@ -25,7 +25,6 @@ import org.mariotaku.twidere.adapter.AbsStatusesAdapter;
import org.mariotaku.twidere.adapter.AbsStatusesAdapter.StatusAdapterListener;
import org.mariotaku.twidere.adapter.decorator.DividerItemDecoration;
import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.constant.IntentConstants;
import org.mariotaku.twidere.fragment.iface.RefreshScrollTopInterface;
import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
@ -193,9 +192,9 @@ public abstract class AbsStatusesFragment<Data> extends BaseSupportFragment impl
switch (id) {
case R.id.reply_count: {
final Context context = getActivity();
final Intent intent = new Intent(IntentConstants.INTENT_ACTION_REPLY);
final Intent intent = new Intent(INTENT_ACTION_REPLY);
intent.setPackage(context.getPackageName());
intent.putExtra(IntentConstants.EXTRA_STATUS, status);
intent.putExtra(EXTRA_STATUS, status);
context.startActivity(intent);
break;
}

View File

@ -528,10 +528,8 @@ public class DirectMessagesConversationFragment extends BaseSupportFragment impl
public void onResume() {
super.onResume();
configBaseCardAdapter(getActivity(), mAdapter);
final boolean displayImagePreview = mPreferences.getBoolean(KEY_DISPLAY_IMAGE_PREVIEW, false);
final String previewScaleType = Utils.getNonEmptyString(mPreferences, KEY_IMAGE_PREVIEW_SCALE_TYPE,
final String previewScaleType = Utils.getNonEmptyString(mPreferences, KEY_MEDIA_PREVIEW_STYLE,
ScaleType.CENTER_CROP.name());
mAdapter.setDisplayImagePreview(displayImagePreview);
mAdapter.setImagePreviewScaleType(previewScaleType);
mAdapter.notifyDataSetChanged();
mLoadMoreAutomatically = mPreferences.getBoolean(KEY_LOAD_MORE_AUTOMATICALLY, false);

View File

@ -36,7 +36,9 @@ import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.ImageLoaderWrapper;
import org.mariotaku.twidere.util.ImageLoadingHandler;
import org.mariotaku.twidere.util.SharedPreferencesWrapper;
import org.mariotaku.twidere.util.ThemeUtils;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.view.holder.StatusViewHolder;
import static org.mariotaku.twidere.util.Utils.isMyRetweet;
@ -70,13 +72,16 @@ public class RetweetQuoteDialogFragment extends BaseSupportDialogFragment implem
final Context wrapped = ThemeUtils.getDialogThemedContext(getActivity());
final AlertDialog.Builder builder = new AlertDialog.Builder(wrapped);
final Context context = builder.getContext();
final SharedPreferencesWrapper preferences = SharedPreferencesWrapper.getInstance(context,
SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
final ImageLoaderWrapper loader = TwidereApplication.getInstance(context).getImageLoaderWrapper();
final ImageLoadingHandler handler = new ImageLoadingHandler(R.id.media_preview_progress);
final AsyncTwitterWrapper twitter = getTwitterWrapper();
final LayoutInflater inflater = LayoutInflater.from(context);
@SuppressLint("InflateParams") final View view = inflater.inflate(R.layout.dialog_scrollable_status, null);
final StatusViewHolder holder = new StatusViewHolder(view.findViewById(R.id.item_content));
final int profileImageStyle = Utils.getProfileImageStyle(preferences.getString(KEY_PROFILE_IMAGE_STYLE, null));
final int mediaPreviewStyle = Utils.getMediaPreviewStyle(preferences.getString(KEY_MEDIA_PREVIEW_STYLE, null));
final ParcelableStatus status = getStatus();
builder.setView(view);
@ -86,7 +91,8 @@ public class RetweetQuoteDialogFragment extends BaseSupportDialogFragment implem
builder.setNegativeButton(android.R.string.cancel, null);
holder.displayStatus(context, loader, handler, twitter, getStatus());
holder.displayStatus(context, loader, handler, twitter, profileImageStyle, mediaPreviewStyle,
getStatus(), null);
view.findViewById(R.id.item_menu).setVisibility(View.GONE);
view.findViewById(R.id.action_buttons).setVisibility(View.GONE);
view.findViewById(R.id.reply_retweet_status).setVisibility(View.GONE);

View File

@ -31,6 +31,8 @@ import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import android.support.v4.util.Pair;
@ -50,6 +52,7 @@ import android.view.MenuItem.OnMenuItemClickListener;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Space;
@ -58,9 +61,11 @@ import android.widget.TextView;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.activity.support.AccountSelectorActivity;
import org.mariotaku.twidere.activity.support.ColorPickerDialogActivity;
import org.mariotaku.twidere.adapter.AbsStatusesAdapter.StatusAdapterListener;
import org.mariotaku.twidere.adapter.decorator.DividerItemDecoration;
import org.mariotaku.twidere.adapter.iface.IStatusesAdapter;
import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.constant.IntentConstants;
import org.mariotaku.twidere.loader.ParcelableStatusLoader;
import org.mariotaku.twidere.loader.support.StatusRepliesLoader;
import org.mariotaku.twidere.model.ListResponse;
@ -86,6 +91,7 @@ import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.view.ShapedImageView;
import org.mariotaku.twidere.view.StatusTextView;
import org.mariotaku.twidere.view.TwidereMenuBar;
import org.mariotaku.twidere.view.holder.GapViewHolder;
import org.mariotaku.twidere.view.holder.LoadIndicatorViewHolder;
import org.mariotaku.twidere.view.holder.StatusViewHolder;
@ -117,26 +123,21 @@ import static org.mariotaku.twidere.util.Utils.startStatusShareChooser;
* Created by mariotaku on 14/12/5.
*/
public class StatusFragment extends BaseSupportFragment
implements LoaderCallbacks<SingleResponse<ParcelableStatus>>, OnMediaClickListener {
implements LoaderCallbacks<SingleResponse<ParcelableStatus>>, OnMediaClickListener, StatusAdapterListener {
private static final int LOADER_ID_DETAIL_STATUS = 1;
private static final int LOADER_ID_STATUS_REPLIES = 2;
private static final int STATE_LOADED = 1;
private static final int STATE_LOADING = 2;
private static final int STATE_ERROR = 3;
private RecyclerView mRecyclerView;
private StatusAdapter mStatusAdapter;
private boolean mRepliesLoaderInitialized;
private LoadConversationTask mLoadConversationTask;
private LinearLayoutManager mLayoutManager;
private View mStatusContent;
private View mProgressContainer;
private View mErrorContainer;
private LoaderCallbacks<List<ParcelableStatus>> mRepliesLoaderCallback = new LoaderCallbacks<List<ParcelableStatus>>() {
@Override
public Loader<List<ParcelableStatus>> onCreateLoader(int id, Bundle args) {
@ -196,14 +197,6 @@ public class StatusFragment extends BaseSupportFragment
return inflater.inflate(R.layout.fragment_status, container, false);
}
@Override
public Loader<SingleResponse<ParcelableStatus>> onCreateLoader(final int id, final Bundle args) {
final Bundle fragmentArgs = getArguments();
final long accountId = fragmentArgs.getLong(EXTRA_ACCOUNT_ID, -1);
final long statusId = fragmentArgs.getLong(EXTRA_STATUS_ID, -1);
return new ParcelableStatusLoader(getActivity(), false, fragmentArgs, accountId, statusId);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
@ -219,6 +212,7 @@ public class StatusFragment extends BaseSupportFragment
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setClipToPadding(false);
mStatusAdapter = new StatusAdapter(this, compact);
mStatusAdapter.setEventListener(this);
mRecyclerView.setAdapter(mStatusAdapter);
setState(STATE_LOADING);
@ -226,11 +220,84 @@ public class StatusFragment extends BaseSupportFragment
getLoaderManager().initLoader(LOADER_ID_DETAIL_STATUS, getArguments(), this);
}
@Override
public void onGapClick(GapViewHolder holder, int position) {
}
@Override
public void onStatusActionClick(StatusViewHolder holder, int id, int position) {
final ParcelableStatus status = mStatusAdapter.getStatus(position);
if (status == null) return;
switch (id) {
case R.id.reply_count: {
final Context context = getActivity();
final Intent intent = new Intent(IntentConstants.INTENT_ACTION_REPLY);
intent.setPackage(context.getPackageName());
intent.putExtra(IntentConstants.EXTRA_STATUS, status);
context.startActivity(intent);
break;
}
case R.id.retweet_count: {
RetweetQuoteDialogFragment.show(getFragmentManager(), status);
break;
}
case R.id.favorite_count: {
final AsyncTwitterWrapper twitter = getTwitterWrapper();
if (twitter == null) return;
if (status.is_favorite) {
twitter.destroyFavoriteAsync(status.account_id, status.id);
} else {
twitter.createFavoriteAsync(status.account_id, status.id);
}
break;
}
}
}
@Override
public void onStatusClick(StatusViewHolder holder, int position) {
openStatus(getActivity(), mStatusAdapter.getStatus(position), null);
}
@Override
public void onStatusMenuClick(StatusViewHolder holder, int position) {
final Bundle args = new Bundle();
args.putParcelable(EXTRA_STATUS, mStatusAdapter.getStatus(position));
final StatusMenuDialogFragment f = new StatusMenuDialogFragment();
f.setArguments(args);
f.show(getActivity().getSupportFragmentManager(), "status_menu");
}
@Override
public Loader<SingleResponse<ParcelableStatus>> onCreateLoader(final int id, final Bundle args) {
final Bundle fragmentArgs = getArguments();
final long accountId = fragmentArgs.getLong(EXTRA_ACCOUNT_ID, -1);
final long statusId = fragmentArgs.getLong(EXTRA_STATUS_ID, -1);
return new ParcelableStatusLoader(getActivity(), false, fragmentArgs, accountId, statusId);
}
@Override
public void onMediaClick(View view, ParcelableMedia media, long accountId) {
Utils.openImageDirectly(getActivity(), accountId, media.url);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mStatusContent = view.findViewById(R.id.status_content);
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
mProgressContainer = view.findViewById(R.id.progress_container);
mErrorContainer = view.findViewById(R.id.error_retry_container);
}
@Override
protected void fitSystemWindows(Rect insets) {
super.fitSystemWindows(insets);
// mRecyclerView.setPadding(insets.left, insets.top, insets.right, insets.bottom);
getView().setPadding(insets.left, insets.top, insets.right, insets.bottom);
}
@Override
public void onLoadFinished(final Loader<SingleResponse<ParcelableStatus>> loader,
final SingleResponse<ParcelableStatus> data) {
@ -256,28 +323,6 @@ public class StatusFragment extends BaseSupportFragment
}
}
private void setState(int state) {
mStatusContent.setVisibility(state == STATE_LOADED ? View.VISIBLE : View.GONE);
mProgressContainer.setVisibility(state == STATE_LOADING ? View.VISIBLE : View.GONE);
mErrorContainer.setVisibility(state == STATE_ERROR ? View.VISIBLE : View.GONE);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mStatusContent = view.findViewById(R.id.status_content);
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
mProgressContainer = view.findViewById(R.id.progress_container);
mErrorContainer = view.findViewById(R.id.error_retry_container);
}
@Override
protected void fitSystemWindows(Rect insets) {
super.fitSystemWindows(insets);
// mRecyclerView.setPadding(insets.left, insets.top, insets.right, insets.bottom);
getView().setPadding(insets.left, insets.top, insets.right, insets.bottom);
}
private void loadConversation(ParcelableStatus status) {
if (mLoadConversationTask != null && mLoadConversationTask.getStatus() == Status.RUNNING) {
mLoadConversationTask.cancel(true);
@ -300,11 +345,6 @@ public class StatusFragment extends BaseSupportFragment
mRepliesLoaderInitialized = true;
}
@Override
public void onLoaderReset(final Loader<SingleResponse<ParcelableStatus>> loader) {
}
private void setConversation(List<ParcelableStatus> data) {
if (mLayoutManager.getChildCount() != 0) {
final long itemId = mStatusAdapter.getItemId(mLayoutManager.findFirstVisibleItemPosition());
@ -329,6 +369,12 @@ public class StatusFragment extends BaseSupportFragment
}
}
private void setState(int state) {
mStatusContent.setVisibility(state == STATE_LOADED ? View.VISIBLE : View.GONE);
mProgressContainer.setVisibility(state == STATE_LOADING ? View.VISIBLE : View.GONE);
mErrorContainer.setVisibility(state == STATE_ERROR ? View.VISIBLE : View.GONE);
}
private static class StatusAdapter extends Adapter<ViewHolder> implements IStatusesAdapter<List<ParcelableStatus>> {
private static final int VIEW_TYPE_DETAIL_STATUS = 0;
@ -352,6 +398,8 @@ public class StatusFragment extends BaseSupportFragment
private ParcelableCredentials mStatusAccount;
private List<ParcelableStatus> mConversation, mReplies;
private boolean mDetailMediaExpanded;
private StatusAdapterListener mStatusAdapterListener;
private DetailStatusViewHolder mCachedHolder;
public StatusAdapter(StatusFragment fragment, boolean compact) {
final Context context = fragment.getActivity();
@ -412,22 +460,26 @@ public class StatusFragment extends BaseSupportFragment
}
}
public ParcelableCredentials getStatusAccount() {
return mStatusAccount;
}
@Override
public int getStatusCount() {
return getConversationCount() + 1 + getRepliesCount() + 1;
}
public float getTextSize() {
return mTextSize;
@Override
public int getProfileImageStyle() {
return 0;
}
@Override
public void onStatusClick(StatusViewHolder holder, int position) {
openStatus(mFragment.getActivity(), getStatus(position), null);
public int getMediaPreviewStyle() {
return 0;
}
@Override
public final void onStatusClick(StatusViewHolder holder, int position) {
if (mStatusAdapterListener != null) {
mStatusAdapterListener.onStatusClick(holder, position);
}
}
@Override
@ -445,20 +497,16 @@ public class StatusFragment extends BaseSupportFragment
return mFragment.getTwitterWrapper();
}
public float getTextSize() {
return mTextSize;
}
public ParcelableStatus getStatus() {
return mStatus;
}
public boolean setStatus(ParcelableStatus status) {
final ParcelableStatus old = mStatus;
mStatus = status;
if (status != null) {
mStatusAccount = ParcelableAccount.getCredentials(mContext, status.account_id);
} else {
mStatusAccount = null;
}
notifyDataSetChanged();
return !CompareUtils.objectEquals(old, status);
public ParcelableCredentials getStatusAccount() {
return mStatusAccount;
}
public boolean isDetailMediaExpanded() {
@ -476,8 +524,10 @@ public class StatusFragment extends BaseSupportFragment
}
@Override
public void onGapClick(ViewHolder holder, int position) {
public final void onGapClick(ViewHolder holder, int position) {
if (mStatusAdapterListener != null) {
mStatusAdapterListener.onGapClick((GapViewHolder) holder, position);
}
}
public boolean isNameFirst() {
@ -488,10 +538,27 @@ public class StatusFragment extends BaseSupportFragment
return mNicknameOnly;
}
@Override
public void onViewDetachedFromWindow(ViewHolder holder) {
super.onViewDetachedFromWindow(holder);
if (holder instanceof DetailStatusViewHolder) {
mCachedHolder = (DetailStatusViewHolder) holder;
}
}
@Override
public void onViewAttachedToWindow(ViewHolder holder) {
super.onViewAttachedToWindow(holder);
if (mCachedHolder == holder) {
mCachedHolder = null;
}
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case VIEW_TYPE_DETAIL_STATUS: {
if (mCachedHolder != null) return mCachedHolder;
final View view = mInflater.inflate(R.layout.header_status, parent, false);
final CardView cardView = (CardView) view.findViewById(R.id.card);
if (cardView != null) {
@ -575,12 +642,16 @@ public class StatusFragment extends BaseSupportFragment
@Override
public void onItemActionClick(ViewHolder holder, int id, int position) {
if (mStatusAdapterListener != null) {
mStatusAdapterListener.onStatusActionClick((StatusViewHolder) holder, id, position);
}
}
@Override
public void onItemMenuClick(ViewHolder holder, int position) {
if (mStatusAdapterListener != null) {
mStatusAdapterListener.onStatusMenuClick((StatusViewHolder) holder, position);
}
}
public void setConversation(List<ParcelableStatus> conversation) {
@ -588,11 +659,27 @@ public class StatusFragment extends BaseSupportFragment
notifyDataSetChanged();
}
public void setEventListener(StatusAdapterListener listener) {
mStatusAdapterListener = listener;
}
public void setReplies(List<ParcelableStatus> replies) {
mReplies = replies;
notifyDataSetChanged();
}
public boolean setStatus(ParcelableStatus status) {
final ParcelableStatus old = mStatus;
mStatus = status;
if (status != null) {
mStatusAccount = ParcelableAccount.getCredentials(mContext, status.account_id);
} else {
mStatusAccount = null;
}
notifyDataSetChanged();
return !CompareUtils.objectEquals(old, status);
}
private int getConversationCount() {
return mConversation != null ? mConversation.size() : 1;
}
@ -602,6 +689,10 @@ public class StatusFragment extends BaseSupportFragment
}
}
@Override
public void onLoaderReset(final Loader<SingleResponse<ParcelableStatus>> loader) {
}
static class LoadConversationTask extends TwidereAsyncTask<ParcelableStatus, ParcelableStatus,
ListResponse<ParcelableStatus>> {
@ -680,6 +771,7 @@ public class StatusFragment extends BaseSupportFragment
private final LinearLayout mediaPreviewGrid;
private final View locationContainer;
private final FrameLayout twitterCard;
public DetailStatusViewHolder(StatusAdapter adapter, View itemView) {
super(itemView);
@ -704,6 +796,7 @@ public class StatusFragment extends BaseSupportFragment
mediaPreviewGrid = (LinearLayout) itemView.findViewById(R.id.media_preview_grid);
locationContainer = itemView.findViewById(R.id.location_container);
profileContainer = itemView.findViewById(R.id.profile_container);
twitterCard = (FrameLayout) itemView.findViewById(R.id.twitter_card);
setIsRecyclable(false);
initViews();
@ -839,6 +932,7 @@ public class StatusFragment extends BaseSupportFragment
public void showStatus(ParcelableStatus status) {
if (status == null) return;
final StatusFragment fragment = adapter.getFragment();
final Context context = adapter.getContext();
final Resources resources = context.getResources();
final ImageLoaderWrapper loader = adapter.getImageLoader();
@ -912,6 +1006,20 @@ public class StatusFragment extends BaseSupportFragment
mediaPreviewGrid.setVisibility(View.GONE);
mediaPreviewGrid.removeAllViews();
}
if (Utils.isCardSupported(status.card)) {
twitterCard.setVisibility(View.VISIBLE);
final Fragment cardFragment = Utils.createTwitterCardFragment(status.card);
final FragmentManager fm = fragment.getChildFragmentManager();
final FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.twitter_card, cardFragment);
ft.commit();
} else {
twitterCard.setVisibility(View.GONE);
final FragmentManager fm = fragment.getChildFragmentManager();
// final FragmentTransaction ft = fm.beginTransaction();
}
setMenuForStatus(context, menuBar.getMenu(), status, adapter.getStatusAccount());
menuBar.show();
}
@ -942,13 +1050,6 @@ public class StatusFragment extends BaseSupportFragment
this.recyclerView = recyclerView;
}
@Override
public void setOrientation(int orientation) {
if (orientation != VERTICAL)
throw new IllegalArgumentException("Only VERTICAL orientation supported");
super.setOrientation(orientation);
}
@Override
public int getDecoratedMeasuredHeight(View child) {
final int height = super.getDecoratedMeasuredHeight(child);
@ -974,6 +1075,13 @@ public class StatusFragment extends BaseSupportFragment
return height;
}
@Override
public void setOrientation(int orientation) {
if (orientation != VERTICAL)
throw new IllegalArgumentException("Only VERTICAL orientation supported");
super.setOrientation(orientation);
}
}
}

View File

@ -43,6 +43,7 @@ import org.mariotaku.twidere.model.SingleResponse;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.ImageLoaderWrapper;
import org.mariotaku.twidere.util.ImageLoadingHandler;
import org.mariotaku.twidere.util.SharedPreferencesWrapper;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.view.holder.StatusViewHolder;
@ -130,7 +131,12 @@ public class StatusTranslateDialogFragment extends BaseSupportDialogFragment imp
final ImageLoaderWrapper loader = application.getImageLoaderWrapper();
final ImageLoadingHandler handler = new ImageLoadingHandler(R.id.media_preview_progress);
final AsyncTwitterWrapper twitter = getTwitterWrapper();
mHolder.displayStatus(activity, loader, handler, twitter, status);
final SharedPreferencesWrapper preferences = SharedPreferencesWrapper.getInstance(activity,
SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
final int profileImageStyle = Utils.getProfileImageStyle(preferences.getString(KEY_PROFILE_IMAGE_STYLE, null));
final int mediaPreviewStyle = Utils.getMediaPreviewStyle(preferences.getString(KEY_MEDIA_PREVIEW_STYLE, null));
mHolder.displayStatus(activity, loader, handler, twitter, profileImageStyle,
mediaPreviewStyle, status, null);
mStatusContainer.findViewById(R.id.item_menu).setVisibility(View.GONE);
mStatusContainer.findViewById(R.id.action_buttons).setVisibility(View.GONE);
mStatusContainer.findViewById(R.id.reply_retweet_status).setVisibility(View.GONE);

View File

@ -0,0 +1,38 @@
/*
* 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 org.mariotaku.twidere.fragment.support;
import android.os.Bundle;
import android.webkit.WebView;
import org.mariotaku.twidere.util.ParseUtils;
public class SupportBrowserFragment extends BaseSupportWebViewFragment {
@Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
final Bundle args = getArguments();
final Object uri = args != null ? args.get(EXTRA_URI) : null;
final WebView view = getWebView();
view.loadUrl(ParseUtils.parseString(uri, "about:blank"));
}
}

View File

@ -1,20 +1,17 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 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/>.
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mariotaku.twidere.fragment.support;
@ -26,18 +23,76 @@ import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import org.mariotaku.twidere.R;
/**
* A fragment that displays a WebView.
* <p/>
* The WebView is automically paused or resumed when the Fragment is paused or resumed.
*/
public class SupportWebViewFragment extends Fragment {
private WebView mWebView;
private boolean mIsWebViewAvailable;
public final WebView getWebView() {
final View view = getView();
return (WebView) view.findViewById(R.id.webview);
}
public SupportWebViewFragment() {
}
@Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_webview, container, false);
}
/**
* Called to instantiate the view. Creates and returns the WebView.
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (mWebView != null) {
mWebView.destroy();
}
mWebView = new WebView(getActivity());
mIsWebViewAvailable = true;
return mWebView;
}
/**
* Called when the fragment is visible to the user and actively running. Resumes the WebView.
*/
@Override
public void onPause() {
super.onPause();
mWebView.onPause();
}
/**
* Called when the fragment is no longer resumed. Pauses the WebView.
*/
@Override
public void onResume() {
mWebView.onResume();
super.onResume();
}
/**
* Called when the WebView has been detached from the fragment.
* The WebView is no longer available after this time.
*/
@Override
public void onDestroyView() {
mIsWebViewAvailable = false;
super.onDestroyView();
}
/**
* Called when the fragment is no longer in use. Destroys the internal state of the WebView.
*/
@Override
public void onDestroy() {
if (mWebView != null) {
mWebView.destroy();
mWebView = null;
}
super.onDestroy();
}
/**
* Gets the WebView.
*/
public WebView getWebView() {
return mIsWebViewAvailable ? mWebView : null;
}
}

View File

@ -44,7 +44,17 @@ public class UserFavoritesFragment extends ParcelableStatusesFragment {
final String screenName = args.getString(EXTRA_SCREEN_NAME);
final int tabPosition = args.getInt(EXTRA_TAB_POSITION, -1);
return new UserFavoritesLoader(context, accountId, userId, screenName, maxId, sinceId,
getAdapterData(), null, tabPosition);
getAdapterData(), getSavedStatusesFileArgs(), tabPosition);
}
@Override
protected String[] getSavedStatusesFileArgs() {
final Bundle args = getArguments();
if (args == null) return null;
final long accountId = args.getLong(EXTRA_ACCOUNT_ID, -1);
final long userId = args.getLong(EXTRA_USER_ID, -1);
final String screenName = args.getString(EXTRA_SCREEN_NAME);
return new String[]{AUTHORITY_USER_FAVORITES, "account" + accountId, "user" + userId, "name" + screenName};
}
}

View File

@ -48,4 +48,17 @@ public class UserListTimelineFragment extends ParcelableStatusesFragment {
listName, maxId, sinceId, getAdapterData(), getSavedStatusesFileArgs(), tabPosition);
}
@Override
protected String[] getSavedStatusesFileArgs() {
final Bundle args = getArguments();
if (args == null) return null;
final int listId = args.getInt(EXTRA_LIST_ID, -1);
final long accountId = args.getLong(EXTRA_ACCOUNT_ID, -1);
final long userId = args.getLong(EXTRA_USER_ID, -1);
final String screenName = args.getString(EXTRA_SCREEN_NAME);
final String listName = args.getString(EXTRA_LIST_NAME);
return new String[]{AUTHORITY_USER_LIST_TIMELINE, "account" + accountId, "list_id" + listId,
"list_name" + listName, "user_id" + userId, "screen_name" + screenName};
}
}

View File

@ -45,7 +45,16 @@ public class UserTimelineFragment extends ParcelableStatusesFragment {
final String screenName = args.getString(EXTRA_SCREEN_NAME);
final int tabPosition = args.getInt(EXTRA_TAB_POSITION, -1);
return new UserTimelineLoader(context, accountId, userId, screenName, maxId, sinceId, data,
null, tabPosition);
getSavedStatusesFileArgs(), tabPosition);
}
@Override
protected String[] getSavedStatusesFileArgs() {
final Bundle args = getArguments();
if (args == null) return null;
final long accountId = args.getLong(EXTRA_ACCOUNT_ID, -1);
final long userId = args.getLong(EXTRA_USER_ID, -1);
final String screenName = args.getString(EXTRA_SCREEN_NAME);
return new String[]{AUTHORITY_USER_TIMELINE, "account" + accountId, "user" + userId + "name" + screenName};
}
}

View File

@ -24,9 +24,13 @@ import android.database.Cursor;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import org.json.JSONException;
import org.json.JSONObject;
import org.mariotaku.jsonserializer.JSONParcel;
import org.mariotaku.jsonserializer.JSONParcelable;
import org.mariotaku.jsonserializer.JSONSerializer;
import org.mariotaku.twidere.provider.TweetStore.Statuses;
import org.mariotaku.twidere.util.ParseUtils;
@ -34,6 +38,11 @@ import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import twitter4j.CardEntity;
import twitter4j.CardEntity.BindingValue;
import twitter4j.CardEntity.ImageValue;
import twitter4j.CardEntity.StringValue;
import twitter4j.CardEntity.UserValue;
import twitter4j.Status;
import twitter4j.User;
@ -109,6 +118,8 @@ public class ParcelableStatus implements TwidereParcelable, Comparable<Parcelabl
public final ParcelableMedia[] media;
public final ParcelableCardEntity card;
public ParcelableStatus(final ContentValues values) {
id = getAsLong(values, Statuses.STATUS_ID, -1);
account_id = getAsLong(values, Statuses.ACCOUNT_ID, -1);
@ -147,6 +158,7 @@ public class ParcelableStatus implements TwidereParcelable, Comparable<Parcelabl
user_is_following = getAsBoolean(values, Statuses.IS_FOLLOWING, false);
mentions = ParcelableUserMention.fromJSONString(values.getAsString(Statuses.MENTIONS));
first_media = values.getAsString(Statuses.FIRST_MEDIA);
card = ParcelableCardEntity.fromJSONString(values.getAsString(Statuses.CARD));
}
public ParcelableStatus(final Cursor c, final CursorIndices idx) {
@ -190,6 +202,7 @@ public class ParcelableStatus implements TwidereParcelable, Comparable<Parcelabl
user_is_following = idx.is_following != -1 && c.getInt(idx.is_following) == 1;
mentions = idx.mentions != -1 ? ParcelableUserMention.fromJSONString(c.getString(idx.mentions)) : null;
first_media = idx.first_media != -1 ? c.getString(idx.first_media) : null;
card = idx.card != -1 ? ParcelableCardEntity.fromJSONString(c.getString(idx.card)) : null;
}
public ParcelableStatus(final JSONParcel in) {
@ -230,6 +243,7 @@ public class ParcelableStatus implements TwidereParcelable, Comparable<Parcelabl
user_is_following = in.readBoolean("is_following");
mentions = in.readParcelableArray("mentions", ParcelableUserMention.JSON_CREATOR);
first_media = media != null && media.length > 0 ? media[0].url : null;
card = in.readParcelable("card", ParcelableCardEntity.JSON_CREATOR);
}
public ParcelableStatus(final Parcel in) {
@ -270,6 +284,7 @@ public class ParcelableStatus implements TwidereParcelable, Comparable<Parcelabl
in_reply_to_name = in.readString();
mentions = in.createTypedArray(ParcelableUserMention.CREATOR);
first_media = media != null && media.length > 0 ? media[0].url : null;
card = in.readParcelable(ParcelableCardEntity.class.getClassLoader());
}
public ParcelableStatus(final ParcelableStatus orig, final long override_my_retweet_id,
@ -311,6 +326,7 @@ public class ParcelableStatus implements TwidereParcelable, Comparable<Parcelabl
in_reply_to_name = orig.in_reply_to_name;
mentions = orig.mentions;
first_media = orig.first_media;
card = orig.card;
}
public ParcelableStatus(final Status orig, final long account_id, final boolean is_gap) {
@ -352,10 +368,11 @@ public class ParcelableStatus implements TwidereParcelable, Comparable<Parcelabl
location = new ParcelableLocation(status.getGeoLocation());
is_favorite = status.isFavorited();
text_unescaped = toPlainText(text_html);
my_retweet_id = retweeted_by_id == account_id ? id : -1;
my_retweet_id = retweeted_by_id == account_id ? id : status.getCurrentUserRetweet();
is_possibly_sensitive = status.isPossiblySensitive();
mentions = ParcelableUserMention.fromUserMentionEntities(status.getUserMentionEntities());
first_media = media != null && media.length > 0 ? media[0].url : null;
card = ParcelableCardEntity.fromCardEntity(status.getCard(), account_id);
}
@Override
@ -470,6 +487,7 @@ public class ParcelableStatus implements TwidereParcelable, Comparable<Parcelabl
out.writeBoolean("is_possibly_sensitive", is_possibly_sensitive);
out.writeBoolean("is_following", user_is_following);
out.writeParcelableArray("mentions", mentions);
out.writeParcelable("card", card);
}
@Override
@ -510,6 +528,7 @@ public class ParcelableStatus implements TwidereParcelable, Comparable<Parcelabl
out.writeLong(in_reply_to_user_id);
out.writeString(in_reply_to_name);
out.writeTypedArray(mentions, flags);
out.writeParcelable(card, flags);
}
private static long getTime(final Date date) {
@ -524,7 +543,8 @@ public class ParcelableStatus implements TwidereParcelable, Comparable<Parcelabl
in_reply_to_user_name, in_reply_to_user_screen_name, my_retweet_id, retweeted_by_user_name,
retweeted_by_user_screen_name, retweeted_by_user_profile_image, retweet_id, retweet_timestamp,
retweeted_by_user_id, user_id, source, retweet_count, favorite_count, reply_count,
descendent_reply_count, is_possibly_sensitive, is_following, media, first_media, mentions;
descendent_reply_count, is_possibly_sensitive, is_following, media, first_media, mentions,
card;
@Override
public String toString() {
@ -608,7 +628,317 @@ public class ParcelableStatus implements TwidereParcelable, Comparable<Parcelabl
media = cursor.getColumnIndex(Statuses.MEDIA);
first_media = cursor.getColumnIndex(Statuses.FIRST_MEDIA);
mentions = cursor.getColumnIndex(Statuses.MENTIONS);
card = cursor.getColumnIndex(Statuses.MENTIONS);
}
}
public static final class ParcelableCardEntity implements TwidereParcelable {
public static ParcelableValueItem getValue(ParcelableCardEntity entity, String key) {
for (ParcelableValueItem item : entity.values) {
if (item.name.equals(key)) return item;
}
return null;
}
public static final Parcelable.Creator<ParcelableCardEntity> CREATOR = new Parcelable.Creator<ParcelableCardEntity>() {
@Override
public ParcelableCardEntity createFromParcel(final Parcel in) {
return new ParcelableCardEntity(in);
}
@Override
public ParcelableCardEntity[] newArray(final int size) {
return new ParcelableCardEntity[size];
}
};
public static final JSONParcelable.Creator<ParcelableCardEntity> JSON_CREATOR = new JSONParcelable.Creator<ParcelableCardEntity>() {
@Override
public ParcelableCardEntity createFromParcel(final JSONParcel in) {
return new ParcelableCardEntity(in);
}
@Override
public ParcelableCardEntity[] newArray(final int size) {
return new ParcelableCardEntity[size];
}
};
public final String name;
public final ParcelableUser[] users;
public final ParcelableValueItem[] values;
public ParcelableCardEntity(Parcel src) {
name = src.readString();
values = src.createTypedArray(ParcelableValueItem.CREATOR);
users = src.createTypedArray(ParcelableUser.CREATOR);
}
public ParcelableCardEntity(JSONParcel src) {
name = src.readString("name");
values = src.readParcelableArray("values", ParcelableValueItem.JSON_CREATOR);
users = src.readParcelableArray("users", ParcelableUser.JSON_CREATOR);
}
public ParcelableCardEntity(CardEntity card, long account_id) {
name = card.getName();
users = ParcelableUser.fromUsersArray(card.gerUsers(), account_id);
final BindingValue[] bindingValues = card.getBindingValues();
if (bindingValues != null) {
values = new ParcelableValueItem[bindingValues.length];
for (int i = 0, j = bindingValues.length; i < j; i++) {
values[i] = new ParcelableValueItem(bindingValues[i]);
}
} else {
values = null;
}
}
@Override
public int describeContents() {
return 0;
}
public static ParcelableCardEntity fromCardEntity(CardEntity card, long account_id) {
if (card == null) return null;
return new ParcelableCardEntity(card, account_id);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeTypedArray(values, flags);
dest.writeTypedArray(users, flags);
}
@Override
public void writeToParcel(JSONParcel dest) {
dest.writeString("name", name);
dest.writeParcelableArray("values", values);
dest.writeParcelableArray("users", users);
}
public static ParcelableCardEntity fromJSONString(final String json) {
if (TextUtils.isEmpty(json)) return null;
try {
return JSONSerializer.createObject(JSON_CREATOR, new JSONObject(json));
} catch (final JSONException e) {
return null;
}
}
public static final class ParcelableValueItem implements TwidereParcelable {
public static final Parcelable.Creator<ParcelableValueItem> CREATOR = new Parcelable.Creator<ParcelableValueItem>() {
@Override
public ParcelableValueItem createFromParcel(final Parcel in) {
return new ParcelableValueItem(in);
}
@Override
public ParcelableValueItem[] newArray(final int size) {
return new ParcelableValueItem[size];
}
};
public static final JSONParcelable.Creator<ParcelableValueItem> JSON_CREATOR = new JSONParcelable.Creator<ParcelableValueItem>() {
@Override
public ParcelableValueItem createFromParcel(final JSONParcel in) {
return new ParcelableValueItem(in);
}
@Override
public ParcelableValueItem[] newArray(final int size) {
return new ParcelableValueItem[size];
}
};
public final String name, type;
public final Object value;
public ParcelableValueItem(JSONParcel in) {
this.name = in.readString("name");
this.type = in.readString("type");
if ("STRING".equals(type)) {
value = in.readString("value");
} else if ("IMAGE".equals(type)) {
value = in.readParcelable("value", ParcelableImageValue.JSON_CREATOR);
} else if ("USER".equals(type)) {
value = in.readParcelable("value", ParcelableUserValue.JSON_CREATOR);
} else {
throw new UnsupportedOperationException();
}
}
public ParcelableValueItem(String name, String type, Object value) {
this.name = name;
this.type = type;
this.value = value;
}
public ParcelableValueItem(Parcel in) {
this.name = in.readString();
this.type = in.readString();
this.value = in.readValue(ParcelableValueItem.class.getClassLoader());
}
public ParcelableValueItem(BindingValue bindingValue) {
name = bindingValue.getName();
type = bindingValue.getType();
if ("STRING".equals(type)) {
value = ((StringValue) bindingValue).getValue();
} else if ("IMAGE".equals(type)) {
value = new ParcelableImageValue((ImageValue) bindingValue);
} else if ("USER".equals(type)) {
value = new ParcelableUserValue((UserValue) bindingValue);
} else {
value = null;
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeString(type);
dest.writeValue(value);
}
@Override
public void writeToParcel(JSONParcel dest) {
dest.writeString("name", name);
dest.writeString("type", type);
dest.writeObject("value", value);
}
}
public static final class ParcelableUserValue implements TwidereParcelable {
public static final Parcelable.Creator<ParcelableUserValue> CREATOR = new Parcelable.Creator<ParcelableUserValue>() {
@Override
public ParcelableUserValue createFromParcel(final Parcel in) {
return new ParcelableUserValue(in);
}
@Override
public ParcelableUserValue[] newArray(final int size) {
return new ParcelableUserValue[size];
}
};
public static final JSONParcelable.Creator<ParcelableUserValue> JSON_CREATOR = new JSONParcelable.Creator<ParcelableUserValue>() {
@Override
public ParcelableUserValue createFromParcel(final JSONParcel in) {
return new ParcelableUserValue(in);
}
@Override
public ParcelableUserValue[] newArray(final int size) {
return new ParcelableUserValue[size];
}
};
public final long id;
public ParcelableUserValue(JSONParcel in) {
this.id = in.readLong("id");
}
public ParcelableUserValue(Parcel in) {
this.id = in.readLong();
}
public ParcelableUserValue(UserValue value) {
this.id = value.getUserId();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(id);
}
@Override
public void writeToParcel(JSONParcel dest) {
dest.writeLong("id", id);
}
}
public static final class ParcelableImageValue implements TwidereParcelable {
public static final Parcelable.Creator<ParcelableImageValue> CREATOR = new Parcelable.Creator<ParcelableImageValue>() {
@Override
public ParcelableImageValue createFromParcel(final Parcel in) {
return new ParcelableImageValue(in);
}
@Override
public ParcelableImageValue[] newArray(final int size) {
return new ParcelableImageValue[size];
}
};
public static final JSONParcelable.Creator<ParcelableImageValue> JSON_CREATOR = new JSONParcelable.Creator<ParcelableImageValue>() {
@Override
public ParcelableImageValue createFromParcel(final JSONParcel in) {
return new ParcelableImageValue(in);
}
@Override
public ParcelableImageValue[] newArray(final int size) {
return new ParcelableImageValue[size];
}
};
public final int width, height;
public final String url;
public ParcelableImageValue(JSONParcel in) {
this.width = in.readInt("width");
this.height = in.readInt("height");
this.url = in.readString("url");
}
public ParcelableImageValue(Parcel in) {
this.width = in.readInt();
this.height = in.readInt();
this.url = in.readString();
}
public ParcelableImageValue(ImageValue value) {
this.width = value.getWidth();
this.height = value.getHeight();
this.url = value.getUrl();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(width);
dest.writeInt(height);
dest.writeString(url);
}
@Override
public void writeToParcel(JSONParcel dest) {
dest.writeInt("width", width);
dest.writeInt("height", height);
dest.writeString("url", url);
}
}
}
}

View File

@ -24,6 +24,7 @@ import android.database.Cursor;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.mariotaku.jsonserializer.JSONParcel;
import org.mariotaku.jsonserializer.JSONParcelable;
@ -257,6 +258,15 @@ public class ParcelableUser implements TwidereParcelable, Comparable<ParcelableU
return 0;
}
public static ParcelableUser[] fromUsersArray(@Nullable final User[] users, long account_id) {
if (users == null) return null;
final ParcelableUser[] result = new ParcelableUser[users.length];
for (int i = 0, j = users.length; i < j; i++) {
result[i] = new ParcelableUser(users[i], account_id);
}
return result;
}
@Override
public void writeToParcel(final Parcel out, final int flags) {
out.writeLong(position);

View File

@ -732,6 +732,8 @@ public interface TweetStore {
public static final String MENTIONS = "mentions";
public static final String CARD = "card";
public static final String SORT_ORDER_TIMESTAMP_DESC = STATUS_TIMESTAMP + " DESC";
public static final String SORT_ORDER_STATUS_ID_DESC = STATUS_ID + " DESC";
@ -745,14 +747,15 @@ public interface TweetStore {
DESCENDENT_REPLY_COUNT, RETWEET_ID, RETWEET_TIMESTAMP, RETWEETED_BY_USER_ID,
RETWEETED_BY_USER_NAME, RETWEETED_BY_USER_SCREEN_NAME, RETWEETED_BY_USER_PROFILE_IMAGE,
MY_RETWEET_ID, IS_RETWEET, IS_FAVORITE, IS_PROTECTED, IS_VERIFIED, IS_FOLLOWING, IS_GAP,
IS_POSSIBLY_SENSITIVE, MEDIA, FIRST_MEDIA, MENTIONS};
IS_POSSIBLY_SENSITIVE, MEDIA, FIRST_MEDIA, MENTIONS, CARD};
public static final String[] TYPES = new String[]{TYPE_PRIMARY_KEY, TYPE_INT, TYPE_INT,
TYPE_INT, TYPE_INT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT,
TYPE_INT, TYPE_INT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT, TYPE_INT, TYPE_INT,
TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_INT, TYPE_TEXT, TYPE_TEXT,
TYPE_TEXT, TYPE_INT, TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_BOOLEAN,
TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT};
TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_BOOLEAN, TYPE_TEXT, TYPE_TEXT, TYPE_TEXT,
TYPE_TEXT};
}

View File

@ -47,6 +47,7 @@ import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.support.annotation.NonNull;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationCompat.Action;
import android.text.Html;
import android.util.Log;
@ -64,9 +65,11 @@ import org.mariotaku.twidere.model.SupportTabSpec;
import org.mariotaku.twidere.model.UnreadItem;
import org.mariotaku.twidere.provider.TweetStore.Accounts;
import org.mariotaku.twidere.provider.TweetStore.DirectMessages;
import org.mariotaku.twidere.provider.TweetStore.Drafts;
import org.mariotaku.twidere.provider.TweetStore.Preferences;
import org.mariotaku.twidere.provider.TweetStore.Statuses;
import org.mariotaku.twidere.provider.TweetStore.UnreadCounts;
import org.mariotaku.twidere.service.BackgroundOperationService;
import org.mariotaku.twidere.util.ArrayUtils;
import org.mariotaku.twidere.util.CustomTabUtils;
import org.mariotaku.twidere.util.HtmlEscapeHelper;
@ -184,7 +187,7 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
if (result > 0) {
onDatabaseUpdated(tableId, uri);
}
onNewItemsInserted(uri, values);
onNewItemsInserted(uri, tableId, values);
return result;
} catch (final SQLException e) {
throw new IllegalStateException(e);
@ -266,7 +269,7 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
rowId = mDatabaseWrapper.insert(table, null, values);
}
onDatabaseUpdated(tableId, uri);
onNewItemsInserted(uri, values);
onNewItemsInserted(uri, tableId, values);
return Uri.withAppendedPath(uri, String.valueOf(rowId));
} catch (final SQLException e) {
throw new IllegalStateException(e);
@ -1092,13 +1095,13 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
notifyContentObserver(getNotificationUri(tableId, uri));
}
private void onNewItemsInserted(final Uri uri, final ContentValues... values) {
if (uri == null || values == null || values.length == 0) return;
preloadImages(values);
private void onNewItemsInserted(final Uri uri, final int tableId, final ContentValues... valuesArray) {
if (uri == null || valuesArray == null || valuesArray.length == 0) return;
preloadImages(valuesArray);
if (!uri.getBooleanQueryParameter(QUERY_PARAM_NOTIFY, true)) return;
switch (getTableId(uri)) {
switch (tableId) {
case TABLE_ID_STATUSES: {
final int notifiedCount = notifyStatusesInserted(values);
final int notifiedCount = notifyStatusesInserted(valuesArray);
final List<ParcelableStatus> items = new ArrayList<ParcelableStatus>(mNewStatuses);
Collections.sort(items);
final AccountPreferences[] prefs = AccountPreferences.getNotificationEnabledPreferences(getContext(),
@ -1118,7 +1121,7 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
case TABLE_ID_MENTIONS: {
final AccountPreferences[] prefs = AccountPreferences.getNotificationEnabledPreferences(getContext(),
getAccountIds(getContext()));
final int notifiedCount = notifyMentionsInserted(prefs, values);
final int notifiedCount = notifyMentionsInserted(prefs, valuesArray);
final List<ParcelableStatus> items = new ArrayList<ParcelableStatus>(mNewMentions);
Collections.sort(items);
for (final AccountPreferences pref : prefs) {
@ -1134,8 +1137,8 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
break;
}
case TABLE_ID_DIRECT_MESSAGES_INBOX: {
final int notifiedCount = notifyIncomingMessagesInserted(values);
final List<ParcelableDirectMessage> items = new ArrayList<ParcelableDirectMessage>(mNewMessages);
final int notifiedCount = notifyIncomingMessagesInserted(valuesArray);
final List<ParcelableDirectMessage> items = new ArrayList<>(mNewMessages);
Collections.sort(items);
final AccountPreferences[] prefs = AccountPreferences.getNotificationEnabledPreferences(getContext(),
getAccountIds(getContext()));
@ -1149,9 +1152,32 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
notifyUnreadCountChanged(NOTIFICATION_ID_DIRECT_MESSAGES);
break;
}
case TABLE_ID_DRAFTS: {
for (ContentValues values : valuesArray) {
displayNewDraftNotification(values);
}
break;
}
}
}
private void displayNewDraftNotification(ContentValues values) {
final Context context = getContext();
final NotificationManager nm = getNotificationManager();
final NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setTicker(context.getString(R.string.draft_saved));
builder.setContentTitle(context.getString(R.string.draft_saved));
builder.setContentText(values.getAsString(Drafts.TEXT));
builder.setSmallIcon(R.drawable.ic_stat_info);
final Intent service = new Intent(context, BackgroundOperationService.class);
service.setAction(INTENT_ACTION_DISCARD_DRAFT);
final PendingIntent discardIntent = PendingIntent.getService(context, 0, service, 0);
final Action.Builder actionBuilder = new Action.Builder(R.drawable.ic_action_delete,
context.getString(R.string.discard), discardIntent);
builder.addAction(actionBuilder.build());
nm.notify(16, builder.build());
}
private void preloadImages(final ContentValues... values) {
if (values == null) return;
for (final ContentValues v : values) {

View File

@ -265,35 +265,39 @@ public final class ContentValuesCreator implements TwidereConstants {
values.put(Statuses.ACCOUNT_ID, accountId);
values.put(Statuses.STATUS_ID, orig.getId());
values.put(Statuses.STATUS_TIMESTAMP, orig.getCreatedAt().getTime());
values.put(Statuses.MY_RETWEET_ID, orig.getCurrentUserRetweet());
final boolean isRetweet = orig.isRetweet();
final Status status;
final Status retweetedStatus = isRetweet ? orig.getRetweetedStatus() : null;
if (retweetedStatus != null) {
final User retweetUser = orig.getUser();
final long retweetedById = retweetUser.getId();
values.put(Statuses.RETWEET_ID, retweetedStatus.getId());
values.put(Statuses.RETWEET_TIMESTAMP, retweetedStatus.getCreatedAt().getTime());
values.put(Statuses.RETWEETED_BY_USER_ID, retweetUser.getId());
values.put(Statuses.RETWEETED_BY_USER_ID, retweetedById);
values.put(Statuses.RETWEETED_BY_USER_NAME, retweetUser.getName());
values.put(Statuses.RETWEETED_BY_USER_SCREEN_NAME, retweetUser.getScreenName());
values.put(Statuses.RETWEETED_BY_USER_PROFILE_IMAGE, ParseUtils.parseString(retweetUser.getProfileImageUrlHttps()));
if (retweetedById == accountId) {
values.put(Statuses.MY_RETWEET_ID, orig.getId());
} else {
values.put(Statuses.MY_RETWEET_ID, orig.getCurrentUserRetweet());
}
status = retweetedStatus;
} else {
values.put(Statuses.MY_RETWEET_ID, orig.getCurrentUserRetweet());
status = orig;
}
final User user = status.getUser();
if (user != null) {
final long userId = user.getId();
final String profileImageUrl = ParseUtils.parseString(user.getProfileImageUrlHttps());
final String name = user.getName(), screenName = user.getScreenName();
values.put(Statuses.USER_ID, userId);
values.put(Statuses.USER_NAME, name);
values.put(Statuses.USER_SCREEN_NAME, screenName);
values.put(Statuses.IS_PROTECTED, user.isProtected());
values.put(Statuses.IS_VERIFIED, user.isVerified());
values.put(Statuses.USER_PROFILE_IMAGE_URL, profileImageUrl);
values.put(CachedUsers.IS_FOLLOWING, user.isFollowing());
}
final long userId = user.getId();
final String profileImageUrl = ParseUtils.parseString(user.getProfileImageUrlHttps());
final String name = user.getName(), screenName = user.getScreenName();
values.put(Statuses.USER_ID, userId);
values.put(Statuses.USER_NAME, name);
values.put(Statuses.USER_SCREEN_NAME, screenName);
values.put(Statuses.IS_PROTECTED, user.isProtected());
values.put(Statuses.IS_VERIFIED, user.isVerified());
values.put(Statuses.USER_PROFILE_IMAGE_URL, profileImageUrl);
values.put(CachedUsers.IS_FOLLOWING, user.isFollowing());
final String text_html = Utils.formatStatusText(status);
values.put(Statuses.TEXT_HTML, text_html);
values.put(Statuses.TEXT_PLAIN, status.getText());

View File

@ -101,6 +101,12 @@ import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.youtube.player.YouTubeInitializationResult;
import com.google.android.youtube.player.YouTubePlayer;
import com.google.android.youtube.player.YouTubePlayer.OnInitializedListener;
import com.google.android.youtube.player.YouTubePlayer.Provider;
import com.google.android.youtube.player.YouTubePlayerSupportFragment;
import org.apache.http.NameValuePair;
import org.json.JSONException;
import org.mariotaku.gallery3d.ImageViewerGLActivity;
@ -160,6 +166,8 @@ import org.mariotaku.twidere.model.ParcelableAccount.ParcelableCredentials;
import org.mariotaku.twidere.model.ParcelableDirectMessage;
import org.mariotaku.twidere.model.ParcelableLocation;
import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.model.ParcelableStatus.ParcelableCardEntity;
import org.mariotaku.twidere.model.ParcelableStatus.ParcelableCardEntity.ParcelableValueItem;
import org.mariotaku.twidere.model.ParcelableUser;
import org.mariotaku.twidere.model.ParcelableUserList;
import org.mariotaku.twidere.model.TwidereParcelable;
@ -189,6 +197,7 @@ import org.mariotaku.twidere.util.content.ContentResolverUtils;
import org.mariotaku.twidere.util.menu.TwidereMenuInfo;
import org.mariotaku.twidere.util.net.TwidereHostResolverFactory;
import org.mariotaku.twidere.util.net.TwidereHttpClientFactory;
import org.mariotaku.twidere.view.ShapedImageView;
import java.io.Closeable;
import java.io.File;
@ -2074,6 +2083,22 @@ public final class Utils implements Constants, TwitterConstants {
return url;
}
public static int getProfileImageStyle(String style) {
if (VALUE_PROFILE_IMAGE_STYLE_SQUARE.equalsIgnoreCase(style)) {
return ShapedImageView.SHAPE_RECTANGLE;
}
return ShapedImageView.SHAPE_CIRCLE;
}
public static int getMediaPreviewStyle(String style) {
if (VALUE_MEDIA_PREVIEW_STYLE_CROP.equalsIgnoreCase(style)) {
return VALUE_MEDIA_PREVIEW_STYLE_CODE_CROP;
} else if (VALUE_MEDIA_PREVIEW_STYLE_SCALE.equalsIgnoreCase(style)) {
return VALUE_MEDIA_PREVIEW_STYLE_CODE_SCALE;
}
return VALUE_MEDIA_PREVIEW_STYLE_CODE_NONE;
}
public static Proxy getProxy(final Context context) {
if (context == null) return null;
final SharedPreferences prefs = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
@ -2478,6 +2503,8 @@ public final class Utils implements Constants, TwitterConstants {
cb.setHttpConnectionTimeout(connection_timeout);
cb.setGZIPEnabled(enableGzip);
cb.setIgnoreSSLError(ignoreSslError);
cb.setIncludeCards(true);
cb.setCardsPlatform("Android-5");
if (enableProxy) {
final String proxy_host = prefs.getString(KEY_PROXY_HOST, null);
final int proxy_port = ParseUtils.parseInt(prefs.getString(KEY_PROXY_PORT, "-1"));
@ -2649,6 +2676,32 @@ public final class Utils implements Constants, TwitterConstants {
return plugged || level / scale > 0.15f;
}
public static boolean isCardSupported(ParcelableCardEntity card) {
return card != null && "player".equals(card.name);
}
public static Fragment createTwitterCardFragment(ParcelableCardEntity card) {
if ("player".equals(card.name)) {
final ParcelableValueItem player_url = ParcelableCardEntity.getValue(card, "player_url");
final YouTubePlayerSupportFragment fragment = YouTubePlayerSupportFragment.newInstance();
fragment.initialize("AIzaSyCVdCIMFFxdNqHnCPrJ9yKUzoTfs8jhYGc", new OnInitializedListener() {
@Override
public void onInitializationSuccess(Provider provider, YouTubePlayer player, boolean b) {
final String url = (String) player_url.value;
final String id = url.substring(url.lastIndexOf('/') + 1);
player.cueVideo(id);
}
@Override
public void onInitializationFailure(Provider provider, YouTubeInitializationResult youTubeInitializationResult) {
}
});
return fragment;
}
return null;
}
public static boolean isCompactCards(final Context context) {
final SharedPreferences prefs = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
return prefs != null && prefs.getBoolean(KEY_COMPACT_CARDS, false);

View File

@ -27,8 +27,10 @@ import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.TextView;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.model.ParcelableMedia;
import org.mariotaku.twidere.util.ImageLoaderWrapper;
@ -38,11 +40,12 @@ import org.mariotaku.twidere.util.MediaPreviewUtils.OnMediaClickListener;
/**
* Created by mariotaku on 14/12/17.
*/
public class CardMediaContainer extends ViewGroup {
public class CardMediaContainer extends ViewGroup implements Constants {
private final int mMaxColumns;
private final int mHorizontalSpacing, mVerticalSpacing;
private int[] mTempIndices;
private int mMediaPreviewStyle;
public CardMediaContainer(Context context) {
this(context, null);
@ -83,7 +86,7 @@ public class CardMediaContainer extends ViewGroup {
final long accountId,
final OnMediaClickListener mediaClickListener,
final ImageLoadingHandler loadingHandler) {
if (mediaArray == null) {
if (mediaArray == null || mMediaPreviewStyle == VALUE_MEDIA_PREVIEW_STYLE_CODE_NONE) {
for (int i = 0, j = getChildCount(); i < j; i++) {
final View child = getChildAt(i);
child.setVisibility(GONE);
@ -95,6 +98,16 @@ public class CardMediaContainer extends ViewGroup {
final View child = getChildAt(i);
child.setOnClickListener(clickListener);
final ImageView imageView = (ImageView) child.findViewById(R.id.media_preview);
switch (mMediaPreviewStyle) {
case VALUE_MEDIA_PREVIEW_STYLE_CODE_CROP: {
imageView.setScaleType(ScaleType.CENTER_CROP);
break;
}
case VALUE_MEDIA_PREVIEW_STYLE_CODE_SCALE: {
imageView.setScaleType(ScaleType.CENTER_INSIDE);
break;
}
}
if (i < k) {
final ParcelableMedia media = mediaArray[i];
loader.displayPreviewImage(imageView, media.url, loadingHandler);
@ -117,6 +130,10 @@ public class CardMediaContainer extends ViewGroup {
}
}
public void setStyle(int style) {
mMediaPreviewStyle = style;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int[] childIndices = createChildIndices();

View File

@ -100,17 +100,13 @@ public class StatusViewHolder extends RecyclerView.ViewHolder implements OnClick
public void displayStatus(final ParcelableStatus status) {
displayStatus(adapter.getContext(), adapter.getImageLoader(),
adapter.getImageLoadingHandler(), adapter.getTwitterWrapper(), status);
}
public void displayStatus(final Context context, final ImageLoaderWrapper loader,
final ImageLoadingHandler handler, final AsyncTwitterWrapper twitter,
final ParcelableStatus status) {
displayStatus(context, loader, handler, twitter, status, null);
adapter.getImageLoadingHandler(), adapter.getTwitterWrapper(),
adapter.getProfileImageStyle(), adapter.getMediaPreviewStyle(), status, null);
}
public void displayStatus(final Context context, final ImageLoaderWrapper loader,
final ImageLoadingHandler handler, final AsyncTwitterWrapper twitter,
final int profileImageStyle, final int mediaPreviewStyle,
@NonNull final ParcelableStatus status,
@Nullable final TranslationResult translation) {
final ParcelableMedia[] media = status.media;
@ -145,9 +141,11 @@ public class StatusViewHolder extends RecyclerView.ViewHolder implements OnClick
final int userColor = UserColorNicknameUtils.getUserColor(context, status.user_id);
profileImageView.setBorderColor(userColor);
profileImageView.setStyle(profileImageStyle);
loader.displayProfileImage(profileImageView, status.user_profile_image_url);
mediaPreviewContainer.setStyle(mediaPreviewStyle);
mediaPreviewContainer.displayMedia(media, loader, status.account_id, null, handler);
if (translation != null) {
textView.setText(translation.getText());
@ -254,10 +252,12 @@ public class StatusViewHolder extends RecyclerView.ViewHolder implements OnClick
final int userColor = UserColorNicknameUtils.getUserColor(context, user_id);
profileImageView.setBorderColor(userColor);
profileImageView.setStyle(adapter.getProfileImageStyle());
loader.displayProfileImage(profileImageView, user_profile_image_url);
final String text_unescaped = cursor.getString(indices.text_unescaped);
mediaPreviewContainer.setStyle(adapter.getMediaPreviewStyle());
mediaPreviewContainer.displayMedia(media, loader, account_id, null,
adapter.getImageLoadingHandler());
textView.setText(text_unescaped);

View File

@ -0,0 +1,60 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 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 twitter4j;
import java.io.Serializable;
/**
* Created by mariotaku on 14/12/31.
*/
public interface CardEntity extends Serializable {
String getName();
User[] gerUsers();
BindingValue getBindingValue(String key);
BindingValue[] getBindingValues();
public interface BindingValue extends Serializable {
String getName();
String getType();
}
public interface UserValue extends BindingValue {
long getUserId();
}
public interface StringValue extends BindingValue {
String getValue();
}
public interface ImageValue extends BindingValue {
int getWidth();
int getHeight();
String getUrl();
}
}

View File

@ -168,4 +168,6 @@ public interface Status extends Comparable<Status>, TwitterResponse, ExtendedEnt
*/
boolean isTruncated();
CardEntity getCard();
}

View File

@ -16,8 +16,15 @@
package twitter4j;
import static twitter4j.http.HttpResponseCode.ENHANCE_YOUR_CLAIM;
import static twitter4j.http.HttpResponseCode.SERVICE_UNAVAILABLE;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import twitter4j.auth.AccessToken;
import twitter4j.auth.Authorization;
import twitter4j.auth.AuthorizationFactory;
@ -36,485 +43,486 @@ import twitter4j.http.HttpResponseListener;
import twitter4j.internal.json.InternalJSONFactory;
import twitter4j.internal.json.InternalJSONFactoryImpl;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import static twitter4j.http.HttpResponseCode.ENHANCE_YOUR_CLAIM;
import static twitter4j.http.HttpResponseCode.SERVICE_UNAVAILABLE;
/**
* Base class of Twitter / AsyncTwitter / TwitterStream supports OAuth.
*
*
* @author Yusuke Yamamoto - yusuke at mac.com
*/
abstract class TwitterBaseImpl implements OAuthSupport, HttpResponseListener, TwitterConstants {
protected Configuration conf;
protected transient String screenName = null;
protected transient long id = 0;
protected Configuration conf;
protected transient String screenName = null;
protected transient long id = 0;
protected transient HttpClientWrapper http;
private List<RateLimitStatusListener> rateLimitStatusListeners = new ArrayList<RateLimitStatusListener>(0);
protected transient HttpClientWrapper http;
private List<RateLimitStatusListener> rateLimitStatusListeners = new ArrayList<RateLimitStatusListener>(0);
protected InternalJSONFactory factory;
protected InternalJSONFactory factory;
protected Authorization auth;
protected Authorization auth;
/* package */TwitterBaseImpl(final Configuration conf, final Authorization auth) {
this.conf = conf;
this.auth = auth;
init();
}
/* package */TwitterBaseImpl(final Configuration conf, final Authorization auth) {
this.conf = conf;
this.auth = auth;
init();
}
/**
* {@inheritDoc}
*/
public void addRateLimitStatusListener(final RateLimitStatusListener listener) {
rateLimitStatusListeners.add(listener);
}
/**
* {@inheritDoc}
*/
public void addRateLimitStatusListener(final RateLimitStatusListener listener) {
rateLimitStatusListeners.add(listener);
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (!(o instanceof TwitterBaseImpl)) return false;
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (!(o instanceof TwitterBaseImpl)) return false;
final TwitterBaseImpl that = (TwitterBaseImpl) o;
final TwitterBaseImpl that = (TwitterBaseImpl) o;
if (auth != null ? !auth.equals(that.auth) : that.auth != null) return false;
if (!conf.equals(that.conf)) return false;
if (http != null ? !http.equals(that.http) : that.http != null) return false;
if (!rateLimitStatusListeners.equals(that.rateLimitStatusListeners)) return false;
if (auth != null ? !auth.equals(that.auth) : that.auth != null) return false;
if (!conf.equals(that.conf)) return false;
if (http != null ? !http.equals(that.http) : that.http != null) return false;
if (!rateLimitStatusListeners.equals(that.rateLimitStatusListeners)) return false;
return true;
}
return true;
}
/**
* {@inheritDoc}
*/
public final Authorization getAuthorization() {
return auth;
}
/**
* {@inheritDoc}
*/
public final Authorization getAuthorization() {
return auth;
}
/**
* {@inheritDoc}
*/
public Configuration getConfiguration() {
return conf;
}
/**
* {@inheritDoc}
*/
public Configuration getConfiguration() {
return conf;
}
/**
* {@inheritDoc}
*/
public long getId() throws TwitterException, IllegalStateException {
if (!auth.isEnabled())
throw new IllegalStateException(
"Neither user ID/password combination nor OAuth consumer key/secret combination supplied");
if (0 == id) {
fillInIDAndScreenName();
}
// retrieve the screen name if this instance is authenticated with OAuth
// or email address
return id;
}
/**
* {@inheritDoc}
*/
public long getId() throws TwitterException, IllegalStateException {
if (!auth.isEnabled())
throw new IllegalStateException(
"Neither user ID/password combination nor OAuth consumer key/secret combination supplied");
if (0 == id) {
fillInIDAndScreenName();
}
// retrieve the screen name if this instance is authenticated with OAuth
// or email address
return id;
}
/**
* {@inheritDoc} Basic authenticated instance of this class will try
* acquiring an AccessToken using xAuth.<br>
* In order to get access acquire AccessToken using xAuth, you must apply by
* sending an email to <a href="mailto:api@twitter.com">api@twitter.com</a>
* all other applications will receive an HTTP 401 error. Web-based
* applications will not be granted access, except on a temporary basis for
* when they are converting from basic-authentication support to full OAuth
* support.<br>
* Storage of Twitter usernames and passwords is forbidden. By using xAuth,
* you are required to store only access tokens and access token secrets. If
* the access token expires or is expunged by a user, you must ask for their
* login and password again before exchanging the credentials for an access
* token.
*
* @throws TwitterException When Twitter service or network is unavailable,
* when the user has not authorized, or when the client
* application is not permitted to use xAuth
* @see <a href="https://dev.twitter.com/docs/oauth/xauth">xAuth | Twitter
* Developers</a>
*/
@Override
public synchronized AccessToken getOAuthAccessToken() throws TwitterException {
Authorization auth = getAuthorization();
AccessToken oauthAccessToken;
if (auth instanceof BasicAuthorization) {
final BasicAuthorization basicAuth = (BasicAuthorization) auth;
auth = AuthorizationFactory.getInstance(conf);
if (auth instanceof OAuthAuthorization) {
this.auth = auth;
final OAuthAuthorization oauthAuth = (OAuthAuthorization) auth;
oauthAccessToken = oauthAuth.getOAuthAccessToken(basicAuth.getUserId(), basicAuth.getPassword());
} else
throw new IllegalStateException("consumer key / secret combination not supplied.");
} else {
if (auth instanceof XAuthAuthorization) {
final XAuthAuthorization xauth = (XAuthAuthorization) auth;
this.auth = xauth;
final OAuthAuthorization oauthAuth = new OAuthAuthorization(conf);
oauthAuth.setOAuthConsumer(xauth.getConsumerKey(), xauth.getConsumerSecret());
oauthAccessToken = oauthAuth.getOAuthAccessToken(xauth.getUserId(), xauth.getPassword());
} else {
oauthAccessToken = getOAuth().getOAuthAccessToken();
}
}
screenName = oauthAccessToken.getScreenName();
id = oauthAccessToken.getUserId();
return oauthAccessToken;
}
/**
* {@inheritDoc} Basic authenticated instance of this class will try
* acquiring an AccessToken using xAuth.<br>
* In order to get access acquire AccessToken using xAuth, you must apply by
* sending an email to <a href="mailto:api@twitter.com">api@twitter.com</a>
* all other applications will receive an HTTP 401 error. Web-based
* applications will not be granted access, except on a temporary basis for
* when they are converting from basic-authentication support to full OAuth
* support.<br>
* Storage of Twitter usernames and passwords is forbidden. By using xAuth,
* you are required to store only access tokens and access token secrets. If
* the access token expires or is expunged by a user, you must ask for their
* login and password again before exchanging the credentials for an access
* token.
*
* @throws TwitterException When Twitter service or network is unavailable,
* when the user has not authorized, or when the client
* application is not permitted to use xAuth
* @see <a href="https://dev.twitter.com/docs/oauth/xauth">xAuth | Twitter
* Developers</a>
*/
@Override
public synchronized AccessToken getOAuthAccessToken() throws TwitterException {
Authorization auth = getAuthorization();
AccessToken oauthAccessToken;
if (auth instanceof BasicAuthorization) {
final BasicAuthorization basicAuth = (BasicAuthorization) auth;
auth = AuthorizationFactory.getInstance(conf);
if (auth instanceof OAuthAuthorization) {
this.auth = auth;
final OAuthAuthorization oauthAuth = (OAuthAuthorization) auth;
oauthAccessToken = oauthAuth.getOAuthAccessToken(basicAuth.getUserId(), basicAuth.getPassword());
} else
throw new IllegalStateException("consumer key / secret combination not supplied.");
} else {
if (auth instanceof XAuthAuthorization) {
final XAuthAuthorization xauth = (XAuthAuthorization) auth;
this.auth = xauth;
final OAuthAuthorization oauthAuth = new OAuthAuthorization(conf);
oauthAuth.setOAuthConsumer(xauth.getConsumerKey(), xauth.getConsumerSecret());
oauthAccessToken = oauthAuth.getOAuthAccessToken(xauth.getUserId(), xauth.getPassword());
} else {
oauthAccessToken = getOAuth().getOAuthAccessToken();
}
}
screenName = oauthAccessToken.getScreenName();
id = oauthAccessToken.getUserId();
return oauthAccessToken;
}
/**
* {@inheritDoc}
*
* @throws IllegalStateException when AccessToken has already been retrieved
* or set
*/
@Override
public synchronized AccessToken getOAuthAccessToken(final RequestToken requestToken) throws TwitterException {
final OAuthSupport oauth = getOAuth();
final AccessToken oauthAccessToken = oauth.getOAuthAccessToken(requestToken);
screenName = oauthAccessToken.getScreenName();
return oauthAccessToken;
}
/**
* {@inheritDoc}
*
* @throws IllegalStateException when AccessToken has already been retrieved
* or set
*/
@Override
public synchronized AccessToken getOAuthAccessToken(final RequestToken requestToken) throws TwitterException {
final OAuthSupport oauth = getOAuth();
final AccessToken oauthAccessToken = oauth.getOAuthAccessToken(requestToken);
screenName = oauthAccessToken.getScreenName();
return oauthAccessToken;
}
/**
* {@inheritDoc}
*
* @throws IllegalStateException when AccessToken has already been retrieved
* or set
*/
@Override
public synchronized AccessToken getOAuthAccessToken(final RequestToken requestToken, final String oauthVerifier)
throws TwitterException {
return getOAuth().getOAuthAccessToken(requestToken, oauthVerifier);
}
/**
* {@inheritDoc}
*
* @throws IllegalStateException when AccessToken has already been retrieved
* or set
*/
@Override
public synchronized AccessToken getOAuthAccessToken(final RequestToken requestToken, final String oauthVerifier)
throws TwitterException {
return getOAuth().getOAuthAccessToken(requestToken, oauthVerifier);
}
/**
* {@inheritDoc}
*
* @throws IllegalStateException when AccessToken has already been retrieved
* or set
*/
@Override
public synchronized AccessToken getOAuthAccessToken(final String oauthVerifier) throws TwitterException {
final AccessToken oauthAccessToken = getOAuth().getOAuthAccessToken(oauthVerifier);
screenName = oauthAccessToken.getScreenName();
return oauthAccessToken;
}
/**
* {@inheritDoc}
*
* @throws IllegalStateException when AccessToken has already been retrieved
* or set
*/
@Override
public synchronized AccessToken getOAuthAccessToken(final String oauthVerifier) throws TwitterException {
final AccessToken oauthAccessToken = getOAuth().getOAuthAccessToken(oauthVerifier);
screenName = oauthAccessToken.getScreenName();
return oauthAccessToken;
}
/**
* {@inheritDoc}
*/
@Override
public synchronized AccessToken getOAuthAccessToken(final String screenName, final String password)
throws TwitterException {
return getOAuth().getOAuthAccessToken(screenName, password);
}
/**
* {@inheritDoc}
*/
@Override
public synchronized AccessToken getOAuthAccessToken(final String screenName, final String password)
throws TwitterException {
return getOAuth().getOAuthAccessToken(screenName, password);
}
/* OAuth support methods */
/**
* {@inheritDoc}
*/
@Override
public RequestToken getOAuthRequestToken() throws TwitterException {
return getOAuthRequestToken(null);
}
/**
* {@inheritDoc}
*/
@Override
public RequestToken getOAuthRequestToken() throws TwitterException {
return getOAuthRequestToken(null);
}
/**
* {@inheritDoc}
*/
@Override
public RequestToken getOAuthRequestToken(final String callbackUrl) throws TwitterException {
return getOAuth().getOAuthRequestToken(callbackUrl);
}
/**
* {@inheritDoc}
*/
@Override
public RequestToken getOAuthRequestToken(final String callbackUrl) throws TwitterException {
return getOAuth().getOAuthRequestToken(callbackUrl);
}
/**
* {@inheritDoc}
*/
@Override
public RequestToken getOAuthRequestToken(final String callbackUrl, final String xAuthAccessType)
throws TwitterException {
return getOAuth().getOAuthRequestToken(callbackUrl, xAuthAccessType);
}
/**
* {@inheritDoc}
*/
@Override
public RequestToken getOAuthRequestToken(final String callbackUrl, final String xAuthAccessType)
throws TwitterException {
return getOAuth().getOAuthRequestToken(callbackUrl, xAuthAccessType);
}
/**
* {@inheritDoc}
*/
public String getScreenName() throws TwitterException, IllegalStateException {
if (!auth.isEnabled())
throw new IllegalStateException(
"Neither user ID/password combination nor OAuth consumer key/secret combination supplied");
if (null == screenName) {
if (auth instanceof BasicAuthorization) {
screenName = ((BasicAuthorization) auth).getUserId();
if (-1 != screenName.indexOf("@")) {
screenName = null;
}
}
if (null == screenName) {
// retrieve the screen name if this instance is authenticated
// with OAuth or email address
fillInIDAndScreenName();
}
}
return screenName;
}
/**
* {@inheritDoc}
*/
public String getScreenName() throws TwitterException, IllegalStateException {
if (!auth.isEnabled())
throw new IllegalStateException(
"Neither user ID/password combination nor OAuth consumer key/secret combination supplied");
if (null == screenName) {
if (auth instanceof BasicAuthorization) {
screenName = ((BasicAuthorization) auth).getUserId();
if (-1 != screenName.indexOf("@")) {
screenName = null;
}
}
if (null == screenName) {
// retrieve the screen name if this instance is authenticated
// with OAuth or email address
fillInIDAndScreenName();
}
}
return screenName;
}
// methods declared in OAuthSupport interface
// methods declared in OAuthSupport interface
@Override
public int hashCode() {
int result = conf.hashCode();
result = 31 * result + (http != null ? http.hashCode() : 0);
result = 31 * result + rateLimitStatusListeners.hashCode();
result = 31 * result + (auth != null ? auth.hashCode() : 0);
return result;
}
@Override
public int hashCode() {
int result = conf.hashCode();
result = 31 * result + (http != null ? http.hashCode() : 0);
result = 31 * result + rateLimitStatusListeners.hashCode();
result = 31 * result + (auth != null ? auth.hashCode() : 0);
return result;
}
@Override
public void httpResponseReceived(final HttpResponseEvent event) {
if (rateLimitStatusListeners.size() != 0) {
final HttpResponse res = event.getResponse();
final TwitterException te = event.getTwitterException();
RateLimitStatus rateLimitStatus;
int statusCode;
if (te != null) {
rateLimitStatus = te.getRateLimitStatus();
statusCode = te.getStatusCode();
} else {
rateLimitStatus = InternalJSONFactoryImpl.createRateLimitStatusFromResponseHeader(res);
statusCode = res.getStatusCode();
}
if (rateLimitStatus != null) {
final RateLimitStatusEvent statusEvent = new RateLimitStatusEvent(this, rateLimitStatus,
event.isAuthenticated());
if (statusCode == ENHANCE_YOUR_CLAIM || statusCode == SERVICE_UNAVAILABLE) {
// EXCEEDED_RATE_LIMIT_QUOTA is returned by Rest API
// SERVICE_UNAVAILABLE is returned by Search API
for (final RateLimitStatusListener listener : rateLimitStatusListeners) {
listener.onRateLimitStatus(statusEvent);
listener.onRateLimitReached(statusEvent);
}
} else {
for (final RateLimitStatusListener listener : rateLimitStatusListeners) {
listener.onRateLimitStatus(statusEvent);
}
}
}
}
}
@Override
public void httpResponseReceived(final HttpResponseEvent event) {
if (rateLimitStatusListeners.size() != 0) {
final HttpResponse res = event.getResponse();
final TwitterException te = event.getTwitterException();
RateLimitStatus rateLimitStatus;
int statusCode;
if (te != null) {
rateLimitStatus = te.getRateLimitStatus();
statusCode = te.getStatusCode();
} else {
rateLimitStatus = InternalJSONFactoryImpl.createRateLimitStatusFromResponseHeader(res);
statusCode = res.getStatusCode();
}
if (rateLimitStatus != null) {
final RateLimitStatusEvent statusEvent = new RateLimitStatusEvent(this, rateLimitStatus,
event.isAuthenticated());
if (statusCode == ENHANCE_YOUR_CLAIM || statusCode == SERVICE_UNAVAILABLE) {
// EXCEEDED_RATE_LIMIT_QUOTA is returned by Rest API
// SERVICE_UNAVAILABLE is returned by Search API
for (final RateLimitStatusListener listener : rateLimitStatusListeners) {
listener.onRateLimitStatus(statusEvent);
listener.onRateLimitReached(statusEvent);
}
} else {
for (final RateLimitStatusListener listener : rateLimitStatusListeners) {
listener.onRateLimitStatus(statusEvent);
}
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void setOAuthAccessToken(final AccessToken accessToken) {
getOAuth().setOAuthAccessToken(accessToken);
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void setOAuthAccessToken(final AccessToken accessToken) {
getOAuth().setOAuthAccessToken(accessToken);
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void setOAuthConsumer(final String consumerKey, final String consumerSecret) {
if (null == consumerKey) throw new NullPointerException("consumer key is null");
if (null == consumerSecret) throw new NullPointerException("consumer secret is null");
if (auth instanceof NullAuthorization) {
final OAuthAuthorization oauth = new OAuthAuthorization(conf);
oauth.setOAuthConsumer(consumerKey, consumerSecret);
auth = oauth;
} else if (auth instanceof BasicAuthorization) {
final XAuthAuthorization xauth = new XAuthAuthorization((BasicAuthorization) auth);
xauth.setOAuthConsumer(consumerKey, consumerSecret);
auth = xauth;
} else if (auth instanceof OAuthAuthorization)
throw new IllegalStateException("consumer key/secret pair already set.");
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void setOAuthConsumer(final String consumerKey, final String consumerSecret) {
if (null == consumerKey) throw new NullPointerException("consumer key is null");
if (null == consumerSecret) throw new NullPointerException("consumer secret is null");
if (auth instanceof NullAuthorization) {
final OAuthAuthorization oauth = new OAuthAuthorization(conf);
oauth.setOAuthConsumer(consumerKey, consumerSecret);
auth = oauth;
} else if (auth instanceof BasicAuthorization) {
final XAuthAuthorization xauth = new XAuthAuthorization((BasicAuthorization) auth);
xauth.setOAuthConsumer(consumerKey, consumerSecret);
auth = xauth;
} else if (auth instanceof OAuthAuthorization)
throw new IllegalStateException("consumer key/secret pair already set.");
}
/**
* {@inheritDoc}
*/
public void shutdown() {
if (http != null) {
http.shutdown();
}
}
/**
* {@inheritDoc}
*/
public void shutdown() {
if (http != null) {
http.shutdown();
}
}
@Override
public String toString() {
return "TwitterBase{" + "conf=" + conf + ", http=" + http + ", rateLimitStatusListeners="
+ rateLimitStatusListeners + ", auth=" + auth + '}';
}
@Override
public String toString() {
return "TwitterBase{" + "conf=" + conf + ", http=" + http + ", rateLimitStatusListeners="
+ rateLimitStatusListeners + ", auth=" + auth + '}';
}
protected void addParameterToList(final List<HttpParameter> params, final String paramName, final Boolean param) {
if (param != null) {
params.add(new HttpParameter(paramName, param));
}
}
protected void addParameterToList(final List<HttpParameter> params, final String paramName, final Boolean param) {
if (param != null) {
params.add(new HttpParameter(paramName, param));
}
}
protected void addParameterToList(final List<HttpParameter> params, final String paramName, final Double param) {
if (param != null) {
params.add(new HttpParameter(paramName, param));
}
}
protected void addParameterToList(final List<HttpParameter> params, final String paramName, final Double param) {
if (param != null) {
params.add(new HttpParameter(paramName, param));
}
}
protected void addParameterToList(final List<HttpParameter> params, final String paramName, final Integer param) {
if (param != null) {
params.add(new HttpParameter(paramName, param));
}
}
protected void addParameterToList(final List<HttpParameter> params, final String paramName, final Integer param) {
if (param != null) {
params.add(new HttpParameter(paramName, param));
}
}
protected void addParameterToList(final List<HttpParameter> params, final String paramName, final String param) {
if (param != null) {
params.add(new HttpParameter(paramName, param));
}
}
protected void addParameterToList(final List<HttpParameter> params, final String paramName, final String param) {
if (param != null) {
params.add(new HttpParameter(paramName, param));
}
}
/**
* Check the existence, and the type of the specified file.
*
* @param image image to be uploaded
* @throws TwitterException when the specified file is not found
* (FileNotFoundException will be nested) , or when the
* specified file object is not representing a file(IOException
* will be nested).
*/
protected void checkFileValidity(final File image) throws TwitterException {
if (!image.exists()) // noinspection ThrowableInstanceNeverThrown
throw new TwitterException(new FileNotFoundException(image + " is not found."));
if (!image.isFile()) // noinspection ThrowableInstanceNeverThrown
throw new TwitterException(new IOException(image + " is not a file."));
}
/**
* Check the existence, and the type of the specified file.
*
* @param image image to be uploaded
* @throws TwitterException when the specified file is not found
* (FileNotFoundException will be nested) , or when the
* specified file object is not representing a file(IOException
* will be nested).
*/
protected void checkFileValidity(final File image) throws TwitterException {
if (!image.exists()) // noinspection ThrowableInstanceNeverThrown
throw new TwitterException(new FileNotFoundException(image + " is not found."));
if (!image.isFile()) // noinspection ThrowableInstanceNeverThrown
throw new TwitterException(new IOException(image + " is not a file."));
}
protected final void ensureAuthorizationEnabled() {
if (!auth.isEnabled())
throw new IllegalStateException(
"Authentication credentials are missing. See http://twitter4j.org/configuration.html for the detail.");
}
protected final void ensureAuthorizationEnabled() {
if (!auth.isEnabled())
throw new IllegalStateException(
"Authentication credentials are missing. See http://twitter4j.org/configuration.html for the detail.");
}
protected final void ensureOAuthEnabled() {
if (!(auth instanceof OAuthAuthorization))
throw new IllegalStateException(
"OAuth required. Authentication credentials are missing. See http://twitter4j.org/configuration.html for the detail.");
}
protected final void ensureOAuthEnabled() {
if (!(auth instanceof OAuthAuthorization))
throw new IllegalStateException(
"OAuth required. Authentication credentials are missing. See http://twitter4j.org/configuration.html for the detail.");
}
protected User fillInIDAndScreenName() throws TwitterException {
ensureAuthorizationEnabled();
final HttpParameter[] params = { new HttpParameter("include_entities", conf.isIncludeEntitiesEnabled()) };
final User user = factory.createUser(http.get(conf.getRestBaseURL() + ENDPOINT_ACCOUNT_VERIFY_CREDENTIALS,
conf.getSigningRestBaseURL() + ENDPOINT_ACCOUNT_VERIFY_CREDENTIALS, params, auth));
screenName = user.getScreenName();
id = user.getId();
return user;
}
protected User fillInIDAndScreenName() throws TwitterException {
ensureAuthorizationEnabled();
final HttpParameter[] params = {new HttpParameter("include_entities", conf.isIncludeEntitiesEnabled())};
final User user = factory.createUser(http.get(conf.getRestBaseURL() + ENDPOINT_ACCOUNT_VERIFY_CREDENTIALS,
conf.getSigningRestBaseURL() + ENDPOINT_ACCOUNT_VERIFY_CREDENTIALS, params, auth));
screenName = user.getScreenName();
id = user.getId();
return user;
}
protected HttpResponse get(final String url, final String sign_url, final HttpParameter... parameters)
throws TwitterException {
// intercept HTTP call for monitoring purposes
HttpResponse response = null;
final long start = System.currentTimeMillis();
try {
response = http.get(url, sign_url, parameters, auth);
} finally {
final long elapsedTime = System.currentTimeMillis() - start;
TwitterAPIMonitor.getInstance().methodCalled(url, elapsedTime, isOk(response));
}
return response;
}
protected HttpResponse get(final String url, final String sign_url, final HttpParameter... parameters)
throws TwitterException {
// intercept HTTP call for monitoring purposes
HttpResponse response = null;
final long start = System.currentTimeMillis();
try {
response = http.get(url, sign_url, parameters, auth);
} finally {
final long elapsedTime = System.currentTimeMillis() - start;
TwitterAPIMonitor.getInstance().methodCalled(url, elapsedTime, isOk(response));
}
return response;
}
protected boolean isOk(final HttpResponse response) {
return response != null && response.getStatusCode() < 300;
}
protected boolean isOk(final HttpResponse response) {
return response != null && response.getStatusCode() < 300;
}
protected HttpParameter[] mergeParameters(final HttpParameter[] params1, final HttpParameter... params2) {
if (params1 != null && params2 != null) {
final HttpParameter[] params = new HttpParameter[params1.length + params2.length];
System.arraycopy(params1, 0, params, 0, params1.length);
System.arraycopy(params2, 0, params, params1.length, params2.length);
return params;
}
if (null == params1 && null == params2) return new HttpParameter[0];
if (params1 != null)
return params1;
else
return params2;
}
protected HttpParameter[] mergeParameters(final Paging paging, final HttpParameter... params2) {
if (paging == null) {
return params2;
}
return mergeParameters(paging.asPostParameterArray(), params2);
}
protected HttpResponse post(final String url, final String sign_url, final HttpParameter... parameters)
throws TwitterException {
// intercept HTTP call for monitoring purposes
HttpResponse response = null;
final long start = System.currentTimeMillis();
try {
response = http.post(url, sign_url, parameters, auth);
} finally {
final long elapsedTime = System.currentTimeMillis() - start;
TwitterAPIMonitor.getInstance().methodCalled(url, elapsedTime, isOk(response));
}
return response;
}
protected HttpParameter[] mergeParameters(final HttpParameter[] params1, final HttpParameter... params2) {
if (params1 != null && params2 != null) {
final HttpParameter[] params = new HttpParameter[params1.length + params2.length];
System.arraycopy(params1, 0, params, 0, params1.length);
System.arraycopy(params2, 0, params, params1.length, params2.length);
return params;
}
if (null == params1 && null == params2) return new HttpParameter[0];
if (params1 != null)
return params1;
else
return params2;
}
protected void setFactory() {
factory = new InternalJSONFactoryImpl(conf);
}
protected HttpResponse post(final String url, final String sign_url, final HttpParameter... parameters)
throws TwitterException {
// intercept HTTP call for monitoring purposes
HttpResponse response = null;
final long start = System.currentTimeMillis();
try {
response = http.post(url, sign_url, parameters, auth);
} finally {
final long elapsedTime = System.currentTimeMillis() - start;
TwitterAPIMonitor.getInstance().methodCalled(url, elapsedTime, isOk(response));
}
return response;
}
private OAuthSupport getOAuth() {
if (!(auth instanceof OAuthSupport))
throw new IllegalStateException("OAuth consumer key/secret combination not supplied");
return (OAuthSupport) auth;
}
protected void setFactory() {
factory = new InternalJSONFactoryImpl(conf);
}
private void init() {
if (null == auth) {
// try to populate OAuthAuthorization if available in the
// configuration
final String consumerKey = conf.getOAuthConsumerKey();
final String consumerSecret = conf.getOAuthConsumerSecret();
// try to find oauth tokens in the configuration
if (consumerKey != null && consumerSecret != null) {
final OAuthAuthorization oauth = new OAuthAuthorization(conf);
final String accessToken = conf.getOAuthAccessToken();
final String accessTokenSecret = conf.getOAuthAccessTokenSecret();
if (accessToken != null && accessTokenSecret != null) {
oauth.setOAuthAccessToken(new AccessToken(accessToken, accessTokenSecret));
}
auth = oauth;
} else {
auth = NullAuthorization.getInstance();
}
}
http = new HttpClientWrapper(conf);
http.setHttpResponseListener(this);
setFactory();
}
private OAuthSupport getOAuth() {
if (!(auth instanceof OAuthSupport))
throw new IllegalStateException("OAuth consumer key/secret combination not supplied");
return (OAuthSupport) auth;
}
@SuppressWarnings("unchecked")
private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException {
conf = (Configuration) stream.readObject();
auth = (Authorization) stream.readObject();
rateLimitStatusListeners = (List<RateLimitStatusListener>) stream.readObject();
http = new HttpClientWrapper(conf);
http.setHttpResponseListener(this);
setFactory();
}
private void init() {
if (null == auth) {
// try to populate OAuthAuthorization if available in the
// configuration
final String consumerKey = conf.getOAuthConsumerKey();
final String consumerSecret = conf.getOAuthConsumerSecret();
// try to find oauth tokens in the configuration
if (consumerKey != null && consumerSecret != null) {
final OAuthAuthorization oauth = new OAuthAuthorization(conf);
final String accessToken = conf.getOAuthAccessToken();
final String accessTokenSecret = conf.getOAuthAccessTokenSecret();
if (accessToken != null && accessTokenSecret != null) {
oauth.setOAuthAccessToken(new AccessToken(accessToken, accessTokenSecret));
}
auth = oauth;
} else {
auth = NullAuthorization.getInstance();
}
}
http = new HttpClientWrapper(conf);
http.setHttpResponseListener(this);
setFactory();
}
private void writeObject(final ObjectOutputStream out) throws IOException {
out.writeObject(conf);
out.writeObject(auth);
final List<RateLimitStatusListener> serializableRateLimitStatusListeners = new ArrayList<RateLimitStatusListener>(
0);
for (final RateLimitStatusListener listener : rateLimitStatusListeners) {
if (listener instanceof Serializable) {
serializableRateLimitStatusListeners.add(listener);
}
}
out.writeObject(serializableRateLimitStatusListeners);
}
@SuppressWarnings("unchecked")
private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException {
conf = (Configuration) stream.readObject();
auth = (Authorization) stream.readObject();
rateLimitStatusListeners = (List<RateLimitStatusListener>) stream.readObject();
http = new HttpClientWrapper(conf);
http.setHttpResponseListener(this);
setFactory();
}
private void writeObject(final ObjectOutputStream out) throws IOException {
out.writeObject(conf);
out.writeObject(auth);
final List<RateLimitStatusListener> serializableRateLimitStatusListeners = new ArrayList<RateLimitStatusListener>(
0);
for (final RateLimitStatusListener listener : rateLimitStatusListeners) {
if (listener instanceof Serializable) {
serializableRateLimitStatusListeners.add(listener);
}
}
out.writeObject(serializableRateLimitStatusListeners);
}
}

View File

@ -46,14 +46,12 @@ import static twitter4j.http.HttpParameter.getParameterArray;
final class TwitterImpl extends TwitterBaseImpl implements Twitter {
private final HttpParameter INCLUDE_ENTITIES;
private final HttpParameter INCLUDE_RTS;
private final HttpParameter INCLUDE_MY_RETWEET;
private final HttpParameter INCLUDE_REPLY_COUNT;
private final HttpParameter INCLUDE_DESCENDENT_REPLY_COUNT;
private final HttpParameter INCLUDE_CARDS;
private final HttpParameter CARDS_PLATFORM;
/* package */
TwitterImpl(final Configuration conf, final Authorization auth) {
@ -63,6 +61,8 @@ final class TwitterImpl extends TwitterBaseImpl implements Twitter {
INCLUDE_MY_RETWEET = new HttpParameter("include_my_retweet", 1);
INCLUDE_REPLY_COUNT = new HttpParameter("include_reply_count", conf.isIncludeReplyCountEnabled());
INCLUDE_DESCENDENT_REPLY_COUNT = new HttpParameter("include_descendent_reply_count", conf.isIncludeDescendentReplyCountEnabled());
INCLUDE_CARDS = new HttpParameter("include_cards", conf.isIncludeCardsEnabled());
CARDS_PLATFORM = new HttpParameter("cards_platform", conf.getCardsPlatform());
}
@Override
@ -485,15 +485,16 @@ final class TwitterImpl extends TwitterBaseImpl implements Twitter {
ensureAuthorizationEnabled();
return factory.createDirectMessageList(get(conf.getRestBaseURL() + ENDPOINT_DIRECT_MESSAGES,
conf.getSigningRestBaseURL() + ENDPOINT_DIRECT_MESSAGES,
mergeParameters(paging != null ? paging.asPostParameterArray() : null, INCLUDE_ENTITIES)));
mergeParameters(paging, INCLUDE_ENTITIES)));
}
@Override
public ResponseList<Status> getFavorites() throws TwitterException {
ensureAuthorizationEnabled();
return factory.createStatusList(get(conf.getRestBaseURL() + ENDPOINT_FAVORITES_LIST,
conf.getSigningRestBaseURL() + ENDPOINT_FAVORITES_LIST, INCLUDE_ENTITIES,
INCLUDE_REPLY_COUNT, INCLUDE_DESCENDENT_REPLY_COUNT));
final String url = conf.getRestBaseURL() + ENDPOINT_FAVORITES_LIST;
final String signUrl = conf.getSigningRestBaseURL() + ENDPOINT_FAVORITES_LIST;
return factory.createStatusList(get(url, signUrl, INCLUDE_ENTITIES, INCLUDE_REPLY_COUNT,
INCLUDE_DESCENDENT_REPLY_COUNT, INCLUDE_CARDS, CARDS_PLATFORM));
}
@Override
@ -502,7 +503,8 @@ final class TwitterImpl extends TwitterBaseImpl implements Twitter {
final String url = conf.getRestBaseURL() + ENDPOINT_FAVORITES_LIST;
final String signUrl = conf.getSigningRestBaseURL() + ENDPOINT_FAVORITES_LIST;
return factory.createStatusList(get(url, signUrl, new HttpParameter("user_id", userId),
INCLUDE_ENTITIES, INCLUDE_REPLY_COUNT, INCLUDE_DESCENDENT_REPLY_COUNT));
INCLUDE_ENTITIES, INCLUDE_REPLY_COUNT, INCLUDE_DESCENDENT_REPLY_COUNT,
INCLUDE_CARDS, CARDS_PLATFORM));
}
@Override
@ -510,9 +512,9 @@ final class TwitterImpl extends TwitterBaseImpl implements Twitter {
ensureAuthorizationEnabled();
final String url = conf.getRestBaseURL() + ENDPOINT_FAVORITES_LIST;
final String signUrl = conf.getSigningRestBaseURL() + ENDPOINT_FAVORITES_LIST;
return factory.createStatusList(get(url, signUrl, mergeParameters(paging.asPostParameterArray(),
return factory.createStatusList(get(url, signUrl, mergeParameters(paging,
new HttpParameter("user_id", userId), INCLUDE_ENTITIES, INCLUDE_REPLY_COUNT,
INCLUDE_DESCENDENT_REPLY_COUNT)));
INCLUDE_DESCENDENT_REPLY_COUNT, INCLUDE_CARDS, CARDS_PLATFORM)));
}
@Override
@ -520,9 +522,8 @@ final class TwitterImpl extends TwitterBaseImpl implements Twitter {
ensureAuthorizationEnabled();
final String url = conf.getRestBaseURL() + ENDPOINT_FAVORITES_LIST;
final String signUrl = conf.getSigningRestBaseURL() + ENDPOINT_FAVORITES_LIST;
return factory.createStatusList(get(url, signUrl, mergeParameters(
paging != null ? paging.asPostParameterArray() : null,
INCLUDE_ENTITIES, INCLUDE_REPLY_COUNT, INCLUDE_DESCENDENT_REPLY_COUNT)));
return factory.createStatusList(get(url, signUrl, mergeParameters(paging, INCLUDE_ENTITIES,
INCLUDE_REPLY_COUNT, INCLUDE_DESCENDENT_REPLY_COUNT, INCLUDE_CARDS, CARDS_PLATFORM)));
}
@Override
@ -531,7 +532,8 @@ final class TwitterImpl extends TwitterBaseImpl implements Twitter {
final String url = conf.getRestBaseURL() + ENDPOINT_FAVORITES_LIST;
final String signUrl = conf.getSigningRestBaseURL() + ENDPOINT_FAVORITES_LIST;
return factory.createStatusList(get(url, signUrl, new HttpParameter("screen_name", screenName),
INCLUDE_ENTITIES, INCLUDE_REPLY_COUNT, INCLUDE_DESCENDENT_REPLY_COUNT));
INCLUDE_ENTITIES, INCLUDE_REPLY_COUNT, INCLUDE_DESCENDENT_REPLY_COUNT, INCLUDE_CARDS,
CARDS_PLATFORM));
}
@Override
@ -541,7 +543,7 @@ final class TwitterImpl extends TwitterBaseImpl implements Twitter {
final String signUrl = conf.getSigningRestBaseURL() + ENDPOINT_FAVORITES_LIST;
return factory.createStatusList(get(url, signUrl, mergeParameters(paging.asPostParameterArray(),
new HttpParameter("screen_name", screenName), INCLUDE_ENTITIES, INCLUDE_REPLY_COUNT,
INCLUDE_DESCENDENT_REPLY_COUNT)));
INCLUDE_DESCENDENT_REPLY_COUNT, INCLUDE_CARDS, CARDS_PLATFORM)));
}
@Override
@ -649,6 +651,8 @@ final class TwitterImpl extends TwitterBaseImpl implements Twitter {
paramsList.add(INCLUDE_MY_RETWEET);
paramsList.add(INCLUDE_REPLY_COUNT);
paramsList.add(INCLUDE_DESCENDENT_REPLY_COUNT);
paramsList.add(INCLUDE_CARDS);
paramsList.add(CARDS_PLATFORM);
if (paging != null) {
paramsList.addAll(paging.asPostParameterList());
}
@ -761,6 +765,8 @@ final class TwitterImpl extends TwitterBaseImpl implements Twitter {
paramsList.add(INCLUDE_REPLY_COUNT);
paramsList.add(INCLUDE_DESCENDENT_REPLY_COUNT);
paramsList.add(INCLUDE_MY_RETWEET);
paramsList.add(INCLUDE_CARDS);
paramsList.add(CARDS_PLATFORM);
if (paging != null) {
paramsList.addAll(paging.asPostParameterList());
}
@ -1170,6 +1176,8 @@ final class TwitterImpl extends TwitterBaseImpl implements Twitter {
paramsList.add(INCLUDE_REPLY_COUNT);
paramsList.add(INCLUDE_DESCENDENT_REPLY_COUNT);
paramsList.add(INCLUDE_MY_RETWEET);
paramsList.add(INCLUDE_CARDS);
paramsList.add(CARDS_PLATFORM);
paramsList.add(new HttpParameter("user_id", userId));
if (paging != null) {
paramsList.addAll(paging.asPostParameterList());

View File

@ -26,6 +26,8 @@ import twitter4j.http.HttpClientWrapperConfiguration;
public interface Configuration extends HttpClientConfiguration, HttpClientWrapperConfiguration,
AuthorizationConfiguration {
String getCardsPlatform();
String getClientName();
String getClientURL();
@ -62,6 +64,8 @@ public interface Configuration extends HttpClientConfiguration, HttpClientWrappe
boolean isDebugEnabled();
boolean isIncludeCardsEnabled();
boolean isIncludeEntitiesEnabled();
boolean isIncludeRTsEnabled();

View File

@ -101,6 +101,8 @@ class ConfigurationBase implements TwitterConstants, Configuration {
Map<String, String> requestHeaders;
private static final List<ConfigurationBase> instances = new ArrayList<ConfigurationBase>();
private boolean includeCards;
private String cardsPlatform;
protected ConfigurationBase() {
setDebug(false);
@ -257,6 +259,10 @@ class ConfigurationBase implements TwitterConstants, Configuration {
return true;
}
public String getCardsPlatform() {
return cardsPlatform;
}
@Override
public final String getClientName() {
return clientName;
@ -498,6 +504,11 @@ class ConfigurationBase implements TwitterConstants, Configuration {
return debug;
}
@Override
public boolean isIncludeCardsEnabled() {
return includeCards;
}
@Override
public boolean isGZIPEnabled() {
return gzipEnabled;
@ -548,6 +559,14 @@ class ConfigurationBase implements TwitterConstants, Configuration {
return includeTwitterClientHeader;
}
public void setCardsPlatform(String cardsPlatform) {
this.cardsPlatform = cardsPlatform;
}
public void setIncludeCards(boolean includeCards) {
this.includeCards = includeCards;
}
@Override
public String toString() {
return "ConfigurationBase{debug=" + debug + ", userAgent=" + userAgent + ", user=" + user + ", password="

View File

@ -76,7 +76,8 @@ public final class ConfigurationBuilder {
configuration.setSigningUploadBaseURL(conf.getSigningUploadBaseURL());
configuration.setUser(conf.getUser());
configuration.setUseSSL(conf.isSSLEnabled());
configuration.setIncludeCards(conf.isIncludeCardsEnabled());
configuration.setCardsPlatform(conf.getCardsPlatform());
}
public Configuration build() {
@ -318,6 +319,19 @@ public final class ConfigurationBuilder {
return this;
}
public ConfigurationBuilder setIncludeCards(final boolean includeCards) {
checkNotBuilt();
configuration.setIncludeCards(includeCards);
return this;
}
public ConfigurationBuilder setCardsPlatform(final String cardsPlatform) {
checkNotBuilt();
configuration.setCardsPlatform(cardsPlatform);
return this;
}
private void checkNotBuilt() {
if (configuration == null)
throw new IllegalStateException("Cannot use this builder any longer, build() has already been called");

View File

@ -0,0 +1,220 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 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 twitter4j.internal.json;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import twitter4j.CardEntity;
import twitter4j.TwitterException;
import twitter4j.User;
import twitter4j.internal.util.InternalParseUtil;
/**
* Created by mariotaku on 14/12/31.
*/
public class CardEntityJSONImpl implements CardEntity {
private final String name;
private final Map<String, BindingValue> bindingValues;
private final User[] users;
public CardEntityJSONImpl(JSONObject json) throws JSONException, TwitterException {
this.name = json.getString("name");
this.bindingValues = BindingValueImpl.valuesOf(json.getJSONObject("binding_values"));
if (!json.isNull("users")) {
final JSONObject usersJSON = json.getJSONObject("users");
final Iterator<String> keys = usersJSON.keys();
this.users = new UserJSONImpl[usersJSON.length()];
int idx = 0;
while (keys.hasNext()) {
users[idx++] = new UserJSONImpl(usersJSON.getJSONObject(keys.next()));
}
} else {
users = null;
}
}
@Override
public String getName() {
return name;
}
@Override
public User[] gerUsers() {
return users;
}
@Override
public BindingValue getBindingValue(String name) {
return bindingValues.get(name);
}
@Override
public BindingValue[] getBindingValues() {
return bindingValues.values().toArray(new BindingValue[bindingValues.size()]);
}
public static abstract class BindingValueImpl implements BindingValue {
protected final String name, type;
BindingValueImpl(String name, JSONObject json) throws JSONException {
this.name = name;
this.type = json.getString("type");
}
@Override
public final String getName() {
return name;
}
@Override
public final String getType() {
return type;
}
private static BindingValue valueOf(String name, JSONObject json) throws JSONException {
final String type = json.optString("type");
if ("STRING".equals(type)) {
return new StringValueImpl(name, json);
} else if ("IMAGE".equals(type)) {
return new ImageValueImpl(name, json);
} else if ("USER".equals(type)) {
return new UserValueImpl(name, json);
}
throw new UnsupportedOperationException(String.format("Unsupported type %s", type));
}
private static HashMap<String, BindingValue> valuesOf(JSONObject json) throws JSONException {
final Iterator<String> keys = json.keys();
final HashMap<String, BindingValue> values = new HashMap<>();
while (keys.hasNext()) {
String key = keys.next();
values.put(key, valueOf(key, json.getJSONObject(key)));
}
return values;
}
}
private static final class ImageValueImpl extends BindingValueImpl implements ImageValue {
private final int width, height;
private final String url;
ImageValueImpl(String name, JSONObject json) throws JSONException {
super(name, json);
final JSONObject imageValue = json.getJSONObject("image_value");
this.width = imageValue.getInt("width");
this.height = imageValue.getInt("height");
this.url = imageValue.getString("url");
}
@Override
public int getWidth() {
return width;
}
@Override
public int getHeight() {
return height;
}
@Override
public String getUrl() {
return url;
}
@Override
public String toString() {
return "ImageValueImpl{" +
"name='" + name + '\'' +
"width=" + width +
", height=" + height +
", url='" + url + '\'' +
'}';
}
}
private static final class StringValueImpl extends BindingValueImpl implements StringValue {
private final String value;
StringValueImpl(String name, JSONObject json) throws JSONException {
super(name, json);
this.value = json.getString("string_value");
}
@Override
public String getValue() {
return value;
}
@Override
public String toString() {
return "StringValueImpl{" +
"name='" + name + '\'' +
"value='" + value + '\'' +
'}';
}
}
private static final class UserValueImpl extends BindingValueImpl implements UserValue {
private final long userId;
UserValueImpl(String name, JSONObject json) throws JSONException {
super(name, json);
final JSONObject userValue = json.getJSONObject("user_value");
this.userId = InternalParseUtil.getLong("id_str", userValue);
}
@Override
public long getUserId() {
return userId;
}
@Override
public String toString() {
return "UserValueImpl{" +
"name='" + name + '\'' +
"userId='" + userId + '\'' +
'}';
}
}
@Override
public String toString() {
return "CardEntityJSONImpl{" +
"name='" + name + '\'' +
", bindingValues=" + bindingValues +
", users=" + Arrays.toString(users) +
'}';
}
}

View File

@ -23,6 +23,7 @@ import org.json.JSONObject;
import java.util.Arrays;
import java.util.Date;
import twitter4j.CardEntity;
import twitter4j.GeoLocation;
import twitter4j.HashtagEntity;
import twitter4j.MediaEntity;
@ -72,6 +73,7 @@ final class StatusJSONImpl extends TwitterResponseImpl implements Status {
private Place place = null;
private long retweetCount;
private long favoriteCount;
private CardEntity cardEntity;
public long getReplyCount() {
return replyCount;
@ -332,6 +334,11 @@ final class StatusJSONImpl extends TwitterResponseImpl implements Status {
return isTruncated;
}
@Override
public CardEntity getCard() {
return cardEntity;
}
@Override
public String toString() {
return "StatusJSONImpl{createdAt=" + createdAt + ", id=" + id + ", text=" + text + ", rawText=" + rawText
@ -470,6 +477,13 @@ final class StatusJSONImpl extends TwitterResponseImpl implements Status {
logger.warn("failed to parse current_user_retweet:" + json);
}
}
if (!json.isNull("card")) {
try {
cardEntity = new CardEntityJSONImpl(json.getJSONObject("card"));
} catch (JSONException jsone) {
throw new TwitterException(jsone);
}
}
}
/* package */

View File

@ -21,8 +21,10 @@
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
>
<android.support.v7.widget.CardView
android:id="@+id/card"
@ -168,6 +170,12 @@
android:textAppearance="?android:attr/textAppearanceMedium"
android:textIsSelectable="true"/>
<FrameLayout
android:id="@+id/twitter_card"
android:layout_width="match_parent"
android:minHeight="200dp"
android:layout_height="wrap_content"></FrameLayout>
<RelativeLayout
android:id="@+id/location_container"
android:layout_width="match_parent"
@ -236,7 +244,8 @@
android:layout_marginRight="@dimen/element_spacing_small"
android:singleLine="true"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorPrimary"/>
android:textColor="?android:textColorPrimary"
tools:text="255"/>
<org.mariotaku.twidere.view.themed.ThemedTextView
android:layout_width="wrap_content"
@ -265,7 +274,8 @@
android:layout_marginRight="@dimen/element_spacing_small"
android:singleLine="true"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorPrimary"/>
android:textColor="?android:textColorPrimary"
tools:text="255"/>
<org.mariotaku.twidere.view.themed.ThemedTextView
android:layout_width="wrap_content"
@ -294,7 +304,8 @@
android:layout_marginRight="@dimen/element_spacing_small"
android:singleLine="true"
android:textAppearance="?android:textAppearanceSmall"
android:textColor="?android:textColorPrimary"/>
android:textColor="?android:textColorPrimary"
tools:text="255"/>
<org.mariotaku.twidere.view.themed.ThemedTextView
android:layout_width="wrap_content"

View File

@ -137,13 +137,15 @@
<item>@string/card_highlight_option_line</item>
<item>@string/none</item>
</string-array>
<string-array name="values_image_preview_scale_type">
<item>CENTER_CROP</item>
<item>FIT_CENTER</item>
<string-array name="values_media_preview_style">
<item>crop</item>
<item>scale</item>
<item>none</item>
</string-array>
<string-array name="entries_image_preview_scale_type">
<item>@string/image_preview_scale_type_crop</item>
<item>@string/image_preview_scale_type_fit_center</item>
<string-array name="entries_media_preview_style">
<item>@string/crop</item>
<item>@string/scale</item>
<item>@string/none</item>
</string-array>
<string-array name="entries_image_sources">
<item>@string/from_camera</item>
@ -153,5 +155,15 @@
<item>camera</item>
<item>gallery</item>
</string-array>
<string-array name="values_profile_image_style">
<item>round</item>
<item>square</item>
</string-array>
<string-array name="entries_profile_image_style">
<item>@string/round</item>
<item>@string/square</item>
</string-array>
<string name="crop">Crop</string>
<string name="scale">Scale</string>
</resources>

View File

@ -692,5 +692,12 @@
<string name="size">Size</string>
<string name="just_now">Just now</string>
<string name="requested">Requested</string>
<string name="round">Round</string>
<string name="square">Square</string>
<string name="profile_image_style">Profile image style</string>
<string name="default_profile_image_style">round</string>
<string name="media_preview_style">Media preview style</string>
<string name="draft_saved">Draft saved</string>
<string name="open_twitter_links">Open Twitter links</string>
</resources>

View File

@ -21,27 +21,7 @@
android:order="23"
android:summary="@string/nickname_only_summary"
android:title="@string/nickname_only"/>
<org.mariotaku.twidere.preference.AutoFixCheckBoxPreference
android:defaultValue="true"
android:key="display_profile_image"
android:order="24"
android:summary="@string/image_load_summary"
android:title="@string/display_profile_image"/>
<org.mariotaku.twidere.preference.AutoFixCheckBoxPreference
android:defaultValue="false"
android:key="display_image_preview"
android:order="25"
android:summary="@string/image_load_summary"
android:title="@string/display_image_preview"/>
<org.mariotaku.twidere.preference.AutoFixListPreference
android:defaultValue="CENTER_CROP"
android:entries="@array/entries_image_preview_scale_type"
android:entryValues="@array/values_image_preview_scale_type"
android:key="image_preview_scale_type"
android:order="26"
android:summary="%s"
android:title="@string/image_preview_scale_type"/>
<org.mariotaku.twidere.preference.LinkHighlightPreference
android:defaultValue="none"

View File

@ -38,12 +38,25 @@
android:key="fast_scroll_thumb"
android:title="@string/fast_scroll_thumb"/>
<org.mariotaku.twidere.preference.AutoInvalidateListPreference
<org.mariotaku.twidere.preference.SummaryListPreference
android:defaultValue="@string/default_tab_display_option"
android:entries="@array/entries_tab_display_option"
android:entryValues="@array/values_tab_display_option"
android:key="tab_display_option"
android:summary="%s"
android:title="@string/tab_display_option"/>
<org.mariotaku.twidere.preference.SummaryListPreference
android:defaultValue="@string/default_profile_image_style"
android:entries="@array/entries_profile_image_style"
android:entryValues="@array/values_profile_image_style"
android:key="profile_image_style"
android:title="@string/profile_image_style"/>
<org.mariotaku.twidere.preference.SummaryListPreference
android:defaultValue="crop"
android:entries="@array/entries_media_preview_style"
android:entryValues="@array/values_media_preview_style"
android:key="media_preview_style"
android:title="@string/media_preview_style"/>
</PreferenceScreen>

View File

@ -18,7 +18,7 @@
<org.mariotaku.twidere.preference.ComponentStatePreference
android:name="org.mariotaku.twidere.activity.TwitterLinkHandlerActivity"
android:key="twitter_link_handler"
android:title="@string/twitter_link_handler"/>
android:title="@string/open_twitter_links"/>
<org.mariotaku.twidere.preference.ComponentStatePreference
android:name="org.mariotaku.twidere.activity.AssistLauncherActivity"
@ -26,13 +26,12 @@
android:summary="@string/compose_now_summary"
android:title="@string/compose_now"/>
<org.mariotaku.twidere.preference.AutoInvalidateListPreference
<org.mariotaku.twidere.preference.SummaryListPreference
android:defaultValue="compose"
android:dependency="compose_now"
android:entries="@array/entries_compose_now_action"
android:entryValues="@array/values_compose_now_action"
android:key="compose_now_action"
android:summary="%s"
android:title="@string/compose_now_action"/>
<Preference