Commit 3e901772 authored by DrKLO's avatar DrKLO

Update to 3.1.2

parent 82f9be23
......@@ -73,7 +73,7 @@ android {
defaultConfig {
minSdkVersion 8
targetSdkVersion 22
versionCode 580
versionName "3.1.1"
versionCode 586
versionName "3.1.2"
}
}
......@@ -939,10 +939,14 @@ public class ContactsController {
@Override
public void run() {
FileLog.e("tmessages", "done loading contacts");
if (from == 1 && contactsArr.isEmpty()) {
if (from == 1 && (contactsArr.isEmpty() || UserConfig.lastContactsSyncTime < (int) (System.currentTimeMillis() / 1000) - 24 * 60 * 60)) {
loadContacts(false, true);
return;
}
if (from == 0) {
UserConfig.lastContactsSyncTime = (int) (System.currentTimeMillis() / 1000);
UserConfig.saveConfig(false);
}
for (TLRPC.TL_contact contact : contactsArr) {
if (usersDict.get(contact.user_id) == null && contact.user_id != UserConfig.getClientUserId()) {
......
......@@ -61,8 +61,6 @@ public class Emoji {
};
public static long[][] data = {
new long[]
{},
new long[]//189
{
0x00000000D83DDE04L, 0x00000000D83DDE03L, 0x00000000D83DDE00L, 0x00000000D83DDE0AL, 0x000000000000263AL, 0x00000000D83DDE09L, 0x00000000D83DDE0DL,
......@@ -215,10 +213,10 @@ public class Emoji {
bigImgSize = AndroidUtilities.dp(32);
}
for (int j = 1; j < data.length; j++) {
for (int j = 0; j < data.length; j++) {
for (int i = 0; i < data[j].length; i++) {
Rect rect = new Rect((i % cols[j - 1]) * emojiFullSize, (i / cols[j - 1]) * emojiFullSize, (i % cols[j - 1] + 1) * emojiFullSize, (i / cols[j - 1] + 1) * emojiFullSize);
rects.put(data[j][i], new DrawableInfo(rect, (byte) (j - 1)));
Rect rect = new Rect((i % cols[j]) * emojiFullSize, (i / cols[j]) * emojiFullSize, (i % cols[j] + 1) * emojiFullSize, (i / cols[j] + 1) * emojiFullSize);
rects.put(data[j][i], new DrawableInfo(rect, (byte) j));
}
}
placeholderPaint = new Paint();
......
......@@ -1206,75 +1206,55 @@ public class ImageLoader {
telegramPath = new File(Environment.getExternalStorageDirectory(), "Telegram");
telegramPath.mkdirs();
boolean canRename = false;
try {
for (int a = 0; a < 5; a++) {
File srcFile = new File(cachePath, "temp.file");
srcFile.createNewFile();
File dstFile = new File(telegramPath, "temp.file");
canRename = srcFile.renameTo(dstFile);
srcFile.delete();
dstFile.delete();
if (canRename) {
break;
if (telegramPath.isDirectory()) {
try {
File imagePath = new File(telegramPath, "Telegram Images");
imagePath.mkdir();
if (imagePath.isDirectory() && canMoveFiles(cachePath, imagePath)) {
mediaDirs.put(FileLoader.MEDIA_DIR_IMAGE, imagePath);
FileLog.e("tmessages", "image path = " + imagePath);
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
if (canRename) {
if (telegramPath.isDirectory()) {
try {
File imagePath = new File(telegramPath, "Telegram Images");
imagePath.mkdir();
if (imagePath.isDirectory()) {
mediaDirs.put(FileLoader.MEDIA_DIR_IMAGE, imagePath);
FileLog.e("tmessages", "image path = " + imagePath);
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
try {
File videoPath = new File(telegramPath, "Telegram Video");
videoPath.mkdir();
if (videoPath.isDirectory()) {
mediaDirs.put(FileLoader.MEDIA_DIR_VIDEO, videoPath);
FileLog.e("tmessages", "video path = " + videoPath);
}
} catch (Exception e) {
FileLog.e("tmessages", e);
try {
File videoPath = new File(telegramPath, "Telegram Video");
videoPath.mkdir();
if (videoPath.isDirectory() && canMoveFiles(cachePath, videoPath)) {
mediaDirs.put(FileLoader.MEDIA_DIR_VIDEO, videoPath);
FileLog.e("tmessages", "video path = " + videoPath);
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
try {
File audioPath = new File(telegramPath, "Telegram Audio");
audioPath.mkdir();
if (audioPath.isDirectory()) {
new File(audioPath, ".nomedia").createNewFile();
mediaDirs.put(FileLoader.MEDIA_DIR_AUDIO, audioPath);
FileLog.e("tmessages", "audio path = " + audioPath);
}
} catch (Exception e) {
FileLog.e("tmessages", e);
try {
File audioPath = new File(telegramPath, "Telegram Audio");
audioPath.mkdir();
if (audioPath.isDirectory() && canMoveFiles(cachePath, audioPath)) {
new File(audioPath, ".nomedia").createNewFile();
mediaDirs.put(FileLoader.MEDIA_DIR_AUDIO, audioPath);
FileLog.e("tmessages", "audio path = " + audioPath);
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
try {
File documentPath = new File(telegramPath, "Telegram Documents");
documentPath.mkdir();
if (documentPath.isDirectory()) {
new File(documentPath, ".nomedia").createNewFile();
mediaDirs.put(FileLoader.MEDIA_DIR_DOCUMENT, documentPath);
FileLog.e("tmessages", "documents path = " + documentPath);
}
} catch (Exception e) {
FileLog.e("tmessages", e);
try {
File documentPath = new File(telegramPath, "Telegram Documents");
documentPath.mkdir();
if (documentPath.isDirectory() && canMoveFiles(cachePath, documentPath)) {
new File(documentPath, ".nomedia").createNewFile();
mediaDirs.put(FileLoader.MEDIA_DIR_DOCUMENT, documentPath);
FileLog.e("tmessages", "documents path = " + documentPath);
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
} else {
FileLog.e("tmessages", "this Android can't rename files");
}
} else {
FileLog.e("tmessages", "this Android can't rename files");
}
MediaController.getInstance().checkSaveToGalleryFiles();
} catch (Exception e) {
......@@ -1284,6 +1264,38 @@ public class ImageLoader {
return mediaDirs;
}
private boolean canMoveFiles(File from, File to) {
RandomAccessFile file = null;
try {
for (int a = 0; a < 5; a++) {
File srcFile = new File(from, "temp.file");
srcFile.createNewFile();
file = new RandomAccessFile(srcFile, "rws");
file.write(1);
file.close();
file = null;
File dstFile = new File(to, "temp.file");
boolean canRename = srcFile.renameTo(dstFile);
srcFile.delete();
dstFile.delete();
if (canRename) {
return true;
}
}
} catch (Exception e) {
FileLog.e("tmessages", e);
} finally {
try {
if (file != null) {
file.close();
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
return false;
}
public Float getFileProgress(String location) {
if (location == null) {
return null;
......
......@@ -1445,6 +1445,9 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
}
private void buildShuffledPlayList() {
if (playlist.isEmpty()) {
return;
}
ArrayList<MessageObject> all = new ArrayList<>(playlist);
shuffledPlaylist.clear();
......@@ -2492,7 +2495,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
albumEntry.addPhoto(photoEntry);
}
}
} catch (Exception e) {
} catch (Throwable e) {
FileLog.e("tmessages", e);
} finally {
if (cursor != null) {
......@@ -2551,7 +2554,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
albumEntry.addPhoto(photoEntry);
}
}
} catch (Exception e) {
} catch (Throwable e) {
FileLog.e("tmessages", e);
} finally {
if (cursor != null) {
......
......@@ -131,6 +131,8 @@ public class MessagesStorage {
database.executeFast("CREATE TABLE sent_files_v2(uid TEXT, type INTEGER, data BLOB, PRIMARY KEY (uid, type))").stepThis().dispose();
database.executeFast("CREATE TABLE search_recent(did INTEGER PRIMARY KEY, date INTEGER);").stepThis().dispose();
//database.executeFast("CREATE TABLE messages_holes(uid INTEGER, start INTEGER, end INTEGER, PRIMARY KEY(uid, start));").stepThis().dispose();
//database.executeFast("CREATE INDEX IF NOT EXISTS type_uid_end_messages_holes ON messages_holes(uid, end);").stepThis().dispose();
//database.executeFast("CREATE TABLE secret_holes(uid INTEGER, seq_in INTEGER, seq_out INTEGER, data BLOB, PRIMARY KEY (uid, seq_in, seq_out));").stepThis().dispose();
......@@ -170,7 +172,7 @@ public class MessagesStorage {
database.executeFast("CREATE INDEX IF NOT EXISTS bot_keyboard_idx_mid ON bot_keyboard(mid);").stepThis().dispose();
//version
database.executeFast("PRAGMA user_version = 20").stepThis().dispose();
database.executeFast("PRAGMA user_version = 21").stepThis().dispose();
} else {
try {
SQLiteCursor cursor = database.queryFinalized("SELECT seq, pts, date, qts, lsv, sg, pbytes FROM params WHERE id = 1");
......@@ -201,7 +203,7 @@ public class MessagesStorage {
}
}
int version = database.executeInt("PRAGMA user_version");
if (version < 20) {
if (version < 21) {
updateDbToLastVersion(version);
}
}
......@@ -410,7 +412,12 @@ public class MessagesStorage {
database.executeFast("CREATE TABLE IF NOT EXISTS bot_keyboard(uid INTEGER PRIMARY KEY, mid INTEGER, info BLOB)").stepThis().dispose();
database.executeFast("CREATE INDEX IF NOT EXISTS bot_keyboard_idx_mid ON bot_keyboard(mid);").stepThis().dispose();
database.executeFast("PRAGMA user_version = 20").stepThis().dispose();
//version = 20;
version = 20;
}
if (version == 20) {
database.executeFast("CREATE TABLE search_recent(did INTEGER PRIMARY KEY, date INTEGER);").stepThis().dispose();
database.executeFast("PRAGMA user_version = 21").stepThis().dispose();
//version = 21;
}
} catch (Exception e) {
FileLog.e("tmessages", e);
......@@ -887,6 +894,7 @@ public class MessagesStorage {
if (!messagesOnly) {
database.executeFast("DELETE FROM dialogs WHERE did = " + did).stepThis().dispose();
database.executeFast("DELETE FROM chat_settings WHERE uid = " + did).stepThis().dispose();
database.executeFast("DELETE FROM search_recent WHERE did = " + did).stepThis().dispose();
int lower_id = (int)did;
int high_id = (int)(did >> 32);
if (lower_id != 0) {
......@@ -2100,11 +2108,19 @@ public class MessagesStorage {
storageQueue.postRunnable(new Runnable() {
@Override
public void run() {
database.commitTransaction();
try {
database.commitTransaction();
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
});
} else {
database.commitTransaction();
try {
database.commitTransaction();
} catch (Exception e) {
FileLog.e("tmessages", e);
}
}
}
......@@ -2745,6 +2761,7 @@ public class MessagesStorage {
database.beginTransaction();
SQLitePreparedStatement state = database.executeFast("UPDATE messages SET data = ? WHERE mid = ?");
SQLitePreparedStatement state2 = database.executeFast("UPDATE media_v2 SET data = ? WHERE mid = ?");
for (TLRPC.Message message : messages) {
ByteBufferDesc data = buffersStorage.getFreeBuffer(message.getObjectSize());
message.serializeToStream(data);
......@@ -2754,9 +2771,15 @@ public class MessagesStorage {
state.bindInteger(2, message.id);
state.step();
state2.requery();
state2.bindByteBuffer(1, data.buffer);
state2.bindInteger(2, message.id);
state2.step();
buffersStorage.reuseFreeBuffer(data);
}
state.dispose();
state2.dispose();
database.commitTransaction();
......
......@@ -32,6 +32,7 @@ public class SharedMediaQuery {
public final static int MEDIA_PHOTOVIDEO = 0;
public final static int MEDIA_FILE = 1;
public final static int MEDIA_AUDIO = 2;
public final static int MEDIA_URL = 3;
public static void loadMedia(final long uid, final int offset, final int count, final int max_id, final int type, final boolean fromCache, final int classGuid) {
int lower_part = (int)uid;
......@@ -48,6 +49,8 @@ public class SharedMediaQuery {
req.filter = new TLRPC.TL_inputMessagesFilterDocument();
} else if (type == MEDIA_AUDIO) {
req.filter = new TLRPC.TL_inputMessagesFilterAudio();
} else if (type == MEDIA_URL) {
req.filter = new TLRPC.TL_inputMessagesFilterUrl();
}
req.q = "";
if (uid < 0) {
......@@ -94,6 +97,8 @@ public class SharedMediaQuery {
req.filter = new TLRPC.TL_inputMessagesFilterDocument();
} else if (type == MEDIA_AUDIO) {
req.filter = new TLRPC.TL_inputMessagesFilterAudio();
} else if (type == MEDIA_URL) {
req.filter = new TLRPC.TL_inputMessagesFilterUrl();
}
req.q = "";
if (uid < 0) {
......@@ -144,15 +149,17 @@ public class SharedMediaQuery {
return -1;
}
if (message.media instanceof TLRPC.TL_messageMediaPhoto || message.media instanceof TLRPC.TL_messageMediaVideo) {
return SharedMediaQuery.MEDIA_PHOTOVIDEO;
return MEDIA_PHOTOVIDEO;
} else if (message.media instanceof TLRPC.TL_messageMediaDocument) {
if (MessageObject.isStickerMessage(message)) {
return -1;
} else {
return SharedMediaQuery.MEDIA_FILE;
return MEDIA_FILE;
}
} else if (message.media instanceof TLRPC.TL_messageMediaAudio) {
return SharedMediaQuery.MEDIA_AUDIO;
return MEDIA_AUDIO;
} else if (message.media instanceof TLRPC.TL_messageMediaWebPage) {
return MEDIA_URL;
}
return -1;
}
......@@ -160,7 +167,11 @@ public class SharedMediaQuery {
public static boolean canAddMessageToMedia(TLRPC.Message message) {
if (message instanceof TLRPC.TL_message_secret && message.media instanceof TLRPC.TL_messageMediaPhoto && message.ttl != 0 && message.ttl <= 60) {
return false;
} else if (message.media instanceof TLRPC.TL_messageMediaPhoto || message.media instanceof TLRPC.TL_messageMediaVideo || message.media instanceof TLRPC.TL_messageMediaDocument || message.media instanceof TLRPC.TL_messageMediaAudio) {
} else if (message.media instanceof TLRPC.TL_messageMediaPhoto ||
message.media instanceof TLRPC.TL_messageMediaVideo ||
message.media instanceof TLRPC.TL_messageMediaDocument ||
message.media instanceof TLRPC.TL_messageMediaAudio/* ||
message.media instanceof TLRPC.TL_messageMediaWebPage && !(message.media.webpage instanceof TLRPC.TL_webPageEmpty)*/) {
return true;
}
return false;
......
......@@ -110,8 +110,9 @@ public class StickersQuery {
ArrayList<TLRPC.TL_messages_stickerSet> newStickerArray = null;
int date = 0;
String hash = null;
SQLiteCursor cursor = null;
try {
SQLiteCursor cursor = MessagesStorage.getInstance().getDatabase().queryFinalized("SELECT data, date, hash FROM stickers_v2 WHERE 1");
cursor = MessagesStorage.getInstance().getDatabase().queryFinalized("SELECT data, date, hash FROM stickers_v2 WHERE 1");
if (cursor.next()) {
ByteBufferDesc data = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(cursor.byteArrayLength(0));
if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) {
......@@ -128,9 +129,12 @@ public class StickersQuery {
hash = cursor.stringValue(2);
MessagesStorage.getInstance().getBuffersStorage().reuseFreeBuffer(data);
}
cursor.dispose();
} catch (Exception e) {
} catch (Throwable e) {
FileLog.e("tmessages", e);
} finally {
if (cursor != null) {
cursor.dispose();
}
}
processLoadedStickers(newStickerArray, true, date, hash);
}
......@@ -229,7 +233,7 @@ public class StickersQuery {
});
}
private static long getStickerSetId(TLRPC.Document document) {
public static long getStickerSetId(TLRPC.Document document) {
for (TLRPC.DocumentAttribute attribute : document.attributes) {
if (attribute instanceof TLRPC.TL_documentAttributeSticker) {
if (attribute.stickerset instanceof TLRPC.TL_inputStickerSetID) {
......
......@@ -28,43 +28,43 @@ public interface Cache {
* @param key Cache key
* @return An {@link Entry} or null in the event of a cache miss
*/
Entry get(String key);
public Entry get(String key);
/**
* Adds or replaces an entry to the cache.
* @param key Cache key
* @param entry Data to store and metadata for cache coherency, TTL, etc.
*/
void put(String key, Entry entry);
public void put(String key, Entry entry);
/**
* Performs any potentially long-running actions needed to initialize the cache;
* will be called from a worker thread.
*/
void initialize();
public void initialize();
/**
* Invalidates an entry in the cache.
* @param key Cache key
* @param fullExpire True to fully expire the entry, false to soft expire
*/
void invalidate(String key, boolean fullExpire);
public void invalidate(String key, boolean fullExpire);
/**
* Removes an entry from the cache.
* @param key Cache key
*/
void remove(String key);
public void remove(String key);
/**
* Empties the cache.
*/
void clear();
public void clear();
/**
* Data and metadata for an entry returned by the cache.
*/
class Entry {
public static class Entry {
/** The data returned from cache. */
public byte[] data;
......@@ -74,6 +74,9 @@ public interface Cache {
/** Date of this response as reported by the server. */
public long serverDate;
/** The last modified date for the requested object. */
public long lastModified;
/** TTL for this record. */
public long ttl;
......
......@@ -151,6 +151,7 @@ public class CacheDispatcher extends Thread {
if (mQuit) {
return;
}
continue;
}
}
}
......
......@@ -26,5 +26,5 @@ public interface Network {
* @return A {@link NetworkResponse} with data and caching metadata; will never be null
* @throws VolleyError on errors
*/
NetworkResponse performRequest(Request<?> request) throws VolleyError;
public NetworkResponse performRequest(Request<?> request) throws VolleyError;
}
......@@ -28,9 +28,9 @@ import java.util.concurrent.BlockingQueue;
* Provides a thread for performing network dispatch from a queue of requests.
*
* Requests added to the specified queue are processed from the network via a
* specified {@link org.telegram.android.volley.Network} interface. Responses are committed to cache, if
* eligible, using a specified {@link org.telegram.android.volley.Cache} interface. Valid responses and
* errors are posted back to the caller via a {@link org.telegram.android.volley.ResponseDelivery}.
* specified {@link Network} interface. Responses are committed to cache, if
* eligible, using a specified {@link Cache} interface. Valid responses and
* errors are posted back to the caller via a {@link ResponseDelivery}.
*/
public class NetworkDispatcher extends Thread {
/** The queue of requests to service. */
......
......@@ -22,7 +22,7 @@ import java.util.Collections;
import java.util.Map;
/**
* Data and headers returned from {@link org.telegram.android.volley.Network#performRequest(org.telegram.android.volley.Request)}.
* Data and headers returned from {@link Network#performRequest(Request)}.
*/
public class NetworkResponse {
/**
......
......@@ -18,9 +18,13 @@ package org.telegram.android.volley;
import android.net.TrafficStats;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import android.text.TextUtils;
import org.telegram.android.volley.VolleyLog.MarkerLog;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Collections;
......@@ -53,6 +57,9 @@ public abstract class Request<T> implements Comparable<Request<T>> {
int PATCH = 7;
}
/** An event log tracing the lifetime of this request; for debugging. */
private final MarkerLog mEventLog = MarkerLog.ENABLED ? new MarkerLog() : null;
/**
* Request method of this request. Currently supports GET, POST, PUT, DELETE, HEAD, OPTIONS,
* TRACE, and PATCH.
......@@ -83,12 +90,6 @@ public abstract class Request<T> implements Comparable<Request<T>> {
/** Whether or not a response has been delivered for this request yet. */
private boolean mResponseDelivered = false;
// A cheap variant of request tracing used to dump slow requests.
private long mRequestBirthTime = 0;
/** Threshold at which we should log the request (even when debug logging is not enabled). */
private static final long SLOW_REQUEST_THRESHOLD_MS = 3000;
/** The retry policy for this request. */
private RetryPolicy mRetryPolicy;
......@@ -108,6 +109,7 @@ public abstract class Request<T> implements Comparable<Request<T>> {
* is provided by subclasses, who have a better idea of how to deliver an
* already-parsed response.
*
* @deprecated Use {@link #Request(int, String, com.android.volley.Response.ErrorListener)}.
*/
@Deprecated
public Request(String url, Response.ErrorListener listener) {
......@@ -155,6 +157,9 @@ public abstract class Request<T> implements Comparable<Request<T>> {
return mTag;
}
/**
* @return this request's {@link com.android.volley.Response.ErrorListener}.
*/
public Response.ErrorListener getErrorListener() {
return mErrorListener;
}
......@@ -196,8 +201,8 @@ public abstract class Request<T> implements Comparable<Request<T>> {
* Adds an event to this request's event log; for debugging.
*/
public void addMarker(String tag) {
if (mRequestBirthTime == 0) {
mRequestBirthTime = SystemClock.elapsedRealtime();
if (MarkerLog.ENABLED) {
mEventLog.add(tag, Thread.currentThread().getId());
}
}
......@@ -210,9 +215,24 @@ public abstract class Request<T> implements Comparable<Request<T>> {
if (mRequestQueue != null) {
mRequestQueue.finish(this);
}
long requestTime = SystemClock.elapsedRealtime() - mRequestBirthTime;
if (requestTime >= SLOW_REQUEST_THRESHOLD_MS) {
VolleyLog.d("%d ms: %s", requestTime, this.toString());
if (MarkerLog.ENABLED) {
final long threadId = Thread.currentThread().getId();
if (Looper.myLooper() != Looper.getMainLooper()) {
// If we finish marking off of the main thread, we need to
// actually do it on the main thread to ensure correct ordering.
Handler mainThread = new Handler(Looper.getMainLooper());
mainThread.post(new Runnable() {
@Override
public void run() {
mEventLog.add(tag, threadId);
mEventLog.finish(this.toString());
}
});
return;
}
mEventLog.add(tag, threadId);
mEventLog.finish(this.toString());
}
}
......
......@@ -19,9 +19,11 @@ package org.telegram.android.volley;
import android.os.Handler;
import android.os.Looper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
......@@ -31,12 +33,18 @@ import java.util.concurrent.atomic.AtomicInteger;
/**
* A request dispatch queue with a thread pool of dispatchers.
*
* Calling {@link #add(org.telegram.android.volley.Request)} will enqueue the given Request for dispatch,
* Calling {@link #add(Request)} will enqueue the given Request for dispatch,
* resolving from either cache or network on a worker thread, and then delivering
* a parsed response on the main thread.
*/
public class RequestQueue {
/** Callback interface for completed requests. */
public static interface RequestFinishedListener<T> {
/** Called when a request has finished processing. */
public void onRequestFinished(Request<T> request);
}
/** Used for generating monotonically-increasing sequence numbers for requests. */
private AtomicInteger mSequenceGenerator = new AtomicInteger();
......@@ -86,6 +94,9 @@ public class RequestQueue {
/** The cache dispatcher. */
private CacheDispatcher mCacheDispatcher;
private List<RequestFinishedListener> mFinishedListeners =
new ArrayList<RequestFinishedListener>();
/**
* Creates the worker pool. Processing will not begin until {@link #start()} is called.
*
......@@ -149,9 +160,9 @@ public class RequestQueue {
if (mCacheDispatcher != null) {
mCacheDispatcher.quit();
}
for (NetworkDispatcher mDispatcher : mDispatchers) {
if (mDispatcher != null) {
mDispatcher.quit();
for (int i = 0; i < mDispatchers.length; i++) {
if (mDispatchers[i] != null) {
mDispatchers[i].quit();
}
}
}
......@@ -175,7 +186,7 @@ public class RequestQueue {
* {@link RequestQueue#cancelAll(RequestFilter)}.
*/
public interface RequestFilter {
boolean apply(Request<?> request);
public boolean apply(Request<?> request);
}
/**
......@@ -261,11 +272,16 @@ public class RequestQueue {
* <p>Releases waiting requests for <code>request.getCacheKey()</code> if
* <code>request.shouldCache()</code>.</p>
*/
void finish(Request<?> request) {
<T> void finish(Request<T> request) {
// Remove from the set of requests currently being processed.
synchronized (mCurrentRequests) {
mCurrentRequests.remove(request);
}
synchronized (mFinishedListeners) {
for (RequestFinishedListener<T> listener : mFinishedListeners) {
listener.onRequestFinished(request);
}
}
if (request.shouldCache()) {
synchronized (mWaitingRequests) {
......@@ -283,4 +299,19 @@ public class RequestQueue {
}
}
}
public <T> void addRequestFinishedListener(RequestFinishedListener<T> listener) {
synchronized (mFinishedListeners) {
mFinishedListeners.add(listener);
}
}
/**
* Remove a RequestFinishedListener. Has no effect if listener was not previously added.
*/
public <T> void removeRequestFinishedListener(RequestFinishedListener<T> listener) {
synchronized (mFinishedListeners) {
mFinishedListeners.remove(listener);
}
}
}
......@@ -26,7 +26,7 @@ public class Response<T> {
/** Callback interface for delivering parsed responses. */
public interface Listener<T> {
/** Called when a response is received. */
void onResponse(T response);
public void onResponse(T response);
}
/** Callback interface for delivering error responses. */
......@@ -35,7 +35,7 @@ public class Response<T> {
* Callback method that an error has been occurred with the
* provided error code and optional user-readable message.
*/
void onErrorResponse(VolleyError error);
public void onErrorResponse(VolleyError error);
}
/** Returns a successful response containing the parsed result. */
......
......@@ -20,16 +20,16 @@ public interface ResponseDelivery {
/**
* Parses a response from the network or cache and delivers it.
*/
void postResponse(Request<?> request, Response<?> response);
public void postResponse(Request<?> request, Response<?> response);
/**
* Parses a response from the network or cache and delivers it. The provided
* Runnable will be executed after delivery.
*/
void postResponse(Request<?> request, Response<?> response, Runnable runnable);
public void postResponse(Request<?> request, Response<?> response, Runnable runnable);
/**
* Posts an error for the given request.
*/
void postError(Request<?> request, VolleyError error);
public void postError(Request<?> request, VolleyError error);
}
......@@ -24,12 +24,12 @@ public interface RetryPolicy {
/**
* Returns the current timeout (used for logging).
*/
int getCurrentTimeout();
public int getCurrentTimeout();
/**
* Returns the current retry count (used for logging).
*/
int getCurrentRetryCount();
public int getCurrentRetryCount();
/**
* Prepares for the next retry by applying a backoff to the timeout.
......@@ -37,5 +37,5 @@ public interface RetryPolicy {
* @throws VolleyError In the event that the retry could not be performed (for example if we
* ran out of attempts), the passed in error is thrown.
*/
void retry(VolleyError error) throws VolleyError;
public void retry(VolleyError error) throws VolleyError;
}
......@@ -29,3 +29,4 @@ public class ServerError extends VolleyError {
super();
}
}
......@@ -23,7 +23,12 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/** Logging helper class. */
/**
* Logging helper class.
* <p/>
* to see Volley logs call:<br/>
* {@code <android-sdk>/platform-tools/adb shell setprop log.tag.Volley VERBOSE}
*/
public class VolleyLog {
public static String TAG = "Volley";
......@@ -89,7 +94,7 @@ public class VolleyLog {
callingClass = callingClass.substring(callingClass.lastIndexOf('.') + 1);
callingClass = callingClass.substring(callingClass.lastIndexOf('$') + 1);
caller = callingClass + "" + trace[i].getMethodName();
caller = callingClass + "." + trace[i].getMethodName();
break;
}
}
......
......@@ -16,6 +16,8 @@
package org.telegram.android.volley.toolbox;
import org.telegram.android.volley.AuthFailureError;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerFuture;
......@@ -23,14 +25,12 @@ import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import org.telegram.android.volley.AuthFailureError;
/**
* An Authenticator that uses {@link AccountManager} to get auth
* tokens of a specified type for a specified account.
*/
public class AndroidAuthenticator implements Authenticator {
private final Context mContext;
private final AccountManager mAccountManager;
private final Account mAccount;
private final String mAuthTokenType;
private final boolean mNotifyAuthFailure;
......@@ -54,7 +54,13 @@ public class AndroidAuthenticator implements Authenticator {
*/
public AndroidAuthenticator(Context context, Account account, String authTokenType,
boolean notifyAuthFailure) {
mContext = context;
this(AccountManager.get(context), account, authTokenType, notifyAuthFailure);
}
// Visible for testing. Allows injection of a mock AccountManager.
AndroidAuthenticator(AccountManager accountManager, Account account,
String authTokenType, boolean notifyAuthFailure) {
mAccountManager = accountManager;
mAccount = account;
mAuthTokenType = authTokenType;
mNotifyAuthFailure = notifyAuthFailure;
......@@ -71,8 +77,7 @@ public class AndroidAuthenticator implements Authenticator {
@SuppressWarnings("deprecation")
@Override
public String getAuthToken() throws AuthFailureError {
final AccountManager accountManager = AccountManager.get(mContext);
AccountManagerFuture<Bundle> future = accountManager.getAuthToken(mAccount,
AccountManagerFuture<Bundle> future = mAccountManager.getAuthToken(mAccount,
mAuthTokenType, mNotifyAuthFailure, null, null);
Bundle result;
try {
......@@ -97,6 +102,6 @@ public class AndroidAuthenticator implements Authenticator {
@Override
public void invalidateAuthToken(String authToken) {
AccountManager.get(mContext).invalidateAuthToken(mAccount.type, authToken);
mAccountManager.invalidateAuthToken(mAccount.type, authToken);
}
}
......@@ -27,10 +27,10 @@ public interface Authenticator {
*
* @throws AuthFailureError If authentication did not succeed
*/
String getAuthToken() throws AuthFailureError;
public String getAuthToken() throws AuthFailureError;
/**
* Invalidates the provided auth token.
*/
void invalidateAuthToken(String authToken);
public void invalidateAuthToken(String authToken);
}
......@@ -18,15 +18,9 @@ package org.telegram.android.volley.toolbox;
import android.os.SystemClock;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.impl.cookie.DateUtils;
import org.telegram.android.volley.AuthFailureError;
import org.telegram.android.volley.Cache;
import org.telegram.android.volley.Cache.Entry;
import org.telegram.android.volley.Network;
import org.telegram.android.volley.NetworkError;
import org.telegram.android.volley.NetworkResponse;
......@@ -38,6 +32,14 @@ import org.telegram.android.volley.TimeoutError;
import org.telegram.android.volley.VolleyError;
import org.telegram.android.volley.VolleyLog;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.impl.cookie.DateUtils;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
......@@ -49,14 +51,14 @@ import java.util.Map;
import java.util.TreeMap;
/**
* A network performing Volley requests over an {@link org.telegram.android.volley.toolbox.HttpStack}.
* A network performing Volley requests over an {@link HttpStack}.
*/
public class BasicNetwork implements Network {
protected static final boolean DEBUG = VolleyLog.DEBUG;
private static final int SLOW_REQUEST_THRESHOLD_MS = 3000;
private static int SLOW_REQUEST_THRESHOLD_MS = 3000;
private static final int DEFAULT_POOL_SIZE = 4096;
private static int DEFAULT_POOL_SIZE = 4096;
protected final HttpStack mHttpStack;
......@@ -99,7 +101,7 @@ public class BasicNetwork implements Network {
// Handle cache validation.
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
Cache.Entry entry = request.getCacheEntry();
Entry entry = request.getCacheEntry();
if (entry == null) {
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
responseHeaders, true,
......@@ -210,8 +212,8 @@ public class BasicNetwork implements Network {
headers.put("If-None-Match", entry.etag);
}
if (entry.serverDate > 0) {
Date refTime = new Date(entry.serverDate);
if (entry.lastModified > 0) {
Date refTime = new Date(entry.lastModified);
headers.put("If-Modified-Since", DateUtils.formatDate(refTime));
}
}
......@@ -256,8 +258,8 @@ public class BasicNetwork implements Network {
*/
protected static Map<String, String> convertHeaders(Header[] headers) {
Map<String, String> result = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
for (Header header : headers) {
result.put(header.getName(), header.getValue());
for (int i = 0; i < headers.length; i++) {
result.put(headers[i].getName(), headers[i].getValue());
}
return result;
}
......
......@@ -16,14 +16,14 @@
package org.telegram.android.volley.toolbox;
import android.os.Handler;
import android.os.Looper;
import org.telegram.android.volley.Cache;
import org.telegram.android.volley.NetworkResponse;
import org.telegram.android.volley.Request;
import org.telegram.android.volley.Response;
import android.os.Handler;
import android.os.Looper;
/**
* A synthetic request used for clearing the cache.
*/
......
......@@ -16,6 +16,10 @@
package org.telegram.android.volley.toolbox;
import org.telegram.android.volley.AuthFailureError;
import org.telegram.android.volley.Request;
import org.telegram.android.volley.Request.Method;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
......@@ -33,8 +37,6 @@ import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.telegram.android.volley.AuthFailureError;
import org.telegram.android.volley.Request;
import java.io.IOException;
import java.net.URI;
......@@ -92,7 +94,7 @@ public class HttpClientStack implements HttpStack {
/* protected */ static HttpUriRequest createHttpRequest(Request<?> request,
Map<String, String> additionalHeaders) throws AuthFailureError {
switch (request.getMethod()) {
case Request.Method.DEPRECATED_GET_OR_POST: {
case Method.DEPRECATED_GET_OR_POST: {
// This is the deprecated way that needs to be handled for backwards compatibility.
// If the request's post body is null, then the assumption is that the request is
// GET. Otherwise, it is assumed that the request is a POST.
......@@ -108,29 +110,29 @@ public class HttpClientStack implements HttpStack {
return new HttpGet(request.getUrl());
}
}
case Request.Method.GET:
case Method.GET:
return new HttpGet(request.getUrl());
case Request.Method.DELETE:
case Method.DELETE:
return new HttpDelete(request.getUrl());
case Request.Method.POST: {
case Method.POST: {
HttpPost postRequest = new HttpPost(request.getUrl());
postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(postRequest, request);
return postRequest;
}
case Request.Method.PUT: {
case Method.PUT: {
HttpPut putRequest = new HttpPut(request.getUrl());
putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(putRequest, request);
return putRequest;
}
case Request.Method.HEAD:
case Method.HEAD:
return new HttpHead(request.getUrl());
case Request.Method.OPTIONS:
case Method.OPTIONS:
return new HttpOptions(request.getUrl());
case Request.Method.TRACE:
case Method.TRACE:
return new HttpTrace(request.getUrl());
case Request.Method.PATCH: {
case Method.PATCH: {
HttpPatch patchRequest = new HttpPatch(request.getUrl());
patchRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(patchRequest, request);
......
......@@ -16,11 +16,12 @@
package org.telegram.android.volley.toolbox;
import org.telegram.android.volley.Cache;
import org.telegram.android.volley.NetworkResponse;
import org.apache.http.impl.cookie.DateParseException;
import org.apache.http.impl.cookie.DateUtils;
import org.apache.http.protocol.HTTP;
import org.telegram.android.volley.Cache;
import org.telegram.android.volley.NetworkResponse;
import java.util.Map;
......@@ -41,10 +42,14 @@ public class HttpHeaderParser {
Map<String, String> headers = response.headers;
long serverDate = 0;
long lastModified = 0;
long serverExpires = 0;
long softExpire = 0;
long finalExpire = 0;
long maxAge = 0;
long staleWhileRevalidate = 0;
boolean hasCacheControl = false;
boolean mustRevalidate = false;
String serverEtag = null;
String headerValue;
......@@ -58,18 +63,22 @@ public class HttpHeaderParser {
if (headerValue != null) {
hasCacheControl = true;
String[] tokens = headerValue.split(",");
for (String token1 : tokens) {
String token = token1.trim();
for (int i = 0; i < tokens.length; i++) {
String token = tokens[i].trim();
if (token.equals("no-cache") || token.equals("no-store")) {
return null;
} else if (token.startsWith("max-age=")) {
try {
maxAge = Long.parseLong(token.substring(8));
} catch (Exception e) {
/**/
}
} else if (token.startsWith("stale-while-revalidate=")) {
try {
staleWhileRevalidate = Long.parseLong(token.substring(23));
} catch (Exception e) {
}
} else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) {
maxAge = 0;
mustRevalidate = true;
}
}
}
......@@ -79,23 +88,33 @@ public class HttpHeaderParser {
serverExpires = parseDateAsEpoch(headerValue);
}
headerValue = headers.get("Last-Modified");
if (headerValue != null) {
lastModified = parseDateAsEpoch(headerValue);
}
serverEtag = headers.get("ETag");
// Cache-Control takes precedence over an Expires header, even if both exist and Expires
// is more restrictive.
if (hasCacheControl) {
softExpire = now + maxAge * 1000;
finalExpire = mustRevalidate
? softExpire
: softExpire + staleWhileRevalidate * 1000;
} else if (serverDate > 0 && serverExpires >= serverDate) {
// Default semantic for Expire header in HTTP specification is softExpire.
softExpire = now + (serverExpires - serverDate);
finalExpire = softExpire;
}
Cache.Entry entry = new Cache.Entry();
entry.data = response.data;
entry.etag = serverEtag;
entry.softTtl = softExpire;
entry.ttl = entry.softTtl;
entry.ttl = finalExpire;
entry.serverDate = serverDate;
entry.lastModified = lastModified;
entry.responseHeaders = headers;
return entry;
......@@ -115,10 +134,14 @@ public class HttpHeaderParser {
}
/**
* Returns the charset specified in the Content-Type of this header,
* or the HTTP default (ISO-8859-1) if none can be found.
* Retrieve a charset from headers
*
* @param headers An {@link java.util.Map} of headers
* @param defaultCharset Charset to return if none can be found
* @return Returns the charset specified in the Content-Type of this header,
* or the defaultCharset if none can be found.
*/
public static String parseCharset(Map<String, String> headers) {
public static String parseCharset(Map<String, String> headers, String defaultCharset) {
String contentType = headers.get(HTTP.CONTENT_TYPE);
if (contentType != null) {
String[] params = contentType.split(";");
......@@ -132,6 +155,14 @@ public class HttpHeaderParser {
}
}
return HTTP.DEFAULT_CONTENT_CHARSET;
return defaultCharset;
}
/**
* Returns the charset specified in the Content-Type of this header,
* or the HTTP default (ISO-8859-1) if none can be found.
*/
public static String parseCharset(Map<String, String> headers) {
return parseCharset(headers, HTTP.DEFAULT_CONTENT_CHARSET);
}
}
......@@ -16,10 +16,11 @@
package org.telegram.android.volley.toolbox;
import org.apache.http.HttpResponse;
import org.telegram.android.volley.AuthFailureError;
import org.telegram.android.volley.Request;
import org.apache.http.HttpResponse;
import java.io.IOException;
import java.util.Map;
......@@ -38,7 +39,7 @@ public interface HttpStack {
* {@link Request#getHeaders()}
* @return the HTTP response
*/
HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError;
}
......@@ -16,17 +16,20 @@
package org.telegram.android.volley.toolbox;
import org.telegram.android.volley.AuthFailureError;
import org.telegram.android.volley.Request;
import org.telegram.android.volley.Request.Method;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.ProtocolVersion;
import org.apache.http.StatusLine;
import org.apache.http.entity.BasicHttpEntity;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.message.BasicStatusLine;
import org.telegram.android.volley.AuthFailureError;
import org.telegram.android.volley.Request;
import java.io.DataOutputStream;
import java.io.IOException;
......@@ -42,7 +45,7 @@ import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSocketFactory;
/**
* An {@link org.telegram.android.volley.toolbox.HttpStack} based on {@link HttpURLConnection}.
* An {@link HttpStack} based on {@link HttpURLConnection}.
*/
public class HurlStack implements HttpStack {
......@@ -56,7 +59,7 @@ public class HurlStack implements HttpStack {
* Returns a URL to use instead of the provided one, or null to indicate
* this URL should not be used at all.
*/
String rewriteUrl(String originalUrl);
public String rewriteUrl(String originalUrl);
}
private final UrlRewriter mUrlRewriter;
......@@ -113,7 +116,9 @@ public class HurlStack implements HttpStack {
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
response.setEntity(entityFromConnection(connection));
if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) {
response.setEntity(entityFromConnection(connection));
}
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
......@@ -123,6 +128,20 @@ public class HurlStack implements HttpStack {
return response;
}
/**
* Checks if a response message contains a body.
* @see <a href="https://tools.ietf.org/html/rfc7230#section-3.3">RFC 7230 section 3.3</a>
* @param requestMethod request method
* @param responseCode response status code
* @return whether the response has a body
*/
private static boolean hasResponseBody(int requestMethod, int responseCode) {
return requestMethod != Request.Method.HEAD
&& !(HttpStatus.SC_CONTINUE <= responseCode && responseCode < HttpStatus.SC_OK)
&& responseCode != HttpStatus.SC_NO_CONTENT
&& responseCode != HttpStatus.SC_NOT_MODIFIED;
}
/**
* Initializes an {@link HttpEntity} from the given {@link HttpURLConnection}.
* @param connection
......@@ -177,7 +196,7 @@ public class HurlStack implements HttpStack {
/* package */ static void setConnectionParametersForRequest(HttpURLConnection connection,
Request<?> request) throws IOException, AuthFailureError {
switch (request.getMethod()) {
case Request.Method.DEPRECATED_GET_OR_POST:
case Method.DEPRECATED_GET_OR_POST:
// This is the deprecated way that needs to be handled for backwards compatibility.
// If the request's post body is null, then the assumption is that the request is
// GET. Otherwise, it is assumed that the request is a POST.
......@@ -195,32 +214,32 @@ public class HurlStack implements HttpStack {
out.close();
}
break;
case Request.Method.GET:
case Method.GET:
// Not necessary to set the request method because connection defaults to GET but
// being explicit here.
connection.setRequestMethod("GET");
break;
case Request.Method.DELETE:
case Method.DELETE:
connection.setRequestMethod("DELETE");
break;
case Request.Method.POST:
case Method.POST:
connection.setRequestMethod("POST");
addBodyIfExists(connection, request);
break;
case Request.Method.PUT:
case Method.PUT:
connection.setRequestMethod("PUT");
addBodyIfExists(connection, request);
break;
case Request.Method.HEAD:
case Method.HEAD:
connection.setRequestMethod("HEAD");
break;
case Request.Method.OPTIONS:
case Method.OPTIONS:
connection.setRequestMethod("OPTIONS");
break;
case Request.Method.TRACE:
case Method.TRACE:
connection.setRequestMethod("TRACE");
break;
case Request.Method.PATCH:
case Method.PATCH:
connection.setRequestMethod("PATCH");
addBodyIfExists(connection, request);
break;
......
......@@ -20,10 +20,11 @@ import android.graphics.Bitmap.Config;
import android.os.Handler;
import android.os.Looper;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import org.telegram.android.volley.Request;
import org.telegram.android.volley.RequestQueue;
import org.telegram.android.volley.Response;
import org.telegram.android.volley.Response.ErrorListener;
import org.telegram.android.volley.Response.Listener;
import org.telegram.android.volley.VolleyError;
import java.util.HashMap;
......@@ -71,8 +72,8 @@ public class ImageLoader {
* must not block. Implementation with an LruCache is recommended.
*/
public interface ImageCache {
Bitmap getBitmap(String url);
void putBitmap(String url, Bitmap bitmap);
public Bitmap getBitmap(String url);
public void putBitmap(String url, Bitmap bitmap);
}
/**
......@@ -127,7 +128,7 @@ public class ImageLoader {
* or
* - onErrorResponse will be called if there was an error loading the image.
*/
public interface ImageListener extends Response.ErrorListener {
public interface ImageListener extends ErrorListener {
/**
* Listens for non-error changes to the loading of the image request.
*
......@@ -138,7 +139,7 @@ public class ImageLoader {
* image loading in order to, for example, run an animation to fade in network loaded
* images.
*/
void onResponse(ImageContainer response, boolean isImmediate);
public void onResponse(ImageContainer response, boolean isImmediate);
}
/**
......@@ -149,9 +150,22 @@ public class ImageLoader {
* @return True if the item exists in cache, false otherwise.
*/
public boolean isCached(String requestUrl, int maxWidth, int maxHeight) {
return isCached(requestUrl, maxWidth, maxHeight, ScaleType.CENTER_INSIDE);
}
/**
* Checks if the item is available in the cache.
*
* @param requestUrl The url of the remote image
* @param maxWidth The maximum width of the returned image.
* @param maxHeight The maximum height of the returned image.
* @param scaleType The scaleType of the imageView.
* @return True if the item exists in cache, false otherwise.
*/
public boolean isCached(String requestUrl, int maxWidth, int maxHeight, ScaleType scaleType) {
throwIfNotOnMainThread();
String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight);
String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType);
return mCache.getBitmap(cacheKey) != null;
}
......@@ -168,6 +182,15 @@ public class ImageLoader {
return get(requestUrl, listener, 0, 0);
}
/**
* Equivalent to calling {@link #get(String, ImageListener, int, int, ScaleType)} with
* {@code Scaletype == ScaleType.CENTER_INSIDE}.
*/
public ImageContainer get(String requestUrl, ImageListener imageListener,
int maxWidth, int maxHeight) {
return get(requestUrl, imageListener, maxWidth, maxHeight, ScaleType.CENTER_INSIDE);
}
/**
* Issues a bitmap request with the given URL if that image is not available
* in the cache, and returns a bitmap container that contains all of the data
......@@ -177,15 +200,17 @@ public class ImageLoader {
* @param imageListener The listener to call when the remote image is loaded
* @param maxWidth The maximum width of the returned image.
* @param maxHeight The maximum height of the returned image.
* @param scaleType The ImageViews ScaleType used to calculate the needed image size.
* @return A container object that contains all of the properties of the request, as well as
* the currently available image (default if remote is not loaded).
*/
public ImageContainer get(String requestUrl, ImageListener imageListener,
int maxWidth, int maxHeight) {
int maxWidth, int maxHeight, ScaleType scaleType) {
// only fulfill requests that were initiated from the main thread.
throwIfNotOnMainThread();
final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight);
final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType);
// Try to look up the request in the cache of remote images.
Bitmap cachedBitmap = mCache.getBitmap(cacheKey);
......@@ -213,7 +238,8 @@ public class ImageLoader {
// The request is not already in flight. Send the new request to the network and
// track it.
Request<Bitmap> newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, cacheKey);
Request<Bitmap> newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, scaleType,
cacheKey);
mRequestQueue.add(newRequest);
mInFlightRequests.put(cacheKey,
......@@ -221,14 +247,14 @@ public class ImageLoader {
return imageContainer;
}
protected Request<Bitmap> makeImageRequest(String requestUrl, int maxWidth, int maxHeight, final String cacheKey) {
return new ImageRequest(requestUrl, new Response.Listener<Bitmap>() {
protected Request<Bitmap> makeImageRequest(String requestUrl, int maxWidth, int maxHeight,
ScaleType scaleType, final String cacheKey) {
return new ImageRequest(requestUrl, new Listener<Bitmap>() {
@Override
public void onResponse(Bitmap response) {
onGetImageSuccess(cacheKey, response);
}
}, maxWidth, maxHeight,
Config.RGB_565, new Response.ErrorListener() {
}, maxWidth, maxHeight, scaleType, Config.RGB_565, new ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
onGetImageError(cacheKey, error);
......@@ -471,8 +497,11 @@ public class ImageLoader {
* @param url The URL of the request.
* @param maxWidth The max-width of the output.
* @param maxHeight The max-height of the output.
* @param scaleType The scaleType of the imageView.
*/
private static String getCacheKey(String url, int maxWidth, int maxHeight) {
return "#W" + maxWidth + "#H" + maxHeight + url;
private static String getCacheKey(String url, int maxWidth, int maxHeight, ScaleType scaleType) {
return new StringBuilder(url.length() + 12).append("#W").append(maxWidth)
.append("#H").append(maxHeight).append("#S").append(scaleType.ordinal()).append(url)
.toString();
}
}
......@@ -16,10 +16,6 @@
package org.telegram.android.volley.toolbox;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import org.telegram.android.volley.DefaultRetryPolicy;
import org.telegram.android.volley.NetworkResponse;
import org.telegram.android.volley.ParseError;
......@@ -27,6 +23,11 @@ import org.telegram.android.volley.Request;
import org.telegram.android.volley.Response;
import org.telegram.android.volley.VolleyLog;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.widget.ImageView.ScaleType;
/**
* A canned request for getting an image at a given URL and calling
* back with a decoded Bitmap.
......@@ -45,6 +46,7 @@ public class ImageRequest extends Request<Bitmap> {
private final Config mDecodeConfig;
private final int mMaxWidth;
private final int mMaxHeight;
private ScaleType mScaleType;
/** Decoding lock so that we don't decode more than one image at a time (to avoid OOM's) */
private static final Object sDecodeLock = new Object();
......@@ -63,20 +65,32 @@ public class ImageRequest extends Request<Bitmap> {
* @param maxWidth Maximum width to decode this bitmap to, or zero for none
* @param maxHeight Maximum height to decode this bitmap to, or zero for
* none
* @param scaleType The ImageViews ScaleType used to calculate the needed image size.
* @param decodeConfig Format to decode the bitmap to
* @param errorListener Error listener, or null to ignore errors
*/
public ImageRequest(String url, Response.Listener<Bitmap> listener, int maxWidth, int maxHeight,
Config decodeConfig, Response.ErrorListener errorListener) {
super(Method.GET, url, errorListener);
ScaleType scaleType, Config decodeConfig, Response.ErrorListener errorListener) {
super(Method.GET, url, errorListener);
setRetryPolicy(
new DefaultRetryPolicy(IMAGE_TIMEOUT_MS, IMAGE_MAX_RETRIES, IMAGE_BACKOFF_MULT));
mListener = listener;
mDecodeConfig = decodeConfig;
mMaxWidth = maxWidth;
mMaxHeight = maxHeight;
mScaleType = scaleType;
}
/**
* For API compatibility with the pre-ScaleType variant of the constructor. Equivalent to
* the normal constructor with {@code ScaleType.CENTER_INSIDE}.
*/
@Deprecated
public ImageRequest(String url, Response.Listener<Bitmap> listener, int maxWidth, int maxHeight,
Config decodeConfig, Response.ErrorListener errorListener) {
this(url, listener, maxWidth, maxHeight,
ScaleType.CENTER_INSIDE, decodeConfig, errorListener);
}
@Override
public Priority getPriority() {
return Priority.LOW;
......@@ -92,14 +106,24 @@ public class ImageRequest extends Request<Bitmap> {
* maintain aspect ratio with primary dimension
* @param actualPrimary Actual size of the primary dimension
* @param actualSecondary Actual size of the secondary dimension
* @param scaleType The ScaleType used to calculate the needed image size.
*/
private static int getResizedDimension(int maxPrimary, int maxSecondary, int actualPrimary,
int actualSecondary) {
int actualSecondary, ScaleType scaleType) {
// If no dominant value at all, just return the actual.
if (maxPrimary == 0 && maxSecondary == 0) {
if ((maxPrimary == 0) && (maxSecondary == 0)) {
return actualPrimary;
}
// If ScaleType.FIT_XY fill the whole rectangle, ignore ratio.
if (scaleType == ScaleType.FIT_XY) {
if (maxPrimary == 0) {
return actualPrimary;
}
return maxPrimary;
}
// If primary is unspecified, scale primary to match secondary's scaling ratio.
if (maxPrimary == 0) {
double ratio = (double) maxSecondary / (double) actualSecondary;
......@@ -112,7 +136,16 @@ public class ImageRequest extends Request<Bitmap> {
double ratio = (double) actualSecondary / (double) actualPrimary;
int resized = maxPrimary;
if (resized * ratio > maxSecondary) {
// If ScaleType.CENTER_CROP fill the whole rectangle, preserve aspect ratio.
if (scaleType == ScaleType.CENTER_CROP) {
if ((resized * ratio) < maxSecondary) {
resized = (int) (maxSecondary / ratio);
}
return resized;
}
if ((resized * ratio) > maxSecondary) {
resized = (int) (maxSecondary / ratio);
}
return resized;
......@@ -150,9 +183,9 @@ public class ImageRequest extends Request<Bitmap> {
// Then compute the dimensions we would ideally like to decode to.
int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight,
actualWidth, actualHeight);
actualWidth, actualHeight, mScaleType);
int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth,
actualHeight, actualWidth);
actualHeight, actualWidth, mScaleType);
// Decode to the nearest power of two scaling factor.
decodeOptions.inJustDecodeBounds = false;
......
......@@ -16,11 +16,14 @@
package org.telegram.android.volley.toolbox;
import org.json.JSONArray;
import org.json.JSONException;
import org.telegram.android.volley.NetworkResponse;
import org.telegram.android.volley.ParseError;
import org.telegram.android.volley.Response;
import org.telegram.android.volley.Response.ErrorListener;
import org.telegram.android.volley.Response.Listener;
import org.json.JSONArray;
import org.json.JSONException;
import java.io.UnsupportedEncodingException;
......@@ -35,15 +38,30 @@ public class JsonArrayRequest extends JsonRequest<JSONArray> {
* @param listener Listener to receive the JSON response
* @param errorListener Error listener, or null to ignore errors.
*/
public JsonArrayRequest(String url, Response.Listener<JSONArray> listener, Response.ErrorListener errorListener) {
public JsonArrayRequest(String url, Listener<JSONArray> listener, ErrorListener errorListener) {
super(Method.GET, url, null, listener, errorListener);
}
/**
* Creates a new request.
* @param method the HTTP method to use
* @param url URL to fetch the JSON from
* @param jsonRequest A {@link JSONArray} to post with the request. Null is allowed and
* indicates no parameters will be posted along with request.
* @param listener Listener to receive the JSON response
* @param errorListener Error listener, or null to ignore errors.
*/
public JsonArrayRequest(int method, String url, JSONArray jsonRequest,
Listener<JSONArray> listener, ErrorListener errorListener) {
super(method, url, (jsonRequest == null) ? null : jsonRequest.toString(), listener,
errorListener);
}
@Override
protected Response<JSONArray> parseNetworkResponse(NetworkResponse response) {
try {
String jsonString =
new String(response.data, HttpHeaderParser.parseCharset(response.headers));
String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
return Response.success(new JSONArray(jsonString),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
......
......@@ -16,11 +16,14 @@
package org.telegram.android.volley.toolbox;
import org.json.JSONException;
import org.json.JSONObject;
import org.telegram.android.volley.NetworkResponse;
import org.telegram.android.volley.ParseError;
import org.telegram.android.volley.Response;
import org.telegram.android.volley.Response.ErrorListener;
import org.telegram.android.volley.Response.Listener;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.UnsupportedEncodingException;
......@@ -40,7 +43,7 @@ public class JsonObjectRequest extends JsonRequest<JSONObject> {
* @param errorListener Error listener, or null to ignore errors.
*/
public JsonObjectRequest(int method, String url, JSONObject jsonRequest,
Response.Listener<JSONObject> listener, Response.ErrorListener errorListener) {
Listener<JSONObject> listener, ErrorListener errorListener) {
super(method, url, (jsonRequest == null) ? null : jsonRequest.toString(), listener,
errorListener);
}
......@@ -49,9 +52,10 @@ public class JsonObjectRequest extends JsonRequest<JSONObject> {
* Constructor which defaults to <code>GET</code> if <code>jsonRequest</code> is
* <code>null</code>, <code>POST</code> otherwise.
*
* @see #JsonObjectRequest(int, String, JSONObject, Listener, ErrorListener)
*/
public JsonObjectRequest(String url, JSONObject jsonRequest, Response.Listener<JSONObject> listener,
Response.ErrorListener errorListener) {
public JsonObjectRequest(String url, JSONObject jsonRequest, Listener<JSONObject> listener,
ErrorListener errorListener) {
this(jsonRequest == null ? Method.GET : Method.POST, url, jsonRequest,
listener, errorListener);
}
......@@ -59,8 +63,8 @@ public class JsonObjectRequest extends JsonRequest<JSONObject> {
@Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
try {
String jsonString =
new String(response.data, HttpHeaderParser.parseCharset(response.headers));
String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
return Response.success(new JSONObject(jsonString),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
......
......@@ -19,6 +19,8 @@ package org.telegram.android.volley.toolbox;
import org.telegram.android.volley.NetworkResponse;
import org.telegram.android.volley.Request;
import org.telegram.android.volley.Response;
import org.telegram.android.volley.Response.ErrorListener;
import org.telegram.android.volley.Response.Listener;
import org.telegram.android.volley.VolleyLog;
import java.io.UnsupportedEncodingException;
......@@ -30,28 +32,29 @@ import java.io.UnsupportedEncodingException;
* @param <T> JSON type of response expected
*/
public abstract class JsonRequest<T> extends Request<T> {
/** Charset for request. */
private static final String PROTOCOL_CHARSET = "utf-8";
/** Default charset for JSON request. */
protected static final String PROTOCOL_CHARSET = "utf-8";
/** Content type for request. */
private static final String PROTOCOL_CONTENT_TYPE =
String.format("application/json; charset=%s", PROTOCOL_CHARSET);
private final Response.Listener<T> mListener;
private final Listener<T> mListener;
private final String mRequestBody;
/**
* Deprecated constructor for a JsonRequest which defaults to GET unless {@link #getPostBody()}
* or {@link #getPostParams()} is overridden (which defaults to POST).
*
* @deprecated Use {@link #JsonRequest(int, String, String, Listener, ErrorListener)}.
*/
public JsonRequest(String url, String requestBody, Response.Listener<T> listener,
Response.ErrorListener errorListener) {
public JsonRequest(String url, String requestBody, Listener<T> listener,
ErrorListener errorListener) {
this(Method.DEPRECATED_GET_OR_POST, url, requestBody, listener, errorListener);
}
public JsonRequest(int method, String url, String requestBody, Response.Listener<T> listener,
Response.ErrorListener errorListener) {
public JsonRequest(int method, String url, String requestBody, Listener<T> listener,
ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
mRequestBody = requestBody;
......
......@@ -103,6 +103,7 @@ public class NetworkImageView extends ImageView {
void loadImageIfNecessary(final boolean isInLayoutPass) {
int width = getWidth();
int height = getHeight();
ScaleType scaleType = getScaleType();
boolean wrapWidth = false, wrapHeight = false;
if (getLayoutParams() != null) {
......@@ -177,7 +178,7 @@ public class NetworkImageView extends ImageView {
setImageResource(mDefaultImageId);
}
}
}, maxWidth, maxHeight);
}, maxWidth, maxHeight, scaleType);
// update the ImageContainer to be the new bitmap container.
mImageContainer = newContainer;
......
......@@ -25,7 +25,7 @@ import java.io.IOException;
*/
public class PoolingByteArrayOutputStream extends ByteArrayOutputStream {
/**
* If the {@link #PoolingByteArrayOutputStream(org.telegram.android.volley.toolbox.ByteArrayPool)} constructor is called, this is
* If the {@link #PoolingByteArrayOutputStream(ByteArrayPool)} constructor is called, this is
* the default size to which the underlying byte array is initialized.
*/
private static final int DEFAULT_SIZE = 256;
......
......@@ -126,7 +126,10 @@ public class RequestFuture<T> implements Future<T>, Response.Listener<T>,
@Override
public boolean isCancelled() {
return mRequest != null && mRequest.isCanceled();
if (mRequest == null) {
return false;
}
return mRequest.isCanceled();
}
@Override
......
......@@ -19,6 +19,8 @@ package org.telegram.android.volley.toolbox;
import org.telegram.android.volley.NetworkResponse;
import org.telegram.android.volley.Request;
import org.telegram.android.volley.Response;
import org.telegram.android.volley.Response.ErrorListener;
import org.telegram.android.volley.Response.Listener;
import java.io.UnsupportedEncodingException;
......@@ -26,7 +28,7 @@ import java.io.UnsupportedEncodingException;
* A canned request for retrieving the response body at a given URL as a String.
*/
public class StringRequest extends Request<String> {
private final Response.Listener<String> mListener;
private final Listener<String> mListener;
/**
* Creates a new request with the given method.
......@@ -36,8 +38,8 @@ public class StringRequest extends Request<String> {
* @param listener Listener to receive the String response
* @param errorListener Error listener, or null to ignore errors
*/
public StringRequest(int method, String url, Response.Listener<String> listener,
Response.ErrorListener errorListener) {
public StringRequest(int method, String url, Listener<String> listener,
ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
}
......@@ -49,7 +51,7 @@ public class StringRequest extends Request<String> {
* @param listener Listener to receive the String response
* @param errorListener Error listener, or null to ignore errors
*/
public StringRequest(String url, Response.Listener<String> listener, Response.ErrorListener errorListener) {
public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
this(Method.GET, url, listener, errorListener);
}
......
......@@ -48,7 +48,6 @@ public class Volley {
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
/**/
}
if (stack == null) {
......
......@@ -113,7 +113,7 @@ public class BuffersStorage {
arrayToReuse = freeBuffers128;
} else if (buffer.buffer.capacity() == 1024 + 200) {
arrayToReuse = freeBuffers1024;
} if (buffer.buffer.capacity() == 4096 + 200) {
} else if (buffer.buffer.capacity() == 4096 + 200) {
arrayToReuse = freeBuffers4096;
} else if (buffer.buffer.capacity() == 16384 + 200) {
arrayToReuse = freeBuffers16384;
......
......@@ -10,7 +10,7 @@ package org.telegram.messenger;
public class BuildVars {
public static boolean DEBUG_VERSION = false;
public static int BUILD_VERSION = 572;
public static int BUILD_VERSION = 586;
public static int APP_ID = 0; //obtain your own APP_ID at https://core.telegram.org/api/obtaining_api_id
public static String APP_HASH = ""; //obtain your own APP_HASH at https://core.telegram.org/api/obtaining_api_id
public static String HOCKEY_APP_HASH = "your-hockeyapp-api-key-here";
......
......@@ -227,6 +227,11 @@ public class FileLoadOperation {
downloadedBytes = (int)cacheFileTemp.length();
nextDownloadOffset = downloadedBytes = downloadedBytes / currentDownloadChunkSize * currentDownloadChunkSize;
}
if (BuildVars.DEBUG_VERSION) {
FileLog.d("tmessages", "start loading file to temp = " + cacheFileTemp + " final = " + cacheFileFinal);
}
if (fileNameIv != null) {
cacheIvTemp = new File(tempPath, fileNameIv);
try {
......@@ -291,6 +296,9 @@ public class FileLoadOperation {
return;
}
state = stateFailed;
if (BuildVars.DEBUG_VERSION) {
FileLog.e("tmessages", "cancel downloading file to " + cacheFileFinal);
}
cleanup();
for (RequestInfo requestInfo : requestInfos) {
if (requestInfo.requestToken != 0) {
......@@ -340,9 +348,15 @@ public class FileLoadOperation {
}
if (cacheFileTemp != null) {
if (!cacheFileTemp.renameTo(cacheFileFinal)) {
if (BuildVars.DEBUG_VERSION) {
FileLog.e("tmessages", "unable to rename temp = " + cacheFileTemp + " to final = " + cacheFileFinal);
}
cacheFileFinal = cacheFileTemp;
}
}
if (BuildVars.DEBUG_VERSION) {
FileLog.e("tmessages", "finished downloading file to " + cacheFileFinal);
}
delegate.didFinishLoadingFile(FileLoadOperation.this, cacheFileFinal);
}
......
......@@ -573,6 +573,14 @@ public class FileLoader {
return getPathToAttach(sizeFull);
}
}
} else if (message.media instanceof TLRPC.TL_messageMediaWebPage && message.media.webpage.photo != null) {
ArrayList<TLRPC.PhotoSize> sizes = message.media.webpage.photo.sizes;
if (sizes.size() > 0) {
TLRPC.PhotoSize sizeFull = getClosestPhotoSizeWithSize(sizes, AndroidUtilities.getPhotoSize());
if (sizeFull != null) {
return getPathToAttach(sizeFull);
}
}
}
}
return new File("");
......@@ -617,7 +625,7 @@ public class FileLoader {
}
} else if (attach instanceof TLRPC.PhotoSize) {
TLRPC.PhotoSize photoSize = (TLRPC.PhotoSize) attach;
if (photoSize.location == null || photoSize.location.key != null || photoSize.location.volume_id == Integer.MIN_VALUE && photoSize.location.local_id < 0) {
if (photoSize.location == null || photoSize.location.key != null || photoSize.location.volume_id == Integer.MIN_VALUE && photoSize.location.local_id < 0 || photoSize.size < 0) {
dir = getInstance().getDirectory(MEDIA_DIR_CACHE);
} else {
dir = getInstance().getDirectory(MEDIA_DIR_IMAGE);
......
......@@ -89,6 +89,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
final Object lock = new Object();
static ArrayList<HashMap<String, Object>> serverPublicKeys = null;
HashMap<String, Object> selectPublicKey(ArrayList<Long> fingerprints) {
synchronized (lock) {
if (serverPublicKeys == null) {
......@@ -150,7 +151,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
}
for (HashMap<String, Object> keyDesc : serverPublicKeys) {
long keyFingerprint = (Long)keyDesc.get("fingerprint");
long keyFingerprint = (Long) keyDesc.get("fingerprint");
for (long nFingerprint : fingerprints) {
if (nFingerprint == keyFingerprint) {
return keyDesc;
......@@ -161,7 +162,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
}
long generateMessageId() {
long messageId = (long)((((double)System.currentTimeMillis()) * 4294967296.0) / 1000.0);
long messageId = (long) ((((double) System.currentTimeMillis()) * 4294967296.0) / 1000.0);
if (messageId <= lastOutgoingMessageId) {
messageId = lastOutgoingMessageId + 1;
}
......@@ -197,7 +198,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
}
processedPQRes = true;
final TLRPC.TL_resPQ resPq = (TLRPC.TL_resPQ)message;
final TLRPC.TL_resPQ resPq = (TLRPC.TL_resPQ) message;
if (Utilities.arraysEquals(authNonce, 0, resPq.nonce, 0)) {
final HashMap<String, Object> publicKey = selectPublicKey(resPq.server_public_key_fingerprints);
if (publicKey == null) {
......@@ -221,11 +222,11 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
@Override
public void run() {
ByteBuffer pBytes = ByteBuffer.allocate(4);
pBytes.putInt((int)factorizedPq.p);
pBytes.putInt((int) factorizedPq.p);
byte[] pData = pBytes.array();
ByteBuffer qBytes = ByteBuffer.allocate(4);
qBytes.putInt((int)factorizedPq.q);
qBytes.putInt((int) factorizedPq.q);
byte[] qData = qBytes.array();
TLRPC.TL_req_DH_params reqDH = new TLRPC.TL_req_DH_params();
......@@ -233,7 +234,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
reqDH.server_nonce = authServerNonce;
reqDH.p = pData;
reqDH.q = qData;
reqDH.public_key_fingerprint = (Long)publicKey.get("fingerprint");
reqDH.public_key_fingerprint = (Long) publicKey.get("fingerprint");
SerializedData os = new SerializedData();
......@@ -261,7 +262,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
dataWithHash.writeByte(b[0]);
}
byte[] encryptedBytes = Utilities.encryptWithRSA((BigInteger[])publicKey.get("key"), dataWithHash.toByteArray());
byte[] encryptedBytes = Utilities.encryptWithRSA((BigInteger[]) publicKey.get("key"), dataWithHash.toByteArray());
dataWithHash.cleanup();
SerializedData encryptedData = new SerializedData();
encryptedData.writeRaw(encryptedBytes);
......@@ -300,7 +301,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
if (authNewNonce == null) {
return;
}
TLRPC.TL_server_DH_params_ok serverDhParams = (TLRPC.TL_server_DH_params_ok)message;
TLRPC.TL_server_DH_params_ok serverDhParams = (TLRPC.TL_server_DH_params_ok) message;
SerializedData tmpAesKey = new SerializedData();
......@@ -423,17 +424,17 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
for (int i = 7; i >= 0; i--) {
byte a_ = authNewNonce[i];
byte b_ = authServerNonce[i];
byte x = (byte)(a_ ^ b_);
byte x = (byte) (a_ ^ b_);
serverSaltData.writeByte(x);
}
ByteBuffer saltBuffer = ByteBuffer.wrap(serverSaltData.toByteArray());
serverSaltData.cleanup();
timeDifference = dhInnerData.server_time - (int)(System.currentTimeMillis() / 1000);
timeDifference = dhInnerData.server_time - (int) (System.currentTimeMillis() / 1000);
serverSalt = new ServerSalt();
serverSalt.validSince = (int)(System.currentTimeMillis() / 1000) + timeDifference;
serverSalt.validUntil = (int)(System.currentTimeMillis() / 1000) + timeDifference + 30 * 60;
serverSalt.validSince = (int) (System.currentTimeMillis() / 1000) + timeDifference;
serverSalt.validUntil = (int) (System.currentTimeMillis() / 1000) + timeDifference + 30 * 60;
serverSalt.value = saltBuffer.getLong();
FileLog.d("tmessages", String.format(Locale.US, "===== Time difference: %d", timeDifference));
......@@ -489,7 +490,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
setClientDHParamsMsgData = null;
}
TLRPC.Set_client_DH_params_answer dhAnswer = (TLRPC.Set_client_DH_params_answer)message;
TLRPC.Set_client_DH_params_answer dhAnswer = (TLRPC.Set_client_DH_params_answer) message;
if (!Utilities.arraysEquals(authNonce, 0, dhAnswer.nonce, 0)) {
FileLog.e("tmessages", "***** Invalid DH answer nonce");
......@@ -544,7 +545,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
System.arraycopy(newNonceHash3Full, newNonceHash3Full.length - 16, newNonceHash3, 0, 16);
if (message instanceof TLRPC.TL_dh_gen_ok) {
TLRPC.TL_dh_gen_ok dhGenOk = (TLRPC.TL_dh_gen_ok)message;
TLRPC.TL_dh_gen_ok dhGenOk = (TLRPC.TL_dh_gen_ok) message;
if (!Utilities.arraysEquals(newNonceHash1, 0, dhGenOk.new_nonce_hash1, 0)) {
FileLog.e("tmessages", "***** Invalid DH answer nonce hash 1");
beginHandshake(false);
......@@ -569,7 +570,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
}
});
} else if (message instanceof TLRPC.TL_dh_gen_retry) {
TLRPC.TL_dh_gen_retry dhRetry = (TLRPC.TL_dh_gen_retry)message;
TLRPC.TL_dh_gen_retry dhRetry = (TLRPC.TL_dh_gen_retry) message;
if (!Utilities.arraysEquals(newNonceHash2, 0, dhRetry.new_nonce_hash2, 0)) {
FileLog.e("tmessages", "***** Invalid DH answer nonce hash 2");
beginHandshake(false);
......@@ -578,7 +579,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
FileLog.d("tmessages", "***** Retry DH");
beginHandshake(false);
} else if (message instanceof TLRPC.TL_dh_gen_fail) {
TLRPC.TL_dh_gen_fail dhFail = (TLRPC.TL_dh_gen_fail)message;
TLRPC.TL_dh_gen_fail dhFail = (TLRPC.TL_dh_gen_fail) message;
if (!Utilities.arraysEquals(newNonceHash3, 0, dhFail.new_nonce_hash3, 0)) {
FileLog.e("tmessages", "***** Invalid DH answer nonce hash 3");
beginHandshake(false);
......
......@@ -11729,6 +11729,15 @@ public class TLRPC {
}
}
public static class TL_inputMessagesFilterUrl extends MessagesFilter {
public static int constructor = 0x7ef0dd87;
public void serializeToStream(AbsSerializedData stream) {
stream.writeInt32(constructor);
}
}
public static class TL_inputMessagesFilterPhotoVideo extends MessagesFilter {
public static int constructor = 0x56e9f0e4;
......
......@@ -300,10 +300,6 @@ public class TcpConnection extends ConnectionContext {
}
}
public void resumeConnection() {
}
private void reconnect() {
suspendConnection(false);
connectionState = TcpConnectionState.TcpConnectionStageReconnecting;
......
......@@ -39,6 +39,7 @@ public class UserConfig {
public static int lastPauseTime = 0;
public static boolean isWaitingForPasscodeEnter = false;
public static int lastUpdateVersion;
public static int lastContactsSyncTime;
public static int getNewMessageId() {
int id;
......@@ -76,6 +77,7 @@ public class UserConfig {
editor.putInt("autoLockIn", autoLockIn);
editor.putInt("lastPauseTime", lastPauseTime);
editor.putInt("lastUpdateVersion", lastUpdateVersion);
editor.putInt("lastContactsSyncTime", lastContactsSyncTime);
if (currentUser != null) {
if (withFile) {
......@@ -206,6 +208,7 @@ public class UserConfig {
autoLockIn = preferences.getInt("autoLockIn", 60 * 60);
lastPauseTime = preferences.getInt("lastPauseTime", 0);
lastUpdateVersion = preferences.getInt("lastUpdateVersion", 511);
lastContactsSyncTime = preferences.getInt("lastContactsSyncTime", (int) (System.currentTimeMillis() / 1000) - 23 * 60 * 60);
String user = preferences.getString("user", null);
if (user != null) {
byte[] userBytes = Base64.decode(user, Base64.DEFAULT);
......@@ -279,6 +282,7 @@ public class UserConfig {
lastPauseTime = 0;
isWaitingForPasscodeEnter = false;
lastUpdateVersion = BuildVars.BUILD_VERSION;
lastContactsSyncTime = (int) (System.currentTimeMillis() / 1000) - 23 * 60 * 60;
saveConfig(true);
}
}
......@@ -135,8 +135,8 @@ public class ActionBarPopupWindow extends PopupWindow {
if (child.getVisibility() != VISIBLE) {
continue;
}
int position = positions.get(child);
if (height - (position * AndroidUtilities.dp(48) + AndroidUtilities.dp(32)) > value * height) {
Integer position = positions.get(child);
if (position != null && height - (position * AndroidUtilities.dp(48) + AndroidUtilities.dp(32)) > value * height) {
break;
}
lastStartedChild = a - 1;
......@@ -148,8 +148,8 @@ public class ActionBarPopupWindow extends PopupWindow {
if (child.getVisibility() != VISIBLE) {
continue;
}
int position = positions.get(child);
if ((position + 1) * AndroidUtilities.dp(48) - AndroidUtilities.dp(24) > value * height) {
Integer position = positions.get(child);
if (position != null && (position + 1) * AndroidUtilities.dp(48) - AndroidUtilities.dp(24) > value * height) {
break;
}
lastStartedChild = a + 1;
......
......@@ -219,6 +219,14 @@ public class BottomSheet extends Dialog {
containerView.measure(MeasureSpec.makeMeasureSpec(width + left * 2, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
}
}
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() == GONE || child == containerView) {
continue;
}
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
}
}
@Override
......@@ -228,6 +236,56 @@ public class BottomSheet extends Dialog {
int t = (bottom - top) - containerView.getMeasuredHeight();
containerView.layout(l, t, l + containerView.getMeasuredWidth(), t + getMeasuredHeight());
}
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE || child == containerView) {
continue;
}
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
int childLeft;
int childTop;
int gravity = lp.gravity;
if (gravity == -1) {
gravity = Gravity.TOP | Gravity.LEFT;
}
final int absoluteGravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = (right - left - width) / 2 + lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
childLeft = right - width - lp.rightMargin;
break;
case Gravity.LEFT:
default:
childLeft = lp.leftMargin;
}
switch (verticalGravity) {
case Gravity.TOP:
childTop = lp.topMargin;
break;
case Gravity.CENTER_VERTICAL:
childTop = (bottom - top - height) / 2 + lp.topMargin - lp.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = (bottom - top) - height - lp.bottomMargin;
break;
default:
childTop = lp.topMargin;
}
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
};
container.setOnTouchListener(new View.OnTouchListener() {
......@@ -371,15 +429,6 @@ public class BottomSheet extends Dialog {
params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
}
getWindow().setAttributes(params);
setOnShowListener(new OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
if (Build.VERSION.SDK_INT >= 21) {
startOpenAnimation();
}
}
});
}
@Override
......@@ -394,7 +443,14 @@ public class BottomSheet extends Dialog {
int left = useRevealAnimation && Build.VERSION.SDK_INT <= 19 ? 0 : backgroundPaddingLeft;
int top = useRevealAnimation && Build.VERSION.SDK_INT <= 19 ? 0 : backgroundPaddingTop;
containerView.setPadding(left, (applyTopPaddings ? AndroidUtilities.dp(8) : 0) + top, left, (applyTopPaddings ? AndroidUtilities.dp(isGrid ? 16 : 8) : 0));
if (Build.VERSION.SDK_INT < 21) {
if (Build.VERSION.SDK_INT >= 21) {
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
startOpenAnimation();
}
});
} else {
startOpenAnimation();
}
}
......@@ -413,7 +469,6 @@ public class BottomSheet extends Dialog {
@SuppressLint("NewApi")
private void startRevealAnimation(final boolean open) {
if (open) {
backgroundDrawable.setAlpha(0);
containerView.setVisibility(View.VISIBLE);
......@@ -460,7 +515,11 @@ public class BottomSheet extends Dialog {
animators.add(ObjectAnimator.ofInt(backgroundDrawable, "alpha", open ? 51 : 0));
if (Build.VERSION.SDK_INT >= 21) {
containerView.setElevation(AndroidUtilities.dp(10));
animators.add(ViewAnimationUtils.createCircularReveal(containerView, revealX <= containerView.getMeasuredWidth() ? revealX : containerView.getMeasuredWidth(), revealY, open ? 0 : finalRevealRadius, open ? finalRevealRadius : 0));
try {
animators.add(ViewAnimationUtils.createCircularReveal(containerView, revealX <= containerView.getMeasuredWidth() ? revealX : containerView.getMeasuredWidth(), revealY, open ? 0 : finalRevealRadius, open ? finalRevealRadius : 0));
} catch (Exception e) {
FileLog.e("tmessages", e);
}
animatorSet.setDuration(300);
} else {
if (!open) {
......
......@@ -208,6 +208,7 @@ public class BaseLocationAdapter extends BaseFragmentAdapter {
}
}
});
jsonObjReq.setShouldCache(false);
jsonObjReq.setTag("search");
requestQueue.add(jsonObjReq);
} catch (Exception e) {
......
......@@ -13,7 +13,6 @@ import android.view.View;
import android.view.ViewGroup;
import org.telegram.android.MediaController;
import org.telegram.android.NotificationCenter;
import org.telegram.android.support.widget.RecyclerView;
import org.telegram.ui.Cells.PhotoAttachPhotoCell;
......@@ -73,24 +72,27 @@ public class PhotoAttachAdapter extends RecyclerView.Adapter {
view = new PhotoAttachCameraCell(mContext);
} else {*/
PhotoAttachPhotoCell cell = new PhotoAttachPhotoCell(mContext);
cell.setOnCheckClickLisnener(new View.OnClickListener() {
/*cell.setOnCheckClickLisnener(new View.OnClickListener() {
@Override
public void onClick(View v) {
PhotoAttachPhotoCell cell = (PhotoAttachPhotoCell) v.getParent();
MediaController.PhotoEntry photoEntry = cell.getPhotoEntry();
if (selectedPhotos.containsKey(photoEntry.imageId)) {
selectedPhotos.remove(photoEntry.imageId);
cell.setChecked(false, true);
} else {
selectedPhotos.put(photoEntry.imageId, photoEntry);
cell.setChecked(true, true);
}
delegate.selectedPhotosChanged();
onItemClick((PhotoAttachPhotoCell) v.getParent());
}
});
view = cell;
view = cell;*/
//}
return new Holder(view);
return new Holder(cell);
}
public void onItemClick(PhotoAttachPhotoCell cell) {
MediaController.PhotoEntry photoEntry = cell.getPhotoEntry();
if (selectedPhotos.containsKey(photoEntry.imageId)) {
selectedPhotos.remove(photoEntry.imageId);
cell.setChecked(false, true);
} else {
selectedPhotos.put(photoEntry.imageId, photoEntry);
cell.setChecked(true, true);
}
delegate.selectedPhotosChanged();
}
@Override
......
......@@ -269,7 +269,7 @@ public class SearchAdapter extends BaseSearchAdapter {
((UserCell) view).setChecked(checkedMap.containsKey(user.id), false);
}
} else {
((ProfileSearchCell) view).setData(user, null, null, name, username);
((ProfileSearchCell) view).setData(user, null, null, name, username, false);
((ProfileSearchCell) view).useSeparator = (i != getCount() - 1 && i != searchResult.size() - 1);
if (ignoreUsers != null) {
if (ignoreUsers.containsKey(user.id)) {
......
......@@ -56,7 +56,9 @@ public class BaseCell extends View {
}
protected void setDrawableBounds(Drawable drawable, int x, int y, int w, int h) {
drawable.setBounds(x, y, x + w, y + h);
if (drawable != null) {
drawable.setBounds(x, y, x + w, y + h);
}
}
protected void startCheckLongPress() {
......
......@@ -42,7 +42,6 @@ public class BotHelpCell extends View {
private int height;
private int textX;
private int textY;
private int textXOffset;
private ClickableSpan pressedLink;
private LinkPath urlPath = new LinkPath();
......@@ -102,10 +101,8 @@ public class BotHelpCell extends View {
width = 0;
height = textLayout.getHeight() + AndroidUtilities.dp(4 + 18);
int count = textLayout.getLineCount();
textXOffset = Integer.MAX_VALUE;
for (int a = 0; a < count; a++) {
textXOffset = (int) Math.ceil(Math.min(textXOffset, textLayout.getLineLeft(a)));
width = (int) Math.ceil(Math.max(width, textLayout.getLineWidth(a) - textLayout.getLineLeft(a)));
width = (int) Math.ceil(Math.max(width, textLayout.getLineWidth(a) + textLayout.getLineLeft(a)));
}
width += AndroidUtilities.dp(4 + 18);
}
......@@ -188,7 +185,7 @@ public class BotHelpCell extends View {
ResourceLoader.backgroundMediaDrawableIn.setBounds(x, y, width + x, height + y);
ResourceLoader.backgroundMediaDrawableIn.draw(canvas);
canvas.save();
canvas.translate(textX = AndroidUtilities.dp(2 + 9) + x - textXOffset, textY = AndroidUtilities.dp(2 + 9) + y);
canvas.translate(textX = AndroidUtilities.dp(2 + 9) + x, textY = AndroidUtilities.dp(2 + 9) + y);
if (pressedLink != null) {
canvas.drawPath(urlPath, urlPaint);
}
......
......@@ -22,16 +22,18 @@ import org.telegram.android.AndroidUtilities;
import org.telegram.android.ImageLoader;
import org.telegram.android.MessagesController;
import org.telegram.android.SendMessagesHelper;
import org.telegram.messenger.BuildVars;
import org.telegram.messenger.FileLoader;
import org.telegram.android.MediaController;
import org.telegram.android.MessageObject;
import org.telegram.messenger.FileLog;
import org.telegram.ui.Components.RadialProgress;
import org.telegram.ui.Components.ResourceLoader;
import org.telegram.ui.Components.SeekBar;
import java.io.File;
public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelegate, MediaController.FileDownloadProgressListener {
public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelegate {
private static TextPaint timePaint;
private static Paint circlePaint;
......@@ -51,11 +53,8 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
private int timeWidth;
private String lastTimeString = null;
private int TAG;
public ChatAudioCell(Context context) {
super(context);
TAG = MediaController.getInstance().generateObserverTag();
seekBar = new SeekBar(context);
seekBar.delegate = this;
......@@ -217,6 +216,9 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
if (cacheFile == null) {
cacheFile = FileLoader.getPathToMessage(currentMessageObject.messageOwner);
}
if (BuildVars.DEBUG_VERSION) {
FileLog.d("tmessages", "looking for audio in " + cacheFile);
}
if (cacheFile.exists()) {
MediaController.getInstance().removeLoadingFileObserver(this);
boolean playing = MediaController.getInstance().isPlayingAudio(currentMessageObject);
......@@ -272,11 +274,6 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
radialProgress.setProgress(progress, true);
}
@Override
public int getObserverTag() {
return TAG;
}
@Override
public void onSeekBarDrag(float progress) {
if (currentMessageObject == null) {
......@@ -369,7 +366,7 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
timePaint.setColor(0xffa1aab3);
circlePaint.setColor(0xff4195e5);
}
radialProgress.onDraw(canvas);
radialProgress.draw(canvas);
canvas.save();
canvas.translate(timeX, AndroidUtilities.dp(42) + namesOffset);
......
......@@ -25,6 +25,7 @@ import android.view.SoundEffectConstants;
import org.telegram.android.AndroidUtilities;
import org.telegram.android.Emoji;
import org.telegram.android.LocaleController;
import org.telegram.android.MediaController;
import org.telegram.android.UserObject;
import org.telegram.messenger.ApplicationLoader;
import org.telegram.messenger.FileLoader;
......@@ -39,7 +40,7 @@ import org.telegram.ui.Components.LinkPath;
import org.telegram.ui.Components.ResourceLoader;
import org.telegram.ui.Components.StaticLayoutEx;
public class ChatBaseCell extends BaseCell {
public class ChatBaseCell extends BaseCell implements MediaController.FileDownloadProgressListener {
public interface ChatBaseCellDelegate {
void didPressedUserAvatar(ChatBaseCell cell, TLRPC.User user);
......@@ -48,6 +49,7 @@ public class ChatBaseCell extends BaseCell {
void didPressReplyMessage(ChatBaseCell cell, int id);
void didPressUrl(MessageObject messageObject, String url);
void needOpenWebView(String url, String title, String originalUrl, int w, int h);
void didClickedImage(ChatBaseCell cell);
boolean canPerformActions();
}
......@@ -55,6 +57,7 @@ public class ChatBaseCell extends BaseCell {
protected boolean linkPreviewPressed;
protected LinkPath urlPath = new LinkPath();
protected static Paint urlPaint;
private int TAG;
public boolean isChat = false;
protected boolean isPressed = false;
......@@ -170,6 +173,7 @@ public class ChatBaseCell extends BaseCell {
avatarImage.setRoundRadius(AndroidUtilities.dp(21));
avatarDrawable = new AvatarDrawable();
replyImageReceiver = new ImageReceiver(this);
TAG = MediaController.getInstance().generateObserverTag();
}
@Override
......@@ -600,6 +604,10 @@ public class ChatBaseCell extends BaseCell {
}
public ImageReceiver getPhotoImage() {
return null;
}
@Override
protected void onLongPress() {
if (delegate != null) {
......@@ -658,7 +666,7 @@ public class ChatBaseCell extends BaseCell {
setDrawableBounds(currentBackgroundDrawable, (!media ? 0 : AndroidUtilities.dp(9)), AndroidUtilities.dp(1), backgroundWidth, layoutHeight - AndroidUtilities.dp(2));
}
}
if (drawBackground) {
if (drawBackground && currentBackgroundDrawable != null) {
currentBackgroundDrawable.draw(canvas);
}
......@@ -858,4 +866,29 @@ public class ChatBaseCell extends BaseCell {
}
}
}
@Override
public void onFailedDownload(String fileName) {
}
@Override
public void onSuccessDownload(String fileName) {
}
@Override
public void onProgressDownload(String fileName, float progress) {
}
@Override
public void onProgressUpload(String fileName, float progress, boolean isEncrypted) {
}
@Override
public int getObserverTag() {
return TAG;
}
}
......@@ -46,11 +46,9 @@ import org.telegram.android.ImageReceiver;
import java.io.File;
import java.util.Locale;
public class ChatMediaCell extends ChatBaseCell implements MediaController.FileDownloadProgressListener {
public class ChatMediaCell extends ChatBaseCell {
public interface ChatMediaCellDelegate {
void didClickedImage(ChatMediaCell cell);
void didPressedOther(ChatMediaCell cell);
}
......@@ -78,8 +76,6 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD
private boolean allowedToSetPhoto = true;
private int TAG;
private int buttonState = 0;
private int buttonPressed = 0;
private boolean imagePressed = false;
......@@ -127,8 +123,6 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD
locationAddressPaint.setTextSize(AndroidUtilities.dp(14));
}
TAG = MediaController.getInstance().generateObserverTag();
photoImage = new ImageReceiver(this);
radialProgress = new RadialProgress(this);
}
......@@ -343,8 +337,8 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD
private void didClickedImage() {
if (currentMessageObject.type == 1) {
if (buttonState == -1) {
if (mediaDelegate != null) {
mediaDelegate.didClickedImage(this);
if (delegate != null) {
delegate.didClickedImage(this);
}
} else if (buttonState == 0) {
didPressedButton(false);
......@@ -365,13 +359,13 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD
didPressedButton(false);
}
} else if (currentMessageObject.type == 4) {
if (mediaDelegate != null) {
mediaDelegate.didClickedImage(this);
if (delegate != null) {
delegate.didClickedImage(this);
}
} else if (currentMessageObject.type == 9) {
if (buttonState == -1) {
if (mediaDelegate != null) {
mediaDelegate.didClickedImage(this);
if (delegate != null) {
delegate.didClickedImage(this);
}
}
}
......@@ -447,8 +441,8 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD
radialProgress.setBackground(getDrawableForCurrentState(), false, animated);
}
} else if (buttonState == 3) {
if (mediaDelegate != null) {
mediaDelegate.didClickedImage(this);
if (delegate != null) {
delegate.didClickedImage(this);
}
}
}
......@@ -828,6 +822,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD
updateButtonState(dataChanged);
}
@Override
public ImageReceiver getPhotoImage() {
return photoImage;
}
......@@ -1066,7 +1061,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD
}
}
radialProgress.onDraw(canvas);
radialProgress.draw(canvas);
if (currentMessageObject.type == 1 || currentMessageObject.type == 3) {
if (nameLayout != null) {
......@@ -1155,9 +1150,4 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD
public void onProgressUpload(String fileName, float progress, boolean isEncrypted) {
radialProgress.setProgress(progress, true);
}
@Override
public int getObserverTag() {
return TAG;
}
}
......@@ -32,7 +32,7 @@ import org.telegram.ui.Components.SeekBar;
import java.io.File;
public class ChatMusicCell extends ChatBaseCell implements SeekBar.SeekBarDelegate, MediaController.FileDownloadProgressListener {
public class ChatMusicCell extends ChatBaseCell implements SeekBar.SeekBarDelegate {
public interface ChatMusicCellDelegate {
boolean needPlayMusic(MessageObject messageObject);
......@@ -62,13 +62,10 @@ public class ChatMusicCell extends ChatBaseCell implements SeekBar.SeekBarDelega
private StaticLayout authorLayout;
private int authorX;
private int TAG;
private ChatMusicCellDelegate musicDelegate;
public ChatMusicCell(Context context) {
super(context);
TAG = MediaController.getInstance().generateObserverTag();
seekBar = new SeekBar(context);
seekBar.delegate = this;
......@@ -299,11 +296,6 @@ public class ChatMusicCell extends ChatBaseCell implements SeekBar.SeekBarDelega
radialProgress.setProgress(progress, true);
}
@Override
public int getObserverTag() {
return TAG;
}
@Override
public void onSeekBarDrag(float progress) {
if (currentMessageObject == null) {
......@@ -403,7 +395,7 @@ public class ChatMusicCell extends ChatBaseCell implements SeekBar.SeekBarDelega
} else {
timePaint.setColor(0xffa1aab3);
}
radialProgress.onDraw(canvas);
radialProgress.draw(canvas);
canvas.save();
canvas.translate(timeX + titleX, AndroidUtilities.dp(12) + namesOffset);
......
......@@ -37,9 +37,11 @@ public class ProfileSearchCell extends BaseCell {
private static TextPaint nameEncryptedPaint;
private static TextPaint onlinePaint;
private static TextPaint offlinePaint;
private static TextPaint countPaint;
private static Drawable lockDrawable;
private static Drawable broadcastDrawable;
private static Drawable groupDrawable;
private static Drawable countDrawable;
private static Paint linePaint;
private CharSequence currentName;
......@@ -50,6 +52,7 @@ public class ProfileSearchCell extends BaseCell {
private TLRPC.User user = null;
private TLRPC.Chat chat = null;
private TLRPC.EncryptedChat encryptedChat = null;
long dialog_id;
private String lastName = null;
private int lastStatus = 0;
......@@ -67,6 +70,13 @@ public class ProfileSearchCell extends BaseCell {
private int nameLockLeft;
private int nameLockTop;
private boolean drawCount;
private int lastUnreadCount;
private int countTop = AndroidUtilities.dp(25);
private int countLeft;
private int countWidth;
private StaticLayout countLayout;
private int onlineLeft;
private StaticLayout onlineLayout;
......@@ -95,9 +105,15 @@ public class ProfileSearchCell extends BaseCell {
linePaint = new Paint();
linePaint.setColor(0xffdcdcdc);
countPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
countPaint.setTextSize(AndroidUtilities.dp(13));
countPaint.setColor(0xffffffff);
countPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
broadcastDrawable = getResources().getDrawable(R.drawable.list_broadcast);
lockDrawable = getResources().getDrawable(R.drawable.list_secret);
groupDrawable = getResources().getDrawable(R.drawable.list_group);
countDrawable = getResources().getDrawable(R.drawable.dialogs_badge);
}
avatarImage = new ImageReceiver(this);
......@@ -115,12 +131,13 @@ public class ProfileSearchCell extends BaseCell {
return super.onTouchEvent(event);
}
public void setData(TLRPC.User u, TLRPC.Chat c, TLRPC.EncryptedChat ec, CharSequence n, CharSequence s) {
public void setData(TLRPC.User u, TLRPC.Chat c, TLRPC.EncryptedChat ec, CharSequence n, CharSequence s, boolean needCount) {
currentName = n;
user = u;
chat = c;
encryptedChat = ec;
subLabel = s;
drawCount = needCount;
update(0);
}
......@@ -162,6 +179,7 @@ public class ProfileSearchCell extends BaseCell {
if (encryptedChat != null) {
drawNameLock = true;
dialog_id = ((long) encryptedChat.id) << 32;
if (!LocaleController.isRTL) {
nameLockLeft = AndroidUtilities.dp(AndroidUtilities.leftBaseline);
nameLeft = AndroidUtilities.dp(AndroidUtilities.leftBaseline + 4) + lockDrawable.getIntrinsicWidth();
......@@ -173,9 +191,11 @@ public class ProfileSearchCell extends BaseCell {
} else {
if (chat != null) {
if (chat.id < 0) {
dialog_id = AndroidUtilities.makeBroadcastId(chat.id);
drawNameBroadcast = true;
nameLockTop = AndroidUtilities.dp(28.5f);
} else {
dialog_id = -chat.id;
drawNameGroup = true;
nameLockTop = AndroidUtilities.dp(30);
}
......@@ -187,6 +207,7 @@ public class ProfileSearchCell extends BaseCell {
nameLeft = AndroidUtilities.dp(11);
}
} else {
dialog_id = user.id;
if (!LocaleController.isRTL) {
nameLeft = AndroidUtilities.dp(AndroidUtilities.leftBaseline);
} else {
......@@ -234,6 +255,30 @@ public class ProfileSearchCell extends BaseCell {
nameWidth -= AndroidUtilities.dp(6) + groupDrawable.getIntrinsicWidth();
}
if (drawCount) {
TLRPC.TL_dialog dialog = MessagesController.getInstance().dialogs_dict.get(dialog_id);
if (dialog != null && dialog.unread_count != 0) {
lastUnreadCount = dialog.unread_count;
String countString = String.format("%d", dialog.unread_count);
countWidth = Math.max(AndroidUtilities.dp(12), (int) Math.ceil(countPaint.measureText(countString)));
countLayout = new StaticLayout(countString, countPaint, countWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false);
int w = countWidth + AndroidUtilities.dp(18);
nameWidth -= w;
if (!LocaleController.isRTL) {
countLeft = getMeasuredWidth() - countWidth - AndroidUtilities.dp(19);
} else {
countLeft = AndroidUtilities.dp(19);
nameLeft += w;
}
} else {
lastUnreadCount = 0;
countLayout = null;
}
} else {
lastUnreadCount = 0;
countLayout = null;
}
CharSequence nameStringFinal = TextUtils.ellipsize(nameString, currentNamePaint, nameWidth - AndroidUtilities.dp(12), TextUtils.TruncateAt.END);
nameLayout = new StaticLayout(nameStringFinal, currentNamePaint, nameWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
......@@ -244,12 +289,12 @@ public class ProfileSearchCell extends BaseCell {
onlineLeft = AndroidUtilities.dp(11);
}
CharSequence onlineString;
CharSequence onlineString = "";
TextPaint currentOnlinePaint = offlinePaint;
if (subLabel != null) {
onlineString = subLabel;
} else {
} else if (user != null) {
if ((user.flags & TLRPC.USER_FLAG_BOT) != 0) {
onlineString = LocaleController.getString("Bot", R.string.Bot);
} else {
......@@ -364,6 +409,12 @@ public class ProfileSearchCell extends BaseCell {
continueUpdate = true;
}
}
if (!continueUpdate && drawCount && (mask & MessagesController.UPDATE_MASK_READ_DIALOG_MESSAGE) != 0) {
TLRPC.TL_dialog dialog = MessagesController.getInstance().dialogs_dict.get(dialog_id);
if (dialog != null && dialog.unread_count != lastUnreadCount) {
continueUpdate = true;
}
}
if (!continueUpdate) {
return;
......@@ -422,10 +473,12 @@ public class ProfileSearchCell extends BaseCell {
broadcastDrawable.draw(canvas);
}
canvas.save();
canvas.translate(nameLeft, nameTop);
nameLayout.draw(canvas);
canvas.restore();
if (nameLayout != null) {
canvas.save();
canvas.translate(nameLeft, nameTop);
nameLayout.draw(canvas);
canvas.restore();
}
if (onlineLayout != null) {
canvas.save();
......@@ -434,6 +487,15 @@ public class ProfileSearchCell extends BaseCell {
canvas.restore();
}
if (countLayout != null) {
setDrawableBounds(countDrawable, countLeft - AndroidUtilities.dp(5.5f), countTop, countWidth + AndroidUtilities.dp(11), countDrawable.getIntrinsicHeight());
countDrawable.draw(canvas);
canvas.save();
canvas.translate(countLeft, countTop + AndroidUtilities.dp(4));
countLayout.draw(canvas);
canvas.restore();
}
avatarImage.draw(canvas);
}
}
......@@ -9,8 +9,12 @@
package org.telegram.ui.Cells;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Build;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.TextView;
......@@ -26,6 +30,13 @@ public class StickerEmojiCell extends FrameLayout {
private BackupImageView imageView;
private TLRPC.Document sticker;
private TextView emojiTextView;
private float alpha = 1;
private boolean changingAlpha;
private long lastUpdateTime;
private boolean scaled;
private float scale;
private long time = 0;
private AccelerateInterpolator interpolator = new AccelerateInterpolator(0.5f);
public StickerEmojiCell(Context context) {
super(context);
......@@ -39,15 +50,6 @@ public class StickerEmojiCell extends FrameLayout {
addView(emojiTextView, LayoutHelper.createFrame(28, 28, Gravity.BOTTOM | Gravity.RIGHT));
}
@Override
public void setPressed(boolean pressed) {
if (imageView.getImageReceiver().getPressed() != pressed) {
imageView.getImageReceiver().setPressed(pressed);
imageView.invalidate();
}
super.setPressed(pressed);
}
public TLRPC.Document getSticker() {
return sticker;
}
......@@ -57,7 +59,6 @@ public class StickerEmojiCell extends FrameLayout {
sticker = document;
imageView.setImage(document.thumb.location, null, "webp", null);
if (showEmoji) {
boolean set = false;
for (TLRPC.DocumentAttribute attribute : document.attributes) {
......@@ -78,4 +79,67 @@ public class StickerEmojiCell extends FrameLayout {
}
}
}
public void disable() {
changingAlpha = true;
alpha = 0.5f;
time = 0;
imageView.getImageReceiver().setAlpha(alpha);
imageView.invalidate();
lastUpdateTime = System.currentTimeMillis();
invalidate();
}
public void setScaled(boolean value) {
scaled = value;
lastUpdateTime = System.currentTimeMillis();
invalidate();
}
public boolean isDisabled() {
return changingAlpha;
}
public boolean showingBitmap() {
return imageView.getImageReceiver().getBitmap() != null;
}
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
boolean result = super.drawChild(canvas, child, drawingTime);
if (child == imageView && (changingAlpha || scaled && scale != 0.8f || !scaled && scale != 1.0f)) {
long newTime = System.currentTimeMillis();
long dt = (newTime - lastUpdateTime);
lastUpdateTime = newTime;
if (changingAlpha) {
time += dt;
if (time > 1050) {
time = 1050;
}
alpha = 0.5f + interpolator.getInterpolation(time / 1050.0f) * 0.5f;
if (alpha >= 1.0f) {
changingAlpha = false;
alpha = 1.0f;
}
imageView.getImageReceiver().setAlpha(alpha);
} else if (scaled && scale != 0.8f) {
scale -= dt / 400.0f;
if (scale < 0.8f) {
scale = 0.8f;
}
} else {
scale += dt / 400.0f;
if (scale > 1.0f) {
scale = 1.0f;
}
}
if (Build.VERSION.SDK_INT >= 11) {
imageView.setScaleX(scale);
imageView.setScaleY(scale);
}
imageView.invalidate();
invalidate();
}
return result;
}
}
......@@ -33,6 +33,7 @@ import org.telegram.android.NotificationCenter;
import org.telegram.android.support.widget.LinearLayoutManager;
import org.telegram.messenger.R;
import org.telegram.ui.Adapters.PhotoAttachAdapter;
import org.telegram.ui.Cells.PhotoAttachPhotoCell;
import org.telegram.ui.ChatActivity;
import java.util.ArrayList;
......@@ -130,6 +131,12 @@ public class ChatAttachView extends FrameLayout implements NotificationCenter.No
updatePhotosButton();
}
});
attachPhotoRecyclerView.setOnItemClickListener(new RecyclerListView.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
photoAttachAdapter.onItemClick((PhotoAttachPhotoCell) view);
}
});
views[9] = progressView = new EmptyTextProgressView(context);
progressView.setText(LocaleController.getString("NoPhotos", R.string.NoPhotos));
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment