Commit 3e901772 authored by DrKLO's avatar DrKLO

Update to 3.1.2

parent 82f9be23
...@@ -73,7 +73,7 @@ android { ...@@ -73,7 +73,7 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 8 minSdkVersion 8
targetSdkVersion 22 targetSdkVersion 22
versionCode 580 versionCode 586
versionName "3.1.1" versionName "3.1.2"
} }
} }
...@@ -939,10 +939,14 @@ public class ContactsController { ...@@ -939,10 +939,14 @@ public class ContactsController {
@Override @Override
public void run() { public void run() {
FileLog.e("tmessages", "done loading contacts"); 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); loadContacts(false, true);
return; return;
} }
if (from == 0) {
UserConfig.lastContactsSyncTime = (int) (System.currentTimeMillis() / 1000);
UserConfig.saveConfig(false);
}
for (TLRPC.TL_contact contact : contactsArr) { for (TLRPC.TL_contact contact : contactsArr) {
if (usersDict.get(contact.user_id) == null && contact.user_id != UserConfig.getClientUserId()) { if (usersDict.get(contact.user_id) == null && contact.user_id != UserConfig.getClientUserId()) {
......
...@@ -61,8 +61,6 @@ public class Emoji { ...@@ -61,8 +61,6 @@ public class Emoji {
}; };
public static long[][] data = { public static long[][] data = {
new long[]
{},
new long[]//189 new long[]//189
{ {
0x00000000D83DDE04L, 0x00000000D83DDE03L, 0x00000000D83DDE00L, 0x00000000D83DDE0AL, 0x000000000000263AL, 0x00000000D83DDE09L, 0x00000000D83DDE0DL, 0x00000000D83DDE04L, 0x00000000D83DDE03L, 0x00000000D83DDE00L, 0x00000000D83DDE0AL, 0x000000000000263AL, 0x00000000D83DDE09L, 0x00000000D83DDE0DL,
...@@ -215,10 +213,10 @@ public class Emoji { ...@@ -215,10 +213,10 @@ public class Emoji {
bigImgSize = AndroidUtilities.dp(32); 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++) { 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); 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 - 1))); rects.put(data[j][i], new DrawableInfo(rect, (byte) j));
} }
} }
placeholderPaint = new Paint(); placeholderPaint = new Paint();
......
...@@ -1206,30 +1206,11 @@ public class ImageLoader { ...@@ -1206,30 +1206,11 @@ public class ImageLoader {
telegramPath = new File(Environment.getExternalStorageDirectory(), "Telegram"); telegramPath = new File(Environment.getExternalStorageDirectory(), "Telegram");
telegramPath.mkdirs(); 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;
}
}
} catch (Exception e) {
FileLog.e("tmessages", e);
}
if (canRename) {
if (telegramPath.isDirectory()) { if (telegramPath.isDirectory()) {
try { try {
File imagePath = new File(telegramPath, "Telegram Images"); File imagePath = new File(telegramPath, "Telegram Images");
imagePath.mkdir(); imagePath.mkdir();
if (imagePath.isDirectory()) { if (imagePath.isDirectory() && canMoveFiles(cachePath, imagePath)) {
mediaDirs.put(FileLoader.MEDIA_DIR_IMAGE, imagePath); mediaDirs.put(FileLoader.MEDIA_DIR_IMAGE, imagePath);
FileLog.e("tmessages", "image path = " + imagePath); FileLog.e("tmessages", "image path = " + imagePath);
} }
...@@ -1240,7 +1221,7 @@ public class ImageLoader { ...@@ -1240,7 +1221,7 @@ public class ImageLoader {
try { try {
File videoPath = new File(telegramPath, "Telegram Video"); File videoPath = new File(telegramPath, "Telegram Video");
videoPath.mkdir(); videoPath.mkdir();
if (videoPath.isDirectory()) { if (videoPath.isDirectory() && canMoveFiles(cachePath, videoPath)) {
mediaDirs.put(FileLoader.MEDIA_DIR_VIDEO, videoPath); mediaDirs.put(FileLoader.MEDIA_DIR_VIDEO, videoPath);
FileLog.e("tmessages", "video path = " + videoPath); FileLog.e("tmessages", "video path = " + videoPath);
} }
...@@ -1251,7 +1232,7 @@ public class ImageLoader { ...@@ -1251,7 +1232,7 @@ public class ImageLoader {
try { try {
File audioPath = new File(telegramPath, "Telegram Audio"); File audioPath = new File(telegramPath, "Telegram Audio");
audioPath.mkdir(); audioPath.mkdir();
if (audioPath.isDirectory()) { if (audioPath.isDirectory() && canMoveFiles(cachePath, audioPath)) {
new File(audioPath, ".nomedia").createNewFile(); new File(audioPath, ".nomedia").createNewFile();
mediaDirs.put(FileLoader.MEDIA_DIR_AUDIO, audioPath); mediaDirs.put(FileLoader.MEDIA_DIR_AUDIO, audioPath);
FileLog.e("tmessages", "audio path = " + audioPath); FileLog.e("tmessages", "audio path = " + audioPath);
...@@ -1263,7 +1244,7 @@ public class ImageLoader { ...@@ -1263,7 +1244,7 @@ public class ImageLoader {
try { try {
File documentPath = new File(telegramPath, "Telegram Documents"); File documentPath = new File(telegramPath, "Telegram Documents");
documentPath.mkdir(); documentPath.mkdir();
if (documentPath.isDirectory()) { if (documentPath.isDirectory() && canMoveFiles(cachePath, documentPath)) {
new File(documentPath, ".nomedia").createNewFile(); new File(documentPath, ".nomedia").createNewFile();
mediaDirs.put(FileLoader.MEDIA_DIR_DOCUMENT, documentPath); mediaDirs.put(FileLoader.MEDIA_DIR_DOCUMENT, documentPath);
FileLog.e("tmessages", "documents path = " + documentPath); FileLog.e("tmessages", "documents path = " + documentPath);
...@@ -1275,7 +1256,6 @@ public class ImageLoader { ...@@ -1275,7 +1256,6 @@ public class ImageLoader {
} else { } else {
FileLog.e("tmessages", "this Android can't rename files"); FileLog.e("tmessages", "this Android can't rename files");
} }
}
MediaController.getInstance().checkSaveToGalleryFiles(); MediaController.getInstance().checkSaveToGalleryFiles();
} catch (Exception e) { } catch (Exception e) {
FileLog.e("tmessages", e); FileLog.e("tmessages", e);
...@@ -1284,6 +1264,38 @@ public class ImageLoader { ...@@ -1284,6 +1264,38 @@ public class ImageLoader {
return mediaDirs; 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) { public Float getFileProgress(String location) {
if (location == null) { if (location == null) {
return null; return null;
......
...@@ -1445,6 +1445,9 @@ public class MediaController implements NotificationCenter.NotificationCenterDel ...@@ -1445,6 +1445,9 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
} }
private void buildShuffledPlayList() { private void buildShuffledPlayList() {
if (playlist.isEmpty()) {
return;
}
ArrayList<MessageObject> all = new ArrayList<>(playlist); ArrayList<MessageObject> all = new ArrayList<>(playlist);
shuffledPlaylist.clear(); shuffledPlaylist.clear();
...@@ -2492,7 +2495,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel ...@@ -2492,7 +2495,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
albumEntry.addPhoto(photoEntry); albumEntry.addPhoto(photoEntry);
} }
} }
} catch (Exception e) { } catch (Throwable e) {
FileLog.e("tmessages", e); FileLog.e("tmessages", e);
} finally { } finally {
if (cursor != null) { if (cursor != null) {
...@@ -2551,7 +2554,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel ...@@ -2551,7 +2554,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
albumEntry.addPhoto(photoEntry); albumEntry.addPhoto(photoEntry);
} }
} }
} catch (Exception e) { } catch (Throwable e) {
FileLog.e("tmessages", e); FileLog.e("tmessages", e);
} finally { } finally {
if (cursor != null) { if (cursor != null) {
......
...@@ -131,6 +131,8 @@ public class MessagesStorage { ...@@ -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 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 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 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(); //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 { ...@@ -170,7 +172,7 @@ public class MessagesStorage {
database.executeFast("CREATE INDEX IF NOT EXISTS bot_keyboard_idx_mid ON bot_keyboard(mid);").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS bot_keyboard_idx_mid ON bot_keyboard(mid);").stepThis().dispose();
//version //version
database.executeFast("PRAGMA user_version = 20").stepThis().dispose(); database.executeFast("PRAGMA user_version = 21").stepThis().dispose();
} else { } else {
try { try {
SQLiteCursor cursor = database.queryFinalized("SELECT seq, pts, date, qts, lsv, sg, pbytes FROM params WHERE id = 1"); SQLiteCursor cursor = database.queryFinalized("SELECT seq, pts, date, qts, lsv, sg, pbytes FROM params WHERE id = 1");
...@@ -201,7 +203,7 @@ public class MessagesStorage { ...@@ -201,7 +203,7 @@ public class MessagesStorage {
} }
} }
int version = database.executeInt("PRAGMA user_version"); int version = database.executeInt("PRAGMA user_version");
if (version < 20) { if (version < 21) {
updateDbToLastVersion(version); updateDbToLastVersion(version);
} }
} }
...@@ -410,7 +412,12 @@ public class MessagesStorage { ...@@ -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 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("CREATE INDEX IF NOT EXISTS bot_keyboard_idx_mid ON bot_keyboard(mid);").stepThis().dispose();
database.executeFast("PRAGMA user_version = 20").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) { } catch (Exception e) {
FileLog.e("tmessages", e); FileLog.e("tmessages", e);
...@@ -887,6 +894,7 @@ public class MessagesStorage { ...@@ -887,6 +894,7 @@ public class MessagesStorage {
if (!messagesOnly) { if (!messagesOnly) {
database.executeFast("DELETE FROM dialogs WHERE did = " + did).stepThis().dispose(); 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 chat_settings WHERE uid = " + did).stepThis().dispose();
database.executeFast("DELETE FROM search_recent WHERE did = " + did).stepThis().dispose();
int lower_id = (int)did; int lower_id = (int)did;
int high_id = (int)(did >> 32); int high_id = (int)(did >> 32);
if (lower_id != 0) { if (lower_id != 0) {
...@@ -2100,11 +2108,19 @@ public class MessagesStorage { ...@@ -2100,11 +2108,19 @@ public class MessagesStorage {
storageQueue.postRunnable(new Runnable() { storageQueue.postRunnable(new Runnable() {
@Override @Override
public void run() { public void run() {
try {
database.commitTransaction(); database.commitTransaction();
} catch (Exception e) {
FileLog.e("tmessages", e);
}
} }
}); });
} else { } else {
try {
database.commitTransaction(); database.commitTransaction();
} catch (Exception e) {
FileLog.e("tmessages", e);
}
} }
} }
...@@ -2745,6 +2761,7 @@ public class MessagesStorage { ...@@ -2745,6 +2761,7 @@ public class MessagesStorage {
database.beginTransaction(); database.beginTransaction();
SQLitePreparedStatement state = database.executeFast("UPDATE messages SET data = ? WHERE mid = ?"); 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) { for (TLRPC.Message message : messages) {
ByteBufferDesc data = buffersStorage.getFreeBuffer(message.getObjectSize()); ByteBufferDesc data = buffersStorage.getFreeBuffer(message.getObjectSize());
message.serializeToStream(data); message.serializeToStream(data);
...@@ -2754,9 +2771,15 @@ public class MessagesStorage { ...@@ -2754,9 +2771,15 @@ public class MessagesStorage {
state.bindInteger(2, message.id); state.bindInteger(2, message.id);
state.step(); state.step();
state2.requery();
state2.bindByteBuffer(1, data.buffer);
state2.bindInteger(2, message.id);
state2.step();
buffersStorage.reuseFreeBuffer(data); buffersStorage.reuseFreeBuffer(data);
} }
state.dispose(); state.dispose();
state2.dispose();
database.commitTransaction(); database.commitTransaction();
......
...@@ -32,6 +32,7 @@ public class SharedMediaQuery { ...@@ -32,6 +32,7 @@ public class SharedMediaQuery {
public final static int MEDIA_PHOTOVIDEO = 0; public final static int MEDIA_PHOTOVIDEO = 0;
public final static int MEDIA_FILE = 1; public final static int MEDIA_FILE = 1;
public final static int MEDIA_AUDIO = 2; 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) { 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; int lower_part = (int)uid;
...@@ -48,6 +49,8 @@ public class SharedMediaQuery { ...@@ -48,6 +49,8 @@ public class SharedMediaQuery {
req.filter = new TLRPC.TL_inputMessagesFilterDocument(); req.filter = new TLRPC.TL_inputMessagesFilterDocument();
} else if (type == MEDIA_AUDIO) { } else if (type == MEDIA_AUDIO) {
req.filter = new TLRPC.TL_inputMessagesFilterAudio(); req.filter = new TLRPC.TL_inputMessagesFilterAudio();
} else if (type == MEDIA_URL) {
req.filter = new TLRPC.TL_inputMessagesFilterUrl();
} }
req.q = ""; req.q = "";
if (uid < 0) { if (uid < 0) {
...@@ -94,6 +97,8 @@ public class SharedMediaQuery { ...@@ -94,6 +97,8 @@ public class SharedMediaQuery {
req.filter = new TLRPC.TL_inputMessagesFilterDocument(); req.filter = new TLRPC.TL_inputMessagesFilterDocument();
} else if (type == MEDIA_AUDIO) { } else if (type == MEDIA_AUDIO) {
req.filter = new TLRPC.TL_inputMessagesFilterAudio(); req.filter = new TLRPC.TL_inputMessagesFilterAudio();
} else if (type == MEDIA_URL) {
req.filter = new TLRPC.TL_inputMessagesFilterUrl();
} }
req.q = ""; req.q = "";
if (uid < 0) { if (uid < 0) {
...@@ -144,15 +149,17 @@ public class SharedMediaQuery { ...@@ -144,15 +149,17 @@ public class SharedMediaQuery {
return -1; return -1;
} }
if (message.media instanceof TLRPC.TL_messageMediaPhoto || message.media instanceof TLRPC.TL_messageMediaVideo) { 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) { } else if (message.media instanceof TLRPC.TL_messageMediaDocument) {
if (MessageObject.isStickerMessage(message)) { if (MessageObject.isStickerMessage(message)) {
return -1; return -1;
} else { } else {
return SharedMediaQuery.MEDIA_FILE; return MEDIA_FILE;
} }
} else if (message.media instanceof TLRPC.TL_messageMediaAudio) { } 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; return -1;
} }
...@@ -160,7 +167,11 @@ public class SharedMediaQuery { ...@@ -160,7 +167,11 @@ public class SharedMediaQuery {
public static boolean canAddMessageToMedia(TLRPC.Message message) { 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) { if (message instanceof TLRPC.TL_message_secret && message.media instanceof TLRPC.TL_messageMediaPhoto && message.ttl != 0 && message.ttl <= 60) {
return false; 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 true;
} }
return false; return false;
......
...@@ -110,8 +110,9 @@ public class StickersQuery { ...@@ -110,8 +110,9 @@ public class StickersQuery {
ArrayList<TLRPC.TL_messages_stickerSet> newStickerArray = null; ArrayList<TLRPC.TL_messages_stickerSet> newStickerArray = null;
int date = 0; int date = 0;
String hash = null; String hash = null;
SQLiteCursor cursor = null;
try { 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()) { if (cursor.next()) {
ByteBufferDesc data = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(cursor.byteArrayLength(0)); ByteBufferDesc data = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(cursor.byteArrayLength(0));
if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) {
...@@ -128,9 +129,12 @@ public class StickersQuery { ...@@ -128,9 +129,12 @@ public class StickersQuery {
hash = cursor.stringValue(2); hash = cursor.stringValue(2);
MessagesStorage.getInstance().getBuffersStorage().reuseFreeBuffer(data); MessagesStorage.getInstance().getBuffersStorage().reuseFreeBuffer(data);
} }
cursor.dispose(); } catch (Throwable e) {
} catch (Exception e) {
FileLog.e("tmessages", e); FileLog.e("tmessages", e);
} finally {
if (cursor != null) {
cursor.dispose();
}
} }
processLoadedStickers(newStickerArray, true, date, hash); processLoadedStickers(newStickerArray, true, date, hash);
} }
...@@ -229,7 +233,7 @@ public class StickersQuery { ...@@ -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) { for (TLRPC.DocumentAttribute attribute : document.attributes) {
if (attribute instanceof TLRPC.TL_documentAttributeSticker) { if (attribute instanceof TLRPC.TL_documentAttributeSticker) {
if (attribute.stickerset instanceof TLRPC.TL_inputStickerSetID) { if (attribute.stickerset instanceof TLRPC.TL_inputStickerSetID) {
......
...@@ -28,43 +28,43 @@ public interface Cache { ...@@ -28,43 +28,43 @@ public interface Cache {
* @param key Cache key * @param key Cache key
* @return An {@link Entry} or null in the event of a cache miss * @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. * Adds or replaces an entry to the cache.
* @param key Cache key * @param key Cache key
* @param entry Data to store and metadata for cache coherency, TTL, etc. * @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; * Performs any potentially long-running actions needed to initialize the cache;
* will be called from a worker thread. * will be called from a worker thread.
*/ */
void initialize(); public void initialize();
/** /**
* Invalidates an entry in the cache. * Invalidates an entry in the cache.
* @param key Cache key * @param key Cache key
* @param fullExpire True to fully expire the entry, false to soft expire * @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. * Removes an entry from the cache.
* @param key Cache key * @param key Cache key
*/ */
void remove(String key); public void remove(String key);
/** /**
* Empties the cache. * Empties the cache.
*/ */
void clear(); public void clear();
/** /**
* Data and metadata for an entry returned by the cache. * Data and metadata for an entry returned by the cache.
*/ */
class Entry { public static class Entry {
/** The data returned from cache. */ /** The data returned from cache. */
public byte[] data; public byte[] data;
...@@ -74,6 +74,9 @@ public interface Cache { ...@@ -74,6 +74,9 @@ public interface Cache {
/** Date of this response as reported by the server. */ /** Date of this response as reported by the server. */
public long serverDate; public long serverDate;
/** The last modified date for the requested object. */
public long lastModified;
/** TTL for this record. */ /** TTL for this record. */
public long ttl; public long ttl;
......
...@@ -151,6 +151,7 @@ public class CacheDispatcher extends Thread { ...@@ -151,6 +151,7 @@ public class CacheDispatcher extends Thread {
if (mQuit) { if (mQuit) {
return; return;
} }
continue;
} }
} }
} }
......
...@@ -26,5 +26,5 @@ public interface Network { ...@@ -26,5 +26,5 @@ public interface Network {
* @return A {@link NetworkResponse} with data and caching metadata; will never be null * @return A {@link NetworkResponse} with data and caching metadata; will never be null
* @throws VolleyError on errors * @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; ...@@ -28,9 +28,9 @@ import java.util.concurrent.BlockingQueue;
* Provides a thread for performing network dispatch from a queue of requests. * 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 * 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 * specified {@link Network} interface. Responses are committed to cache, if
* eligible, using a specified {@link org.telegram.android.volley.Cache} interface. Valid responses and * eligible, using a specified {@link Cache} interface. Valid responses and
* errors are posted back to the caller via a {@link org.telegram.android.volley.ResponseDelivery}. * errors are posted back to the caller via a {@link ResponseDelivery}.
*/ */
public class NetworkDispatcher extends Thread { public class NetworkDispatcher extends Thread {
/** The queue of requests to service. */ /** The queue of requests to service. */
......
...@@ -22,7 +22,7 @@ import java.util.Collections; ...@@ -22,7 +22,7 @@ import java.util.Collections;
import java.util.Map; 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 { public class NetworkResponse {
/** /**
......
...@@ -18,9 +18,13 @@ package org.telegram.android.volley; ...@@ -18,9 +18,13 @@ package org.telegram.android.volley;
import android.net.TrafficStats; import android.net.TrafficStats;
import android.net.Uri; import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock; import android.os.SystemClock;
import android.text.TextUtils; import android.text.TextUtils;
import org.telegram.android.volley.VolleyLog.MarkerLog;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.Collections; import java.util.Collections;
...@@ -53,6 +57,9 @@ public abstract class Request<T> implements Comparable<Request<T>> { ...@@ -53,6 +57,9 @@ public abstract class Request<T> implements Comparable<Request<T>> {
int PATCH = 7; 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, * Request method of this request. Currently supports GET, POST, PUT, DELETE, HEAD, OPTIONS,
* TRACE, and PATCH. * TRACE, and PATCH.
...@@ -83,12 +90,6 @@ public abstract class Request<T> implements Comparable<Request<T>> { ...@@ -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. */ /** Whether or not a response has been delivered for this request yet. */
private boolean mResponseDelivered = false; 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. */ /** The retry policy for this request. */
private RetryPolicy mRetryPolicy; private RetryPolicy mRetryPolicy;
...@@ -108,6 +109,7 @@ public abstract class Request<T> implements Comparable<Request<T>> { ...@@ -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 * is provided by subclasses, who have a better idea of how to deliver an
* already-parsed response. * already-parsed response.
* *
* @deprecated Use {@link #Request(int, String, com.android.volley.Response.ErrorListener)}.
*/ */
@Deprecated @Deprecated
public Request(String url, Response.ErrorListener listener) { public Request(String url, Response.ErrorListener listener) {
...@@ -155,6 +157,9 @@ public abstract class Request<T> implements Comparable<Request<T>> { ...@@ -155,6 +157,9 @@ public abstract class Request<T> implements Comparable<Request<T>> {
return mTag; return mTag;
} }
/**
* @return this request's {@link com.android.volley.Response.ErrorListener}.
*/
public Response.ErrorListener getErrorListener() { public Response.ErrorListener getErrorListener() {
return mErrorListener; return mErrorListener;
} }
...@@ -196,8 +201,8 @@ public abstract class Request<T> implements Comparable<Request<T>> { ...@@ -196,8 +201,8 @@ public abstract class Request<T> implements Comparable<Request<T>> {
* Adds an event to this request's event log; for debugging. * Adds an event to this request's event log; for debugging.
*/ */
public void addMarker(String tag) { public void addMarker(String tag) {
if (mRequestBirthTime == 0) { if (MarkerLog.ENABLED) {
mRequestBirthTime = SystemClock.elapsedRealtime(); mEventLog.add(tag, Thread.currentThread().getId());
} }
} }
...@@ -210,9 +215,24 @@ public abstract class Request<T> implements Comparable<Request<T>> { ...@@ -210,9 +215,24 @@ public abstract class Request<T> implements Comparable<Request<T>> {
if (mRequestQueue != null) { if (mRequestQueue != null) {
mRequestQueue.finish(this); mRequestQueue.finish(this);
} }
long requestTime = SystemClock.elapsedRealtime() - mRequestBirthTime; if (MarkerLog.ENABLED) {
if (requestTime >= SLOW_REQUEST_THRESHOLD_MS) { final long threadId = Thread.currentThread().getId();
VolleyLog.d("%d ms: %s", requestTime, this.toString()); 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; ...@@ -19,9 +19,11 @@ package org.telegram.android.volley;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Queue; import java.util.Queue;
import java.util.Set; import java.util.Set;
...@@ -31,12 +33,18 @@ import java.util.concurrent.atomic.AtomicInteger; ...@@ -31,12 +33,18 @@ import java.util.concurrent.atomic.AtomicInteger;
/** /**
* A request dispatch queue with a thread pool of dispatchers. * 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 * resolving from either cache or network on a worker thread, and then delivering
* a parsed response on the main thread. * a parsed response on the main thread.
*/ */
public class RequestQueue { 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. */ /** Used for generating monotonically-increasing sequence numbers for requests. */
private AtomicInteger mSequenceGenerator = new AtomicInteger(); private AtomicInteger mSequenceGenerator = new AtomicInteger();
...@@ -86,6 +94,9 @@ public class RequestQueue { ...@@ -86,6 +94,9 @@ public class RequestQueue {
/** The cache dispatcher. */ /** The cache dispatcher. */
private CacheDispatcher mCacheDispatcher; private CacheDispatcher mCacheDispatcher;
private List<RequestFinishedListener> mFinishedListeners =
new ArrayList<RequestFinishedListener>();
/** /**
* Creates the worker pool. Processing will not begin until {@link #start()} is called. * Creates the worker pool. Processing will not begin until {@link #start()} is called.
* *
...@@ -149,9 +160,9 @@ public class RequestQueue { ...@@ -149,9 +160,9 @@ public class RequestQueue {
if (mCacheDispatcher != null) { if (mCacheDispatcher != null) {
mCacheDispatcher.quit(); mCacheDispatcher.quit();
} }
for (NetworkDispatcher mDispatcher : mDispatchers) { for (int i = 0; i < mDispatchers.length; i++) {
if (mDispatcher != null) { if (mDispatchers[i] != null) {
mDispatcher.quit(); mDispatchers[i].quit();
} }
} }
} }
...@@ -175,7 +186,7 @@ public class RequestQueue { ...@@ -175,7 +186,7 @@ public class RequestQueue {
* {@link RequestQueue#cancelAll(RequestFilter)}. * {@link RequestQueue#cancelAll(RequestFilter)}.
*/ */
public interface RequestFilter { public interface RequestFilter {
boolean apply(Request<?> request); public boolean apply(Request<?> request);
} }
/** /**
...@@ -261,11 +272,16 @@ public class RequestQueue { ...@@ -261,11 +272,16 @@ public class RequestQueue {
* <p>Releases waiting requests for <code>request.getCacheKey()</code> if * <p>Releases waiting requests for <code>request.getCacheKey()</code> if
* <code>request.shouldCache()</code>.</p> * <code>request.shouldCache()</code>.</p>
*/ */
void finish(Request<?> request) { <T> void finish(Request<T> request) {
// Remove from the set of requests currently being processed. // Remove from the set of requests currently being processed.
synchronized (mCurrentRequests) { synchronized (mCurrentRequests) {
mCurrentRequests.remove(request); mCurrentRequests.remove(request);
} }
synchronized (mFinishedListeners) {
for (RequestFinishedListener<T> listener : mFinishedListeners) {
listener.onRequestFinished(request);
}
}
if (request.shouldCache()) { if (request.shouldCache()) {
synchronized (mWaitingRequests) { synchronized (mWaitingRequests) {
...@@ -283,4 +299,19 @@ public class RequestQueue { ...@@ -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> { ...@@ -26,7 +26,7 @@ public class Response<T> {
/** Callback interface for delivering parsed responses. */ /** Callback interface for delivering parsed responses. */
public interface Listener<T> { public interface Listener<T> {
/** Called when a response is received. */ /** Called when a response is received. */
void onResponse(T response); public void onResponse(T response);
} }
/** Callback interface for delivering error responses. */ /** Callback interface for delivering error responses. */
...@@ -35,7 +35,7 @@ public class Response<T> { ...@@ -35,7 +35,7 @@ public class Response<T> {
* Callback method that an error has been occurred with the * Callback method that an error has been occurred with the
* provided error code and optional user-readable message. * 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. */ /** Returns a successful response containing the parsed result. */
......
...@@ -20,16 +20,16 @@ public interface ResponseDelivery { ...@@ -20,16 +20,16 @@ public interface ResponseDelivery {
/** /**
* Parses a response from the network or cache and delivers it. * 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 * Parses a response from the network or cache and delivers it. The provided
* Runnable will be executed after delivery. * 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. * 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 { ...@@ -24,12 +24,12 @@ public interface RetryPolicy {
/** /**
* Returns the current timeout (used for logging). * Returns the current timeout (used for logging).
*/ */
int getCurrentTimeout(); public int getCurrentTimeout();
/** /**
* Returns the current retry count (used for logging). * 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. * Prepares for the next retry by applying a backoff to the timeout.
...@@ -37,5 +37,5 @@ public interface RetryPolicy { ...@@ -37,5 +37,5 @@ public interface RetryPolicy {
* @throws VolleyError In the event that the retry could not be performed (for example if we * @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. * 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 { ...@@ -29,3 +29,4 @@ public class ServerError extends VolleyError {
super(); super();
} }
} }
...@@ -23,7 +23,12 @@ import java.util.ArrayList; ...@@ -23,7 +23,12 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; 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 class VolleyLog {
public static String TAG = "Volley"; public static String TAG = "Volley";
...@@ -89,7 +94,7 @@ public class VolleyLog { ...@@ -89,7 +94,7 @@ public class VolleyLog {
callingClass = callingClass.substring(callingClass.lastIndexOf('.') + 1); callingClass = callingClass.substring(callingClass.lastIndexOf('.') + 1);
callingClass = callingClass.substring(callingClass.lastIndexOf('$') + 1); callingClass = callingClass.substring(callingClass.lastIndexOf('$') + 1);
caller = callingClass + "" + trace[i].getMethodName(); caller = callingClass + "." + trace[i].getMethodName();
break; break;
} }
} }
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
package org.telegram.android.volley.toolbox; package org.telegram.android.volley.toolbox;
import org.telegram.android.volley.AuthFailureError;
import android.accounts.Account; import android.accounts.Account;
import android.accounts.AccountManager; import android.accounts.AccountManager;
import android.accounts.AccountManagerFuture; import android.accounts.AccountManagerFuture;
...@@ -23,14 +25,12 @@ import android.content.Context; ...@@ -23,14 +25,12 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import org.telegram.android.volley.AuthFailureError;
/** /**
* An Authenticator that uses {@link AccountManager} to get auth * An Authenticator that uses {@link AccountManager} to get auth
* tokens of a specified type for a specified account. * tokens of a specified type for a specified account.
*/ */
public class AndroidAuthenticator implements Authenticator { public class AndroidAuthenticator implements Authenticator {
private final Context mContext; private final AccountManager mAccountManager;
private final Account mAccount; private final Account mAccount;
private final String mAuthTokenType; private final String mAuthTokenType;
private final boolean mNotifyAuthFailure; private final boolean mNotifyAuthFailure;
...@@ -54,7 +54,13 @@ public class AndroidAuthenticator implements Authenticator { ...@@ -54,7 +54,13 @@ public class AndroidAuthenticator implements Authenticator {
*/ */
public AndroidAuthenticator(Context context, Account account, String authTokenType, public AndroidAuthenticator(Context context, Account account, String authTokenType,
boolean notifyAuthFailure) { 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; mAccount = account;
mAuthTokenType = authTokenType; mAuthTokenType = authTokenType;
mNotifyAuthFailure = notifyAuthFailure; mNotifyAuthFailure = notifyAuthFailure;
...@@ -71,8 +77,7 @@ public class AndroidAuthenticator implements Authenticator { ...@@ -71,8 +77,7 @@ public class AndroidAuthenticator implements Authenticator {
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Override @Override
public String getAuthToken() throws AuthFailureError { public String getAuthToken() throws AuthFailureError {
final AccountManager accountManager = AccountManager.get(mContext); AccountManagerFuture<Bundle> future = mAccountManager.getAuthToken(mAccount,
AccountManagerFuture<Bundle> future = accountManager.getAuthToken(mAccount,
mAuthTokenType, mNotifyAuthFailure, null, null); mAuthTokenType, mNotifyAuthFailure, null, null);
Bundle result; Bundle result;
try { try {
...@@ -97,6 +102,6 @@ public class AndroidAuthenticator implements Authenticator { ...@@ -97,6 +102,6 @@ public class AndroidAuthenticator implements Authenticator {
@Override @Override
public void invalidateAuthToken(String authToken) { public void invalidateAuthToken(String authToken) {
AccountManager.get(mContext).invalidateAuthToken(mAccount.type, authToken); mAccountManager.invalidateAuthToken(mAccount.type, authToken);
} }
} }
...@@ -27,10 +27,10 @@ public interface Authenticator { ...@@ -27,10 +27,10 @@ public interface Authenticator {
* *
* @throws AuthFailureError If authentication did not succeed * @throws AuthFailureError If authentication did not succeed
*/ */
String getAuthToken() throws AuthFailureError; public String getAuthToken() throws AuthFailureError;
/** /**
* Invalidates the provided auth token. * Invalidates the provided auth token.
*/ */
void invalidateAuthToken(String authToken); public void invalidateAuthToken(String authToken);
} }
...@@ -18,15 +18,9 @@ package org.telegram.android.volley.toolbox; ...@@ -18,15 +18,9 @@ package org.telegram.android.volley.toolbox;
import android.os.SystemClock; 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.AuthFailureError;
import org.telegram.android.volley.Cache; import org.telegram.android.volley.Cache;
import org.telegram.android.volley.Cache.Entry;
import org.telegram.android.volley.Network; import org.telegram.android.volley.Network;
import org.telegram.android.volley.NetworkError; import org.telegram.android.volley.NetworkError;
import org.telegram.android.volley.NetworkResponse; import org.telegram.android.volley.NetworkResponse;
...@@ -38,6 +32,14 @@ import org.telegram.android.volley.TimeoutError; ...@@ -38,6 +32,14 @@ import org.telegram.android.volley.TimeoutError;
import org.telegram.android.volley.VolleyError; import org.telegram.android.volley.VolleyError;
import org.telegram.android.volley.VolleyLog; 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.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.MalformedURLException; import java.net.MalformedURLException;
...@@ -49,14 +51,14 @@ import java.util.Map; ...@@ -49,14 +51,14 @@ import java.util.Map;
import java.util.TreeMap; 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 { public class BasicNetwork implements Network {
protected static final boolean DEBUG = VolleyLog.DEBUG; 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; protected final HttpStack mHttpStack;
...@@ -99,7 +101,7 @@ public class BasicNetwork implements Network { ...@@ -99,7 +101,7 @@ public class BasicNetwork implements Network {
// Handle cache validation. // Handle cache validation.
if (statusCode == HttpStatus.SC_NOT_MODIFIED) { if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
Cache.Entry entry = request.getCacheEntry(); Entry entry = request.getCacheEntry();
if (entry == null) { if (entry == null) {
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null, return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
responseHeaders, true, responseHeaders, true,
...@@ -210,8 +212,8 @@ public class BasicNetwork implements Network { ...@@ -210,8 +212,8 @@ public class BasicNetwork implements Network {
headers.put("If-None-Match", entry.etag); headers.put("If-None-Match", entry.etag);
} }
if (entry.serverDate > 0) { if (entry.lastModified > 0) {
Date refTime = new Date(entry.serverDate); Date refTime = new Date(entry.lastModified);
headers.put("If-Modified-Since", DateUtils.formatDate(refTime)); headers.put("If-Modified-Since", DateUtils.formatDate(refTime));
} }
} }
...@@ -256,8 +258,8 @@ public class BasicNetwork implements Network { ...@@ -256,8 +258,8 @@ public class BasicNetwork implements Network {
*/ */
protected static Map<String, String> convertHeaders(Header[] headers) { protected static Map<String, String> convertHeaders(Header[] headers) {
Map<String, String> result = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER); Map<String, String> result = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
for (Header header : headers) { for (int i = 0; i < headers.length; i++) {
result.put(header.getName(), header.getValue()); result.put(headers[i].getName(), headers[i].getValue());
} }
return result; return result;
} }
......
...@@ -16,14 +16,14 @@ ...@@ -16,14 +16,14 @@
package org.telegram.android.volley.toolbox; 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.Cache;
import org.telegram.android.volley.NetworkResponse; import org.telegram.android.volley.NetworkResponse;
import org.telegram.android.volley.Request; import org.telegram.android.volley.Request;
import org.telegram.android.volley.Response; import org.telegram.android.volley.Response;
import android.os.Handler;
import android.os.Looper;
/** /**
* A synthetic request used for clearing the cache. * A synthetic request used for clearing the cache.
*/ */
......
...@@ -22,6 +22,7 @@ import org.telegram.android.volley.Cache; ...@@ -22,6 +22,7 @@ import org.telegram.android.volley.Cache;
import org.telegram.android.volley.VolleyLog; import org.telegram.android.volley.VolleyLog;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.EOFException; import java.io.EOFException;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
...@@ -42,45 +43,30 @@ import java.util.Map; ...@@ -42,45 +43,30 @@ import java.util.Map;
*/ */
public class DiskBasedCache implements Cache { public class DiskBasedCache implements Cache {
/** /** Map of the Key, CacheHeader pairs */
* Map of the Key, CacheHeader pairs
*/
private final Map<String, CacheHeader> mEntries = private final Map<String, CacheHeader> mEntries =
new LinkedHashMap<String, CacheHeader>(16, .75f, true); new LinkedHashMap<String, CacheHeader>(16, .75f, true);
/** /** Total amount of space currently used by the cache in bytes. */
* Total amount of space currently used by the cache in bytes.
*/
private long mTotalSize = 0; private long mTotalSize = 0;
/** /** The root directory to use for the cache. */
* The root directory to use for the cache.
*/
private final File mRootDirectory; private final File mRootDirectory;
/** /** The maximum size of the cache in bytes. */
* The maximum size of the cache in bytes.
*/
private final int mMaxCacheSizeInBytes; private final int mMaxCacheSizeInBytes;
/** /** Default maximum disk usage in bytes. */
* Default maximum disk usage in bytes.
*/
private static final int DEFAULT_DISK_USAGE_BYTES = 5 * 1024 * 1024; private static final int DEFAULT_DISK_USAGE_BYTES = 5 * 1024 * 1024;
/** /** High water mark percentage for the cache */
* High water mark percentage for the cache
*/
private static final float HYSTERESIS_FACTOR = 0.9f; private static final float HYSTERESIS_FACTOR = 0.9f;
/** /** Magic number for current version of cache file format. */
* Magic number for current version of cache file format. private static final int CACHE_MAGIC = 0x20150306;
*/
private static final int CACHE_MAGIC = 0x20140623;
/** /**
* Constructs an instance of the DiskBasedCache at the specified directory. * Constructs an instance of the DiskBasedCache at the specified directory.
*
* @param rootDirectory The root directory of the cache. * @param rootDirectory The root directory of the cache.
* @param maxCacheSizeInBytes The maximum size of the cache in bytes. * @param maxCacheSizeInBytes The maximum size of the cache in bytes.
*/ */
...@@ -92,7 +78,6 @@ public class DiskBasedCache implements Cache { ...@@ -92,7 +78,6 @@ public class DiskBasedCache implements Cache {
/** /**
* Constructs an instance of the DiskBasedCache at the specified directory using * Constructs an instance of the DiskBasedCache at the specified directory using
* the default maximum cache size of 5MB. * the default maximum cache size of 5MB.
*
* @param rootDirectory The root directory of the cache. * @param rootDirectory The root directory of the cache.
*/ */
public DiskBasedCache(File rootDirectory) { public DiskBasedCache(File rootDirectory) {
...@@ -129,7 +114,7 @@ public class DiskBasedCache implements Cache { ...@@ -129,7 +114,7 @@ public class DiskBasedCache implements Cache {
File file = getFileForKey(key); File file = getFileForKey(key);
CountingInputStream cis = null; CountingInputStream cis = null;
try { try {
cis = new CountingInputStream(new FileInputStream(file)); cis = new CountingInputStream(new BufferedInputStream(new FileInputStream(file)));
CacheHeader.readHeader(cis); // eat header CacheHeader.readHeader(cis); // eat header
byte[] data = streamToBytes(cis, (int) (file.length() - cis.bytesRead)); byte[] data = streamToBytes(cis, (int) (file.length() - cis.bytesRead));
return entry.toCacheEntry(data); return entry.toCacheEntry(data);
...@@ -181,15 +166,13 @@ public class DiskBasedCache implements Cache { ...@@ -181,15 +166,13 @@ public class DiskBasedCache implements Cache {
if (fis != null) { if (fis != null) {
fis.close(); fis.close();
} }
} catch (IOException ignored) { } catch (IOException ignored) { }
}
} }
} }
} }
/** /**
* Invalidates an entry in the cache. * Invalidates an entry in the cache.
*
* @param key Cache key * @param key Cache key
* @param fullExpire True to fully expire the entry, false to soft expire * @param fullExpire True to fully expire the entry, false to soft expire
*/ */
...@@ -214,7 +197,7 @@ public class DiskBasedCache implements Cache { ...@@ -214,7 +197,7 @@ public class DiskBasedCache implements Cache {
pruneIfNeeded(entry.data.length); pruneIfNeeded(entry.data.length);
File file = getFileForKey(key); File file = getFileForKey(key);
try { try {
FileOutputStream fos = new FileOutputStream(file); BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(file));
CacheHeader e = new CacheHeader(key, entry); CacheHeader e = new CacheHeader(key, entry);
boolean success = e.writeHeader(fos); boolean success = e.writeHeader(fos);
if (!success) { if (!success) {
...@@ -227,7 +210,6 @@ public class DiskBasedCache implements Cache { ...@@ -227,7 +210,6 @@ public class DiskBasedCache implements Cache {
putEntry(key, e); putEntry(key, e);
return; return;
} catch (IOException e) { } catch (IOException e) {
/**/
} }
boolean deleted = file.delete(); boolean deleted = file.delete();
if (!deleted) { if (!deleted) {
...@@ -250,7 +232,6 @@ public class DiskBasedCache implements Cache { ...@@ -250,7 +232,6 @@ public class DiskBasedCache implements Cache {
/** /**
* Creates a pseudo-unique filename for the specified cache key. * Creates a pseudo-unique filename for the specified cache key.
*
* @param key The key to generate a file name for. * @param key The key to generate a file name for.
* @return A pseudo-unique filename. * @return A pseudo-unique filename.
*/ */
...@@ -270,7 +251,6 @@ public class DiskBasedCache implements Cache { ...@@ -270,7 +251,6 @@ public class DiskBasedCache implements Cache {
/** /**
* Prunes the cache to fit the amount of bytes specified. * Prunes the cache to fit the amount of bytes specified.
*
* @param neededSpace The amount of bytes we are trying to fit into the cache. * @param neededSpace The amount of bytes we are trying to fit into the cache.
*/ */
private void pruneIfNeeded(int neededSpace) { private void pruneIfNeeded(int neededSpace) {
...@@ -312,7 +292,6 @@ public class DiskBasedCache implements Cache { ...@@ -312,7 +292,6 @@ public class DiskBasedCache implements Cache {
/** /**
* Puts the entry with the specified key into the cache. * Puts the entry with the specified key into the cache.
*
* @param key The key to identify the entry by. * @param key The key to identify the entry by.
* @param entry The entry to cache. * @param entry The entry to cache.
*/ */
...@@ -339,7 +318,7 @@ public class DiskBasedCache implements Cache { ...@@ -339,7 +318,7 @@ public class DiskBasedCache implements Cache {
/** /**
* Reads the contents of an InputStream into a byte[]. * Reads the contents of an InputStream into a byte[].
*/ * */
private static byte[] streamToBytes(InputStream in, int length) throws IOException { private static byte[] streamToBytes(InputStream in, int length) throws IOException {
byte[] bytes = new byte[length]; byte[] bytes = new byte[length];
int count; int count;
...@@ -358,48 +337,35 @@ public class DiskBasedCache implements Cache { ...@@ -358,48 +337,35 @@ public class DiskBasedCache implements Cache {
*/ */
// Visible for testing. // Visible for testing.
static class CacheHeader { static class CacheHeader {
/** /** The size of the data identified by this CacheHeader. (This is not
* The size of the data identified by this CacheHeader. (This is not * serialized to disk. */
* serialized to disk.
*/
public long size; public long size;
/** /** The key that identifies the cache entry. */
* The key that identifies the cache entry.
*/
public String key; public String key;
/** /** ETag for cache coherence. */
* ETag for cache coherence.
*/
public String etag; public String etag;
/** /** Date of this response as reported by the server. */
* Date of this response as reported by the server.
*/
public long serverDate; public long serverDate;
/** /** The last modified date for the requested object. */
* TTL for this record. public long lastModified;
*/
/** TTL for this record. */
public long ttl; public long ttl;
/** /** Soft TTL for this record. */
* Soft TTL for this record.
*/
public long softTtl; public long softTtl;
/** /** Headers from the response resulting in this cache entry. */
* Headers from the response resulting in this cache entry.
*/
public Map<String, String> responseHeaders; public Map<String, String> responseHeaders;
private CacheHeader() { private CacheHeader() { }
}
/** /**
* Instantiates a new CacheHeader object * Instantiates a new CacheHeader object
*
* @param key The key that identifies the cache entry * @param key The key that identifies the cache entry
* @param entry The cache entry. * @param entry The cache entry.
*/ */
...@@ -408,6 +374,7 @@ public class DiskBasedCache implements Cache { ...@@ -408,6 +374,7 @@ public class DiskBasedCache implements Cache {
this.size = entry.data.length; this.size = entry.data.length;
this.etag = entry.etag; this.etag = entry.etag;
this.serverDate = entry.serverDate; this.serverDate = entry.serverDate;
this.lastModified = entry.lastModified;
this.ttl = entry.ttl; this.ttl = entry.ttl;
this.softTtl = entry.softTtl; this.softTtl = entry.softTtl;
this.responseHeaders = entry.responseHeaders; this.responseHeaders = entry.responseHeaders;
...@@ -415,7 +382,6 @@ public class DiskBasedCache implements Cache { ...@@ -415,7 +382,6 @@ public class DiskBasedCache implements Cache {
/** /**
* Reads the header off of an InputStream and returns a CacheHeader object. * Reads the header off of an InputStream and returns a CacheHeader object.
*
* @param is The InputStream to read from. * @param is The InputStream to read from.
* @throws IOException * @throws IOException
*/ */
...@@ -432,9 +398,11 @@ public class DiskBasedCache implements Cache { ...@@ -432,9 +398,11 @@ public class DiskBasedCache implements Cache {
entry.etag = null; entry.etag = null;
} }
entry.serverDate = readLong(is); entry.serverDate = readLong(is);
entry.lastModified = readLong(is);
entry.ttl = readLong(is); entry.ttl = readLong(is);
entry.softTtl = readLong(is); entry.softTtl = readLong(is);
entry.responseHeaders = readStringStringMap(is); entry.responseHeaders = readStringStringMap(is);
return entry; return entry;
} }
...@@ -446,6 +414,7 @@ public class DiskBasedCache implements Cache { ...@@ -446,6 +414,7 @@ public class DiskBasedCache implements Cache {
e.data = data; e.data = data;
e.etag = etag; e.etag = etag;
e.serverDate = serverDate; e.serverDate = serverDate;
e.lastModified = lastModified;
e.ttl = ttl; e.ttl = ttl;
e.softTtl = softTtl; e.softTtl = softTtl;
e.responseHeaders = responseHeaders; e.responseHeaders = responseHeaders;
...@@ -462,6 +431,7 @@ public class DiskBasedCache implements Cache { ...@@ -462,6 +431,7 @@ public class DiskBasedCache implements Cache {
writeString(os, key); writeString(os, key);
writeString(os, etag == null ? "" : etag); writeString(os, etag == null ? "" : etag);
writeLong(os, serverDate); writeLong(os, serverDate);
writeLong(os, lastModified);
writeLong(os, ttl); writeLong(os, ttl);
writeLong(os, softTtl); writeLong(os, softTtl);
writeStringStringMap(responseHeaders, os); writeStringStringMap(responseHeaders, os);
...@@ -537,14 +507,14 @@ public class DiskBasedCache implements Cache { ...@@ -537,14 +507,14 @@ public class DiskBasedCache implements Cache {
} }
static void writeLong(OutputStream os, long n) throws IOException { static void writeLong(OutputStream os, long n) throws IOException {
os.write((byte) (n >>> 0)); os.write((byte)(n >>> 0));
os.write((byte) (n >>> 8)); os.write((byte)(n >>> 8));
os.write((byte) (n >>> 16)); os.write((byte)(n >>> 16));
os.write((byte) (n >>> 24)); os.write((byte)(n >>> 24));
os.write((byte) (n >>> 32)); os.write((byte)(n >>> 32));
os.write((byte) (n >>> 40)); os.write((byte)(n >>> 40));
os.write((byte) (n >>> 48)); os.write((byte)(n >>> 48));
os.write((byte) (n >>> 56)); os.write((byte)(n >>> 56));
} }
static long readLong(InputStream is) throws IOException { static long readLong(InputStream is) throws IOException {
...@@ -596,4 +566,6 @@ public class DiskBasedCache implements Cache { ...@@ -596,4 +566,6 @@ public class DiskBasedCache implements Cache {
} }
return result; return result;
} }
} }
...@@ -16,6 +16,10 @@ ...@@ -16,6 +16,10 @@
package org.telegram.android.volley.toolbox; 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.HttpEntity;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair; import org.apache.http.NameValuePair;
...@@ -33,8 +37,6 @@ import org.apache.http.entity.ByteArrayEntity; ...@@ -33,8 +37,6 @@ import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.message.BasicNameValuePair; import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams; import org.apache.http.params.HttpParams;
import org.telegram.android.volley.AuthFailureError;
import org.telegram.android.volley.Request;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
...@@ -92,7 +94,7 @@ public class HttpClientStack implements HttpStack { ...@@ -92,7 +94,7 @@ public class HttpClientStack implements HttpStack {
/* protected */ static HttpUriRequest createHttpRequest(Request<?> request, /* protected */ static HttpUriRequest createHttpRequest(Request<?> request,
Map<String, String> additionalHeaders) throws AuthFailureError { Map<String, String> additionalHeaders) throws AuthFailureError {
switch (request.getMethod()) { 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. // 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 // 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. // GET. Otherwise, it is assumed that the request is a POST.
...@@ -108,29 +110,29 @@ public class HttpClientStack implements HttpStack { ...@@ -108,29 +110,29 @@ public class HttpClientStack implements HttpStack {
return new HttpGet(request.getUrl()); return new HttpGet(request.getUrl());
} }
} }
case Request.Method.GET: case Method.GET:
return new HttpGet(request.getUrl()); return new HttpGet(request.getUrl());
case Request.Method.DELETE: case Method.DELETE:
return new HttpDelete(request.getUrl()); return new HttpDelete(request.getUrl());
case Request.Method.POST: { case Method.POST: {
HttpPost postRequest = new HttpPost(request.getUrl()); HttpPost postRequest = new HttpPost(request.getUrl());
postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType()); postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(postRequest, request); setEntityIfNonEmptyBody(postRequest, request);
return postRequest; return postRequest;
} }
case Request.Method.PUT: { case Method.PUT: {
HttpPut putRequest = new HttpPut(request.getUrl()); HttpPut putRequest = new HttpPut(request.getUrl());
putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType()); putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(putRequest, request); setEntityIfNonEmptyBody(putRequest, request);
return putRequest; return putRequest;
} }
case Request.Method.HEAD: case Method.HEAD:
return new HttpHead(request.getUrl()); return new HttpHead(request.getUrl());
case Request.Method.OPTIONS: case Method.OPTIONS:
return new HttpOptions(request.getUrl()); return new HttpOptions(request.getUrl());
case Request.Method.TRACE: case Method.TRACE:
return new HttpTrace(request.getUrl()); return new HttpTrace(request.getUrl());
case Request.Method.PATCH: { case Method.PATCH: {
HttpPatch patchRequest = new HttpPatch(request.getUrl()); HttpPatch patchRequest = new HttpPatch(request.getUrl());
patchRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType()); patchRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(patchRequest, request); setEntityIfNonEmptyBody(patchRequest, request);
......
...@@ -16,11 +16,12 @@ ...@@ -16,11 +16,12 @@
package org.telegram.android.volley.toolbox; 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.DateParseException;
import org.apache.http.impl.cookie.DateUtils; import org.apache.http.impl.cookie.DateUtils;
import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HTTP;
import org.telegram.android.volley.Cache;
import org.telegram.android.volley.NetworkResponse;
import java.util.Map; import java.util.Map;
...@@ -41,10 +42,14 @@ public class HttpHeaderParser { ...@@ -41,10 +42,14 @@ public class HttpHeaderParser {
Map<String, String> headers = response.headers; Map<String, String> headers = response.headers;
long serverDate = 0; long serverDate = 0;
long lastModified = 0;
long serverExpires = 0; long serverExpires = 0;
long softExpire = 0; long softExpire = 0;
long finalExpire = 0;
long maxAge = 0; long maxAge = 0;
long staleWhileRevalidate = 0;
boolean hasCacheControl = false; boolean hasCacheControl = false;
boolean mustRevalidate = false;
String serverEtag = null; String serverEtag = null;
String headerValue; String headerValue;
...@@ -58,18 +63,22 @@ public class HttpHeaderParser { ...@@ -58,18 +63,22 @@ public class HttpHeaderParser {
if (headerValue != null) { if (headerValue != null) {
hasCacheControl = true; hasCacheControl = true;
String[] tokens = headerValue.split(","); String[] tokens = headerValue.split(",");
for (String token1 : tokens) { for (int i = 0; i < tokens.length; i++) {
String token = token1.trim(); String token = tokens[i].trim();
if (token.equals("no-cache") || token.equals("no-store")) { if (token.equals("no-cache") || token.equals("no-store")) {
return null; return null;
} else if (token.startsWith("max-age=")) { } else if (token.startsWith("max-age=")) {
try { try {
maxAge = Long.parseLong(token.substring(8)); maxAge = Long.parseLong(token.substring(8));
} catch (Exception e) { } 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")) { } else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) {
maxAge = 0; mustRevalidate = true;
} }
} }
} }
...@@ -79,23 +88,33 @@ public class HttpHeaderParser { ...@@ -79,23 +88,33 @@ public class HttpHeaderParser {
serverExpires = parseDateAsEpoch(headerValue); serverExpires = parseDateAsEpoch(headerValue);
} }
headerValue = headers.get("Last-Modified");
if (headerValue != null) {
lastModified = parseDateAsEpoch(headerValue);
}
serverEtag = headers.get("ETag"); serverEtag = headers.get("ETag");
// Cache-Control takes precedence over an Expires header, even if both exist and Expires // Cache-Control takes precedence over an Expires header, even if both exist and Expires
// is more restrictive. // is more restrictive.
if (hasCacheControl) { if (hasCacheControl) {
softExpire = now + maxAge * 1000; softExpire = now + maxAge * 1000;
finalExpire = mustRevalidate
? softExpire
: softExpire + staleWhileRevalidate * 1000;
} else if (serverDate > 0 && serverExpires >= serverDate) { } else if (serverDate > 0 && serverExpires >= serverDate) {
// Default semantic for Expire header in HTTP specification is softExpire. // Default semantic for Expire header in HTTP specification is softExpire.
softExpire = now + (serverExpires - serverDate); softExpire = now + (serverExpires - serverDate);
finalExpire = softExpire;
} }
Cache.Entry entry = new Cache.Entry(); Cache.Entry entry = new Cache.Entry();
entry.data = response.data; entry.data = response.data;
entry.etag = serverEtag; entry.etag = serverEtag;
entry.softTtl = softExpire; entry.softTtl = softExpire;
entry.ttl = entry.softTtl; entry.ttl = finalExpire;
entry.serverDate = serverDate; entry.serverDate = serverDate;
entry.lastModified = lastModified;
entry.responseHeaders = headers; entry.responseHeaders = headers;
return entry; return entry;
...@@ -115,10 +134,14 @@ public class HttpHeaderParser { ...@@ -115,10 +134,14 @@ public class HttpHeaderParser {
} }
/** /**
* Returns the charset specified in the Content-Type of this header, * Retrieve a charset from headers
* or the HTTP default (ISO-8859-1) if none can be found. *
* @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); String contentType = headers.get(HTTP.CONTENT_TYPE);
if (contentType != null) { if (contentType != null) {
String[] params = contentType.split(";"); String[] params = contentType.split(";");
...@@ -132,6 +155,14 @@ public class HttpHeaderParser { ...@@ -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 @@ ...@@ -16,10 +16,11 @@
package org.telegram.android.volley.toolbox; package org.telegram.android.volley.toolbox;
import org.apache.http.HttpResponse;
import org.telegram.android.volley.AuthFailureError; import org.telegram.android.volley.AuthFailureError;
import org.telegram.android.volley.Request; import org.telegram.android.volley.Request;
import org.apache.http.HttpResponse;
import java.io.IOException; import java.io.IOException;
import java.util.Map; import java.util.Map;
...@@ -38,7 +39,7 @@ public interface HttpStack { ...@@ -38,7 +39,7 @@ public interface HttpStack {
* {@link Request#getHeaders()} * {@link Request#getHeaders()}
* @return the HTTP response * @return the HTTP response
*/ */
HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError; throws IOException, AuthFailureError;
} }
...@@ -16,17 +16,20 @@ ...@@ -16,17 +16,20 @@
package org.telegram.android.volley.toolbox; 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.Header;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.ProtocolVersion; import org.apache.http.ProtocolVersion;
import org.apache.http.StatusLine; import org.apache.http.StatusLine;
import org.apache.http.entity.BasicHttpEntity; import org.apache.http.entity.BasicHttpEntity;
import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHttpResponse; import org.apache.http.message.BasicHttpResponse;
import org.apache.http.message.BasicStatusLine; 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.DataOutputStream;
import java.io.IOException; import java.io.IOException;
...@@ -42,7 +45,7 @@ import javax.net.ssl.HttpsURLConnection; ...@@ -42,7 +45,7 @@ import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSocketFactory; 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 { public class HurlStack implements HttpStack {
...@@ -56,7 +59,7 @@ 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 * Returns a URL to use instead of the provided one, or null to indicate
* this URL should not be used at all. * this URL should not be used at all.
*/ */
String rewriteUrl(String originalUrl); public String rewriteUrl(String originalUrl);
} }
private final UrlRewriter mUrlRewriter; private final UrlRewriter mUrlRewriter;
...@@ -113,7 +116,9 @@ public class HurlStack implements HttpStack { ...@@ -113,7 +116,9 @@ public class HurlStack implements HttpStack {
StatusLine responseStatus = new BasicStatusLine(protocolVersion, StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage()); connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus); BasicHttpResponse response = new BasicHttpResponse(responseStatus);
if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) {
response.setEntity(entityFromConnection(connection)); response.setEntity(entityFromConnection(connection));
}
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) { for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) { if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0)); Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
...@@ -123,6 +128,20 @@ public class HurlStack implements HttpStack { ...@@ -123,6 +128,20 @@ public class HurlStack implements HttpStack {
return response; 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}. * Initializes an {@link HttpEntity} from the given {@link HttpURLConnection}.
* @param connection * @param connection
...@@ -177,7 +196,7 @@ public class HurlStack implements HttpStack { ...@@ -177,7 +196,7 @@ public class HurlStack implements HttpStack {
/* package */ static void setConnectionParametersForRequest(HttpURLConnection connection, /* package */ static void setConnectionParametersForRequest(HttpURLConnection connection,
Request<?> request) throws IOException, AuthFailureError { Request<?> request) throws IOException, AuthFailureError {
switch (request.getMethod()) { 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. // 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 // 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. // GET. Otherwise, it is assumed that the request is a POST.
...@@ -195,32 +214,32 @@ public class HurlStack implements HttpStack { ...@@ -195,32 +214,32 @@ public class HurlStack implements HttpStack {
out.close(); out.close();
} }
break; break;
case Request.Method.GET: case Method.GET:
// Not necessary to set the request method because connection defaults to GET but // Not necessary to set the request method because connection defaults to GET but
// being explicit here. // being explicit here.
connection.setRequestMethod("GET"); connection.setRequestMethod("GET");
break; break;
case Request.Method.DELETE: case Method.DELETE:
connection.setRequestMethod("DELETE"); connection.setRequestMethod("DELETE");
break; break;
case Request.Method.POST: case Method.POST:
connection.setRequestMethod("POST"); connection.setRequestMethod("POST");
addBodyIfExists(connection, request); addBodyIfExists(connection, request);
break; break;
case Request.Method.PUT: case Method.PUT:
connection.setRequestMethod("PUT"); connection.setRequestMethod("PUT");
addBodyIfExists(connection, request); addBodyIfExists(connection, request);
break; break;
case Request.Method.HEAD: case Method.HEAD:
connection.setRequestMethod("HEAD"); connection.setRequestMethod("HEAD");
break; break;
case Request.Method.OPTIONS: case Method.OPTIONS:
connection.setRequestMethod("OPTIONS"); connection.setRequestMethod("OPTIONS");
break; break;
case Request.Method.TRACE: case Method.TRACE:
connection.setRequestMethod("TRACE"); connection.setRequestMethod("TRACE");
break; break;
case Request.Method.PATCH: case Method.PATCH:
connection.setRequestMethod("PATCH"); connection.setRequestMethod("PATCH");
addBodyIfExists(connection, request); addBodyIfExists(connection, request);
break; break;
......
...@@ -20,10 +20,11 @@ import android.graphics.Bitmap.Config; ...@@ -20,10 +20,11 @@ import android.graphics.Bitmap.Config;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import org.telegram.android.volley.Request; import org.telegram.android.volley.Request;
import org.telegram.android.volley.RequestQueue; 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 org.telegram.android.volley.VolleyError;
import java.util.HashMap; import java.util.HashMap;
...@@ -71,8 +72,8 @@ public class ImageLoader { ...@@ -71,8 +72,8 @@ public class ImageLoader {
* must not block. Implementation with an LruCache is recommended. * must not block. Implementation with an LruCache is recommended.
*/ */
public interface ImageCache { public interface ImageCache {
Bitmap getBitmap(String url); public Bitmap getBitmap(String url);
void putBitmap(String url, Bitmap bitmap); public void putBitmap(String url, Bitmap bitmap);
} }
/** /**
...@@ -127,7 +128,7 @@ public class ImageLoader { ...@@ -127,7 +128,7 @@ public class ImageLoader {
* or * or
* - onErrorResponse will be called if there was an error loading the image. * - 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. * Listens for non-error changes to the loading of the image request.
* *
...@@ -138,7 +139,7 @@ public class ImageLoader { ...@@ -138,7 +139,7 @@ public class ImageLoader {
* image loading in order to, for example, run an animation to fade in network loaded * image loading in order to, for example, run an animation to fade in network loaded
* images. * images.
*/ */
void onResponse(ImageContainer response, boolean isImmediate); public void onResponse(ImageContainer response, boolean isImmediate);
} }
/** /**
...@@ -149,9 +150,22 @@ public class ImageLoader { ...@@ -149,9 +150,22 @@ public class ImageLoader {
* @return True if the item exists in cache, false otherwise. * @return True if the item exists in cache, false otherwise.
*/ */
public boolean isCached(String requestUrl, int maxWidth, int maxHeight) { 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(); throwIfNotOnMainThread();
String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight); String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType);
return mCache.getBitmap(cacheKey) != null; return mCache.getBitmap(cacheKey) != null;
} }
...@@ -168,6 +182,15 @@ public class ImageLoader { ...@@ -168,6 +182,15 @@ public class ImageLoader {
return get(requestUrl, listener, 0, 0); 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 * 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 * in the cache, and returns a bitmap container that contains all of the data
...@@ -177,15 +200,17 @@ public class ImageLoader { ...@@ -177,15 +200,17 @@ public class ImageLoader {
* @param imageListener The listener to call when the remote image is loaded * @param imageListener The listener to call when the remote image is loaded
* @param maxWidth The maximum width of the returned image. * @param maxWidth The maximum width of the returned image.
* @param maxHeight The maximum height 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 * @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). * the currently available image (default if remote is not loaded).
*/ */
public ImageContainer get(String requestUrl, ImageListener imageListener, 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. // only fulfill requests that were initiated from the main thread.
throwIfNotOnMainThread(); 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. // Try to look up the request in the cache of remote images.
Bitmap cachedBitmap = mCache.getBitmap(cacheKey); Bitmap cachedBitmap = mCache.getBitmap(cacheKey);
...@@ -213,7 +238,8 @@ public class ImageLoader { ...@@ -213,7 +238,8 @@ public class ImageLoader {
// The request is not already in flight. Send the new request to the network and // The request is not already in flight. Send the new request to the network and
// track it. // track it.
Request<Bitmap> newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, cacheKey); Request<Bitmap> newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, scaleType,
cacheKey);
mRequestQueue.add(newRequest); mRequestQueue.add(newRequest);
mInFlightRequests.put(cacheKey, mInFlightRequests.put(cacheKey,
...@@ -221,14 +247,14 @@ public class ImageLoader { ...@@ -221,14 +247,14 @@ public class ImageLoader {
return imageContainer; return imageContainer;
} }
protected Request<Bitmap> makeImageRequest(String requestUrl, int maxWidth, int maxHeight, final String cacheKey) { protected Request<Bitmap> makeImageRequest(String requestUrl, int maxWidth, int maxHeight,
return new ImageRequest(requestUrl, new Response.Listener<Bitmap>() { ScaleType scaleType, final String cacheKey) {
return new ImageRequest(requestUrl, new Listener<Bitmap>() {
@Override @Override
public void onResponse(Bitmap response) { public void onResponse(Bitmap response) {
onGetImageSuccess(cacheKey, response); onGetImageSuccess(cacheKey, response);
} }
}, maxWidth, maxHeight, }, maxWidth, maxHeight, scaleType, Config.RGB_565, new ErrorListener() {
Config.RGB_565, new Response.ErrorListener() {
@Override @Override
public void onErrorResponse(VolleyError error) { public void onErrorResponse(VolleyError error) {
onGetImageError(cacheKey, error); onGetImageError(cacheKey, error);
...@@ -471,8 +497,11 @@ public class ImageLoader { ...@@ -471,8 +497,11 @@ public class ImageLoader {
* @param url The URL of the request. * @param url The URL of the request.
* @param maxWidth The max-width of the output. * @param maxWidth The max-width of the output.
* @param maxHeight The max-height 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) { private static String getCacheKey(String url, int maxWidth, int maxHeight, ScaleType scaleType) {
return "#W" + maxWidth + "#H" + maxHeight + url; 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 @@ ...@@ -16,10 +16,6 @@
package org.telegram.android.volley.toolbox; 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.DefaultRetryPolicy;
import org.telegram.android.volley.NetworkResponse; import org.telegram.android.volley.NetworkResponse;
import org.telegram.android.volley.ParseError; import org.telegram.android.volley.ParseError;
...@@ -27,6 +23,11 @@ import org.telegram.android.volley.Request; ...@@ -27,6 +23,11 @@ import org.telegram.android.volley.Request;
import org.telegram.android.volley.Response; import org.telegram.android.volley.Response;
import org.telegram.android.volley.VolleyLog; 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 * A canned request for getting an image at a given URL and calling
* back with a decoded Bitmap. * back with a decoded Bitmap.
...@@ -45,6 +46,7 @@ public class ImageRequest extends Request<Bitmap> { ...@@ -45,6 +46,7 @@ public class ImageRequest extends Request<Bitmap> {
private final Config mDecodeConfig; private final Config mDecodeConfig;
private final int mMaxWidth; private final int mMaxWidth;
private final int mMaxHeight; 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) */ /** 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(); private static final Object sDecodeLock = new Object();
...@@ -63,11 +65,12 @@ public class ImageRequest extends Request<Bitmap> { ...@@ -63,11 +65,12 @@ public class ImageRequest extends Request<Bitmap> {
* @param maxWidth Maximum width to decode this bitmap to, or zero for none * @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 * @param maxHeight Maximum height to decode this bitmap to, or zero for
* none * none
* @param scaleType The ImageViews ScaleType used to calculate the needed image size.
* @param decodeConfig Format to decode the bitmap to * @param decodeConfig Format to decode the bitmap to
* @param errorListener Error listener, or null to ignore errors * @param errorListener Error listener, or null to ignore errors
*/ */
public ImageRequest(String url, Response.Listener<Bitmap> listener, int maxWidth, int maxHeight, public ImageRequest(String url, Response.Listener<Bitmap> listener, int maxWidth, int maxHeight,
Config decodeConfig, Response.ErrorListener errorListener) { ScaleType scaleType, Config decodeConfig, Response.ErrorListener errorListener) {
super(Method.GET, url, errorListener); super(Method.GET, url, errorListener);
setRetryPolicy( setRetryPolicy(
new DefaultRetryPolicy(IMAGE_TIMEOUT_MS, IMAGE_MAX_RETRIES, IMAGE_BACKOFF_MULT)); new DefaultRetryPolicy(IMAGE_TIMEOUT_MS, IMAGE_MAX_RETRIES, IMAGE_BACKOFF_MULT));
...@@ -75,8 +78,19 @@ public class ImageRequest extends Request<Bitmap> { ...@@ -75,8 +78,19 @@ public class ImageRequest extends Request<Bitmap> {
mDecodeConfig = decodeConfig; mDecodeConfig = decodeConfig;
mMaxWidth = maxWidth; mMaxWidth = maxWidth;
mMaxHeight = maxHeight; 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 @Override
public Priority getPriority() { public Priority getPriority() {
return Priority.LOW; return Priority.LOW;
...@@ -92,14 +106,24 @@ public class ImageRequest extends Request<Bitmap> { ...@@ -92,14 +106,24 @@ public class ImageRequest extends Request<Bitmap> {
* maintain aspect ratio with primary dimension * maintain aspect ratio with primary dimension
* @param actualPrimary Actual size of the primary dimension * @param actualPrimary Actual size of the primary dimension
* @param actualSecondary Actual size of the secondary 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, 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 no dominant value at all, just return the actual.
if (maxPrimary == 0 && maxSecondary == 0) { if ((maxPrimary == 0) && (maxSecondary == 0)) {
return actualPrimary; 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 primary is unspecified, scale primary to match secondary's scaling ratio.
if (maxPrimary == 0) { if (maxPrimary == 0) {
double ratio = (double) maxSecondary / (double) actualSecondary; double ratio = (double) maxSecondary / (double) actualSecondary;
...@@ -112,7 +136,16 @@ public class ImageRequest extends Request<Bitmap> { ...@@ -112,7 +136,16 @@ public class ImageRequest extends Request<Bitmap> {
double ratio = (double) actualSecondary / (double) actualPrimary; double ratio = (double) actualSecondary / (double) actualPrimary;
int resized = maxPrimary; 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); resized = (int) (maxSecondary / ratio);
} }
return resized; return resized;
...@@ -150,9 +183,9 @@ public class ImageRequest extends Request<Bitmap> { ...@@ -150,9 +183,9 @@ public class ImageRequest extends Request<Bitmap> {
// Then compute the dimensions we would ideally like to decode to. // Then compute the dimensions we would ideally like to decode to.
int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight, int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight,
actualWidth, actualHeight); actualWidth, actualHeight, mScaleType);
int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth, int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth,
actualHeight, actualWidth); actualHeight, actualWidth, mScaleType);
// Decode to the nearest power of two scaling factor. // Decode to the nearest power of two scaling factor.
decodeOptions.inJustDecodeBounds = false; decodeOptions.inJustDecodeBounds = false;
......
...@@ -16,11 +16,14 @@ ...@@ -16,11 +16,14 @@
package org.telegram.android.volley.toolbox; 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.NetworkResponse;
import org.telegram.android.volley.ParseError; import org.telegram.android.volley.ParseError;
import org.telegram.android.volley.Response; 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; import java.io.UnsupportedEncodingException;
...@@ -35,15 +38,30 @@ public class JsonArrayRequest extends JsonRequest<JSONArray> { ...@@ -35,15 +38,30 @@ public class JsonArrayRequest extends JsonRequest<JSONArray> {
* @param listener Listener to receive the JSON response * @param listener Listener to receive the JSON response
* @param errorListener Error listener, or null to ignore errors. * @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); 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 @Override
protected Response<JSONArray> parseNetworkResponse(NetworkResponse response) { protected Response<JSONArray> parseNetworkResponse(NetworkResponse response) {
try { try {
String jsonString = String jsonString = new String(response.data,
new String(response.data, HttpHeaderParser.parseCharset(response.headers)); HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
return Response.success(new JSONArray(jsonString), return Response.success(new JSONArray(jsonString),
HttpHeaderParser.parseCacheHeaders(response)); HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
......
...@@ -16,11 +16,14 @@ ...@@ -16,11 +16,14 @@
package org.telegram.android.volley.toolbox; 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.NetworkResponse;
import org.telegram.android.volley.ParseError; import org.telegram.android.volley.ParseError;
import org.telegram.android.volley.Response; 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; import java.io.UnsupportedEncodingException;
...@@ -40,7 +43,7 @@ public class JsonObjectRequest extends JsonRequest<JSONObject> { ...@@ -40,7 +43,7 @@ public class JsonObjectRequest extends JsonRequest<JSONObject> {
* @param errorListener Error listener, or null to ignore errors. * @param errorListener Error listener, or null to ignore errors.
*/ */
public JsonObjectRequest(int method, String url, JSONObject jsonRequest, 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, super(method, url, (jsonRequest == null) ? null : jsonRequest.toString(), listener,
errorListener); errorListener);
} }
...@@ -49,9 +52,10 @@ public class JsonObjectRequest extends JsonRequest<JSONObject> { ...@@ -49,9 +52,10 @@ public class JsonObjectRequest extends JsonRequest<JSONObject> {
* Constructor which defaults to <code>GET</code> if <code>jsonRequest</code> is * Constructor which defaults to <code>GET</code> if <code>jsonRequest</code> is
* <code>null</code>, <code>POST</code> otherwise. * <code>null</code>, <code>POST</code> otherwise.
* *
* @see #JsonObjectRequest(int, String, JSONObject, Listener, ErrorListener)
*/ */
public JsonObjectRequest(String url, JSONObject jsonRequest, Response.Listener<JSONObject> listener, public JsonObjectRequest(String url, JSONObject jsonRequest, Listener<JSONObject> listener,
Response.ErrorListener errorListener) { ErrorListener errorListener) {
this(jsonRequest == null ? Method.GET : Method.POST, url, jsonRequest, this(jsonRequest == null ? Method.GET : Method.POST, url, jsonRequest,
listener, errorListener); listener, errorListener);
} }
...@@ -59,8 +63,8 @@ public class JsonObjectRequest extends JsonRequest<JSONObject> { ...@@ -59,8 +63,8 @@ public class JsonObjectRequest extends JsonRequest<JSONObject> {
@Override @Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) { protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
try { try {
String jsonString = String jsonString = new String(response.data,
new String(response.data, HttpHeaderParser.parseCharset(response.headers)); HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
return Response.success(new JSONObject(jsonString), return Response.success(new JSONObject(jsonString),
HttpHeaderParser.parseCacheHeaders(response)); HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
......
...@@ -19,6 +19,8 @@ package org.telegram.android.volley.toolbox; ...@@ -19,6 +19,8 @@ package org.telegram.android.volley.toolbox;
import org.telegram.android.volley.NetworkResponse; import org.telegram.android.volley.NetworkResponse;
import org.telegram.android.volley.Request; import org.telegram.android.volley.Request;
import org.telegram.android.volley.Response; 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 org.telegram.android.volley.VolleyLog;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
...@@ -30,28 +32,29 @@ import java.io.UnsupportedEncodingException; ...@@ -30,28 +32,29 @@ import java.io.UnsupportedEncodingException;
* @param <T> JSON type of response expected * @param <T> JSON type of response expected
*/ */
public abstract class JsonRequest<T> extends Request<T> { public abstract class JsonRequest<T> extends Request<T> {
/** Charset for request. */ /** Default charset for JSON request. */
private static final String PROTOCOL_CHARSET = "utf-8"; protected static final String PROTOCOL_CHARSET = "utf-8";
/** Content type for request. */ /** Content type for request. */
private static final String PROTOCOL_CONTENT_TYPE = private static final String PROTOCOL_CONTENT_TYPE =
String.format("application/json; charset=%s", PROTOCOL_CHARSET); String.format("application/json; charset=%s", PROTOCOL_CHARSET);
private final Response.Listener<T> mListener; private final Listener<T> mListener;
private final String mRequestBody; private final String mRequestBody;
/** /**
* Deprecated constructor for a JsonRequest which defaults to GET unless {@link #getPostBody()} * Deprecated constructor for a JsonRequest which defaults to GET unless {@link #getPostBody()}
* or {@link #getPostParams()} is overridden (which defaults to POST). * 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, public JsonRequest(String url, String requestBody, Listener<T> listener,
Response.ErrorListener errorListener) { ErrorListener errorListener) {
this(Method.DEPRECATED_GET_OR_POST, url, requestBody, listener, errorListener); this(Method.DEPRECATED_GET_OR_POST, url, requestBody, listener, errorListener);
} }
public JsonRequest(int method, String url, String requestBody, Response.Listener<T> listener, public JsonRequest(int method, String url, String requestBody, Listener<T> listener,
Response.ErrorListener errorListener) { ErrorListener errorListener) {
super(method, url, errorListener); super(method, url, errorListener);
mListener = listener; mListener = listener;
mRequestBody = requestBody; mRequestBody = requestBody;
......
...@@ -103,6 +103,7 @@ public class NetworkImageView extends ImageView { ...@@ -103,6 +103,7 @@ public class NetworkImageView extends ImageView {
void loadImageIfNecessary(final boolean isInLayoutPass) { void loadImageIfNecessary(final boolean isInLayoutPass) {
int width = getWidth(); int width = getWidth();
int height = getHeight(); int height = getHeight();
ScaleType scaleType = getScaleType();
boolean wrapWidth = false, wrapHeight = false; boolean wrapWidth = false, wrapHeight = false;
if (getLayoutParams() != null) { if (getLayoutParams() != null) {
...@@ -177,7 +178,7 @@ public class NetworkImageView extends ImageView { ...@@ -177,7 +178,7 @@ public class NetworkImageView extends ImageView {
setImageResource(mDefaultImageId); setImageResource(mDefaultImageId);
} }
} }
}, maxWidth, maxHeight); }, maxWidth, maxHeight, scaleType);
// update the ImageContainer to be the new bitmap container. // update the ImageContainer to be the new bitmap container.
mImageContainer = newContainer; mImageContainer = newContainer;
......
...@@ -25,7 +25,7 @@ import java.io.IOException; ...@@ -25,7 +25,7 @@ import java.io.IOException;
*/ */
public class PoolingByteArrayOutputStream extends ByteArrayOutputStream { 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. * the default size to which the underlying byte array is initialized.
*/ */
private static final int DEFAULT_SIZE = 256; private static final int DEFAULT_SIZE = 256;
......
...@@ -126,7 +126,10 @@ public class RequestFuture<T> implements Future<T>, Response.Listener<T>, ...@@ -126,7 +126,10 @@ public class RequestFuture<T> implements Future<T>, Response.Listener<T>,
@Override @Override
public boolean isCancelled() { public boolean isCancelled() {
return mRequest != null && mRequest.isCanceled(); if (mRequest == null) {
return false;
}
return mRequest.isCanceled();
} }
@Override @Override
......
...@@ -19,6 +19,8 @@ package org.telegram.android.volley.toolbox; ...@@ -19,6 +19,8 @@ package org.telegram.android.volley.toolbox;
import org.telegram.android.volley.NetworkResponse; import org.telegram.android.volley.NetworkResponse;
import org.telegram.android.volley.Request; import org.telegram.android.volley.Request;
import org.telegram.android.volley.Response; import org.telegram.android.volley.Response;
import org.telegram.android.volley.Response.ErrorListener;
import org.telegram.android.volley.Response.Listener;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
...@@ -26,7 +28,7 @@ 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. * A canned request for retrieving the response body at a given URL as a String.
*/ */
public class StringRequest extends Request<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. * Creates a new request with the given method.
...@@ -36,8 +38,8 @@ public class StringRequest extends Request<String> { ...@@ -36,8 +38,8 @@ public class StringRequest extends Request<String> {
* @param listener Listener to receive the String response * @param listener Listener to receive the String response
* @param errorListener Error listener, or null to ignore errors * @param errorListener Error listener, or null to ignore errors
*/ */
public StringRequest(int method, String url, Response.Listener<String> listener, public StringRequest(int method, String url, Listener<String> listener,
Response.ErrorListener errorListener) { ErrorListener errorListener) {
super(method, url, errorListener); super(method, url, errorListener);
mListener = listener; mListener = listener;
} }
...@@ -49,7 +51,7 @@ public class StringRequest extends Request<String> { ...@@ -49,7 +51,7 @@ public class StringRequest extends Request<String> {
* @param listener Listener to receive the String response * @param listener Listener to receive the String response
* @param errorListener Error listener, or null to ignore errors * @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); this(Method.GET, url, listener, errorListener);
} }
......
...@@ -48,7 +48,6 @@ public class Volley { ...@@ -48,7 +48,6 @@ public class Volley {
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0); PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode; userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) { } catch (NameNotFoundException e) {
/**/
} }
if (stack == null) { if (stack == null) {
......
...@@ -113,7 +113,7 @@ public class BuffersStorage { ...@@ -113,7 +113,7 @@ public class BuffersStorage {
arrayToReuse = freeBuffers128; arrayToReuse = freeBuffers128;
} else if (buffer.buffer.capacity() == 1024 + 200) { } else if (buffer.buffer.capacity() == 1024 + 200) {
arrayToReuse = freeBuffers1024; arrayToReuse = freeBuffers1024;
} if (buffer.buffer.capacity() == 4096 + 200) { } else if (buffer.buffer.capacity() == 4096 + 200) {
arrayToReuse = freeBuffers4096; arrayToReuse = freeBuffers4096;
} else if (buffer.buffer.capacity() == 16384 + 200) { } else if (buffer.buffer.capacity() == 16384 + 200) {
arrayToReuse = freeBuffers16384; arrayToReuse = freeBuffers16384;
......
...@@ -10,7 +10,7 @@ package org.telegram.messenger; ...@@ -10,7 +10,7 @@ package org.telegram.messenger;
public class BuildVars { public class BuildVars {
public static boolean DEBUG_VERSION = false; 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 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 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"; public static String HOCKEY_APP_HASH = "your-hockeyapp-api-key-here";
......
...@@ -227,6 +227,11 @@ public class FileLoadOperation { ...@@ -227,6 +227,11 @@ public class FileLoadOperation {
downloadedBytes = (int)cacheFileTemp.length(); downloadedBytes = (int)cacheFileTemp.length();
nextDownloadOffset = downloadedBytes = downloadedBytes / currentDownloadChunkSize * currentDownloadChunkSize; nextDownloadOffset = downloadedBytes = downloadedBytes / currentDownloadChunkSize * currentDownloadChunkSize;
} }
if (BuildVars.DEBUG_VERSION) {
FileLog.d("tmessages", "start loading file to temp = " + cacheFileTemp + " final = " + cacheFileFinal);
}
if (fileNameIv != null) { if (fileNameIv != null) {
cacheIvTemp = new File(tempPath, fileNameIv); cacheIvTemp = new File(tempPath, fileNameIv);
try { try {
...@@ -291,6 +296,9 @@ public class FileLoadOperation { ...@@ -291,6 +296,9 @@ public class FileLoadOperation {
return; return;
} }
state = stateFailed; state = stateFailed;
if (BuildVars.DEBUG_VERSION) {
FileLog.e("tmessages", "cancel downloading file to " + cacheFileFinal);
}
cleanup(); cleanup();
for (RequestInfo requestInfo : requestInfos) { for (RequestInfo requestInfo : requestInfos) {
if (requestInfo.requestToken != 0) { if (requestInfo.requestToken != 0) {
...@@ -340,9 +348,15 @@ public class FileLoadOperation { ...@@ -340,9 +348,15 @@ public class FileLoadOperation {
} }
if (cacheFileTemp != null) { if (cacheFileTemp != null) {
if (!cacheFileTemp.renameTo(cacheFileFinal)) { if (!cacheFileTemp.renameTo(cacheFileFinal)) {
if (BuildVars.DEBUG_VERSION) {
FileLog.e("tmessages", "unable to rename temp = " + cacheFileTemp + " to final = " + cacheFileFinal);
}
cacheFileFinal = cacheFileTemp; cacheFileFinal = cacheFileTemp;
} }
} }
if (BuildVars.DEBUG_VERSION) {
FileLog.e("tmessages", "finished downloading file to " + cacheFileFinal);
}
delegate.didFinishLoadingFile(FileLoadOperation.this, cacheFileFinal); delegate.didFinishLoadingFile(FileLoadOperation.this, cacheFileFinal);
} }
......
...@@ -573,6 +573,14 @@ public class FileLoader { ...@@ -573,6 +573,14 @@ public class FileLoader {
return getPathToAttach(sizeFull); 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(""); return new File("");
...@@ -617,7 +625,7 @@ public class FileLoader { ...@@ -617,7 +625,7 @@ public class FileLoader {
} }
} else if (attach instanceof TLRPC.PhotoSize) { } else if (attach instanceof TLRPC.PhotoSize) {
TLRPC.PhotoSize photoSize = (TLRPC.PhotoSize) attach; 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); dir = getInstance().getDirectory(MEDIA_DIR_CACHE);
} else { } else {
dir = getInstance().getDirectory(MEDIA_DIR_IMAGE); dir = getInstance().getDirectory(MEDIA_DIR_IMAGE);
......
...@@ -89,6 +89,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti ...@@ -89,6 +89,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
final Object lock = new Object(); final Object lock = new Object();
static ArrayList<HashMap<String, Object>> serverPublicKeys = null; static ArrayList<HashMap<String, Object>> serverPublicKeys = null;
HashMap<String, Object> selectPublicKey(ArrayList<Long> fingerprints) { HashMap<String, Object> selectPublicKey(ArrayList<Long> fingerprints) {
synchronized (lock) { synchronized (lock) {
if (serverPublicKeys == null) { if (serverPublicKeys == null) {
...@@ -150,7 +151,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti ...@@ -150,7 +151,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
} }
for (HashMap<String, Object> keyDesc : serverPublicKeys) { for (HashMap<String, Object> keyDesc : serverPublicKeys) {
long keyFingerprint = (Long)keyDesc.get("fingerprint"); long keyFingerprint = (Long) keyDesc.get("fingerprint");
for (long nFingerprint : fingerprints) { for (long nFingerprint : fingerprints) {
if (nFingerprint == keyFingerprint) { if (nFingerprint == keyFingerprint) {
return keyDesc; return keyDesc;
...@@ -161,7 +162,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti ...@@ -161,7 +162,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
} }
long generateMessageId() { 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) { if (messageId <= lastOutgoingMessageId) {
messageId = lastOutgoingMessageId + 1; messageId = lastOutgoingMessageId + 1;
} }
...@@ -197,7 +198,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti ...@@ -197,7 +198,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
} }
processedPQRes = true; 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)) { if (Utilities.arraysEquals(authNonce, 0, resPq.nonce, 0)) {
final HashMap<String, Object> publicKey = selectPublicKey(resPq.server_public_key_fingerprints); final HashMap<String, Object> publicKey = selectPublicKey(resPq.server_public_key_fingerprints);
if (publicKey == null) { if (publicKey == null) {
...@@ -221,11 +222,11 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti ...@@ -221,11 +222,11 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
@Override @Override
public void run() { public void run() {
ByteBuffer pBytes = ByteBuffer.allocate(4); ByteBuffer pBytes = ByteBuffer.allocate(4);
pBytes.putInt((int)factorizedPq.p); pBytes.putInt((int) factorizedPq.p);
byte[] pData = pBytes.array(); byte[] pData = pBytes.array();
ByteBuffer qBytes = ByteBuffer.allocate(4); ByteBuffer qBytes = ByteBuffer.allocate(4);
qBytes.putInt((int)factorizedPq.q); qBytes.putInt((int) factorizedPq.q);
byte[] qData = qBytes.array(); byte[] qData = qBytes.array();
TLRPC.TL_req_DH_params reqDH = new TLRPC.TL_req_DH_params(); TLRPC.TL_req_DH_params reqDH = new TLRPC.TL_req_DH_params();
...@@ -233,7 +234,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti ...@@ -233,7 +234,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
reqDH.server_nonce = authServerNonce; reqDH.server_nonce = authServerNonce;
reqDH.p = pData; reqDH.p = pData;
reqDH.q = qData; reqDH.q = qData;
reqDH.public_key_fingerprint = (Long)publicKey.get("fingerprint"); reqDH.public_key_fingerprint = (Long) publicKey.get("fingerprint");
SerializedData os = new SerializedData(); SerializedData os = new SerializedData();
...@@ -261,7 +262,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti ...@@ -261,7 +262,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
dataWithHash.writeByte(b[0]); 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(); dataWithHash.cleanup();
SerializedData encryptedData = new SerializedData(); SerializedData encryptedData = new SerializedData();
encryptedData.writeRaw(encryptedBytes); encryptedData.writeRaw(encryptedBytes);
...@@ -300,7 +301,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti ...@@ -300,7 +301,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
if (authNewNonce == null) { if (authNewNonce == null) {
return; 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(); SerializedData tmpAesKey = new SerializedData();
...@@ -423,17 +424,17 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti ...@@ -423,17 +424,17 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
for (int i = 7; i >= 0; i--) { for (int i = 7; i >= 0; i--) {
byte a_ = authNewNonce[i]; byte a_ = authNewNonce[i];
byte b_ = authServerNonce[i]; byte b_ = authServerNonce[i];
byte x = (byte)(a_ ^ b_); byte x = (byte) (a_ ^ b_);
serverSaltData.writeByte(x); serverSaltData.writeByte(x);
} }
ByteBuffer saltBuffer = ByteBuffer.wrap(serverSaltData.toByteArray()); ByteBuffer saltBuffer = ByteBuffer.wrap(serverSaltData.toByteArray());
serverSaltData.cleanup(); serverSaltData.cleanup();
timeDifference = dhInnerData.server_time - (int)(System.currentTimeMillis() / 1000); timeDifference = dhInnerData.server_time - (int) (System.currentTimeMillis() / 1000);
serverSalt = new ServerSalt(); serverSalt = new ServerSalt();
serverSalt.validSince = (int)(System.currentTimeMillis() / 1000) + timeDifference; serverSalt.validSince = (int) (System.currentTimeMillis() / 1000) + timeDifference;
serverSalt.validUntil = (int)(System.currentTimeMillis() / 1000) + timeDifference + 30 * 60; serverSalt.validUntil = (int) (System.currentTimeMillis() / 1000) + timeDifference + 30 * 60;
serverSalt.value = saltBuffer.getLong(); serverSalt.value = saltBuffer.getLong();
FileLog.d("tmessages", String.format(Locale.US, "===== Time difference: %d", timeDifference)); FileLog.d("tmessages", String.format(Locale.US, "===== Time difference: %d", timeDifference));
...@@ -489,7 +490,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti ...@@ -489,7 +490,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
setClientDHParamsMsgData = null; 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)) { if (!Utilities.arraysEquals(authNonce, 0, dhAnswer.nonce, 0)) {
FileLog.e("tmessages", "***** Invalid DH answer nonce"); FileLog.e("tmessages", "***** Invalid DH answer nonce");
...@@ -544,7 +545,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti ...@@ -544,7 +545,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
System.arraycopy(newNonceHash3Full, newNonceHash3Full.length - 16, newNonceHash3, 0, 16); System.arraycopy(newNonceHash3Full, newNonceHash3Full.length - 16, newNonceHash3, 0, 16);
if (message instanceof TLRPC.TL_dh_gen_ok) { 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)) { if (!Utilities.arraysEquals(newNonceHash1, 0, dhGenOk.new_nonce_hash1, 0)) {
FileLog.e("tmessages", "***** Invalid DH answer nonce hash 1"); FileLog.e("tmessages", "***** Invalid DH answer nonce hash 1");
beginHandshake(false); beginHandshake(false);
...@@ -569,7 +570,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti ...@@ -569,7 +570,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
} }
}); });
} else if (message instanceof TLRPC.TL_dh_gen_retry) { } 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)) { if (!Utilities.arraysEquals(newNonceHash2, 0, dhRetry.new_nonce_hash2, 0)) {
FileLog.e("tmessages", "***** Invalid DH answer nonce hash 2"); FileLog.e("tmessages", "***** Invalid DH answer nonce hash 2");
beginHandshake(false); beginHandshake(false);
...@@ -578,7 +579,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti ...@@ -578,7 +579,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
FileLog.d("tmessages", "***** Retry DH"); FileLog.d("tmessages", "***** Retry DH");
beginHandshake(false); beginHandshake(false);
} else if (message instanceof TLRPC.TL_dh_gen_fail) { } 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)) { if (!Utilities.arraysEquals(newNonceHash3, 0, dhFail.new_nonce_hash3, 0)) {
FileLog.e("tmessages", "***** Invalid DH answer nonce hash 3"); FileLog.e("tmessages", "***** Invalid DH answer nonce hash 3");
beginHandshake(false); beginHandshake(false);
......
...@@ -11729,6 +11729,15 @@ public class TLRPC { ...@@ -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 class TL_inputMessagesFilterPhotoVideo extends MessagesFilter {
public static int constructor = 0x56e9f0e4; public static int constructor = 0x56e9f0e4;
......
...@@ -300,10 +300,6 @@ public class TcpConnection extends ConnectionContext { ...@@ -300,10 +300,6 @@ public class TcpConnection extends ConnectionContext {
} }
} }
public void resumeConnection() {
}
private void reconnect() { private void reconnect() {
suspendConnection(false); suspendConnection(false);
connectionState = TcpConnectionState.TcpConnectionStageReconnecting; connectionState = TcpConnectionState.TcpConnectionStageReconnecting;
......
...@@ -39,6 +39,7 @@ public class UserConfig { ...@@ -39,6 +39,7 @@ public class UserConfig {
public static int lastPauseTime = 0; public static int lastPauseTime = 0;
public static boolean isWaitingForPasscodeEnter = false; public static boolean isWaitingForPasscodeEnter = false;
public static int lastUpdateVersion; public static int lastUpdateVersion;
public static int lastContactsSyncTime;
public static int getNewMessageId() { public static int getNewMessageId() {
int id; int id;
...@@ -76,6 +77,7 @@ public class UserConfig { ...@@ -76,6 +77,7 @@ public class UserConfig {
editor.putInt("autoLockIn", autoLockIn); editor.putInt("autoLockIn", autoLockIn);
editor.putInt("lastPauseTime", lastPauseTime); editor.putInt("lastPauseTime", lastPauseTime);
editor.putInt("lastUpdateVersion", lastUpdateVersion); editor.putInt("lastUpdateVersion", lastUpdateVersion);
editor.putInt("lastContactsSyncTime", lastContactsSyncTime);
if (currentUser != null) { if (currentUser != null) {
if (withFile) { if (withFile) {
...@@ -206,6 +208,7 @@ public class UserConfig { ...@@ -206,6 +208,7 @@ public class UserConfig {
autoLockIn = preferences.getInt("autoLockIn", 60 * 60); autoLockIn = preferences.getInt("autoLockIn", 60 * 60);
lastPauseTime = preferences.getInt("lastPauseTime", 0); lastPauseTime = preferences.getInt("lastPauseTime", 0);
lastUpdateVersion = preferences.getInt("lastUpdateVersion", 511); lastUpdateVersion = preferences.getInt("lastUpdateVersion", 511);
lastContactsSyncTime = preferences.getInt("lastContactsSyncTime", (int) (System.currentTimeMillis() / 1000) - 23 * 60 * 60);
String user = preferences.getString("user", null); String user = preferences.getString("user", null);
if (user != null) { if (user != null) {
byte[] userBytes = Base64.decode(user, Base64.DEFAULT); byte[] userBytes = Base64.decode(user, Base64.DEFAULT);
...@@ -279,6 +282,7 @@ public class UserConfig { ...@@ -279,6 +282,7 @@ public class UserConfig {
lastPauseTime = 0; lastPauseTime = 0;
isWaitingForPasscodeEnter = false; isWaitingForPasscodeEnter = false;
lastUpdateVersion = BuildVars.BUILD_VERSION; lastUpdateVersion = BuildVars.BUILD_VERSION;
lastContactsSyncTime = (int) (System.currentTimeMillis() / 1000) - 23 * 60 * 60;
saveConfig(true); saveConfig(true);
} }
} }
...@@ -135,8 +135,8 @@ public class ActionBarPopupWindow extends PopupWindow { ...@@ -135,8 +135,8 @@ public class ActionBarPopupWindow extends PopupWindow {
if (child.getVisibility() != VISIBLE) { if (child.getVisibility() != VISIBLE) {
continue; continue;
} }
int position = positions.get(child); Integer position = positions.get(child);
if (height - (position * AndroidUtilities.dp(48) + AndroidUtilities.dp(32)) > value * height) { if (position != null && height - (position * AndroidUtilities.dp(48) + AndroidUtilities.dp(32)) > value * height) {
break; break;
} }
lastStartedChild = a - 1; lastStartedChild = a - 1;
...@@ -148,8 +148,8 @@ public class ActionBarPopupWindow extends PopupWindow { ...@@ -148,8 +148,8 @@ public class ActionBarPopupWindow extends PopupWindow {
if (child.getVisibility() != VISIBLE) { if (child.getVisibility() != VISIBLE) {
continue; continue;
} }
int position = positions.get(child); Integer position = positions.get(child);
if ((position + 1) * AndroidUtilities.dp(48) - AndroidUtilities.dp(24) > value * height) { if (position != null && (position + 1) * AndroidUtilities.dp(48) - AndroidUtilities.dp(24) > value * height) {
break; break;
} }
lastStartedChild = a + 1; lastStartedChild = a + 1;
......
...@@ -219,6 +219,14 @@ public class BottomSheet extends Dialog { ...@@ -219,6 +219,14 @@ public class BottomSheet extends Dialog {
containerView.measure(MeasureSpec.makeMeasureSpec(width + left * 2, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST)); 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 @Override
...@@ -228,6 +236,56 @@ public class BottomSheet extends Dialog { ...@@ -228,6 +236,56 @@ public class BottomSheet extends Dialog {
int t = (bottom - top) - containerView.getMeasuredHeight(); int t = (bottom - top) - containerView.getMeasuredHeight();
containerView.layout(l, t, l + containerView.getMeasuredWidth(), t + 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() { container.setOnTouchListener(new View.OnTouchListener() {
...@@ -371,15 +429,6 @@ public class BottomSheet extends Dialog { ...@@ -371,15 +429,6 @@ public class BottomSheet extends Dialog {
params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
} }
getWindow().setAttributes(params); getWindow().setAttributes(params);
setOnShowListener(new OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
if (Build.VERSION.SDK_INT >= 21) {
startOpenAnimation();
}
}
});
} }
@Override @Override
...@@ -394,7 +443,14 @@ public class BottomSheet extends Dialog { ...@@ -394,7 +443,14 @@ public class BottomSheet extends Dialog {
int left = useRevealAnimation && Build.VERSION.SDK_INT <= 19 ? 0 : backgroundPaddingLeft; int left = useRevealAnimation && Build.VERSION.SDK_INT <= 19 ? 0 : backgroundPaddingLeft;
int top = useRevealAnimation && Build.VERSION.SDK_INT <= 19 ? 0 : backgroundPaddingTop; 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)); 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(); startOpenAnimation();
} }
} }
...@@ -413,7 +469,6 @@ public class BottomSheet extends Dialog { ...@@ -413,7 +469,6 @@ public class BottomSheet extends Dialog {
@SuppressLint("NewApi") @SuppressLint("NewApi")
private void startRevealAnimation(final boolean open) { private void startRevealAnimation(final boolean open) {
if (open) { if (open) {
backgroundDrawable.setAlpha(0); backgroundDrawable.setAlpha(0);
containerView.setVisibility(View.VISIBLE); containerView.setVisibility(View.VISIBLE);
...@@ -460,7 +515,11 @@ public class BottomSheet extends Dialog { ...@@ -460,7 +515,11 @@ public class BottomSheet extends Dialog {
animators.add(ObjectAnimator.ofInt(backgroundDrawable, "alpha", open ? 51 : 0)); animators.add(ObjectAnimator.ofInt(backgroundDrawable, "alpha", open ? 51 : 0));
if (Build.VERSION.SDK_INT >= 21) { if (Build.VERSION.SDK_INT >= 21) {
containerView.setElevation(AndroidUtilities.dp(10)); containerView.setElevation(AndroidUtilities.dp(10));
try {
animators.add(ViewAnimationUtils.createCircularReveal(containerView, revealX <= containerView.getMeasuredWidth() ? revealX : containerView.getMeasuredWidth(), revealY, open ? 0 : finalRevealRadius, open ? finalRevealRadius : 0)); 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); animatorSet.setDuration(300);
} else { } else {
if (!open) { if (!open) {
......
...@@ -208,6 +208,7 @@ public class BaseLocationAdapter extends BaseFragmentAdapter { ...@@ -208,6 +208,7 @@ public class BaseLocationAdapter extends BaseFragmentAdapter {
} }
} }
}); });
jsonObjReq.setShouldCache(false);
jsonObjReq.setTag("search"); jsonObjReq.setTag("search");
requestQueue.add(jsonObjReq); requestQueue.add(jsonObjReq);
} catch (Exception e) { } catch (Exception e) {
......
...@@ -13,7 +13,6 @@ import android.view.View; ...@@ -13,7 +13,6 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import org.telegram.android.MediaController; import org.telegram.android.MediaController;
import org.telegram.android.NotificationCenter;
import org.telegram.android.support.widget.RecyclerView; import org.telegram.android.support.widget.RecyclerView;
import org.telegram.ui.Cells.PhotoAttachPhotoCell; import org.telegram.ui.Cells.PhotoAttachPhotoCell;
...@@ -73,10 +72,18 @@ public class PhotoAttachAdapter extends RecyclerView.Adapter { ...@@ -73,10 +72,18 @@ public class PhotoAttachAdapter extends RecyclerView.Adapter {
view = new PhotoAttachCameraCell(mContext); view = new PhotoAttachCameraCell(mContext);
} else {*/ } else {*/
PhotoAttachPhotoCell cell = new PhotoAttachPhotoCell(mContext); PhotoAttachPhotoCell cell = new PhotoAttachPhotoCell(mContext);
cell.setOnCheckClickLisnener(new View.OnClickListener() { /*cell.setOnCheckClickLisnener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
PhotoAttachPhotoCell cell = (PhotoAttachPhotoCell) v.getParent(); onItemClick((PhotoAttachPhotoCell) v.getParent());
}
});
view = cell;*/
//}
return new Holder(cell);
}
public void onItemClick(PhotoAttachPhotoCell cell) {
MediaController.PhotoEntry photoEntry = cell.getPhotoEntry(); MediaController.PhotoEntry photoEntry = cell.getPhotoEntry();
if (selectedPhotos.containsKey(photoEntry.imageId)) { if (selectedPhotos.containsKey(photoEntry.imageId)) {
selectedPhotos.remove(photoEntry.imageId); selectedPhotos.remove(photoEntry.imageId);
...@@ -87,11 +94,6 @@ public class PhotoAttachAdapter extends RecyclerView.Adapter { ...@@ -87,11 +94,6 @@ public class PhotoAttachAdapter extends RecyclerView.Adapter {
} }
delegate.selectedPhotosChanged(); delegate.selectedPhotosChanged();
} }
});
view = cell;
//}
return new Holder(view);
}
@Override @Override
public int getItemCount() { public int getItemCount() {
......
...@@ -269,7 +269,7 @@ public class SearchAdapter extends BaseSearchAdapter { ...@@ -269,7 +269,7 @@ public class SearchAdapter extends BaseSearchAdapter {
((UserCell) view).setChecked(checkedMap.containsKey(user.id), false); ((UserCell) view).setChecked(checkedMap.containsKey(user.id), false);
} }
} else { } 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); ((ProfileSearchCell) view).useSeparator = (i != getCount() - 1 && i != searchResult.size() - 1);
if (ignoreUsers != null) { if (ignoreUsers != null) {
if (ignoreUsers.containsKey(user.id)) { if (ignoreUsers.containsKey(user.id)) {
......
...@@ -56,8 +56,10 @@ public class BaseCell extends View { ...@@ -56,8 +56,10 @@ public class BaseCell extends View {
} }
protected void setDrawableBounds(Drawable drawable, int x, int y, int w, int h) { protected void setDrawableBounds(Drawable drawable, int x, int y, int w, int h) {
if (drawable != null) {
drawable.setBounds(x, y, x + w, y + h); drawable.setBounds(x, y, x + w, y + h);
} }
}
protected void startCheckLongPress() { protected void startCheckLongPress() {
if (checkingForLongPress) { if (checkingForLongPress) {
......
...@@ -42,7 +42,6 @@ public class BotHelpCell extends View { ...@@ -42,7 +42,6 @@ public class BotHelpCell extends View {
private int height; private int height;
private int textX; private int textX;
private int textY; private int textY;
private int textXOffset;
private ClickableSpan pressedLink; private ClickableSpan pressedLink;
private LinkPath urlPath = new LinkPath(); private LinkPath urlPath = new LinkPath();
...@@ -102,10 +101,8 @@ public class BotHelpCell extends View { ...@@ -102,10 +101,8 @@ public class BotHelpCell extends View {
width = 0; width = 0;
height = textLayout.getHeight() + AndroidUtilities.dp(4 + 18); height = textLayout.getHeight() + AndroidUtilities.dp(4 + 18);
int count = textLayout.getLineCount(); int count = textLayout.getLineCount();
textXOffset = Integer.MAX_VALUE;
for (int a = 0; a < count; a++) { 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); width += AndroidUtilities.dp(4 + 18);
} }
...@@ -188,7 +185,7 @@ public class BotHelpCell extends View { ...@@ -188,7 +185,7 @@ public class BotHelpCell extends View {
ResourceLoader.backgroundMediaDrawableIn.setBounds(x, y, width + x, height + y); ResourceLoader.backgroundMediaDrawableIn.setBounds(x, y, width + x, height + y);
ResourceLoader.backgroundMediaDrawableIn.draw(canvas); ResourceLoader.backgroundMediaDrawableIn.draw(canvas);
canvas.save(); 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) { if (pressedLink != null) {
canvas.drawPath(urlPath, urlPaint); canvas.drawPath(urlPath, urlPaint);
} }
......
...@@ -22,16 +22,18 @@ import org.telegram.android.AndroidUtilities; ...@@ -22,16 +22,18 @@ import org.telegram.android.AndroidUtilities;
import org.telegram.android.ImageLoader; import org.telegram.android.ImageLoader;
import org.telegram.android.MessagesController; import org.telegram.android.MessagesController;
import org.telegram.android.SendMessagesHelper; import org.telegram.android.SendMessagesHelper;
import org.telegram.messenger.BuildVars;
import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLoader;
import org.telegram.android.MediaController; import org.telegram.android.MediaController;
import org.telegram.android.MessageObject; import org.telegram.android.MessageObject;
import org.telegram.messenger.FileLog;
import org.telegram.ui.Components.RadialProgress; import org.telegram.ui.Components.RadialProgress;
import org.telegram.ui.Components.ResourceLoader; import org.telegram.ui.Components.ResourceLoader;
import org.telegram.ui.Components.SeekBar; import org.telegram.ui.Components.SeekBar;
import java.io.File; 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 TextPaint timePaint;
private static Paint circlePaint; private static Paint circlePaint;
...@@ -51,11 +53,8 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega ...@@ -51,11 +53,8 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
private int timeWidth; private int timeWidth;
private String lastTimeString = null; private String lastTimeString = null;
private int TAG;
public ChatAudioCell(Context context) { public ChatAudioCell(Context context) {
super(context); super(context);
TAG = MediaController.getInstance().generateObserverTag();
seekBar = new SeekBar(context); seekBar = new SeekBar(context);
seekBar.delegate = this; seekBar.delegate = this;
...@@ -217,6 +216,9 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega ...@@ -217,6 +216,9 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
if (cacheFile == null) { if (cacheFile == null) {
cacheFile = FileLoader.getPathToMessage(currentMessageObject.messageOwner); cacheFile = FileLoader.getPathToMessage(currentMessageObject.messageOwner);
} }
if (BuildVars.DEBUG_VERSION) {
FileLog.d("tmessages", "looking for audio in " + cacheFile);
}
if (cacheFile.exists()) { if (cacheFile.exists()) {
MediaController.getInstance().removeLoadingFileObserver(this); MediaController.getInstance().removeLoadingFileObserver(this);
boolean playing = MediaController.getInstance().isPlayingAudio(currentMessageObject); boolean playing = MediaController.getInstance().isPlayingAudio(currentMessageObject);
...@@ -272,11 +274,6 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega ...@@ -272,11 +274,6 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
radialProgress.setProgress(progress, true); radialProgress.setProgress(progress, true);
} }
@Override
public int getObserverTag() {
return TAG;
}
@Override @Override
public void onSeekBarDrag(float progress) { public void onSeekBarDrag(float progress) {
if (currentMessageObject == null) { if (currentMessageObject == null) {
...@@ -369,7 +366,7 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega ...@@ -369,7 +366,7 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
timePaint.setColor(0xffa1aab3); timePaint.setColor(0xffa1aab3);
circlePaint.setColor(0xff4195e5); circlePaint.setColor(0xff4195e5);
} }
radialProgress.onDraw(canvas); radialProgress.draw(canvas);
canvas.save(); canvas.save();
canvas.translate(timeX, AndroidUtilities.dp(42) + namesOffset); canvas.translate(timeX, AndroidUtilities.dp(42) + namesOffset);
......
...@@ -25,6 +25,7 @@ import android.view.SoundEffectConstants; ...@@ -25,6 +25,7 @@ import android.view.SoundEffectConstants;
import org.telegram.android.AndroidUtilities; import org.telegram.android.AndroidUtilities;
import org.telegram.android.Emoji; import org.telegram.android.Emoji;
import org.telegram.android.LocaleController; import org.telegram.android.LocaleController;
import org.telegram.android.MediaController;
import org.telegram.android.UserObject; import org.telegram.android.UserObject;
import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.ApplicationLoader;
import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLoader;
...@@ -39,7 +40,7 @@ import org.telegram.ui.Components.LinkPath; ...@@ -39,7 +40,7 @@ import org.telegram.ui.Components.LinkPath;
import org.telegram.ui.Components.ResourceLoader; import org.telegram.ui.Components.ResourceLoader;
import org.telegram.ui.Components.StaticLayoutEx; import org.telegram.ui.Components.StaticLayoutEx;
public class ChatBaseCell extends BaseCell { public class ChatBaseCell extends BaseCell implements MediaController.FileDownloadProgressListener {
public interface ChatBaseCellDelegate { public interface ChatBaseCellDelegate {
void didPressedUserAvatar(ChatBaseCell cell, TLRPC.User user); void didPressedUserAvatar(ChatBaseCell cell, TLRPC.User user);
...@@ -48,6 +49,7 @@ public class ChatBaseCell extends BaseCell { ...@@ -48,6 +49,7 @@ public class ChatBaseCell extends BaseCell {
void didPressReplyMessage(ChatBaseCell cell, int id); void didPressReplyMessage(ChatBaseCell cell, int id);
void didPressUrl(MessageObject messageObject, String url); void didPressUrl(MessageObject messageObject, String url);
void needOpenWebView(String url, String title, String originalUrl, int w, int h); void needOpenWebView(String url, String title, String originalUrl, int w, int h);
void didClickedImage(ChatBaseCell cell);
boolean canPerformActions(); boolean canPerformActions();
} }
...@@ -55,6 +57,7 @@ public class ChatBaseCell extends BaseCell { ...@@ -55,6 +57,7 @@ public class ChatBaseCell extends BaseCell {
protected boolean linkPreviewPressed; protected boolean linkPreviewPressed;
protected LinkPath urlPath = new LinkPath(); protected LinkPath urlPath = new LinkPath();
protected static Paint urlPaint; protected static Paint urlPaint;
private int TAG;
public boolean isChat = false; public boolean isChat = false;
protected boolean isPressed = false; protected boolean isPressed = false;
...@@ -170,6 +173,7 @@ public class ChatBaseCell extends BaseCell { ...@@ -170,6 +173,7 @@ public class ChatBaseCell extends BaseCell {
avatarImage.setRoundRadius(AndroidUtilities.dp(21)); avatarImage.setRoundRadius(AndroidUtilities.dp(21));
avatarDrawable = new AvatarDrawable(); avatarDrawable = new AvatarDrawable();
replyImageReceiver = new ImageReceiver(this); replyImageReceiver = new ImageReceiver(this);
TAG = MediaController.getInstance().generateObserverTag();
} }
@Override @Override
...@@ -600,6 +604,10 @@ public class ChatBaseCell extends BaseCell { ...@@ -600,6 +604,10 @@ public class ChatBaseCell extends BaseCell {
} }
public ImageReceiver getPhotoImage() {
return null;
}
@Override @Override
protected void onLongPress() { protected void onLongPress() {
if (delegate != null) { if (delegate != null) {
...@@ -658,7 +666,7 @@ public class ChatBaseCell extends BaseCell { ...@@ -658,7 +666,7 @@ public class ChatBaseCell extends BaseCell {
setDrawableBounds(currentBackgroundDrawable, (!media ? 0 : AndroidUtilities.dp(9)), AndroidUtilities.dp(1), backgroundWidth, layoutHeight - AndroidUtilities.dp(2)); setDrawableBounds(currentBackgroundDrawable, (!media ? 0 : AndroidUtilities.dp(9)), AndroidUtilities.dp(1), backgroundWidth, layoutHeight - AndroidUtilities.dp(2));
} }
} }
if (drawBackground) { if (drawBackground && currentBackgroundDrawable != null) {
currentBackgroundDrawable.draw(canvas); currentBackgroundDrawable.draw(canvas);
} }
...@@ -858,4 +866,29 @@ public class ChatBaseCell extends BaseCell { ...@@ -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; ...@@ -46,11 +46,9 @@ import org.telegram.android.ImageReceiver;
import java.io.File; import java.io.File;
import java.util.Locale; import java.util.Locale;
public class ChatMediaCell extends ChatBaseCell implements MediaController.FileDownloadProgressListener { public class ChatMediaCell extends ChatBaseCell {
public interface ChatMediaCellDelegate { public interface ChatMediaCellDelegate {
void didClickedImage(ChatMediaCell cell);
void didPressedOther(ChatMediaCell cell); void didPressedOther(ChatMediaCell cell);
} }
...@@ -78,8 +76,6 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD ...@@ -78,8 +76,6 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD
private boolean allowedToSetPhoto = true; private boolean allowedToSetPhoto = true;
private int TAG;
private int buttonState = 0; private int buttonState = 0;
private int buttonPressed = 0; private int buttonPressed = 0;
private boolean imagePressed = false; private boolean imagePressed = false;
...@@ -127,8 +123,6 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD ...@@ -127,8 +123,6 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD
locationAddressPaint.setTextSize(AndroidUtilities.dp(14)); locationAddressPaint.setTextSize(AndroidUtilities.dp(14));
} }
TAG = MediaController.getInstance().generateObserverTag();
photoImage = new ImageReceiver(this); photoImage = new ImageReceiver(this);
radialProgress = new RadialProgress(this); radialProgress = new RadialProgress(this);
} }
...@@ -343,8 +337,8 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD ...@@ -343,8 +337,8 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD
private void didClickedImage() { private void didClickedImage() {
if (currentMessageObject.type == 1) { if (currentMessageObject.type == 1) {
if (buttonState == -1) { if (buttonState == -1) {
if (mediaDelegate != null) { if (delegate != null) {
mediaDelegate.didClickedImage(this); delegate.didClickedImage(this);
} }
} else if (buttonState == 0) { } else if (buttonState == 0) {
didPressedButton(false); didPressedButton(false);
...@@ -365,13 +359,13 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD ...@@ -365,13 +359,13 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD
didPressedButton(false); didPressedButton(false);
} }
} else if (currentMessageObject.type == 4) { } else if (currentMessageObject.type == 4) {
if (mediaDelegate != null) { if (delegate != null) {
mediaDelegate.didClickedImage(this); delegate.didClickedImage(this);
} }
} else if (currentMessageObject.type == 9) { } else if (currentMessageObject.type == 9) {
if (buttonState == -1) { if (buttonState == -1) {
if (mediaDelegate != null) { if (delegate != null) {
mediaDelegate.didClickedImage(this); delegate.didClickedImage(this);
} }
} }
} }
...@@ -447,8 +441,8 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD ...@@ -447,8 +441,8 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD
radialProgress.setBackground(getDrawableForCurrentState(), false, animated); radialProgress.setBackground(getDrawableForCurrentState(), false, animated);
} }
} else if (buttonState == 3) { } else if (buttonState == 3) {
if (mediaDelegate != null) { if (delegate != null) {
mediaDelegate.didClickedImage(this); delegate.didClickedImage(this);
} }
} }
} }
...@@ -828,6 +822,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD ...@@ -828,6 +822,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD
updateButtonState(dataChanged); updateButtonState(dataChanged);
} }
@Override
public ImageReceiver getPhotoImage() { public ImageReceiver getPhotoImage() {
return photoImage; return photoImage;
} }
...@@ -1066,7 +1061,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD ...@@ -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 (currentMessageObject.type == 1 || currentMessageObject.type == 3) {
if (nameLayout != null) { if (nameLayout != null) {
...@@ -1155,9 +1150,4 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD ...@@ -1155,9 +1150,4 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD
public void onProgressUpload(String fileName, float progress, boolean isEncrypted) { public void onProgressUpload(String fileName, float progress, boolean isEncrypted) {
radialProgress.setProgress(progress, true); radialProgress.setProgress(progress, true);
} }
@Override
public int getObserverTag() {
return TAG;
}
} }
...@@ -32,7 +32,7 @@ import org.telegram.ui.Components.SeekBar; ...@@ -32,7 +32,7 @@ import org.telegram.ui.Components.SeekBar;
import java.io.File; 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 { public interface ChatMusicCellDelegate {
boolean needPlayMusic(MessageObject messageObject); boolean needPlayMusic(MessageObject messageObject);
...@@ -62,13 +62,10 @@ public class ChatMusicCell extends ChatBaseCell implements SeekBar.SeekBarDelega ...@@ -62,13 +62,10 @@ public class ChatMusicCell extends ChatBaseCell implements SeekBar.SeekBarDelega
private StaticLayout authorLayout; private StaticLayout authorLayout;
private int authorX; private int authorX;
private int TAG;
private ChatMusicCellDelegate musicDelegate; private ChatMusicCellDelegate musicDelegate;
public ChatMusicCell(Context context) { public ChatMusicCell(Context context) {
super(context); super(context);
TAG = MediaController.getInstance().generateObserverTag();
seekBar = new SeekBar(context); seekBar = new SeekBar(context);
seekBar.delegate = this; seekBar.delegate = this;
...@@ -299,11 +296,6 @@ public class ChatMusicCell extends ChatBaseCell implements SeekBar.SeekBarDelega ...@@ -299,11 +296,6 @@ public class ChatMusicCell extends ChatBaseCell implements SeekBar.SeekBarDelega
radialProgress.setProgress(progress, true); radialProgress.setProgress(progress, true);
} }
@Override
public int getObserverTag() {
return TAG;
}
@Override @Override
public void onSeekBarDrag(float progress) { public void onSeekBarDrag(float progress) {
if (currentMessageObject == null) { if (currentMessageObject == null) {
...@@ -403,7 +395,7 @@ public class ChatMusicCell extends ChatBaseCell implements SeekBar.SeekBarDelega ...@@ -403,7 +395,7 @@ public class ChatMusicCell extends ChatBaseCell implements SeekBar.SeekBarDelega
} else { } else {
timePaint.setColor(0xffa1aab3); timePaint.setColor(0xffa1aab3);
} }
radialProgress.onDraw(canvas); radialProgress.draw(canvas);
canvas.save(); canvas.save();
canvas.translate(timeX + titleX, AndroidUtilities.dp(12) + namesOffset); canvas.translate(timeX + titleX, AndroidUtilities.dp(12) + namesOffset);
......
...@@ -37,9 +37,11 @@ public class ProfileSearchCell extends BaseCell { ...@@ -37,9 +37,11 @@ public class ProfileSearchCell extends BaseCell {
private static TextPaint nameEncryptedPaint; private static TextPaint nameEncryptedPaint;
private static TextPaint onlinePaint; private static TextPaint onlinePaint;
private static TextPaint offlinePaint; private static TextPaint offlinePaint;
private static TextPaint countPaint;
private static Drawable lockDrawable; private static Drawable lockDrawable;
private static Drawable broadcastDrawable; private static Drawable broadcastDrawable;
private static Drawable groupDrawable; private static Drawable groupDrawable;
private static Drawable countDrawable;
private static Paint linePaint; private static Paint linePaint;
private CharSequence currentName; private CharSequence currentName;
...@@ -50,6 +52,7 @@ public class ProfileSearchCell extends BaseCell { ...@@ -50,6 +52,7 @@ public class ProfileSearchCell extends BaseCell {
private TLRPC.User user = null; private TLRPC.User user = null;
private TLRPC.Chat chat = null; private TLRPC.Chat chat = null;
private TLRPC.EncryptedChat encryptedChat = null; private TLRPC.EncryptedChat encryptedChat = null;
long dialog_id;
private String lastName = null; private String lastName = null;
private int lastStatus = 0; private int lastStatus = 0;
...@@ -67,6 +70,13 @@ public class ProfileSearchCell extends BaseCell { ...@@ -67,6 +70,13 @@ public class ProfileSearchCell extends BaseCell {
private int nameLockLeft; private int nameLockLeft;
private int nameLockTop; 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 int onlineLeft;
private StaticLayout onlineLayout; private StaticLayout onlineLayout;
...@@ -95,9 +105,15 @@ public class ProfileSearchCell extends BaseCell { ...@@ -95,9 +105,15 @@ public class ProfileSearchCell extends BaseCell {
linePaint = new Paint(); linePaint = new Paint();
linePaint.setColor(0xffdcdcdc); 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); broadcastDrawable = getResources().getDrawable(R.drawable.list_broadcast);
lockDrawable = getResources().getDrawable(R.drawable.list_secret); lockDrawable = getResources().getDrawable(R.drawable.list_secret);
groupDrawable = getResources().getDrawable(R.drawable.list_group); groupDrawable = getResources().getDrawable(R.drawable.list_group);
countDrawable = getResources().getDrawable(R.drawable.dialogs_badge);
} }
avatarImage = new ImageReceiver(this); avatarImage = new ImageReceiver(this);
...@@ -115,12 +131,13 @@ public class ProfileSearchCell extends BaseCell { ...@@ -115,12 +131,13 @@ public class ProfileSearchCell extends BaseCell {
return super.onTouchEvent(event); 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; currentName = n;
user = u; user = u;
chat = c; chat = c;
encryptedChat = ec; encryptedChat = ec;
subLabel = s; subLabel = s;
drawCount = needCount;
update(0); update(0);
} }
...@@ -162,6 +179,7 @@ public class ProfileSearchCell extends BaseCell { ...@@ -162,6 +179,7 @@ public class ProfileSearchCell extends BaseCell {
if (encryptedChat != null) { if (encryptedChat != null) {
drawNameLock = true; drawNameLock = true;
dialog_id = ((long) encryptedChat.id) << 32;
if (!LocaleController.isRTL) { if (!LocaleController.isRTL) {
nameLockLeft = AndroidUtilities.dp(AndroidUtilities.leftBaseline); nameLockLeft = AndroidUtilities.dp(AndroidUtilities.leftBaseline);
nameLeft = AndroidUtilities.dp(AndroidUtilities.leftBaseline + 4) + lockDrawable.getIntrinsicWidth(); nameLeft = AndroidUtilities.dp(AndroidUtilities.leftBaseline + 4) + lockDrawable.getIntrinsicWidth();
...@@ -173,9 +191,11 @@ public class ProfileSearchCell extends BaseCell { ...@@ -173,9 +191,11 @@ public class ProfileSearchCell extends BaseCell {
} else { } else {
if (chat != null) { if (chat != null) {
if (chat.id < 0) { if (chat.id < 0) {
dialog_id = AndroidUtilities.makeBroadcastId(chat.id);
drawNameBroadcast = true; drawNameBroadcast = true;
nameLockTop = AndroidUtilities.dp(28.5f); nameLockTop = AndroidUtilities.dp(28.5f);
} else { } else {
dialog_id = -chat.id;
drawNameGroup = true; drawNameGroup = true;
nameLockTop = AndroidUtilities.dp(30); nameLockTop = AndroidUtilities.dp(30);
} }
...@@ -187,6 +207,7 @@ public class ProfileSearchCell extends BaseCell { ...@@ -187,6 +207,7 @@ public class ProfileSearchCell extends BaseCell {
nameLeft = AndroidUtilities.dp(11); nameLeft = AndroidUtilities.dp(11);
} }
} else { } else {
dialog_id = user.id;
if (!LocaleController.isRTL) { if (!LocaleController.isRTL) {
nameLeft = AndroidUtilities.dp(AndroidUtilities.leftBaseline); nameLeft = AndroidUtilities.dp(AndroidUtilities.leftBaseline);
} else { } else {
...@@ -234,6 +255,30 @@ public class ProfileSearchCell extends BaseCell { ...@@ -234,6 +255,30 @@ public class ProfileSearchCell extends BaseCell {
nameWidth -= AndroidUtilities.dp(6) + groupDrawable.getIntrinsicWidth(); 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); 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); nameLayout = new StaticLayout(nameStringFinal, currentNamePaint, nameWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
...@@ -244,12 +289,12 @@ public class ProfileSearchCell extends BaseCell { ...@@ -244,12 +289,12 @@ public class ProfileSearchCell extends BaseCell {
onlineLeft = AndroidUtilities.dp(11); onlineLeft = AndroidUtilities.dp(11);
} }
CharSequence onlineString; CharSequence onlineString = "";
TextPaint currentOnlinePaint = offlinePaint; TextPaint currentOnlinePaint = offlinePaint;
if (subLabel != null) { if (subLabel != null) {
onlineString = subLabel; onlineString = subLabel;
} else { } else if (user != null) {
if ((user.flags & TLRPC.USER_FLAG_BOT) != 0) { if ((user.flags & TLRPC.USER_FLAG_BOT) != 0) {
onlineString = LocaleController.getString("Bot", R.string.Bot); onlineString = LocaleController.getString("Bot", R.string.Bot);
} else { } else {
...@@ -364,6 +409,12 @@ public class ProfileSearchCell extends BaseCell { ...@@ -364,6 +409,12 @@ public class ProfileSearchCell extends BaseCell {
continueUpdate = true; 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) { if (!continueUpdate) {
return; return;
...@@ -422,10 +473,12 @@ public class ProfileSearchCell extends BaseCell { ...@@ -422,10 +473,12 @@ public class ProfileSearchCell extends BaseCell {
broadcastDrawable.draw(canvas); broadcastDrawable.draw(canvas);
} }
if (nameLayout != null) {
canvas.save(); canvas.save();
canvas.translate(nameLeft, nameTop); canvas.translate(nameLeft, nameTop);
nameLayout.draw(canvas); nameLayout.draw(canvas);
canvas.restore(); canvas.restore();
}
if (onlineLayout != null) { if (onlineLayout != null) {
canvas.save(); canvas.save();
...@@ -434,6 +487,15 @@ public class ProfileSearchCell extends BaseCell { ...@@ -434,6 +487,15 @@ public class ProfileSearchCell extends BaseCell {
canvas.restore(); 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); avatarImage.draw(canvas);
} }
} }
...@@ -9,8 +9,12 @@ ...@@ -9,8 +9,12 @@
package org.telegram.ui.Cells; package org.telegram.ui.Cells;
import android.content.Context; import android.content.Context;
import android.graphics.Canvas;
import android.os.Build;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.Gravity; import android.view.Gravity;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.TextView; import android.widget.TextView;
...@@ -26,6 +30,13 @@ public class StickerEmojiCell extends FrameLayout { ...@@ -26,6 +30,13 @@ public class StickerEmojiCell extends FrameLayout {
private BackupImageView imageView; private BackupImageView imageView;
private TLRPC.Document sticker; private TLRPC.Document sticker;
private TextView emojiTextView; 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) { public StickerEmojiCell(Context context) {
super(context); super(context);
...@@ -39,15 +50,6 @@ public class StickerEmojiCell extends FrameLayout { ...@@ -39,15 +50,6 @@ public class StickerEmojiCell extends FrameLayout {
addView(emojiTextView, LayoutHelper.createFrame(28, 28, Gravity.BOTTOM | Gravity.RIGHT)); 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() { public TLRPC.Document getSticker() {
return sticker; return sticker;
} }
...@@ -57,7 +59,6 @@ public class StickerEmojiCell extends FrameLayout { ...@@ -57,7 +59,6 @@ public class StickerEmojiCell extends FrameLayout {
sticker = document; sticker = document;
imageView.setImage(document.thumb.location, null, "webp", null); imageView.setImage(document.thumb.location, null, "webp", null);
if (showEmoji) { if (showEmoji) {
boolean set = false; boolean set = false;
for (TLRPC.DocumentAttribute attribute : document.attributes) { for (TLRPC.DocumentAttribute attribute : document.attributes) {
...@@ -78,4 +79,67 @@ public class StickerEmojiCell extends FrameLayout { ...@@ -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;
}
} }
...@@ -812,23 +812,31 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not ...@@ -812,23 +812,31 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
@Override @Override
public void onRevealAnimationStart(boolean open) { public void onRevealAnimationStart(boolean open) {
if (chatAttachView != null) {
chatAttachView.onRevealAnimationStart(open); chatAttachView.onRevealAnimationStart(open);
} }
}
@Override @Override
public void onRevealAnimationProgress(boolean open, float radius, int x, int y) { public void onRevealAnimationProgress(boolean open, float radius, int x, int y) {
if (chatAttachView != null) {
chatAttachView.onRevealAnimationProgress(open, radius, x, y); chatAttachView.onRevealAnimationProgress(open, radius, x, y);
} }
}
@Override @Override
public void onRevealAnimationEnd(boolean open) { public void onRevealAnimationEnd(boolean open) {
if (chatAttachView != null) {
chatAttachView.onRevealAnimationEnd(open); chatAttachView.onRevealAnimationEnd(open);
} }
}
@Override @Override
public void onOpenAnimationEnd() { public void onOpenAnimationEnd() {
if (chatAttachView != null) {
chatAttachView.onRevealAnimationEnd(true); chatAttachView.onRevealAnimationEnd(true);
} }
}
@Override @Override
public View getRevealView() { public View getRevealView() {
...@@ -1379,8 +1387,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not ...@@ -1379,8 +1387,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
} catch (Exception e) { } catch (Exception e) {
FileLog.e("tmessages", e); FileLog.e("tmessages", e);
} }
} else { } else if (SecretPhotoViewer.getInstance().isVisible()) {
if (SecretPhotoViewer.getInstance().isVisible()) {
AndroidUtilities.runOnUIThread(new Runnable() { AndroidUtilities.runOnUIThread(new Runnable() {
@Override @Override
public void run() { public void run() {
...@@ -1390,7 +1397,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not ...@@ -1390,7 +1397,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
}); });
SecretPhotoViewer.getInstance().closePhoto(); SecretPhotoViewer.getInstance().closePhoto();
} }
}
} else if (event.getAction() != MotionEvent.ACTION_DOWN) { } else if (event.getAction() != MotionEvent.ACTION_DOWN) {
if (SecretPhotoViewer.getInstance().isVisible()) { if (SecretPhotoViewer.getInstance().isVisible()) {
return true; return true;
...@@ -1420,12 +1426,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not ...@@ -1420,12 +1426,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
int x = (int) event.getX(); int x = (int) event.getX();
int y = (int) event.getY(); int y = (int) event.getY();
int count = chatListView.getChildCount(); int count = chatListView.getChildCount();
Rect rect = new Rect();
for (int a = 0; a < count; a++) { for (int a = 0; a < count; a++) {
View view = chatListView.getChildAt(a); View view = chatListView.getChildAt(a);
int top = view.getTop(); int top = view.getTop();
int bottom = view.getBottom(); int bottom = view.getBottom();
view.getLocalVisibleRect(rect);
if (top > y || bottom < y) { if (top > y || bottom < y) {
continue; continue;
} }
...@@ -3195,8 +3199,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not ...@@ -3195,8 +3199,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
if (extractUriFrom.contains("com.google.android.apps.photos.contentprovider")) { if (extractUriFrom.contains("com.google.android.apps.photos.contentprovider")) {
try { try {
String firstExtraction = extractUriFrom.split("/1/")[1]; String firstExtraction = extractUriFrom.split("/1/")[1];
if (firstExtraction.contains("/ACTUAL")) { int index = firstExtraction.indexOf("/ACTUAL");
firstExtraction = firstExtraction.replace("/ACTUAL", ""); if (index != -1) {
firstExtraction = firstExtraction.substring(0, index);
String secondExtraction = URLDecoder.decode(firstExtraction, "UTF-8"); String secondExtraction = URLDecoder.decode(firstExtraction, "UTF-8");
uri = Uri.parse(secondExtraction); uri = Uri.parse(secondExtraction);
} }
...@@ -4067,9 +4072,11 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not ...@@ -4067,9 +4072,11 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
if (cell.getMessageObject() != null && cell.getMessageObject().getId() == mid) { if (cell.getMessageObject() != null && cell.getMessageObject().getId() == mid) {
MessageObject playing = cell.getMessageObject(); MessageObject playing = cell.getMessageObject();
MessageObject player = MediaController.getInstance().getPlayingMessageObject(); MessageObject player = MediaController.getInstance().getPlayingMessageObject();
if (player != null) {
playing.audioProgress = player.audioProgress; playing.audioProgress = player.audioProgress;
playing.audioProgressSec = player.audioProgressSec; playing.audioProgressSec = player.audioProgressSec;
cell.updateProgress(); cell.updateProgress();
}
break; break;
} }
} }
...@@ -4823,6 +4830,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not ...@@ -4823,6 +4830,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
moveScrollToLastMessage(); moveScrollToLastMessage();
} }
} else if (option == 1) { } else if (option == 1) {
if (getParentActivity() == null) {
return;
}
final MessageObject finalSelectedObject = selectedObject; final MessageObject finalSelectedObject = selectedObject;
AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity());
builder.setMessage(LocaleController.formatString("AreYouSureDeleteMessages", R.string.AreYouSureDeleteMessages, LocaleController.formatPluralString("messages", 1))); builder.setMessage(LocaleController.formatString("AreYouSureDeleteMessages", R.string.AreYouSureDeleteMessages, LocaleController.formatPluralString("messages", 1)));
...@@ -5125,8 +5135,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not ...@@ -5125,8 +5135,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
MessageObject messageToOpen = null; MessageObject messageToOpen = null;
ImageReceiver imageReceiver = null; ImageReceiver imageReceiver = null;
View view = chatListView.getChildAt(a); View view = chatListView.getChildAt(a);
if (view instanceof ChatMediaCell) { if (view instanceof ChatBaseCell) {
ChatMediaCell cell = (ChatMediaCell) view; ChatBaseCell cell = (ChatBaseCell) view;
MessageObject message = cell.getMessageObject(); MessageObject message = cell.getMessageObject();
if (message != null && message.getId() == messageObject.getId()) { if (message != null && message.getId() == messageObject.getId()) {
messageToOpen = message; messageToOpen = message;
...@@ -5359,12 +5369,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not ...@@ -5359,12 +5369,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
public void didPressReplyMessage(ChatBaseCell cell, int id) { public void didPressReplyMessage(ChatBaseCell cell, int id) {
scrollToMessageId(id, cell.getMessageObject().getId(), true); scrollToMessageId(id, cell.getMessageObject().getId(), true);
} }
});
if (view instanceof ChatMediaCell) {
((ChatMediaCell) view).setAllowedToSetPhoto(openAnimationEnded);
((ChatMediaCell) view).setMediaDelegate(new ChatMediaCell.ChatMediaCellDelegate() {
@Override @Override
public void didClickedImage(ChatMediaCell cell) { public void didClickedImage(ChatBaseCell cell) {
MessageObject message = cell.getMessageObject(); MessageObject message = cell.getMessageObject();
if (message.isSendError()) { if (message.isSendError()) {
createMenu(cell, false); createMenu(cell, false);
...@@ -5372,7 +5379,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not ...@@ -5372,7 +5379,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
} else if (message.isSending()) { } else if (message.isSending()) {
return; return;
} }
if (message.type == 1) { if (message.type == 1 || message.type == 0) {
PhotoViewer.getInstance().setParentActivity(getParentActivity()); PhotoViewer.getInstance().setParentActivity(getParentActivity());
PhotoViewer.getInstance().openPhoto(message, ChatActivity.this); PhotoViewer.getInstance().openPhoto(message, ChatActivity.this);
} else if (message.type == 3) { } else if (message.type == 3) {
...@@ -5448,7 +5455,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not ...@@ -5448,7 +5455,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
} }
} }
} }
});
if (view instanceof ChatMediaCell) {
((ChatMediaCell) view).setAllowedToSetPhoto(openAnimationEnded);
((ChatMediaCell) view).setMediaDelegate(new ChatMediaCell.ChatMediaCellDelegate() {
@Override @Override
public void didPressedOther(ChatMediaCell cell) { public void didPressedOther(ChatMediaCell cell) {
createMenu(cell, true); createMenu(cell, true);
......
...@@ -33,6 +33,7 @@ import org.telegram.android.NotificationCenter; ...@@ -33,6 +33,7 @@ import org.telegram.android.NotificationCenter;
import org.telegram.android.support.widget.LinearLayoutManager; import org.telegram.android.support.widget.LinearLayoutManager;
import org.telegram.messenger.R; import org.telegram.messenger.R;
import org.telegram.ui.Adapters.PhotoAttachAdapter; import org.telegram.ui.Adapters.PhotoAttachAdapter;
import org.telegram.ui.Cells.PhotoAttachPhotoCell;
import org.telegram.ui.ChatActivity; import org.telegram.ui.ChatActivity;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -130,6 +131,12 @@ public class ChatAttachView extends FrameLayout implements NotificationCenter.No ...@@ -130,6 +131,12 @@ public class ChatAttachView extends FrameLayout implements NotificationCenter.No
updatePhotosButton(); updatePhotosButton();
} }
}); });
attachPhotoRecyclerView.setOnItemClickListener(new RecyclerListView.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
photoAttachAdapter.onItemClick((PhotoAttachPhotoCell) view);
}
});
views[9] = progressView = new EmptyTextProgressView(context); views[9] = progressView = new EmptyTextProgressView(context);
progressView.setText(LocaleController.getString("NoPhotos", R.string.NoPhotos)); progressView.setText(LocaleController.getString("NoPhotos", R.string.NoPhotos));
......
...@@ -135,7 +135,7 @@ public class RadialProgress { ...@@ -135,7 +135,7 @@ public class RadialProgress {
return previousDrawable != null || currentDrawable != null ? animatedAlphaValue : 0.0f; return previousDrawable != null || currentDrawable != null ? animatedAlphaValue : 0.0f;
} }
public void onDraw(Canvas canvas) { public void draw(Canvas canvas) {
if (previousDrawable != null) { if (previousDrawable != null) {
previousDrawable.setAlpha((int)(255 * animatedAlphaValue)); previousDrawable.setAlpha((int)(255 * animatedAlphaValue));
previousDrawable.setBounds((int)progressRect.left, (int)progressRect.top, (int)progressRect.right, (int)progressRect.bottom); previousDrawable.setBounds((int)progressRect.left, (int)progressRect.top, (int)progressRect.right, (int)progressRect.bottom);
......
...@@ -52,6 +52,7 @@ import org.telegram.ui.Adapters.DialogsAdapter; ...@@ -52,6 +52,7 @@ import org.telegram.ui.Adapters.DialogsAdapter;
import org.telegram.ui.Adapters.DialogsSearchAdapter; import org.telegram.ui.Adapters.DialogsSearchAdapter;
import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; import org.telegram.android.AnimationCompat.ObjectAnimatorProxy;
import org.telegram.android.AnimationCompat.ViewProxy; import org.telegram.android.AnimationCompat.ViewProxy;
import org.telegram.ui.Cells.ProfileSearchCell;
import org.telegram.ui.Cells.UserCell; import org.telegram.ui.Cells.UserCell;
import org.telegram.ui.Cells.DialogCell; import org.telegram.ui.Cells.DialogCell;
import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBar;
...@@ -226,7 +227,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. ...@@ -226,7 +227,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter.
} }
} }
if (dialogsSearchAdapter != null) { if (dialogsSearchAdapter != null) {
dialogsSearchAdapter.searchDialogs(null, dialogsType); dialogsSearchAdapter.searchDialogs(null);
} }
updatePasscodeButton(); updatePasscodeButton();
} }
...@@ -234,7 +235,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. ...@@ -234,7 +235,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter.
@Override @Override
public void onTextChanged(EditText editText) { public void onTextChanged(EditText editText) {
String text = editText.getText().toString(); String text = editText.getText().toString();
if (text.length() != 0) { if (text.length() != 0 || dialogsSearchAdapter != null && dialogsSearchAdapter.hasRecentRearch()) {
searchWas = true; searchWas = true;
if (dialogsSearchAdapter != null) { if (dialogsSearchAdapter != null) {
listView.setAdapter(dialogsSearchAdapter); listView.setAdapter(dialogsSearchAdapter);
...@@ -248,7 +249,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. ...@@ -248,7 +249,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter.
} }
} }
if (dialogsSearchAdapter != null) { if (dialogsSearchAdapter != null) {
dialogsSearchAdapter.searchDialogs(text, dialogsType); dialogsSearchAdapter.searchDialogs(text);
} }
} }
}); });
...@@ -329,14 +330,17 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. ...@@ -329,14 +330,17 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter.
MessagesController.getInstance().putUsers(users, false); MessagesController.getInstance().putUsers(users, false);
MessagesStorage.getInstance().putUsersAndChats(users, null, false, true); MessagesStorage.getInstance().putUsersAndChats(users, null, false, true);
} }
dialogsSearchAdapter.putRecentSearch(dialog_id, (TLRPC.User) obj);
} else if (obj instanceof TLRPC.Chat) { } else if (obj instanceof TLRPC.Chat) {
if (((TLRPC.Chat) obj).id > 0) { if (((TLRPC.Chat) obj).id > 0) {
dialog_id = -((TLRPC.Chat) obj).id; dialog_id = -((TLRPC.Chat) obj).id;
} else { } else {
dialog_id = AndroidUtilities.makeBroadcastId(((TLRPC.Chat) obj).id); dialog_id = AndroidUtilities.makeBroadcastId(((TLRPC.Chat) obj).id);
} }
dialogsSearchAdapter.putRecentSearch(dialog_id, (TLRPC.Chat) obj);
} else if (obj instanceof TLRPC.EncryptedChat) { } else if (obj instanceof TLRPC.EncryptedChat) {
dialog_id = ((long) ((TLRPC.EncryptedChat) obj).id) << 32; dialog_id = ((long) ((TLRPC.EncryptedChat) obj).id) << 32;
dialogsSearchAdapter.putRecentSearch(dialog_id, (TLRPC.EncryptedChat) obj);
} else if (obj instanceof MessageObject) { } else if (obj instanceof MessageObject) {
MessageObject messageObject = (MessageObject) obj; MessageObject messageObject = (MessageObject) obj;
dialog_id = messageObject.getDialogId(); dialog_id = messageObject.getDialogId();
...@@ -399,19 +403,23 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. ...@@ -399,19 +403,23 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter.
@Override @Override
public void onItemClick(View view, int position) { public void onItemClick(View view, int position) {
if (onlySelect || searching && searchWas || getParentActivity() == null) { if (onlySelect || searching && searchWas || getParentActivity() == null) {
if (searchWas && searching) { if (searchWas && searching || dialogsSearchAdapter.isRecentSearchDisplayed()) {
RecyclerView.Adapter adapter = listView.getAdapter(); RecyclerView.Adapter adapter = listView.getAdapter();
if (adapter == dialogsSearchAdapter) { if (adapter == dialogsSearchAdapter) {
Object item = dialogsSearchAdapter.getItem(position); Object item = dialogsSearchAdapter.getItem(position);
if (item instanceof String) { if (item instanceof String || dialogsSearchAdapter.isRecentSearchDisplayed()) {
AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity());
builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); builder.setTitle(LocaleController.getString("AppName", R.string.AppName));
builder.setMessage(LocaleController.getString("ClearSearch", R.string.ClearSearch)); builder.setMessage(LocaleController.getString("ClearSearch", R.string.ClearSearch));
builder.setPositiveButton(LocaleController.getString("ClearButton", R.string.ClearButton).toUpperCase(), new DialogInterface.OnClickListener() { builder.setPositiveButton(LocaleController.getString("ClearButton", R.string.ClearButton).toUpperCase(), new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialogInterface, int i) { public void onClick(DialogInterface dialogInterface, int i) {
if (dialogsSearchAdapter.isRecentSearchDisplayed()) {
dialogsSearchAdapter.clearRecentSearch();
} else {
dialogsSearchAdapter.clearRecentHashtags(); dialogsSearchAdapter.clearRecentHashtags();
} }
}
}); });
builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null);
showDialog(builder.create()); showDialog(builder.create());
...@@ -620,7 +628,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. ...@@ -620,7 +628,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter.
} else if (!onlySelect) { } else if (!onlySelect) {
type = 1; type = 1;
} }
dialogsSearchAdapter = new DialogsSearchAdapter(context, type); dialogsSearchAdapter = new DialogsSearchAdapter(context, type, dialogsType);
dialogsSearchAdapter.setDelegate(new DialogsSearchAdapter.MessagesActivitySearchAdapterDelegate() { dialogsSearchAdapter.setDelegate(new DialogsSearchAdapter.MessagesActivitySearchAdapterDelegate() {
@Override @Override
public void searchStateChanged(boolean search) { public void searchStateChanged(boolean search) {
...@@ -816,6 +824,8 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. ...@@ -816,6 +824,8 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter.
} }
} else if (child instanceof UserCell) { } else if (child instanceof UserCell) {
((UserCell) child).update(mask); ((UserCell) child).update(mask);
} else if (child instanceof ProfileSearchCell) {
((ProfileSearchCell) child).update(mask);
} }
} }
} }
......
...@@ -29,6 +29,7 @@ import android.widget.ListView; ...@@ -29,6 +29,7 @@ import android.widget.ListView;
import android.widget.TextView; import android.widget.TextView;
import org.telegram.android.AndroidUtilities; import org.telegram.android.AndroidUtilities;
import org.telegram.messenger.ApplicationLoader;
import org.telegram.messenger.FileLog; import org.telegram.messenger.FileLog;
import org.telegram.android.LocaleController; import org.telegram.android.LocaleController;
import org.telegram.messenger.R; import org.telegram.messenger.R;
...@@ -48,6 +49,8 @@ import java.util.ArrayList; ...@@ -48,6 +49,8 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.StringTokenizer;
public class DocumentSelectActivity extends BaseFragment { public class DocumentSelectActivity extends BaseFragment {
...@@ -116,7 +119,7 @@ public class DocumentSelectActivity extends BaseFragment { ...@@ -116,7 +119,7 @@ public class DocumentSelectActivity extends BaseFragment {
public void onFragmentDestroy() { public void onFragmentDestroy() {
try { try {
if (receiverRegistered) { if (receiverRegistered) {
getParentActivity().unregisterReceiver(receiver); ApplicationLoader.applicationContext.unregisterReceiver(receiver);
} }
} catch (Exception e) { } catch (Exception e) {
FileLog.e("tmessages", e); FileLog.e("tmessages", e);
...@@ -139,7 +142,7 @@ public class DocumentSelectActivity extends BaseFragment { ...@@ -139,7 +142,7 @@ public class DocumentSelectActivity extends BaseFragment {
filter.addAction(Intent.ACTION_MEDIA_UNMOUNTABLE); filter.addAction(Intent.ACTION_MEDIA_UNMOUNTABLE);
filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED); filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
filter.addDataScheme("file"); filter.addDataScheme("file");
getParentActivity().registerReceiver(receiver, filter); ApplicationLoader.applicationContext.registerReceiver(receiver, filter);
} }
actionBar.setBackButtonImage(R.drawable.ic_ab_back); actionBar.setBackButtonImage(R.drawable.ic_ab_back);
...@@ -308,7 +311,7 @@ public class DocumentSelectActivity extends BaseFragment { ...@@ -308,7 +311,7 @@ public class DocumentSelectActivity extends BaseFragment {
} else { } else {
if (!file.canRead()) { if (!file.canRead()) {
showErrorBox(LocaleController.getString("AccessError", R.string.AccessError)); showErrorBox(LocaleController.getString("AccessError", R.string.AccessError));
return; file = new File("/mnt/sdcard");
} }
if (sizeLimit != 0) { if (sizeLimit != 0) {
if (file.length() > sizeLimit) { if (file.length() > sizeLimit) {
...@@ -488,47 +491,57 @@ public class DocumentSelectActivity extends BaseFragment { ...@@ -488,47 +491,57 @@ public class DocumentSelectActivity extends BaseFragment {
private void listRoots() { private void listRoots() {
currentDir = null; currentDir = null;
items.clear(); items.clear();
String extStorage = Environment.getExternalStorageDirectory().getAbsolutePath();
HashSet<String> paths = new HashSet<>();
String defaultPath = Environment.getExternalStorageDirectory().getPath();
boolean isDefaultPathRemovable = Build.VERSION.SDK_INT >= 9 && Environment.isExternalStorageRemovable();
String defaultPathState = Environment.getExternalStorageState();
if (defaultPathState.equals(Environment.MEDIA_MOUNTED) || defaultPathState.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
ListItem ext = new ListItem(); ListItem ext = new ListItem();
if (Build.VERSION.SDK_INT < 9 || Environment.isExternalStorageRemovable()) { if (Build.VERSION.SDK_INT < 9 || Environment.isExternalStorageRemovable()) {
ext.title = LocaleController.getString("SdCard", R.string.SdCard); ext.title = LocaleController.getString("SdCard", R.string.SdCard);
ext.icon = R.drawable.ic_external_storage;
} else { } else {
ext.title = LocaleController.getString("InternalStorage", R.string.InternalStorage); ext.title = LocaleController.getString("InternalStorage", R.string.InternalStorage);
ext.icon = R.drawable.ic_storage;
} }
ext.icon = Build.VERSION.SDK_INT < 9 || Environment.isExternalStorageRemovable() ? R.drawable.ic_external_storage : R.drawable.ic_storage; ext.subtitle = getRootSubtitle(defaultPath);
ext.subtitle = getRootSubtitle(extStorage);
ext.file = Environment.getExternalStorageDirectory(); ext.file = Environment.getExternalStorageDirectory();
items.add(ext); items.add(ext);
paths.add(defaultPath);
}
BufferedReader bufferedReader = null;
try { try {
BufferedReader reader = new BufferedReader(new FileReader("/proc/mounts")); bufferedReader = new BufferedReader(new FileReader("/proc/mounts"));
String line; String line;
HashMap<String, ArrayList<String>> aliases = new HashMap<>(); while ((line = bufferedReader.readLine()) != null) {
ArrayList<String> result = new ArrayList<>(); if (line.contains("vfat") || line.contains("/mnt")) {
String extDevice = null; FileLog.e("tmessages", line);
while ((line = reader.readLine()) != null) { StringTokenizer tokens = new StringTokenizer(line, " ");
if ((!line.contains("/mnt") && !line.contains("/storage") && !line.contains("/sdcard")) || line.contains("asec") || line.contains("tmpfs") || line.contains("none")) { String unused = tokens.nextToken();
String path = tokens.nextToken();
if (paths.contains(path)) {
continue; continue;
} }
String[] info = line.split(" "); if (line.contains("/dev/block/vold")) {
if (!aliases.containsKey(info[0])) { if (!line.contains("/mnt/secure") && !line.contains("/mnt/asec") && !line.contains("/mnt/obb") && !line.contains("/dev/mapper") && !line.contains("tmpfs")) {
aliases.put(info[0], new ArrayList<String>()); if (!new File(path).isDirectory()) {
int index = path.lastIndexOf('/');
if (index != -1) {
String newPath = "/storage/" + path.substring(index + 1);
if (new File(newPath).isDirectory()) {
path = newPath;
} }
aliases.get(info[0]).add(info[1]);
if (info[1].equals(extStorage)) {
extDevice=info[0];
} }
result.add(info[1]);
} }
reader.close(); paths.add(path);
if (extDevice != null) {
result.removeAll(aliases.get(extDevice));
for (String path : result) {
try { try {
ListItem item = new ListItem(); ListItem item = new ListItem();
if (path.toLowerCase().contains("sd")) { if (path.toLowerCase().contains("sd")) {
ext.title = LocaleController.getString("SdCard", R.string.SdCard); item.title = LocaleController.getString("SdCard", R.string.SdCard);
} else { } else {
ext.title = LocaleController.getString("ExternalStorage", R.string.ExternalStorage); item.title = LocaleController.getString("ExternalStorage", R.string.ExternalStorage);
} }
item.icon = R.drawable.ic_external_storage; item.icon = R.drawable.ic_external_storage;
item.subtitle = getRootSubtitle(path); item.subtitle = getRootSubtitle(path);
...@@ -539,9 +552,19 @@ public class DocumentSelectActivity extends BaseFragment { ...@@ -539,9 +552,19 @@ public class DocumentSelectActivity extends BaseFragment {
} }
} }
} }
}
}
} catch (Exception e) {
FileLog.e("tmessages", e);
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (Exception e) { } catch (Exception e) {
FileLog.e("tmessages", e); FileLog.e("tmessages", e);
} }
}
}
ListItem fs = new ListItem(); ListItem fs = new ListItem();
fs.title = "/"; fs.title = "/";
fs.subtitle = LocaleController.getString("SystemRoot", R.string.SystemRoot); fs.subtitle = LocaleController.getString("SystemRoot", R.string.SystemRoot);
...@@ -576,6 +599,7 @@ public class DocumentSelectActivity extends BaseFragment { ...@@ -576,6 +599,7 @@ public class DocumentSelectActivity extends BaseFragment {
} }
private String getRootSubtitle(String path) { private String getRootSubtitle(String path) {
try {
StatFs stat = new StatFs(path); StatFs stat = new StatFs(path);
long total = (long)stat.getBlockCount() * (long)stat.getBlockSize(); long total = (long)stat.getBlockCount() * (long)stat.getBlockSize();
long free = (long)stat.getAvailableBlocks() * (long)stat.getBlockSize(); long free = (long)stat.getAvailableBlocks() * (long)stat.getBlockSize();
...@@ -583,6 +607,10 @@ public class DocumentSelectActivity extends BaseFragment { ...@@ -583,6 +607,10 @@ public class DocumentSelectActivity extends BaseFragment {
return ""; return "";
} }
return LocaleController.formatString("FreeOfTotal", R.string.FreeOfTotal, AndroidUtilities.formatFileSize(free), AndroidUtilities.formatFileSize(total)); return LocaleController.formatString("FreeOfTotal", R.string.FreeOfTotal, AndroidUtilities.formatFileSize(free), AndroidUtilities.formatFileSize(total));
} catch (Exception e) {
FileLog.e("tmessages", e);
}
return path;
} }
private class ListAdapter extends BaseFragmentAdapter { private class ListAdapter extends BaseFragmentAdapter {
......
...@@ -780,6 +780,7 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen ...@@ -780,6 +780,7 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen
updateSearchInterface(); updateSearchInterface();
} }
}); });
jsonObjReq.setShouldCache(false);
jsonObjReq.setTag("search"); jsonObjReq.setTag("search");
requestQueue.add(jsonObjReq); requestQueue.add(jsonObjReq);
} catch (Exception e) { } catch (Exception e) {
...@@ -866,6 +867,7 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen ...@@ -866,6 +867,7 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen
return headers; return headers;
} }
}; };
jsonObjReq.setShouldCache(false);
jsonObjReq.setTag("search"); jsonObjReq.setTag("search");
requestQueue.add(jsonObjReq); requestQueue.add(jsonObjReq);
} catch (Exception e) { } catch (Exception e) {
......
...@@ -58,18 +58,6 @@ public class SecretPhotoViewer implements NotificationCenter.NotificationCenterD ...@@ -58,18 +58,6 @@ public class SecretPhotoViewer implements NotificationCenter.NotificationCenterD
} }
} }
private class FrameLayoutTouchListener extends FrameLayout {
public FrameLayoutTouchListener(Context context) {
super(context);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
FileLog.e("tmessages", event.toString());
return super.onTouchEvent(event);
}
}
private class SecretDeleteTimer extends FrameLayout { private class SecretDeleteTimer extends FrameLayout {
private String currentInfoString; private String currentInfoString;
private int infoWidth; private int infoWidth;
...@@ -147,7 +135,7 @@ public class SecretPhotoViewer implements NotificationCenter.NotificationCenterD ...@@ -147,7 +135,7 @@ public class SecretPhotoViewer implements NotificationCenter.NotificationCenterD
private Activity parentActivity; private Activity parentActivity;
private WindowManager.LayoutParams windowLayoutParams; private WindowManager.LayoutParams windowLayoutParams;
private FrameLayoutTouchListener windowView; private FrameLayout windowView;
private FrameLayoutDrawer containerView; private FrameLayoutDrawer containerView;
private ImageReceiver centerImage = new ImageReceiver(); private ImageReceiver centerImage = new ImageReceiver();
private SecretDeleteTimer secretDeleteTimer; private SecretDeleteTimer secretDeleteTimer;
...@@ -205,7 +193,7 @@ public class SecretPhotoViewer implements NotificationCenter.NotificationCenterD ...@@ -205,7 +193,7 @@ public class SecretPhotoViewer implements NotificationCenter.NotificationCenterD
} }
parentActivity = activity; parentActivity = activity;
windowView = new FrameLayoutTouchListener(activity); windowView = new FrameLayout(activity);
windowView.setBackgroundColor(0xff000000); windowView.setBackgroundColor(0xff000000);
windowView.setFocusable(true); windowView.setFocusable(true);
windowView.setFocusableInTouchMode(true); windowView.setFocusableInTouchMode(true);
......
...@@ -4,6 +4,6 @@ buildscript { ...@@ -4,6 +4,6 @@ buildscript {
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:1.2.3' classpath 'com.android.tools.build:gradle:1.3.1'
} }
} }
\ No newline at end of file
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