From 3e901772774515f3363d4c9894740e5b2e52641b Mon Sep 17 00:00:00 2001 From: DrKLO Date: Thu, 13 Aug 2015 12:23:31 +0300 Subject: [PATCH] Update to 3.1.2 --- TMessagesProj/build.gradle | 4 +- .../telegram/android/ContactsController.java | 6 +- .../main/java/org/telegram/android/Emoji.java | 8 +- .../org/telegram/android/ImageLoader.java | 138 ++--- .../org/telegram/android/MediaController.java | 7 +- .../org/telegram/android/MessagesStorage.java | 33 +- .../android/query/SharedMediaQuery.java | 19 +- .../telegram/android/query/StickersQuery.java | 12 +- .../org/telegram/android/volley/Cache.java | 17 +- .../android/volley/CacheDispatcher.java | 1 + .../org/telegram/android/volley/Network.java | 2 +- .../android/volley/NetworkDispatcher.java | 6 +- .../android/volley/NetworkResponse.java | 2 +- .../org/telegram/android/volley/Request.java | 42 +- .../telegram/android/volley/RequestQueue.java | 43 +- .../org/telegram/android/volley/Response.java | 4 +- .../android/volley/ResponseDelivery.java | 6 +- .../telegram/android/volley/RetryPolicy.java | 6 +- .../telegram/android/volley/ServerError.java | 1 + .../telegram/android/volley/VolleyLog.java | 9 +- .../volley/toolbox/AndroidAuthenticator.java | 19 +- .../android/volley/toolbox/Authenticator.java | 4 +- .../android/volley/toolbox/BasicNetwork.java | 32 +- .../volley/toolbox/ClearCacheRequest.java | 6 +- .../volley/toolbox/DiskBasedCache.java | 122 ++-- .../volley/toolbox/HttpClientStack.java | 24 +- .../volley/toolbox/HttpHeaderParser.java | 53 +- .../android/volley/toolbox/HttpStack.java | 5 +- .../android/volley/toolbox/HurlStack.java | 47 +- .../android/volley/toolbox/ImageLoader.java | 61 +- .../android/volley/toolbox/ImageRequest.java | 55 +- .../volley/toolbox/JsonArrayRequest.java | 28 +- .../volley/toolbox/JsonObjectRequest.java | 18 +- .../android/volley/toolbox/JsonRequest.java | 17 +- .../volley/toolbox/NetworkImageView.java | 3 +- .../toolbox/PoolingByteArrayOutputStream.java | 2 +- .../android/volley/toolbox/RequestFuture.java | 5 +- .../android/volley/toolbox/StringRequest.java | 10 +- .../android/volley/toolbox/Volley.java | 1 - .../telegram/messenger/BuffersStorage.java | 2 +- .../org/telegram/messenger/BuildVars.java | 2 +- .../telegram/messenger/FileLoadOperation.java | 14 + .../org/telegram/messenger/FileLoader.java | 10 +- .../telegram/messenger/HandshakeAction.java | 33 +- .../java/org/telegram/messenger/TLRPC.java | 9 + .../org/telegram/messenger/TcpConnection.java | 4 - .../org/telegram/messenger/UserConfig.java | 4 + .../ui/ActionBar/ActionBarPopupWindow.java | 8 +- .../telegram/ui/ActionBar/BottomSheet.java | 83 ++- .../ui/Adapters/BaseLocationAdapter.java | 1 + .../ui/Adapters/DialogsSearchAdapter.java | 295 ++++++++-- .../ui/Adapters/PhotoAttachAdapter.java | 30 +- .../telegram/ui/Adapters/SearchAdapter.java | 2 +- .../java/org/telegram/ui/Cells/BaseCell.java | 4 +- .../org/telegram/ui/Cells/BotHelpCell.java | 7 +- .../org/telegram/ui/Cells/ChatAudioCell.java | 17 +- .../org/telegram/ui/Cells/ChatBaseCell.java | 37 +- .../org/telegram/ui/Cells/ChatMediaCell.java | 32 +- .../telegram/ui/Cells/ChatMessageCell.java | 198 ++++++- .../org/telegram/ui/Cells/ChatMusicCell.java | 12 +- .../telegram/ui/Cells/ProfileSearchCell.java | 76 ++- .../org/telegram/ui/Cells/SharedLinkCell.java | 247 ++++++++ .../telegram/ui/Cells/StickerEmojiCell.java | 84 ++- .../java/org/telegram/ui/ChatActivity.java | 202 +++---- .../ui/Components/ChatAttachView.java | 7 + .../org/telegram/ui/Components/EmojiView.java | 529 ++++++++++++++---- .../ui/Components/RadialProgress.java | 2 +- .../java/org/telegram/ui/DialogsActivity.java | 24 +- .../telegram/ui/DocumentSelectActivity.java | 138 +++-- .../java/org/telegram/ui/LaunchActivity.java | 81 ++- .../java/org/telegram/ui/MediaActivity.java | 347 +++++++++--- .../org/telegram/ui/PhotoPickerActivity.java | 2 + .../java/org/telegram/ui/PhotoViewer.java | 18 +- .../org/telegram/ui/SecretPhotoViewer.java | 16 +- .../org/telegram/ui/StickerPreviewViewer.java | 221 ++++++++ .../src/main/res/values-ar/strings.xml | 9 +- .../src/main/res/values-de/strings.xml | 11 +- .../src/main/res/values-es/strings.xml | 7 +- .../src/main/res/values-it/strings.xml | 15 +- .../src/main/res/values-ko/strings.xml | 9 +- .../src/main/res/values-nl/strings.xml | 9 +- .../src/main/res/values-pt-rBR/strings.xml | 9 +- .../src/main/res/values-pt-rPT/strings.xml | 9 +- TMessagesProj/src/main/res/values/strings.xml | 9 +- build.gradle | 2 +- 85 files changed, 2856 insertions(+), 909 deletions(-) create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedLinkCell.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/StickerPreviewViewer.java diff --git a/TMessagesProj/build.gradle b/TMessagesProj/build.gradle index 207b7a544..ed0db1e8e 100644 --- a/TMessagesProj/build.gradle +++ b/TMessagesProj/build.gradle @@ -73,7 +73,7 @@ android { defaultConfig { minSdkVersion 8 targetSdkVersion 22 - versionCode 580 - versionName "3.1.1" + versionCode 586 + versionName "3.1.2" } } diff --git a/TMessagesProj/src/main/java/org/telegram/android/ContactsController.java b/TMessagesProj/src/main/java/org/telegram/android/ContactsController.java index 4e492ceaf..f6f710767 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/ContactsController.java +++ b/TMessagesProj/src/main/java/org/telegram/android/ContactsController.java @@ -939,10 +939,14 @@ public class ContactsController { @Override public void run() { FileLog.e("tmessages", "done loading contacts"); - if (from == 1 && contactsArr.isEmpty()) { + if (from == 1 && (contactsArr.isEmpty() || UserConfig.lastContactsSyncTime < (int) (System.currentTimeMillis() / 1000) - 24 * 60 * 60)) { loadContacts(false, true); return; } + if (from == 0) { + UserConfig.lastContactsSyncTime = (int) (System.currentTimeMillis() / 1000); + UserConfig.saveConfig(false); + } for (TLRPC.TL_contact contact : contactsArr) { if (usersDict.get(contact.user_id) == null && contact.user_id != UserConfig.getClientUserId()) { diff --git a/TMessagesProj/src/main/java/org/telegram/android/Emoji.java b/TMessagesProj/src/main/java/org/telegram/android/Emoji.java index 7dd282aae..cc1ba3a64 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/Emoji.java +++ b/TMessagesProj/src/main/java/org/telegram/android/Emoji.java @@ -61,8 +61,6 @@ public class Emoji { }; public static long[][] data = { - new long[] - {}, new long[]//189 { 0x00000000D83DDE04L, 0x00000000D83DDE03L, 0x00000000D83DDE00L, 0x00000000D83DDE0AL, 0x000000000000263AL, 0x00000000D83DDE09L, 0x00000000D83DDE0DL, @@ -215,10 +213,10 @@ public class Emoji { bigImgSize = AndroidUtilities.dp(32); } - for (int j = 1; j < data.length; j++) { + for (int j = 0; j < data.length; j++) { for (int i = 0; i < data[j].length; i++) { - Rect rect = new Rect((i % cols[j - 1]) * emojiFullSize, (i / cols[j - 1]) * emojiFullSize, (i % cols[j - 1] + 1) * emojiFullSize, (i / cols[j - 1] + 1) * emojiFullSize); - rects.put(data[j][i], new DrawableInfo(rect, (byte) (j - 1))); + Rect rect = new Rect((i % cols[j]) * emojiFullSize, (i / cols[j]) * emojiFullSize, (i % cols[j] + 1) * emojiFullSize, (i / cols[j] + 1) * emojiFullSize); + rects.put(data[j][i], new DrawableInfo(rect, (byte) j)); } } placeholderPaint = new Paint(); diff --git a/TMessagesProj/src/main/java/org/telegram/android/ImageLoader.java b/TMessagesProj/src/main/java/org/telegram/android/ImageLoader.java index 21355c5d2..8ea74bf06 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/ImageLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/android/ImageLoader.java @@ -1206,75 +1206,55 @@ public class ImageLoader { telegramPath = new File(Environment.getExternalStorageDirectory(), "Telegram"); telegramPath.mkdirs(); - boolean canRename = false; - - try { - for (int a = 0; a < 5; a++) { - File srcFile = new File(cachePath, "temp.file"); - srcFile.createNewFile(); - File dstFile = new File(telegramPath, "temp.file"); - canRename = srcFile.renameTo(dstFile); - srcFile.delete(); - dstFile.delete(); - if (canRename) { - break; + if (telegramPath.isDirectory()) { + try { + File imagePath = new File(telegramPath, "Telegram Images"); + imagePath.mkdir(); + if (imagePath.isDirectory() && canMoveFiles(cachePath, imagePath)) { + mediaDirs.put(FileLoader.MEDIA_DIR_IMAGE, imagePath); + FileLog.e("tmessages", "image path = " + imagePath); } + } catch (Exception e) { + FileLog.e("tmessages", e); } - } catch (Exception e) { - FileLog.e("tmessages", e); - } - if (canRename) { - if (telegramPath.isDirectory()) { - try { - File imagePath = new File(telegramPath, "Telegram Images"); - imagePath.mkdir(); - if (imagePath.isDirectory()) { - mediaDirs.put(FileLoader.MEDIA_DIR_IMAGE, imagePath); - FileLog.e("tmessages", "image path = " + imagePath); - } - } catch (Exception e) { - FileLog.e("tmessages", e); - } - - try { - File videoPath = new File(telegramPath, "Telegram Video"); - videoPath.mkdir(); - if (videoPath.isDirectory()) { - mediaDirs.put(FileLoader.MEDIA_DIR_VIDEO, videoPath); - FileLog.e("tmessages", "video path = " + videoPath); - } - } catch (Exception e) { - FileLog.e("tmessages", e); - } - - try { - File audioPath = new File(telegramPath, "Telegram Audio"); - audioPath.mkdir(); - if (audioPath.isDirectory()) { - new File(audioPath, ".nomedia").createNewFile(); - mediaDirs.put(FileLoader.MEDIA_DIR_AUDIO, audioPath); - FileLog.e("tmessages", "audio path = " + audioPath); - } - } catch (Exception e) { - FileLog.e("tmessages", e); - } - - try { - File documentPath = new File(telegramPath, "Telegram Documents"); - documentPath.mkdir(); - if (documentPath.isDirectory()) { - new File(documentPath, ".nomedia").createNewFile(); - mediaDirs.put(FileLoader.MEDIA_DIR_DOCUMENT, documentPath); - FileLog.e("tmessages", "documents path = " + documentPath); - } - } catch (Exception e) { - FileLog.e("tmessages", e); + try { + File videoPath = new File(telegramPath, "Telegram Video"); + videoPath.mkdir(); + if (videoPath.isDirectory() && canMoveFiles(cachePath, videoPath)) { + mediaDirs.put(FileLoader.MEDIA_DIR_VIDEO, videoPath); + FileLog.e("tmessages", "video path = " + videoPath); } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + + try { + File audioPath = new File(telegramPath, "Telegram Audio"); + audioPath.mkdir(); + if (audioPath.isDirectory() && canMoveFiles(cachePath, audioPath)) { + new File(audioPath, ".nomedia").createNewFile(); + mediaDirs.put(FileLoader.MEDIA_DIR_AUDIO, audioPath); + FileLog.e("tmessages", "audio path = " + audioPath); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + + try { + File documentPath = new File(telegramPath, "Telegram Documents"); + documentPath.mkdir(); + if (documentPath.isDirectory() && canMoveFiles(cachePath, documentPath)) { + new File(documentPath, ".nomedia").createNewFile(); + mediaDirs.put(FileLoader.MEDIA_DIR_DOCUMENT, documentPath); + FileLog.e("tmessages", "documents path = " + documentPath); + } + } catch (Exception e) { + FileLog.e("tmessages", e); } - } else { - FileLog.e("tmessages", "this Android can't rename files"); } + } else { + FileLog.e("tmessages", "this Android can't rename files"); } MediaController.getInstance().checkSaveToGalleryFiles(); } catch (Exception e) { @@ -1284,6 +1264,38 @@ public class ImageLoader { return mediaDirs; } + private boolean canMoveFiles(File from, File to) { + RandomAccessFile file = null; + try { + for (int a = 0; a < 5; a++) { + File srcFile = new File(from, "temp.file"); + srcFile.createNewFile(); + file = new RandomAccessFile(srcFile, "rws"); + file.write(1); + file.close(); + file = null; + File dstFile = new File(to, "temp.file"); + boolean canRename = srcFile.renameTo(dstFile); + srcFile.delete(); + dstFile.delete(); + if (canRename) { + return true; + } + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } finally { + try { + if (file != null) { + file.close(); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + return false; + } + public Float getFileProgress(String location) { if (location == null) { return null; diff --git a/TMessagesProj/src/main/java/org/telegram/android/MediaController.java b/TMessagesProj/src/main/java/org/telegram/android/MediaController.java index d7fda8509..69e12e748 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/MediaController.java +++ b/TMessagesProj/src/main/java/org/telegram/android/MediaController.java @@ -1445,6 +1445,9 @@ public class MediaController implements NotificationCenter.NotificationCenterDel } private void buildShuffledPlayList() { + if (playlist.isEmpty()) { + return; + } ArrayList all = new ArrayList<>(playlist); shuffledPlaylist.clear(); @@ -2492,7 +2495,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel albumEntry.addPhoto(photoEntry); } } - } catch (Exception e) { + } catch (Throwable e) { FileLog.e("tmessages", e); } finally { if (cursor != null) { @@ -2551,7 +2554,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel albumEntry.addPhoto(photoEntry); } } - } catch (Exception e) { + } catch (Throwable e) { FileLog.e("tmessages", e); } finally { if (cursor != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/android/MessagesStorage.java b/TMessagesProj/src/main/java/org/telegram/android/MessagesStorage.java index ad37757fc..754ce0967 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/MessagesStorage.java +++ b/TMessagesProj/src/main/java/org/telegram/android/MessagesStorage.java @@ -131,6 +131,8 @@ public class MessagesStorage { database.executeFast("CREATE TABLE sent_files_v2(uid TEXT, type INTEGER, data BLOB, PRIMARY KEY (uid, type))").stepThis().dispose(); + database.executeFast("CREATE TABLE search_recent(did INTEGER PRIMARY KEY, date INTEGER);").stepThis().dispose(); + //database.executeFast("CREATE TABLE messages_holes(uid INTEGER, start INTEGER, end INTEGER, PRIMARY KEY(uid, start));").stepThis().dispose(); //database.executeFast("CREATE INDEX IF NOT EXISTS type_uid_end_messages_holes ON messages_holes(uid, end);").stepThis().dispose(); //database.executeFast("CREATE TABLE secret_holes(uid INTEGER, seq_in INTEGER, seq_out INTEGER, data BLOB, PRIMARY KEY (uid, seq_in, seq_out));").stepThis().dispose(); @@ -170,7 +172,7 @@ public class MessagesStorage { database.executeFast("CREATE INDEX IF NOT EXISTS bot_keyboard_idx_mid ON bot_keyboard(mid);").stepThis().dispose(); //version - database.executeFast("PRAGMA user_version = 20").stepThis().dispose(); + database.executeFast("PRAGMA user_version = 21").stepThis().dispose(); } else { try { SQLiteCursor cursor = database.queryFinalized("SELECT seq, pts, date, qts, lsv, sg, pbytes FROM params WHERE id = 1"); @@ -201,7 +203,7 @@ public class MessagesStorage { } } int version = database.executeInt("PRAGMA user_version"); - if (version < 20) { + if (version < 21) { updateDbToLastVersion(version); } } @@ -410,7 +412,12 @@ public class MessagesStorage { database.executeFast("CREATE TABLE IF NOT EXISTS bot_keyboard(uid INTEGER PRIMARY KEY, mid INTEGER, info BLOB)").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS bot_keyboard_idx_mid ON bot_keyboard(mid);").stepThis().dispose(); database.executeFast("PRAGMA user_version = 20").stepThis().dispose(); - //version = 20; + version = 20; + } + if (version == 20) { + database.executeFast("CREATE TABLE search_recent(did INTEGER PRIMARY KEY, date INTEGER);").stepThis().dispose(); + database.executeFast("PRAGMA user_version = 21").stepThis().dispose(); + //version = 21; } } catch (Exception e) { FileLog.e("tmessages", e); @@ -887,6 +894,7 @@ public class MessagesStorage { if (!messagesOnly) { database.executeFast("DELETE FROM dialogs WHERE did = " + did).stepThis().dispose(); database.executeFast("DELETE FROM chat_settings WHERE uid = " + did).stepThis().dispose(); + database.executeFast("DELETE FROM search_recent WHERE did = " + did).stepThis().dispose(); int lower_id = (int)did; int high_id = (int)(did >> 32); if (lower_id != 0) { @@ -2100,11 +2108,19 @@ public class MessagesStorage { storageQueue.postRunnable(new Runnable() { @Override public void run() { - database.commitTransaction(); + try { + database.commitTransaction(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } } }); } else { - database.commitTransaction(); + try { + database.commitTransaction(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } } } @@ -2745,6 +2761,7 @@ public class MessagesStorage { database.beginTransaction(); SQLitePreparedStatement state = database.executeFast("UPDATE messages SET data = ? WHERE mid = ?"); + SQLitePreparedStatement state2 = database.executeFast("UPDATE media_v2 SET data = ? WHERE mid = ?"); for (TLRPC.Message message : messages) { ByteBufferDesc data = buffersStorage.getFreeBuffer(message.getObjectSize()); message.serializeToStream(data); @@ -2754,9 +2771,15 @@ public class MessagesStorage { state.bindInteger(2, message.id); state.step(); + state2.requery(); + state2.bindByteBuffer(1, data.buffer); + state2.bindInteger(2, message.id); + state2.step(); + buffersStorage.reuseFreeBuffer(data); } state.dispose(); + state2.dispose(); database.commitTransaction(); diff --git a/TMessagesProj/src/main/java/org/telegram/android/query/SharedMediaQuery.java b/TMessagesProj/src/main/java/org/telegram/android/query/SharedMediaQuery.java index 492fea8d9..f7275f504 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/query/SharedMediaQuery.java +++ b/TMessagesProj/src/main/java/org/telegram/android/query/SharedMediaQuery.java @@ -32,6 +32,7 @@ public class SharedMediaQuery { public final static int MEDIA_PHOTOVIDEO = 0; public final static int MEDIA_FILE = 1; public final static int MEDIA_AUDIO = 2; + public final static int MEDIA_URL = 3; public static void loadMedia(final long uid, final int offset, final int count, final int max_id, final int type, final boolean fromCache, final int classGuid) { int lower_part = (int)uid; @@ -48,6 +49,8 @@ public class SharedMediaQuery { req.filter = new TLRPC.TL_inputMessagesFilterDocument(); } else if (type == MEDIA_AUDIO) { req.filter = new TLRPC.TL_inputMessagesFilterAudio(); + } else if (type == MEDIA_URL) { + req.filter = new TLRPC.TL_inputMessagesFilterUrl(); } req.q = ""; if (uid < 0) { @@ -94,6 +97,8 @@ public class SharedMediaQuery { req.filter = new TLRPC.TL_inputMessagesFilterDocument(); } else if (type == MEDIA_AUDIO) { req.filter = new TLRPC.TL_inputMessagesFilterAudio(); + } else if (type == MEDIA_URL) { + req.filter = new TLRPC.TL_inputMessagesFilterUrl(); } req.q = ""; if (uid < 0) { @@ -144,15 +149,17 @@ public class SharedMediaQuery { return -1; } if (message.media instanceof TLRPC.TL_messageMediaPhoto || message.media instanceof TLRPC.TL_messageMediaVideo) { - return SharedMediaQuery.MEDIA_PHOTOVIDEO; + return MEDIA_PHOTOVIDEO; } else if (message.media instanceof TLRPC.TL_messageMediaDocument) { if (MessageObject.isStickerMessage(message)) { return -1; } else { - return SharedMediaQuery.MEDIA_FILE; + return MEDIA_FILE; } } else if (message.media instanceof TLRPC.TL_messageMediaAudio) { - return SharedMediaQuery.MEDIA_AUDIO; + return MEDIA_AUDIO; + } else if (message.media instanceof TLRPC.TL_messageMediaWebPage) { + return MEDIA_URL; } return -1; } @@ -160,7 +167,11 @@ public class SharedMediaQuery { public static boolean canAddMessageToMedia(TLRPC.Message message) { if (message instanceof TLRPC.TL_message_secret && message.media instanceof TLRPC.TL_messageMediaPhoto && message.ttl != 0 && message.ttl <= 60) { return false; - } else if (message.media instanceof TLRPC.TL_messageMediaPhoto || message.media instanceof TLRPC.TL_messageMediaVideo || message.media instanceof TLRPC.TL_messageMediaDocument || message.media instanceof TLRPC.TL_messageMediaAudio) { + } else if (message.media instanceof TLRPC.TL_messageMediaPhoto || + message.media instanceof TLRPC.TL_messageMediaVideo || + message.media instanceof TLRPC.TL_messageMediaDocument || + message.media instanceof TLRPC.TL_messageMediaAudio/* || + message.media instanceof TLRPC.TL_messageMediaWebPage && !(message.media.webpage instanceof TLRPC.TL_webPageEmpty)*/) { return true; } return false; diff --git a/TMessagesProj/src/main/java/org/telegram/android/query/StickersQuery.java b/TMessagesProj/src/main/java/org/telegram/android/query/StickersQuery.java index d4187165f..29ceb0438 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/query/StickersQuery.java +++ b/TMessagesProj/src/main/java/org/telegram/android/query/StickersQuery.java @@ -110,8 +110,9 @@ public class StickersQuery { ArrayList newStickerArray = null; int date = 0; String hash = null; + SQLiteCursor cursor = null; try { - SQLiteCursor cursor = MessagesStorage.getInstance().getDatabase().queryFinalized("SELECT data, date, hash FROM stickers_v2 WHERE 1"); + cursor = MessagesStorage.getInstance().getDatabase().queryFinalized("SELECT data, date, hash FROM stickers_v2 WHERE 1"); if (cursor.next()) { ByteBufferDesc data = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(cursor.byteArrayLength(0)); if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) { @@ -128,9 +129,12 @@ public class StickersQuery { hash = cursor.stringValue(2); MessagesStorage.getInstance().getBuffersStorage().reuseFreeBuffer(data); } - cursor.dispose(); - } catch (Exception e) { + } catch (Throwable e) { FileLog.e("tmessages", e); + } finally { + if (cursor != null) { + cursor.dispose(); + } } processLoadedStickers(newStickerArray, true, date, hash); } @@ -229,7 +233,7 @@ public class StickersQuery { }); } - private static long getStickerSetId(TLRPC.Document document) { + public static long getStickerSetId(TLRPC.Document document) { for (TLRPC.DocumentAttribute attribute : document.attributes) { if (attribute instanceof TLRPC.TL_documentAttributeSticker) { if (attribute.stickerset instanceof TLRPC.TL_inputStickerSetID) { diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/Cache.java b/TMessagesProj/src/main/java/org/telegram/android/volley/Cache.java index 90433e7ef..5d6e4e548 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/Cache.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/Cache.java @@ -28,43 +28,43 @@ public interface Cache { * @param key Cache key * @return An {@link Entry} or null in the event of a cache miss */ - Entry get(String key); + public Entry get(String key); /** * Adds or replaces an entry to the cache. * @param key Cache key * @param entry Data to store and metadata for cache coherency, TTL, etc. */ - void put(String key, Entry entry); + public void put(String key, Entry entry); /** * Performs any potentially long-running actions needed to initialize the cache; * will be called from a worker thread. */ - void initialize(); + public void initialize(); /** * Invalidates an entry in the cache. * @param key Cache key * @param fullExpire True to fully expire the entry, false to soft expire */ - void invalidate(String key, boolean fullExpire); + public void invalidate(String key, boolean fullExpire); /** * Removes an entry from the cache. * @param key Cache key */ - void remove(String key); + public void remove(String key); /** * Empties the cache. */ - void clear(); + public void clear(); /** * Data and metadata for an entry returned by the cache. */ - class Entry { + public static class Entry { /** The data returned from cache. */ public byte[] data; @@ -74,6 +74,9 @@ public interface Cache { /** Date of this response as reported by the server. */ public long serverDate; + /** The last modified date for the requested object. */ + public long lastModified; + /** TTL for this record. */ public long ttl; diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/CacheDispatcher.java b/TMessagesProj/src/main/java/org/telegram/android/volley/CacheDispatcher.java index 3477accc1..f55e85ea2 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/CacheDispatcher.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/CacheDispatcher.java @@ -151,6 +151,7 @@ public class CacheDispatcher extends Thread { if (mQuit) { return; } + continue; } } } diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/Network.java b/TMessagesProj/src/main/java/org/telegram/android/volley/Network.java index f43165409..ac3c24b12 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/Network.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/Network.java @@ -26,5 +26,5 @@ public interface Network { * @return A {@link NetworkResponse} with data and caching metadata; will never be null * @throws VolleyError on errors */ - NetworkResponse performRequest(Request request) throws VolleyError; + public NetworkResponse performRequest(Request request) throws VolleyError; } diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/NetworkDispatcher.java b/TMessagesProj/src/main/java/org/telegram/android/volley/NetworkDispatcher.java index 088d9d345..74f67028c 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/NetworkDispatcher.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/NetworkDispatcher.java @@ -28,9 +28,9 @@ import java.util.concurrent.BlockingQueue; * Provides a thread for performing network dispatch from a queue of requests. * * Requests added to the specified queue are processed from the network via a - * specified {@link org.telegram.android.volley.Network} interface. Responses are committed to cache, if - * eligible, using a specified {@link org.telegram.android.volley.Cache} interface. Valid responses and - * errors are posted back to the caller via a {@link org.telegram.android.volley.ResponseDelivery}. + * specified {@link Network} interface. Responses are committed to cache, if + * eligible, using a specified {@link Cache} interface. Valid responses and + * errors are posted back to the caller via a {@link ResponseDelivery}. */ public class NetworkDispatcher extends Thread { /** The queue of requests to service. */ diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/NetworkResponse.java b/TMessagesProj/src/main/java/org/telegram/android/volley/NetworkResponse.java index e9beb49d6..14bcb3e53 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/NetworkResponse.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/NetworkResponse.java @@ -22,7 +22,7 @@ import java.util.Collections; import java.util.Map; /** - * Data and headers returned from {@link org.telegram.android.volley.Network#performRequest(org.telegram.android.volley.Request)}. + * Data and headers returned from {@link Network#performRequest(Request)}. */ public class NetworkResponse { /** diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/Request.java b/TMessagesProj/src/main/java/org/telegram/android/volley/Request.java index e1d57a28a..5e3f6870a 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/Request.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/Request.java @@ -18,9 +18,13 @@ package org.telegram.android.volley; import android.net.TrafficStats; import android.net.Uri; +import android.os.Handler; +import android.os.Looper; import android.os.SystemClock; import android.text.TextUtils; +import org.telegram.android.volley.VolleyLog.MarkerLog; + import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Collections; @@ -53,6 +57,9 @@ public abstract class Request implements Comparable> { int PATCH = 7; } + /** An event log tracing the lifetime of this request; for debugging. */ + private final MarkerLog mEventLog = MarkerLog.ENABLED ? new MarkerLog() : null; + /** * Request method of this request. Currently supports GET, POST, PUT, DELETE, HEAD, OPTIONS, * TRACE, and PATCH. @@ -83,12 +90,6 @@ public abstract class Request implements Comparable> { /** Whether or not a response has been delivered for this request yet. */ private boolean mResponseDelivered = false; - // A cheap variant of request tracing used to dump slow requests. - private long mRequestBirthTime = 0; - - /** Threshold at which we should log the request (even when debug logging is not enabled). */ - private static final long SLOW_REQUEST_THRESHOLD_MS = 3000; - /** The retry policy for this request. */ private RetryPolicy mRetryPolicy; @@ -108,6 +109,7 @@ public abstract class Request implements Comparable> { * is provided by subclasses, who have a better idea of how to deliver an * already-parsed response. * + * @deprecated Use {@link #Request(int, String, com.android.volley.Response.ErrorListener)}. */ @Deprecated public Request(String url, Response.ErrorListener listener) { @@ -155,6 +157,9 @@ public abstract class Request implements Comparable> { return mTag; } + /** + * @return this request's {@link com.android.volley.Response.ErrorListener}. + */ public Response.ErrorListener getErrorListener() { return mErrorListener; } @@ -196,8 +201,8 @@ public abstract class Request implements Comparable> { * Adds an event to this request's event log; for debugging. */ public void addMarker(String tag) { - if (mRequestBirthTime == 0) { - mRequestBirthTime = SystemClock.elapsedRealtime(); + if (MarkerLog.ENABLED) { + mEventLog.add(tag, Thread.currentThread().getId()); } } @@ -210,9 +215,24 @@ public abstract class Request implements Comparable> { if (mRequestQueue != null) { mRequestQueue.finish(this); } - long requestTime = SystemClock.elapsedRealtime() - mRequestBirthTime; - if (requestTime >= SLOW_REQUEST_THRESHOLD_MS) { - VolleyLog.d("%d ms: %s", requestTime, this.toString()); + if (MarkerLog.ENABLED) { + final long threadId = Thread.currentThread().getId(); + if (Looper.myLooper() != Looper.getMainLooper()) { + // If we finish marking off of the main thread, we need to + // actually do it on the main thread to ensure correct ordering. + Handler mainThread = new Handler(Looper.getMainLooper()); + mainThread.post(new Runnable() { + @Override + public void run() { + mEventLog.add(tag, threadId); + mEventLog.finish(this.toString()); + } + }); + return; + } + + mEventLog.add(tag, threadId); + mEventLog.finish(this.toString()); } } diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/RequestQueue.java b/TMessagesProj/src/main/java/org/telegram/android/volley/RequestQueue.java index 869c29965..f43cf91c6 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/RequestQueue.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/RequestQueue.java @@ -19,9 +19,11 @@ package org.telegram.android.volley; import android.os.Handler; import android.os.Looper; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; +import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Set; @@ -31,12 +33,18 @@ import java.util.concurrent.atomic.AtomicInteger; /** * A request dispatch queue with a thread pool of dispatchers. * - * Calling {@link #add(org.telegram.android.volley.Request)} will enqueue the given Request for dispatch, + * Calling {@link #add(Request)} will enqueue the given Request for dispatch, * resolving from either cache or network on a worker thread, and then delivering * a parsed response on the main thread. */ public class RequestQueue { + /** Callback interface for completed requests. */ + public static interface RequestFinishedListener { + /** Called when a request has finished processing. */ + public void onRequestFinished(Request request); + } + /** Used for generating monotonically-increasing sequence numbers for requests. */ private AtomicInteger mSequenceGenerator = new AtomicInteger(); @@ -86,6 +94,9 @@ public class RequestQueue { /** The cache dispatcher. */ private CacheDispatcher mCacheDispatcher; + private List mFinishedListeners = + new ArrayList(); + /** * Creates the worker pool. Processing will not begin until {@link #start()} is called. * @@ -149,9 +160,9 @@ public class RequestQueue { if (mCacheDispatcher != null) { mCacheDispatcher.quit(); } - for (NetworkDispatcher mDispatcher : mDispatchers) { - if (mDispatcher != null) { - mDispatcher.quit(); + for (int i = 0; i < mDispatchers.length; i++) { + if (mDispatchers[i] != null) { + mDispatchers[i].quit(); } } } @@ -175,7 +186,7 @@ public class RequestQueue { * {@link RequestQueue#cancelAll(RequestFilter)}. */ public interface RequestFilter { - boolean apply(Request request); + public boolean apply(Request request); } /** @@ -261,11 +272,16 @@ public class RequestQueue { *

Releases waiting requests for request.getCacheKey() if * request.shouldCache().

*/ - void finish(Request request) { + void finish(Request request) { // Remove from the set of requests currently being processed. synchronized (mCurrentRequests) { mCurrentRequests.remove(request); } + synchronized (mFinishedListeners) { + for (RequestFinishedListener listener : mFinishedListeners) { + listener.onRequestFinished(request); + } + } if (request.shouldCache()) { synchronized (mWaitingRequests) { @@ -283,4 +299,19 @@ public class RequestQueue { } } } + + public void addRequestFinishedListener(RequestFinishedListener listener) { + synchronized (mFinishedListeners) { + mFinishedListeners.add(listener); + } + } + + /** + * Remove a RequestFinishedListener. Has no effect if listener was not previously added. + */ + public void removeRequestFinishedListener(RequestFinishedListener listener) { + synchronized (mFinishedListeners) { + mFinishedListeners.remove(listener); + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/Response.java b/TMessagesProj/src/main/java/org/telegram/android/volley/Response.java index af7d96f69..c2dd5419e 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/Response.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/Response.java @@ -26,7 +26,7 @@ public class Response { /** Callback interface for delivering parsed responses. */ public interface Listener { /** Called when a response is received. */ - void onResponse(T response); + public void onResponse(T response); } /** Callback interface for delivering error responses. */ @@ -35,7 +35,7 @@ public class Response { * Callback method that an error has been occurred with the * provided error code and optional user-readable message. */ - void onErrorResponse(VolleyError error); + public void onErrorResponse(VolleyError error); } /** Returns a successful response containing the parsed result. */ diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/ResponseDelivery.java b/TMessagesProj/src/main/java/org/telegram/android/volley/ResponseDelivery.java index 9ad24f355..6eb35eb12 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/ResponseDelivery.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/ResponseDelivery.java @@ -20,16 +20,16 @@ public interface ResponseDelivery { /** * Parses a response from the network or cache and delivers it. */ - void postResponse(Request request, Response response); + public void postResponse(Request request, Response response); /** * Parses a response from the network or cache and delivers it. The provided * Runnable will be executed after delivery. */ - void postResponse(Request request, Response response, Runnable runnable); + public void postResponse(Request request, Response response, Runnable runnable); /** * Posts an error for the given request. */ - void postError(Request request, VolleyError error); + public void postError(Request request, VolleyError error); } diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/RetryPolicy.java b/TMessagesProj/src/main/java/org/telegram/android/volley/RetryPolicy.java index c2c1d24d5..7218777a1 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/RetryPolicy.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/RetryPolicy.java @@ -24,12 +24,12 @@ public interface RetryPolicy { /** * Returns the current timeout (used for logging). */ - int getCurrentTimeout(); + public int getCurrentTimeout(); /** * Returns the current retry count (used for logging). */ - int getCurrentRetryCount(); + public int getCurrentRetryCount(); /** * Prepares for the next retry by applying a backoff to the timeout. @@ -37,5 +37,5 @@ public interface RetryPolicy { * @throws VolleyError In the event that the retry could not be performed (for example if we * ran out of attempts), the passed in error is thrown. */ - void retry(VolleyError error) throws VolleyError; + public void retry(VolleyError error) throws VolleyError; } diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/ServerError.java b/TMessagesProj/src/main/java/org/telegram/android/volley/ServerError.java index 634e1ec96..0665a0990 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/ServerError.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/ServerError.java @@ -29,3 +29,4 @@ public class ServerError extends VolleyError { super(); } } + diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/VolleyLog.java b/TMessagesProj/src/main/java/org/telegram/android/volley/VolleyLog.java index 985333943..973ccd266 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/VolleyLog.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/VolleyLog.java @@ -23,7 +23,12 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; -/** Logging helper class. */ +/** + * Logging helper class. + *

+ * to see Volley logs call:
+ * {@code /platform-tools/adb shell setprop log.tag.Volley VERBOSE} + */ public class VolleyLog { public static String TAG = "Volley"; @@ -89,7 +94,7 @@ public class VolleyLog { callingClass = callingClass.substring(callingClass.lastIndexOf('.') + 1); callingClass = callingClass.substring(callingClass.lastIndexOf('$') + 1); - caller = callingClass + "" + trace[i].getMethodName(); + caller = callingClass + "." + trace[i].getMethodName(); break; } } diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/AndroidAuthenticator.java b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/AndroidAuthenticator.java index ccad06639..ddbb5d1f4 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/AndroidAuthenticator.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/AndroidAuthenticator.java @@ -16,6 +16,8 @@ package org.telegram.android.volley.toolbox; +import org.telegram.android.volley.AuthFailureError; + import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AccountManagerFuture; @@ -23,14 +25,12 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; -import org.telegram.android.volley.AuthFailureError; - /** * An Authenticator that uses {@link AccountManager} to get auth * tokens of a specified type for a specified account. */ public class AndroidAuthenticator implements Authenticator { - private final Context mContext; + private final AccountManager mAccountManager; private final Account mAccount; private final String mAuthTokenType; private final boolean mNotifyAuthFailure; @@ -54,7 +54,13 @@ public class AndroidAuthenticator implements Authenticator { */ public AndroidAuthenticator(Context context, Account account, String authTokenType, boolean notifyAuthFailure) { - mContext = context; + this(AccountManager.get(context), account, authTokenType, notifyAuthFailure); + } + + // Visible for testing. Allows injection of a mock AccountManager. + AndroidAuthenticator(AccountManager accountManager, Account account, + String authTokenType, boolean notifyAuthFailure) { + mAccountManager = accountManager; mAccount = account; mAuthTokenType = authTokenType; mNotifyAuthFailure = notifyAuthFailure; @@ -71,8 +77,7 @@ public class AndroidAuthenticator implements Authenticator { @SuppressWarnings("deprecation") @Override public String getAuthToken() throws AuthFailureError { - final AccountManager accountManager = AccountManager.get(mContext); - AccountManagerFuture future = accountManager.getAuthToken(mAccount, + AccountManagerFuture future = mAccountManager.getAuthToken(mAccount, mAuthTokenType, mNotifyAuthFailure, null, null); Bundle result; try { @@ -97,6 +102,6 @@ public class AndroidAuthenticator implements Authenticator { @Override public void invalidateAuthToken(String authToken) { - AccountManager.get(mContext).invalidateAuthToken(mAccount.type, authToken); + mAccountManager.invalidateAuthToken(mAccount.type, authToken); } } diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/Authenticator.java b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/Authenticator.java index f0d4845b1..e87dfc100 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/Authenticator.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/Authenticator.java @@ -27,10 +27,10 @@ public interface Authenticator { * * @throws AuthFailureError If authentication did not succeed */ - String getAuthToken() throws AuthFailureError; + public String getAuthToken() throws AuthFailureError; /** * Invalidates the provided auth token. */ - void invalidateAuthToken(String authToken); + public void invalidateAuthToken(String authToken); } diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/BasicNetwork.java b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/BasicNetwork.java index 0caf738ac..988ac30b1 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/BasicNetwork.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/BasicNetwork.java @@ -18,15 +18,9 @@ package org.telegram.android.volley.toolbox; import android.os.SystemClock; -import org.apache.http.Header; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.StatusLine; -import org.apache.http.conn.ConnectTimeoutException; -import org.apache.http.impl.cookie.DateUtils; import org.telegram.android.volley.AuthFailureError; import org.telegram.android.volley.Cache; +import org.telegram.android.volley.Cache.Entry; import org.telegram.android.volley.Network; import org.telegram.android.volley.NetworkError; import org.telegram.android.volley.NetworkResponse; @@ -38,6 +32,14 @@ import org.telegram.android.volley.TimeoutError; import org.telegram.android.volley.VolleyError; import org.telegram.android.volley.VolleyLog; +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.StatusLine; +import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.impl.cookie.DateUtils; + import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; @@ -49,14 +51,14 @@ import java.util.Map; import java.util.TreeMap; /** - * A network performing Volley requests over an {@link org.telegram.android.volley.toolbox.HttpStack}. + * A network performing Volley requests over an {@link HttpStack}. */ public class BasicNetwork implements Network { protected static final boolean DEBUG = VolleyLog.DEBUG; - private static final int SLOW_REQUEST_THRESHOLD_MS = 3000; + private static int SLOW_REQUEST_THRESHOLD_MS = 3000; - private static final int DEFAULT_POOL_SIZE = 4096; + private static int DEFAULT_POOL_SIZE = 4096; protected final HttpStack mHttpStack; @@ -99,7 +101,7 @@ public class BasicNetwork implements Network { // Handle cache validation. if (statusCode == HttpStatus.SC_NOT_MODIFIED) { - Cache.Entry entry = request.getCacheEntry(); + Entry entry = request.getCacheEntry(); if (entry == null) { return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null, responseHeaders, true, @@ -210,8 +212,8 @@ public class BasicNetwork implements Network { headers.put("If-None-Match", entry.etag); } - if (entry.serverDate > 0) { - Date refTime = new Date(entry.serverDate); + if (entry.lastModified > 0) { + Date refTime = new Date(entry.lastModified); headers.put("If-Modified-Since", DateUtils.formatDate(refTime)); } } @@ -256,8 +258,8 @@ public class BasicNetwork implements Network { */ protected static Map convertHeaders(Header[] headers) { Map result = new TreeMap(String.CASE_INSENSITIVE_ORDER); - for (Header header : headers) { - result.put(header.getName(), header.getValue()); + for (int i = 0; i < headers.length; i++) { + result.put(headers[i].getName(), headers[i].getValue()); } return result; } diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/ClearCacheRequest.java b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/ClearCacheRequest.java index 67bc57f05..136997d20 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/ClearCacheRequest.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/ClearCacheRequest.java @@ -16,14 +16,14 @@ package org.telegram.android.volley.toolbox; -import android.os.Handler; -import android.os.Looper; - import org.telegram.android.volley.Cache; import org.telegram.android.volley.NetworkResponse; import org.telegram.android.volley.Request; import org.telegram.android.volley.Response; +import android.os.Handler; +import android.os.Looper; + /** * A synthetic request used for clearing the cache. */ diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/DiskBasedCache.java b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/DiskBasedCache.java index 64fd0ea4c..b26ca10bd 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/DiskBasedCache.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/DiskBasedCache.java @@ -22,6 +22,7 @@ import org.telegram.android.volley.Cache; import org.telegram.android.volley.VolleyLog; import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; import java.io.EOFException; import java.io.File; import java.io.FileInputStream; @@ -42,46 +43,31 @@ import java.util.Map; */ public class DiskBasedCache implements Cache { - /** - * Map of the Key, CacheHeader pairs - */ + /** Map of the Key, CacheHeader pairs */ private final Map mEntries = new LinkedHashMap(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; - /** - * The root directory to use for the cache. - */ + /** The root directory to use for the cache. */ private final File mRootDirectory; - /** - * The maximum size of the cache in bytes. - */ + /** The maximum size of the cache in bytes. */ 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; - /** - * High water mark percentage for the cache - */ + /** High water mark percentage for the cache */ private static final float HYSTERESIS_FACTOR = 0.9f; - /** - * Magic number for current version of cache file format. - */ - private static final int CACHE_MAGIC = 0x20140623; + /** Magic number for current version of cache file format. */ + private static final int CACHE_MAGIC = 0x20150306; /** * 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. */ public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes) { @@ -92,7 +78,6 @@ public class DiskBasedCache implements Cache { /** * Constructs an instance of the DiskBasedCache at the specified directory using * the default maximum cache size of 5MB. - * * @param rootDirectory The root directory of the cache. */ public DiskBasedCache(File rootDirectory) { @@ -129,7 +114,7 @@ public class DiskBasedCache implements Cache { File file = getFileForKey(key); CountingInputStream cis = null; try { - cis = new CountingInputStream(new FileInputStream(file)); + cis = new CountingInputStream(new BufferedInputStream(new FileInputStream(file))); CacheHeader.readHeader(cis); // eat header byte[] data = streamToBytes(cis, (int) (file.length() - cis.bytesRead)); return entry.toCacheEntry(data); @@ -174,23 +159,21 @@ public class DiskBasedCache implements Cache { putEntry(entry.key, entry); } catch (IOException e) { if (file != null) { - file.delete(); + file.delete(); } } finally { try { if (fis != null) { fis.close(); } - } catch (IOException ignored) { - } + } catch (IOException ignored) { } } } } /** * 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 */ @Override @@ -214,7 +197,7 @@ public class DiskBasedCache implements Cache { pruneIfNeeded(entry.data.length); File file = getFileForKey(key); try { - FileOutputStream fos = new FileOutputStream(file); + BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(file)); CacheHeader e = new CacheHeader(key, entry); boolean success = e.writeHeader(fos); if (!success) { @@ -227,7 +210,6 @@ public class DiskBasedCache implements Cache { putEntry(key, e); return; } catch (IOException e) { - /**/ } boolean deleted = file.delete(); if (!deleted) { @@ -250,7 +232,6 @@ public class DiskBasedCache implements Cache { /** * Creates a pseudo-unique filename for the specified cache key. - * * @param key The key to generate a file name for. * @return A pseudo-unique filename. */ @@ -270,7 +251,6 @@ public class DiskBasedCache implements Cache { /** * Prunes the cache to fit the amount of bytes specified. - * * @param neededSpace The amount of bytes we are trying to fit into the cache. */ private void pruneIfNeeded(int neededSpace) { @@ -293,8 +273,8 @@ public class DiskBasedCache implements Cache { if (deleted) { mTotalSize -= e.size; } else { - VolleyLog.d("Could not delete cache entry for key=%s, filename=%s", - e.key, getFilenameForKey(e.key)); + VolleyLog.d("Could not delete cache entry for key=%s, filename=%s", + e.key, getFilenameForKey(e.key)); } iterator.remove(); prunedFiles++; @@ -312,8 +292,7 @@ public class DiskBasedCache implements 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. */ private void putEntry(String key, CacheHeader entry) { @@ -339,7 +318,7 @@ public class DiskBasedCache implements Cache { /** * Reads the contents of an InputStream into a byte[]. - */ + * */ private static byte[] streamToBytes(InputStream in, int length) throws IOException { byte[] bytes = new byte[length]; int count; @@ -358,49 +337,36 @@ public class DiskBasedCache implements Cache { */ // Visible for testing. static class CacheHeader { - /** - * The size of the data identified by this CacheHeader. (This is not - * serialized to disk. - */ + /** The size of the data identified by this CacheHeader. (This is not + * serialized to disk. */ public long size; - /** - * The key that identifies the cache entry. - */ + /** The key that identifies the cache entry. */ public String key; - /** - * ETag for cache coherence. - */ + /** ETag for cache coherence. */ public String etag; - /** - * Date of this response as reported by the server. - */ + /** Date of this response as reported by the server. */ public long serverDate; - /** - * TTL for this record. - */ + /** The last modified date for the requested object. */ + public long lastModified; + + /** TTL for this record. */ public long ttl; - /** - * Soft TTL for this record. - */ + /** Soft TTL for this record. */ public long softTtl; - /** - * Headers from the response resulting in this cache entry. - */ + /** Headers from the response resulting in this cache entry. */ public Map responseHeaders; - private CacheHeader() { - } + private CacheHeader() { } /** * 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. */ public CacheHeader(String key, Entry entry) { @@ -408,6 +374,7 @@ public class DiskBasedCache implements Cache { this.size = entry.data.length; this.etag = entry.etag; this.serverDate = entry.serverDate; + this.lastModified = entry.lastModified; this.ttl = entry.ttl; this.softTtl = entry.softTtl; this.responseHeaders = entry.responseHeaders; @@ -415,7 +382,6 @@ public class DiskBasedCache implements Cache { /** * Reads the header off of an InputStream and returns a CacheHeader object. - * * @param is The InputStream to read from. * @throws IOException */ @@ -432,9 +398,11 @@ public class DiskBasedCache implements Cache { entry.etag = null; } entry.serverDate = readLong(is); + entry.lastModified = readLong(is); entry.ttl = readLong(is); entry.softTtl = readLong(is); entry.responseHeaders = readStringStringMap(is); + return entry; } @@ -446,6 +414,7 @@ public class DiskBasedCache implements Cache { e.data = data; e.etag = etag; e.serverDate = serverDate; + e.lastModified = lastModified; e.ttl = ttl; e.softTtl = softTtl; e.responseHeaders = responseHeaders; @@ -462,6 +431,7 @@ public class DiskBasedCache implements Cache { writeString(os, key); writeString(os, etag == null ? "" : etag); writeLong(os, serverDate); + writeLong(os, lastModified); writeLong(os, ttl); writeLong(os, softTtl); writeStringStringMap(responseHeaders, os); @@ -537,14 +507,14 @@ public class DiskBasedCache implements Cache { } static void writeLong(OutputStream os, long n) throws IOException { - os.write((byte) (n >>> 0)); - os.write((byte) (n >>> 8)); - os.write((byte) (n >>> 16)); - os.write((byte) (n >>> 24)); - os.write((byte) (n >>> 32)); - os.write((byte) (n >>> 40)); - os.write((byte) (n >>> 48)); - os.write((byte) (n >>> 56)); + os.write((byte)(n >>> 0)); + os.write((byte)(n >>> 8)); + os.write((byte)(n >>> 16)); + os.write((byte)(n >>> 24)); + os.write((byte)(n >>> 32)); + os.write((byte)(n >>> 40)); + os.write((byte)(n >>> 48)); + os.write((byte)(n >>> 56)); } static long readLong(InputStream is) throws IOException { @@ -596,4 +566,6 @@ public class DiskBasedCache implements Cache { } return result; } + + } diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/HttpClientStack.java b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/HttpClientStack.java index 3f42e8561..bbb327e8a 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/HttpClientStack.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/HttpClientStack.java @@ -16,6 +16,10 @@ package org.telegram.android.volley.toolbox; +import org.telegram.android.volley.AuthFailureError; +import org.telegram.android.volley.Request; +import org.telegram.android.volley.Request.Method; + import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; @@ -33,8 +37,6 @@ import org.apache.http.entity.ByteArrayEntity; import org.apache.http.message.BasicNameValuePair; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; -import org.telegram.android.volley.AuthFailureError; -import org.telegram.android.volley.Request; import java.io.IOException; import java.net.URI; @@ -92,7 +94,7 @@ public class HttpClientStack implements HttpStack { /* protected */ static HttpUriRequest createHttpRequest(Request request, Map additionalHeaders) throws AuthFailureError { switch (request.getMethod()) { - case Request.Method.DEPRECATED_GET_OR_POST: { + case Method.DEPRECATED_GET_OR_POST: { // This is the deprecated way that needs to be handled for backwards compatibility. // If the request's post body is null, then the assumption is that the request is // GET. Otherwise, it is assumed that the request is a POST. @@ -108,29 +110,29 @@ public class HttpClientStack implements HttpStack { return new HttpGet(request.getUrl()); } } - case Request.Method.GET: + case Method.GET: return new HttpGet(request.getUrl()); - case Request.Method.DELETE: + case Method.DELETE: return new HttpDelete(request.getUrl()); - case Request.Method.POST: { + case Method.POST: { HttpPost postRequest = new HttpPost(request.getUrl()); postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType()); setEntityIfNonEmptyBody(postRequest, request); return postRequest; } - case Request.Method.PUT: { + case Method.PUT: { HttpPut putRequest = new HttpPut(request.getUrl()); putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType()); setEntityIfNonEmptyBody(putRequest, request); return putRequest; } - case Request.Method.HEAD: + case Method.HEAD: return new HttpHead(request.getUrl()); - case Request.Method.OPTIONS: + case Method.OPTIONS: return new HttpOptions(request.getUrl()); - case Request.Method.TRACE: + case Method.TRACE: return new HttpTrace(request.getUrl()); - case Request.Method.PATCH: { + case Method.PATCH: { HttpPatch patchRequest = new HttpPatch(request.getUrl()); patchRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType()); setEntityIfNonEmptyBody(patchRequest, request); diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/HttpHeaderParser.java b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/HttpHeaderParser.java index ea2451dfe..4af14acbe 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/HttpHeaderParser.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/HttpHeaderParser.java @@ -16,11 +16,12 @@ package org.telegram.android.volley.toolbox; +import org.telegram.android.volley.Cache; +import org.telegram.android.volley.NetworkResponse; + import org.apache.http.impl.cookie.DateParseException; import org.apache.http.impl.cookie.DateUtils; import org.apache.http.protocol.HTTP; -import org.telegram.android.volley.Cache; -import org.telegram.android.volley.NetworkResponse; import java.util.Map; @@ -41,10 +42,14 @@ public class HttpHeaderParser { Map headers = response.headers; long serverDate = 0; + long lastModified = 0; long serverExpires = 0; long softExpire = 0; + long finalExpire = 0; long maxAge = 0; + long staleWhileRevalidate = 0; boolean hasCacheControl = false; + boolean mustRevalidate = false; String serverEtag = null; String headerValue; @@ -58,18 +63,22 @@ public class HttpHeaderParser { if (headerValue != null) { hasCacheControl = true; String[] tokens = headerValue.split(","); - for (String token1 : tokens) { - String token = token1.trim(); + for (int i = 0; i < tokens.length; i++) { + String token = tokens[i].trim(); if (token.equals("no-cache") || token.equals("no-store")) { return null; } else if (token.startsWith("max-age=")) { try { maxAge = Long.parseLong(token.substring(8)); } catch (Exception e) { - /**/ + } + } else if (token.startsWith("stale-while-revalidate=")) { + try { + staleWhileRevalidate = Long.parseLong(token.substring(23)); + } catch (Exception e) { } } else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) { - maxAge = 0; + mustRevalidate = true; } } } @@ -79,23 +88,33 @@ public class HttpHeaderParser { serverExpires = parseDateAsEpoch(headerValue); } + headerValue = headers.get("Last-Modified"); + if (headerValue != null) { + lastModified = parseDateAsEpoch(headerValue); + } + serverEtag = headers.get("ETag"); // Cache-Control takes precedence over an Expires header, even if both exist and Expires // is more restrictive. if (hasCacheControl) { softExpire = now + maxAge * 1000; + finalExpire = mustRevalidate + ? softExpire + : softExpire + staleWhileRevalidate * 1000; } else if (serverDate > 0 && serverExpires >= serverDate) { // Default semantic for Expire header in HTTP specification is softExpire. softExpire = now + (serverExpires - serverDate); + finalExpire = softExpire; } Cache.Entry entry = new Cache.Entry(); entry.data = response.data; entry.etag = serverEtag; entry.softTtl = softExpire; - entry.ttl = entry.softTtl; + entry.ttl = finalExpire; entry.serverDate = serverDate; + entry.lastModified = lastModified; entry.responseHeaders = headers; return entry; @@ -115,10 +134,14 @@ public class HttpHeaderParser { } /** - * Returns the charset specified in the Content-Type of this header, - * or the HTTP default (ISO-8859-1) if none can be found. + * Retrieve a charset from headers + * + * @param headers An {@link java.util.Map} of headers + * @param defaultCharset Charset to return if none can be found + * @return Returns the charset specified in the Content-Type of this header, + * or the defaultCharset if none can be found. */ - public static String parseCharset(Map headers) { + public static String parseCharset(Map headers, String defaultCharset) { String contentType = headers.get(HTTP.CONTENT_TYPE); if (contentType != null) { String[] params = contentType.split(";"); @@ -132,6 +155,14 @@ public class HttpHeaderParser { } } - return HTTP.DEFAULT_CONTENT_CHARSET; + return defaultCharset; + } + + /** + * Returns the charset specified in the Content-Type of this header, + * or the HTTP default (ISO-8859-1) if none can be found. + */ + public static String parseCharset(Map headers) { + return parseCharset(headers, HTTP.DEFAULT_CONTENT_CHARSET); } } diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/HttpStack.java b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/HttpStack.java index 97816eda7..d3f641c19 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/HttpStack.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/HttpStack.java @@ -16,10 +16,11 @@ package org.telegram.android.volley.toolbox; -import org.apache.http.HttpResponse; import org.telegram.android.volley.AuthFailureError; import org.telegram.android.volley.Request; +import org.apache.http.HttpResponse; + import java.io.IOException; import java.util.Map; @@ -38,7 +39,7 @@ public interface HttpStack { * {@link Request#getHeaders()} * @return the HTTP response */ - HttpResponse performRequest(Request request, Map additionalHeaders) + public HttpResponse performRequest(Request request, Map additionalHeaders) throws IOException, AuthFailureError; } diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/HurlStack.java b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/HurlStack.java index e374fbaf4..06f2f7a24 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/HurlStack.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/HurlStack.java @@ -16,17 +16,20 @@ package org.telegram.android.volley.toolbox; +import org.telegram.android.volley.AuthFailureError; +import org.telegram.android.volley.Request; +import org.telegram.android.volley.Request.Method; + import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; import org.apache.http.ProtocolVersion; import org.apache.http.StatusLine; import org.apache.http.entity.BasicHttpEntity; import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicHttpResponse; import org.apache.http.message.BasicStatusLine; -import org.telegram.android.volley.AuthFailureError; -import org.telegram.android.volley.Request; import java.io.DataOutputStream; import java.io.IOException; @@ -42,7 +45,7 @@ import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSocketFactory; /** - * An {@link org.telegram.android.volley.toolbox.HttpStack} based on {@link HttpURLConnection}. + * An {@link HttpStack} based on {@link HttpURLConnection}. */ public class HurlStack implements HttpStack { @@ -56,7 +59,7 @@ public class HurlStack implements HttpStack { * Returns a URL to use instead of the provided one, or null to indicate * this URL should not be used at all. */ - String rewriteUrl(String originalUrl); + public String rewriteUrl(String originalUrl); } private final UrlRewriter mUrlRewriter; @@ -113,7 +116,9 @@ public class HurlStack implements HttpStack { StatusLine responseStatus = new BasicStatusLine(protocolVersion, connection.getResponseCode(), connection.getResponseMessage()); BasicHttpResponse response = new BasicHttpResponse(responseStatus); - response.setEntity(entityFromConnection(connection)); + if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) { + response.setEntity(entityFromConnection(connection)); + } for (Entry> header : connection.getHeaderFields().entrySet()) { if (header.getKey() != null) { Header h = new BasicHeader(header.getKey(), header.getValue().get(0)); @@ -123,6 +128,20 @@ public class HurlStack implements HttpStack { return response; } + /** + * Checks if a response message contains a body. + * @see RFC 7230 section 3.3 + * @param requestMethod request method + * @param responseCode response status code + * @return whether the response has a body + */ + private static boolean hasResponseBody(int requestMethod, int responseCode) { + return requestMethod != Request.Method.HEAD + && !(HttpStatus.SC_CONTINUE <= responseCode && responseCode < HttpStatus.SC_OK) + && responseCode != HttpStatus.SC_NO_CONTENT + && responseCode != HttpStatus.SC_NOT_MODIFIED; + } + /** * Initializes an {@link HttpEntity} from the given {@link HttpURLConnection}. * @param connection @@ -177,7 +196,7 @@ public class HurlStack implements HttpStack { /* package */ static void setConnectionParametersForRequest(HttpURLConnection connection, Request request) throws IOException, AuthFailureError { switch (request.getMethod()) { - case Request.Method.DEPRECATED_GET_OR_POST: + case Method.DEPRECATED_GET_OR_POST: // This is the deprecated way that needs to be handled for backwards compatibility. // If the request's post body is null, then the assumption is that the request is // GET. Otherwise, it is assumed that the request is a POST. @@ -195,32 +214,32 @@ public class HurlStack implements HttpStack { out.close(); } break; - case Request.Method.GET: + case Method.GET: // Not necessary to set the request method because connection defaults to GET but // being explicit here. connection.setRequestMethod("GET"); break; - case Request.Method.DELETE: + case Method.DELETE: connection.setRequestMethod("DELETE"); break; - case Request.Method.POST: + case Method.POST: connection.setRequestMethod("POST"); addBodyIfExists(connection, request); break; - case Request.Method.PUT: + case Method.PUT: connection.setRequestMethod("PUT"); addBodyIfExists(connection, request); break; - case Request.Method.HEAD: + case Method.HEAD: connection.setRequestMethod("HEAD"); break; - case Request.Method.OPTIONS: + case Method.OPTIONS: connection.setRequestMethod("OPTIONS"); break; - case Request.Method.TRACE: + case Method.TRACE: connection.setRequestMethod("TRACE"); break; - case Request.Method.PATCH: + case Method.PATCH: connection.setRequestMethod("PATCH"); addBodyIfExists(connection, request); break; diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/ImageLoader.java b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/ImageLoader.java index f22b65d80..031942d1d 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/ImageLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/ImageLoader.java @@ -20,10 +20,11 @@ import android.graphics.Bitmap.Config; import android.os.Handler; import android.os.Looper; import android.widget.ImageView; - +import android.widget.ImageView.ScaleType; import org.telegram.android.volley.Request; import org.telegram.android.volley.RequestQueue; -import org.telegram.android.volley.Response; +import org.telegram.android.volley.Response.ErrorListener; +import org.telegram.android.volley.Response.Listener; import org.telegram.android.volley.VolleyError; import java.util.HashMap; @@ -71,8 +72,8 @@ public class ImageLoader { * must not block. Implementation with an LruCache is recommended. */ public interface ImageCache { - Bitmap getBitmap(String url); - void putBitmap(String url, Bitmap bitmap); + public Bitmap getBitmap(String url); + public void putBitmap(String url, Bitmap bitmap); } /** @@ -127,7 +128,7 @@ public class ImageLoader { * or * - onErrorResponse will be called if there was an error loading the image. */ - public interface ImageListener extends Response.ErrorListener { + public interface ImageListener extends ErrorListener { /** * Listens for non-error changes to the loading of the image request. * @@ -138,7 +139,7 @@ public class ImageLoader { * image loading in order to, for example, run an animation to fade in network loaded * images. */ - void onResponse(ImageContainer response, boolean isImmediate); + public void onResponse(ImageContainer response, boolean isImmediate); } /** @@ -149,9 +150,22 @@ public class ImageLoader { * @return True if the item exists in cache, false otherwise. */ public boolean isCached(String requestUrl, int maxWidth, int maxHeight) { + return isCached(requestUrl, maxWidth, maxHeight, ScaleType.CENTER_INSIDE); + } + + /** + * Checks if the item is available in the cache. + * + * @param requestUrl The url of the remote image + * @param maxWidth The maximum width of the returned image. + * @param maxHeight The maximum height of the returned image. + * @param scaleType The scaleType of the imageView. + * @return True if the item exists in cache, false otherwise. + */ + public boolean isCached(String requestUrl, int maxWidth, int maxHeight, ScaleType scaleType) { throwIfNotOnMainThread(); - String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight); + String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType); return mCache.getBitmap(cacheKey) != null; } @@ -168,6 +182,15 @@ public class ImageLoader { return get(requestUrl, listener, 0, 0); } + /** + * Equivalent to calling {@link #get(String, ImageListener, int, int, ScaleType)} with + * {@code Scaletype == ScaleType.CENTER_INSIDE}. + */ + public ImageContainer get(String requestUrl, ImageListener imageListener, + int maxWidth, int maxHeight) { + return get(requestUrl, imageListener, maxWidth, maxHeight, ScaleType.CENTER_INSIDE); + } + /** * Issues a bitmap request with the given URL if that image is not available * in the cache, and returns a bitmap container that contains all of the data @@ -177,15 +200,17 @@ public class ImageLoader { * @param imageListener The listener to call when the remote image is loaded * @param maxWidth The maximum width of the returned image. * @param maxHeight The maximum height of the returned image. + * @param scaleType The ImageViews ScaleType used to calculate the needed image size. * @return A container object that contains all of the properties of the request, as well as * the currently available image (default if remote is not loaded). */ public ImageContainer get(String requestUrl, ImageListener imageListener, - int maxWidth, int maxHeight) { + int maxWidth, int maxHeight, ScaleType scaleType) { + // only fulfill requests that were initiated from the main thread. throwIfNotOnMainThread(); - final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight); + final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType); // Try to look up the request in the cache of remote images. Bitmap cachedBitmap = mCache.getBitmap(cacheKey); @@ -213,7 +238,8 @@ public class ImageLoader { // The request is not already in flight. Send the new request to the network and // track it. - Request newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, cacheKey); + Request newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, scaleType, + cacheKey); mRequestQueue.add(newRequest); mInFlightRequests.put(cacheKey, @@ -221,14 +247,14 @@ public class ImageLoader { return imageContainer; } - protected Request makeImageRequest(String requestUrl, int maxWidth, int maxHeight, final String cacheKey) { - return new ImageRequest(requestUrl, new Response.Listener() { + protected Request makeImageRequest(String requestUrl, int maxWidth, int maxHeight, + ScaleType scaleType, final String cacheKey) { + return new ImageRequest(requestUrl, new Listener() { @Override public void onResponse(Bitmap response) { onGetImageSuccess(cacheKey, response); } - }, maxWidth, maxHeight, - Config.RGB_565, new Response.ErrorListener() { + }, maxWidth, maxHeight, scaleType, Config.RGB_565, new ErrorListener() { @Override public void onErrorResponse(VolleyError error) { onGetImageError(cacheKey, error); @@ -471,8 +497,11 @@ public class ImageLoader { * @param url The URL of the request. * @param maxWidth The max-width of the output. * @param maxHeight The max-height of the output. + * @param scaleType The scaleType of the imageView. */ - private static String getCacheKey(String url, int maxWidth, int maxHeight) { - return "#W" + maxWidth + "#H" + maxHeight + url; + private static String getCacheKey(String url, int maxWidth, int maxHeight, ScaleType scaleType) { + return new StringBuilder(url.length() + 12).append("#W").append(maxWidth) + .append("#H").append(maxHeight).append("#S").append(scaleType.ordinal()).append(url) + .toString(); } } diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/ImageRequest.java b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/ImageRequest.java index 1882f011a..f22b6f90b 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/ImageRequest.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/ImageRequest.java @@ -16,10 +16,6 @@ package org.telegram.android.volley.toolbox; -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.graphics.BitmapFactory; - import org.telegram.android.volley.DefaultRetryPolicy; import org.telegram.android.volley.NetworkResponse; import org.telegram.android.volley.ParseError; @@ -27,6 +23,11 @@ import org.telegram.android.volley.Request; import org.telegram.android.volley.Response; import org.telegram.android.volley.VolleyLog; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.BitmapFactory; +import android.widget.ImageView.ScaleType; + /** * A canned request for getting an image at a given URL and calling * back with a decoded Bitmap. @@ -45,6 +46,7 @@ public class ImageRequest extends Request { private final Config mDecodeConfig; private final int mMaxWidth; private final int mMaxHeight; + private ScaleType mScaleType; /** Decoding lock so that we don't decode more than one image at a time (to avoid OOM's) */ private static final Object sDecodeLock = new Object(); @@ -63,20 +65,32 @@ public class ImageRequest extends Request { * @param maxWidth Maximum width to decode this bitmap to, or zero for none * @param maxHeight Maximum height to decode this bitmap to, or zero for * none + * @param scaleType The ImageViews ScaleType used to calculate the needed image size. * @param decodeConfig Format to decode the bitmap to * @param errorListener Error listener, or null to ignore errors */ public ImageRequest(String url, Response.Listener listener, int maxWidth, int maxHeight, - Config decodeConfig, Response.ErrorListener errorListener) { - super(Method.GET, url, errorListener); + ScaleType scaleType, Config decodeConfig, Response.ErrorListener errorListener) { + super(Method.GET, url, errorListener); setRetryPolicy( new DefaultRetryPolicy(IMAGE_TIMEOUT_MS, IMAGE_MAX_RETRIES, IMAGE_BACKOFF_MULT)); mListener = listener; mDecodeConfig = decodeConfig; mMaxWidth = maxWidth; mMaxHeight = maxHeight; + mScaleType = scaleType; } + /** + * For API compatibility with the pre-ScaleType variant of the constructor. Equivalent to + * the normal constructor with {@code ScaleType.CENTER_INSIDE}. + */ + @Deprecated + public ImageRequest(String url, Response.Listener listener, int maxWidth, int maxHeight, + Config decodeConfig, Response.ErrorListener errorListener) { + this(url, listener, maxWidth, maxHeight, + ScaleType.CENTER_INSIDE, decodeConfig, errorListener); + } @Override public Priority getPriority() { return Priority.LOW; @@ -92,14 +106,24 @@ public class ImageRequest extends Request { * maintain aspect ratio with primary dimension * @param actualPrimary Actual size of the primary dimension * @param actualSecondary Actual size of the secondary dimension + * @param scaleType The ScaleType used to calculate the needed image size. */ private static int getResizedDimension(int maxPrimary, int maxSecondary, int actualPrimary, - int actualSecondary) { + int actualSecondary, ScaleType scaleType) { + // If no dominant value at all, just return the actual. - if (maxPrimary == 0 && maxSecondary == 0) { + if ((maxPrimary == 0) && (maxSecondary == 0)) { return actualPrimary; } + // If ScaleType.FIT_XY fill the whole rectangle, ignore ratio. + if (scaleType == ScaleType.FIT_XY) { + if (maxPrimary == 0) { + return actualPrimary; + } + return maxPrimary; + } + // If primary is unspecified, scale primary to match secondary's scaling ratio. if (maxPrimary == 0) { double ratio = (double) maxSecondary / (double) actualSecondary; @@ -112,7 +136,16 @@ public class ImageRequest extends Request { double ratio = (double) actualSecondary / (double) actualPrimary; int resized = maxPrimary; - if (resized * ratio > maxSecondary) { + + // If ScaleType.CENTER_CROP fill the whole rectangle, preserve aspect ratio. + if (scaleType == ScaleType.CENTER_CROP) { + if ((resized * ratio) < maxSecondary) { + resized = (int) (maxSecondary / ratio); + } + return resized; + } + + if ((resized * ratio) > maxSecondary) { resized = (int) (maxSecondary / ratio); } return resized; @@ -150,9 +183,9 @@ public class ImageRequest extends Request { // Then compute the dimensions we would ideally like to decode to. int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight, - actualWidth, actualHeight); + actualWidth, actualHeight, mScaleType); int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth, - actualHeight, actualWidth); + actualHeight, actualWidth, mScaleType); // Decode to the nearest power of two scaling factor. decodeOptions.inJustDecodeBounds = false; diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/JsonArrayRequest.java b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/JsonArrayRequest.java index 4ef8cd6b8..5f14e7a72 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/JsonArrayRequest.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/JsonArrayRequest.java @@ -16,11 +16,14 @@ package org.telegram.android.volley.toolbox; -import org.json.JSONArray; -import org.json.JSONException; import org.telegram.android.volley.NetworkResponse; import org.telegram.android.volley.ParseError; import org.telegram.android.volley.Response; +import org.telegram.android.volley.Response.ErrorListener; +import org.telegram.android.volley.Response.Listener; + +import org.json.JSONArray; +import org.json.JSONException; import java.io.UnsupportedEncodingException; @@ -35,15 +38,30 @@ public class JsonArrayRequest extends JsonRequest { * @param listener Listener to receive the JSON response * @param errorListener Error listener, or null to ignore errors. */ - public JsonArrayRequest(String url, Response.Listener listener, Response.ErrorListener errorListener) { + public JsonArrayRequest(String url, Listener listener, ErrorListener errorListener) { super(Method.GET, url, null, listener, errorListener); } + /** + * Creates a new request. + * @param method the HTTP method to use + * @param url URL to fetch the JSON from + * @param jsonRequest A {@link JSONArray} to post with the request. Null is allowed and + * indicates no parameters will be posted along with request. + * @param listener Listener to receive the JSON response + * @param errorListener Error listener, or null to ignore errors. + */ + public JsonArrayRequest(int method, String url, JSONArray jsonRequest, + Listener listener, ErrorListener errorListener) { + super(method, url, (jsonRequest == null) ? null : jsonRequest.toString(), listener, + errorListener); + } + @Override protected Response parseNetworkResponse(NetworkResponse response) { try { - String jsonString = - new String(response.data, HttpHeaderParser.parseCharset(response.headers)); + String jsonString = new String(response.data, + HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET)); return Response.success(new JSONArray(jsonString), HttpHeaderParser.parseCacheHeaders(response)); } catch (UnsupportedEncodingException e) { diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/JsonObjectRequest.java b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/JsonObjectRequest.java index a21e05318..c13865a6a 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/JsonObjectRequest.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/JsonObjectRequest.java @@ -16,11 +16,14 @@ package org.telegram.android.volley.toolbox; -import org.json.JSONException; -import org.json.JSONObject; import org.telegram.android.volley.NetworkResponse; import org.telegram.android.volley.ParseError; import org.telegram.android.volley.Response; +import org.telegram.android.volley.Response.ErrorListener; +import org.telegram.android.volley.Response.Listener; + +import org.json.JSONException; +import org.json.JSONObject; import java.io.UnsupportedEncodingException; @@ -40,7 +43,7 @@ public class JsonObjectRequest extends JsonRequest { * @param errorListener Error listener, or null to ignore errors. */ public JsonObjectRequest(int method, String url, JSONObject jsonRequest, - Response.Listener listener, Response.ErrorListener errorListener) { + Listener listener, ErrorListener errorListener) { super(method, url, (jsonRequest == null) ? null : jsonRequest.toString(), listener, errorListener); } @@ -49,9 +52,10 @@ public class JsonObjectRequest extends JsonRequest { * Constructor which defaults to GET if jsonRequest is * null, POST otherwise. * + * @see #JsonObjectRequest(int, String, JSONObject, Listener, ErrorListener) */ - public JsonObjectRequest(String url, JSONObject jsonRequest, Response.Listener listener, - Response.ErrorListener errorListener) { + public JsonObjectRequest(String url, JSONObject jsonRequest, Listener listener, + ErrorListener errorListener) { this(jsonRequest == null ? Method.GET : Method.POST, url, jsonRequest, listener, errorListener); } @@ -59,8 +63,8 @@ public class JsonObjectRequest extends JsonRequest { @Override protected Response parseNetworkResponse(NetworkResponse response) { try { - String jsonString = - new String(response.data, HttpHeaderParser.parseCharset(response.headers)); + String jsonString = new String(response.data, + HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET)); return Response.success(new JSONObject(jsonString), HttpHeaderParser.parseCacheHeaders(response)); } catch (UnsupportedEncodingException e) { diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/JsonRequest.java b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/JsonRequest.java index 5c2fca478..0be79ac9d 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/JsonRequest.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/JsonRequest.java @@ -19,6 +19,8 @@ package org.telegram.android.volley.toolbox; import org.telegram.android.volley.NetworkResponse; import org.telegram.android.volley.Request; import org.telegram.android.volley.Response; +import org.telegram.android.volley.Response.ErrorListener; +import org.telegram.android.volley.Response.Listener; import org.telegram.android.volley.VolleyLog; import java.io.UnsupportedEncodingException; @@ -30,28 +32,29 @@ import java.io.UnsupportedEncodingException; * @param JSON type of response expected */ public abstract class JsonRequest extends Request { - /** Charset for request. */ - private static final String PROTOCOL_CHARSET = "utf-8"; + /** Default charset for JSON request. */ + protected static final String PROTOCOL_CHARSET = "utf-8"; /** Content type for request. */ private static final String PROTOCOL_CONTENT_TYPE = String.format("application/json; charset=%s", PROTOCOL_CHARSET); - private final Response.Listener mListener; + private final Listener mListener; private final String mRequestBody; /** * Deprecated constructor for a JsonRequest which defaults to GET unless {@link #getPostBody()} * or {@link #getPostParams()} is overridden (which defaults to POST). * + * @deprecated Use {@link #JsonRequest(int, String, String, Listener, ErrorListener)}. */ - public JsonRequest(String url, String requestBody, Response.Listener listener, - Response.ErrorListener errorListener) { + public JsonRequest(String url, String requestBody, Listener listener, + ErrorListener errorListener) { this(Method.DEPRECATED_GET_OR_POST, url, requestBody, listener, errorListener); } - public JsonRequest(int method, String url, String requestBody, Response.Listener listener, - Response.ErrorListener errorListener) { + public JsonRequest(int method, String url, String requestBody, Listener listener, + ErrorListener errorListener) { super(method, url, errorListener); mListener = listener; mRequestBody = requestBody; diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/NetworkImageView.java b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/NetworkImageView.java index 413cb8a01..03bac11fb 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/NetworkImageView.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/NetworkImageView.java @@ -103,6 +103,7 @@ public class NetworkImageView extends ImageView { void loadImageIfNecessary(final boolean isInLayoutPass) { int width = getWidth(); int height = getHeight(); + ScaleType scaleType = getScaleType(); boolean wrapWidth = false, wrapHeight = false; if (getLayoutParams() != null) { @@ -177,7 +178,7 @@ public class NetworkImageView extends ImageView { setImageResource(mDefaultImageId); } } - }, maxWidth, maxHeight); + }, maxWidth, maxHeight, scaleType); // update the ImageContainer to be the new bitmap container. mImageContainer = newContainer; diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/PoolingByteArrayOutputStream.java b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/PoolingByteArrayOutputStream.java index 060a9d86f..d26edb7c7 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/PoolingByteArrayOutputStream.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/PoolingByteArrayOutputStream.java @@ -25,7 +25,7 @@ import java.io.IOException; */ public class PoolingByteArrayOutputStream extends ByteArrayOutputStream { /** - * If the {@link #PoolingByteArrayOutputStream(org.telegram.android.volley.toolbox.ByteArrayPool)} constructor is called, this is + * If the {@link #PoolingByteArrayOutputStream(ByteArrayPool)} constructor is called, this is * the default size to which the underlying byte array is initialized. */ private static final int DEFAULT_SIZE = 256; diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/RequestFuture.java b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/RequestFuture.java index 132d9578d..f819124d3 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/RequestFuture.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/RequestFuture.java @@ -126,7 +126,10 @@ public class RequestFuture implements Future, Response.Listener, @Override public boolean isCancelled() { - return mRequest != null && mRequest.isCanceled(); + if (mRequest == null) { + return false; + } + return mRequest.isCanceled(); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/StringRequest.java b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/StringRequest.java index 2bfed1a38..5c04dec62 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/StringRequest.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/StringRequest.java @@ -19,6 +19,8 @@ package org.telegram.android.volley.toolbox; import org.telegram.android.volley.NetworkResponse; import org.telegram.android.volley.Request; import org.telegram.android.volley.Response; +import org.telegram.android.volley.Response.ErrorListener; +import org.telegram.android.volley.Response.Listener; import java.io.UnsupportedEncodingException; @@ -26,7 +28,7 @@ import java.io.UnsupportedEncodingException; * A canned request for retrieving the response body at a given URL as a String. */ public class StringRequest extends Request { - private final Response.Listener mListener; + private final Listener mListener; /** * Creates a new request with the given method. @@ -36,8 +38,8 @@ public class StringRequest extends Request { * @param listener Listener to receive the String response * @param errorListener Error listener, or null to ignore errors */ - public StringRequest(int method, String url, Response.Listener listener, - Response.ErrorListener errorListener) { + public StringRequest(int method, String url, Listener listener, + ErrorListener errorListener) { super(method, url, errorListener); mListener = listener; } @@ -49,7 +51,7 @@ public class StringRequest extends Request { * @param listener Listener to receive the String response * @param errorListener Error listener, or null to ignore errors */ - public StringRequest(String url, Response.Listener listener, Response.ErrorListener errorListener) { + public StringRequest(String url, Listener listener, ErrorListener errorListener) { this(Method.GET, url, listener, errorListener); } diff --git a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/Volley.java b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/Volley.java index dd0c4aed3..77c33f8b0 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/Volley.java +++ b/TMessagesProj/src/main/java/org/telegram/android/volley/toolbox/Volley.java @@ -48,7 +48,6 @@ public class Volley { PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0); userAgent = packageName + "/" + info.versionCode; } catch (NameNotFoundException e) { - /**/ } if (stack == null) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/BuffersStorage.java b/TMessagesProj/src/main/java/org/telegram/messenger/BuffersStorage.java index d6e733709..7961ae171 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/BuffersStorage.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/BuffersStorage.java @@ -113,7 +113,7 @@ public class BuffersStorage { arrayToReuse = freeBuffers128; } else if (buffer.buffer.capacity() == 1024 + 200) { arrayToReuse = freeBuffers1024; - } if (buffer.buffer.capacity() == 4096 + 200) { + } else if (buffer.buffer.capacity() == 4096 + 200) { arrayToReuse = freeBuffers4096; } else if (buffer.buffer.capacity() == 16384 + 200) { arrayToReuse = freeBuffers16384; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java b/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java index 15c858b24..58c244048 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java @@ -10,7 +10,7 @@ package org.telegram.messenger; public class BuildVars { public static boolean DEBUG_VERSION = false; - public static int BUILD_VERSION = 572; + public static int BUILD_VERSION = 586; public static int APP_ID = 0; //obtain your own APP_ID at https://core.telegram.org/api/obtaining_api_id public static String APP_HASH = ""; //obtain your own APP_HASH at https://core.telegram.org/api/obtaining_api_id public static String HOCKEY_APP_HASH = "your-hockeyapp-api-key-here"; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java index e7268e2ba..fab62217b 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java @@ -227,6 +227,11 @@ public class FileLoadOperation { downloadedBytes = (int)cacheFileTemp.length(); nextDownloadOffset = downloadedBytes = downloadedBytes / currentDownloadChunkSize * currentDownloadChunkSize; } + + if (BuildVars.DEBUG_VERSION) { + FileLog.d("tmessages", "start loading file to temp = " + cacheFileTemp + " final = " + cacheFileFinal); + } + if (fileNameIv != null) { cacheIvTemp = new File(tempPath, fileNameIv); try { @@ -291,6 +296,9 @@ public class FileLoadOperation { return; } state = stateFailed; + if (BuildVars.DEBUG_VERSION) { + FileLog.e("tmessages", "cancel downloading file to " + cacheFileFinal); + } cleanup(); for (RequestInfo requestInfo : requestInfos) { if (requestInfo.requestToken != 0) { @@ -340,9 +348,15 @@ public class FileLoadOperation { } if (cacheFileTemp != null) { if (!cacheFileTemp.renameTo(cacheFileFinal)) { + if (BuildVars.DEBUG_VERSION) { + FileLog.e("tmessages", "unable to rename temp = " + cacheFileTemp + " to final = " + cacheFileFinal); + } cacheFileFinal = cacheFileTemp; } } + if (BuildVars.DEBUG_VERSION) { + FileLog.e("tmessages", "finished downloading file to " + cacheFileFinal); + } delegate.didFinishLoadingFile(FileLoadOperation.this, cacheFileFinal); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java index 19ebeba03..b13f00922 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java @@ -573,6 +573,14 @@ public class FileLoader { return getPathToAttach(sizeFull); } } + } else if (message.media instanceof TLRPC.TL_messageMediaWebPage && message.media.webpage.photo != null) { + ArrayList sizes = message.media.webpage.photo.sizes; + if (sizes.size() > 0) { + TLRPC.PhotoSize sizeFull = getClosestPhotoSizeWithSize(sizes, AndroidUtilities.getPhotoSize()); + if (sizeFull != null) { + return getPathToAttach(sizeFull); + } + } } } return new File(""); @@ -617,7 +625,7 @@ public class FileLoader { } } else if (attach instanceof TLRPC.PhotoSize) { TLRPC.PhotoSize photoSize = (TLRPC.PhotoSize) attach; - if (photoSize.location == null || photoSize.location.key != null || photoSize.location.volume_id == Integer.MIN_VALUE && photoSize.location.local_id < 0) { + if (photoSize.location == null || photoSize.location.key != null || photoSize.location.volume_id == Integer.MIN_VALUE && photoSize.location.local_id < 0 || photoSize.size < 0) { dir = getInstance().getDirectory(MEDIA_DIR_CACHE); } else { dir = getInstance().getDirectory(MEDIA_DIR_IMAGE); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/HandshakeAction.java b/TMessagesProj/src/main/java/org/telegram/messenger/HandshakeAction.java index b99706fe0..258b26182 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/HandshakeAction.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/HandshakeAction.java @@ -89,6 +89,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti final Object lock = new Object(); static ArrayList> serverPublicKeys = null; + HashMap selectPublicKey(ArrayList fingerprints) { synchronized (lock) { if (serverPublicKeys == null) { @@ -150,7 +151,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti } for (HashMap keyDesc : serverPublicKeys) { - long keyFingerprint = (Long)keyDesc.get("fingerprint"); + long keyFingerprint = (Long) keyDesc.get("fingerprint"); for (long nFingerprint : fingerprints) { if (nFingerprint == keyFingerprint) { return keyDesc; @@ -161,7 +162,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti } long generateMessageId() { - long messageId = (long)((((double)System.currentTimeMillis()) * 4294967296.0) / 1000.0); + long messageId = (long) ((((double) System.currentTimeMillis()) * 4294967296.0) / 1000.0); if (messageId <= lastOutgoingMessageId) { messageId = lastOutgoingMessageId + 1; } @@ -197,7 +198,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti } processedPQRes = true; - final TLRPC.TL_resPQ resPq = (TLRPC.TL_resPQ)message; + final TLRPC.TL_resPQ resPq = (TLRPC.TL_resPQ) message; if (Utilities.arraysEquals(authNonce, 0, resPq.nonce, 0)) { final HashMap publicKey = selectPublicKey(resPq.server_public_key_fingerprints); if (publicKey == null) { @@ -221,11 +222,11 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti @Override public void run() { ByteBuffer pBytes = ByteBuffer.allocate(4); - pBytes.putInt((int)factorizedPq.p); + pBytes.putInt((int) factorizedPq.p); byte[] pData = pBytes.array(); ByteBuffer qBytes = ByteBuffer.allocate(4); - qBytes.putInt((int)factorizedPq.q); + qBytes.putInt((int) factorizedPq.q); byte[] qData = qBytes.array(); TLRPC.TL_req_DH_params reqDH = new TLRPC.TL_req_DH_params(); @@ -233,7 +234,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti reqDH.server_nonce = authServerNonce; reqDH.p = pData; reqDH.q = qData; - reqDH.public_key_fingerprint = (Long)publicKey.get("fingerprint"); + reqDH.public_key_fingerprint = (Long) publicKey.get("fingerprint"); SerializedData os = new SerializedData(); @@ -261,7 +262,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti dataWithHash.writeByte(b[0]); } - byte[] encryptedBytes = Utilities.encryptWithRSA((BigInteger[])publicKey.get("key"), dataWithHash.toByteArray()); + byte[] encryptedBytes = Utilities.encryptWithRSA((BigInteger[]) publicKey.get("key"), dataWithHash.toByteArray()); dataWithHash.cleanup(); SerializedData encryptedData = new SerializedData(); encryptedData.writeRaw(encryptedBytes); @@ -300,7 +301,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti if (authNewNonce == null) { return; } - TLRPC.TL_server_DH_params_ok serverDhParams = (TLRPC.TL_server_DH_params_ok)message; + TLRPC.TL_server_DH_params_ok serverDhParams = (TLRPC.TL_server_DH_params_ok) message; SerializedData tmpAesKey = new SerializedData(); @@ -423,17 +424,17 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti for (int i = 7; i >= 0; i--) { byte a_ = authNewNonce[i]; byte b_ = authServerNonce[i]; - byte x = (byte)(a_ ^ b_); + byte x = (byte) (a_ ^ b_); serverSaltData.writeByte(x); } ByteBuffer saltBuffer = ByteBuffer.wrap(serverSaltData.toByteArray()); serverSaltData.cleanup(); - timeDifference = dhInnerData.server_time - (int)(System.currentTimeMillis() / 1000); + timeDifference = dhInnerData.server_time - (int) (System.currentTimeMillis() / 1000); serverSalt = new ServerSalt(); - serverSalt.validSince = (int)(System.currentTimeMillis() / 1000) + timeDifference; - serverSalt.validUntil = (int)(System.currentTimeMillis() / 1000) + timeDifference + 30 * 60; + serverSalt.validSince = (int) (System.currentTimeMillis() / 1000) + timeDifference; + serverSalt.validUntil = (int) (System.currentTimeMillis() / 1000) + timeDifference + 30 * 60; serverSalt.value = saltBuffer.getLong(); FileLog.d("tmessages", String.format(Locale.US, "===== Time difference: %d", timeDifference)); @@ -489,7 +490,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti setClientDHParamsMsgData = null; } - TLRPC.Set_client_DH_params_answer dhAnswer = (TLRPC.Set_client_DH_params_answer)message; + TLRPC.Set_client_DH_params_answer dhAnswer = (TLRPC.Set_client_DH_params_answer) message; if (!Utilities.arraysEquals(authNonce, 0, dhAnswer.nonce, 0)) { FileLog.e("tmessages", "***** Invalid DH answer nonce"); @@ -544,7 +545,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti System.arraycopy(newNonceHash3Full, newNonceHash3Full.length - 16, newNonceHash3, 0, 16); if (message instanceof TLRPC.TL_dh_gen_ok) { - TLRPC.TL_dh_gen_ok dhGenOk = (TLRPC.TL_dh_gen_ok)message; + TLRPC.TL_dh_gen_ok dhGenOk = (TLRPC.TL_dh_gen_ok) message; if (!Utilities.arraysEquals(newNonceHash1, 0, dhGenOk.new_nonce_hash1, 0)) { FileLog.e("tmessages", "***** Invalid DH answer nonce hash 1"); beginHandshake(false); @@ -569,7 +570,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti } }); } else if (message instanceof TLRPC.TL_dh_gen_retry) { - TLRPC.TL_dh_gen_retry dhRetry = (TLRPC.TL_dh_gen_retry)message; + TLRPC.TL_dh_gen_retry dhRetry = (TLRPC.TL_dh_gen_retry) message; if (!Utilities.arraysEquals(newNonceHash2, 0, dhRetry.new_nonce_hash2, 0)) { FileLog.e("tmessages", "***** Invalid DH answer nonce hash 2"); beginHandshake(false); @@ -578,7 +579,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti FileLog.d("tmessages", "***** Retry DH"); beginHandshake(false); } else if (message instanceof TLRPC.TL_dh_gen_fail) { - TLRPC.TL_dh_gen_fail dhFail = (TLRPC.TL_dh_gen_fail)message; + TLRPC.TL_dh_gen_fail dhFail = (TLRPC.TL_dh_gen_fail) message; if (!Utilities.arraysEquals(newNonceHash3, 0, dhFail.new_nonce_hash3, 0)) { FileLog.e("tmessages", "***** Invalid DH answer nonce hash 3"); beginHandshake(false); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/TLRPC.java b/TMessagesProj/src/main/java/org/telegram/messenger/TLRPC.java index 954025cc1..c660c1dd1 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/TLRPC.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/TLRPC.java @@ -11729,6 +11729,15 @@ public class TLRPC { } } + public static class TL_inputMessagesFilterUrl extends MessagesFilter { + public static int constructor = 0x7ef0dd87; + + + public void serializeToStream(AbsSerializedData stream) { + stream.writeInt32(constructor); + } + } + public static class TL_inputMessagesFilterPhotoVideo extends MessagesFilter { public static int constructor = 0x56e9f0e4; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/TcpConnection.java b/TMessagesProj/src/main/java/org/telegram/messenger/TcpConnection.java index 417d5c4e5..48178794c 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/TcpConnection.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/TcpConnection.java @@ -300,10 +300,6 @@ public class TcpConnection extends ConnectionContext { } } - public void resumeConnection() { - - } - private void reconnect() { suspendConnection(false); connectionState = TcpConnectionState.TcpConnectionStageReconnecting; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java b/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java index 38e3ff667..86922bead 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java @@ -39,6 +39,7 @@ public class UserConfig { public static int lastPauseTime = 0; public static boolean isWaitingForPasscodeEnter = false; public static int lastUpdateVersion; + public static int lastContactsSyncTime; public static int getNewMessageId() { int id; @@ -76,6 +77,7 @@ public class UserConfig { editor.putInt("autoLockIn", autoLockIn); editor.putInt("lastPauseTime", lastPauseTime); editor.putInt("lastUpdateVersion", lastUpdateVersion); + editor.putInt("lastContactsSyncTime", lastContactsSyncTime); if (currentUser != null) { if (withFile) { @@ -206,6 +208,7 @@ public class UserConfig { autoLockIn = preferences.getInt("autoLockIn", 60 * 60); lastPauseTime = preferences.getInt("lastPauseTime", 0); lastUpdateVersion = preferences.getInt("lastUpdateVersion", 511); + lastContactsSyncTime = preferences.getInt("lastContactsSyncTime", (int) (System.currentTimeMillis() / 1000) - 23 * 60 * 60); String user = preferences.getString("user", null); if (user != null) { byte[] userBytes = Base64.decode(user, Base64.DEFAULT); @@ -279,6 +282,7 @@ public class UserConfig { lastPauseTime = 0; isWaitingForPasscodeEnter = false; lastUpdateVersion = BuildVars.BUILD_VERSION; + lastContactsSyncTime = (int) (System.currentTimeMillis() / 1000) - 23 * 60 * 60; saveConfig(true); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarPopupWindow.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarPopupWindow.java index ee42b1eb9..c7d89c13f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarPopupWindow.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarPopupWindow.java @@ -135,8 +135,8 @@ public class ActionBarPopupWindow extends PopupWindow { if (child.getVisibility() != VISIBLE) { continue; } - int position = positions.get(child); - if (height - (position * AndroidUtilities.dp(48) + AndroidUtilities.dp(32)) > value * height) { + Integer position = positions.get(child); + if (position != null && height - (position * AndroidUtilities.dp(48) + AndroidUtilities.dp(32)) > value * height) { break; } lastStartedChild = a - 1; @@ -148,8 +148,8 @@ public class ActionBarPopupWindow extends PopupWindow { if (child.getVisibility() != VISIBLE) { continue; } - int position = positions.get(child); - if ((position + 1) * AndroidUtilities.dp(48) - AndroidUtilities.dp(24) > value * height) { + Integer position = positions.get(child); + if (position != null && (position + 1) * AndroidUtilities.dp(48) - AndroidUtilities.dp(24) > value * height) { break; } lastStartedChild = a + 1; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheet.java index 87ac403ba..6cb544177 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheet.java @@ -219,6 +219,14 @@ public class BottomSheet extends Dialog { containerView.measure(MeasureSpec.makeMeasureSpec(width + left * 2, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST)); } } + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + if (child.getVisibility() == GONE || child == containerView) { + continue; + } + measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); + } } @Override @@ -228,6 +236,56 @@ public class BottomSheet extends Dialog { int t = (bottom - top) - containerView.getMeasuredHeight(); containerView.layout(l, t, l + containerView.getMeasuredWidth(), t + getMeasuredHeight()); } + + final int count = getChildCount(); + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (child.getVisibility() == GONE || child == containerView) { + continue; + } + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + + final int width = child.getMeasuredWidth(); + final int height = child.getMeasuredHeight(); + + int childLeft; + int childTop; + + int gravity = lp.gravity; + if (gravity == -1) { + gravity = Gravity.TOP | Gravity.LEFT; + } + + final int absoluteGravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK; + final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; + + switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { + case Gravity.CENTER_HORIZONTAL: + childLeft = (right - left - width) / 2 + lp.leftMargin - lp.rightMargin; + break; + case Gravity.RIGHT: + childLeft = right - width - lp.rightMargin; + break; + case Gravity.LEFT: + default: + childLeft = lp.leftMargin; + } + + switch (verticalGravity) { + case Gravity.TOP: + childTop = lp.topMargin; + break; + case Gravity.CENTER_VERTICAL: + childTop = (bottom - top - height) / 2 + lp.topMargin - lp.bottomMargin; + break; + case Gravity.BOTTOM: + childTop = (bottom - top) - height - lp.bottomMargin; + break; + default: + childTop = lp.topMargin; + } + child.layout(childLeft, childTop, childLeft + width, childTop + height); + } } }; container.setOnTouchListener(new View.OnTouchListener() { @@ -371,15 +429,6 @@ public class BottomSheet extends Dialog { params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; } getWindow().setAttributes(params); - - setOnShowListener(new OnShowListener() { - @Override - public void onShow(DialogInterface dialog) { - if (Build.VERSION.SDK_INT >= 21) { - startOpenAnimation(); - } - } - }); } @Override @@ -394,7 +443,14 @@ public class BottomSheet extends Dialog { int left = useRevealAnimation && Build.VERSION.SDK_INT <= 19 ? 0 : backgroundPaddingLeft; int top = useRevealAnimation && Build.VERSION.SDK_INT <= 19 ? 0 : backgroundPaddingTop; containerView.setPadding(left, (applyTopPaddings ? AndroidUtilities.dp(8) : 0) + top, left, (applyTopPaddings ? AndroidUtilities.dp(isGrid ? 16 : 8) : 0)); - if (Build.VERSION.SDK_INT < 21) { + if (Build.VERSION.SDK_INT >= 21) { + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + startOpenAnimation(); + } + }); + } else { startOpenAnimation(); } } @@ -413,7 +469,6 @@ public class BottomSheet extends Dialog { @SuppressLint("NewApi") private void startRevealAnimation(final boolean open) { - if (open) { backgroundDrawable.setAlpha(0); containerView.setVisibility(View.VISIBLE); @@ -460,7 +515,11 @@ public class BottomSheet extends Dialog { animators.add(ObjectAnimator.ofInt(backgroundDrawable, "alpha", open ? 51 : 0)); if (Build.VERSION.SDK_INT >= 21) { containerView.setElevation(AndroidUtilities.dp(10)); - animators.add(ViewAnimationUtils.createCircularReveal(containerView, revealX <= containerView.getMeasuredWidth() ? revealX : containerView.getMeasuredWidth(), revealY, open ? 0 : finalRevealRadius, open ? finalRevealRadius : 0)); + try { + animators.add(ViewAnimationUtils.createCircularReveal(containerView, revealX <= containerView.getMeasuredWidth() ? revealX : containerView.getMeasuredWidth(), revealY, open ? 0 : finalRevealRadius, open ? finalRevealRadius : 0)); + } catch (Exception e) { + FileLog.e("tmessages", e); + } animatorSet.setDuration(300); } else { if (!open) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/BaseLocationAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/BaseLocationAdapter.java index 81d26e865..5c09f0c73 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/BaseLocationAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/BaseLocationAdapter.java @@ -208,6 +208,7 @@ public class BaseLocationAdapter extends BaseFragmentAdapter { } } }); + jsonObjReq.setShouldCache(false); jsonObjReq.setTag("search"); requestQueue.add(jsonObjReq); } catch (Exception e) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java index cf5a0d4f6..5a5014b8d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java @@ -14,6 +14,7 @@ import android.view.View; import android.view.ViewGroup; import org.telegram.SQLite.SQLiteCursor; +import org.telegram.SQLite.SQLitePreparedStatement; import org.telegram.android.AndroidUtilities; import org.telegram.android.ContactsController; import org.telegram.android.LocaleController; @@ -58,6 +59,10 @@ public class DialogsSearchAdapter extends BaseSearchAdapterRecycler { private boolean messagesSearchEndReached; private String lastMessagesSearchString; private int lastSearchId = 0; + private int dialogsType; + + private ArrayList recentSearchObjects = new ArrayList<>(); + private HashMap recentSearchObjectsById = new HashMap<>(); private class Holder extends RecyclerView.ViewHolder { @@ -72,13 +77,21 @@ public class DialogsSearchAdapter extends BaseSearchAdapterRecycler { public CharSequence name; } + protected static class RecentSearchObject { + TLObject object; + int date; + long did; + } + public interface MessagesActivitySearchAdapterDelegate { void searchStateChanged(boolean searching); } - public DialogsSearchAdapter(Context context, int messagesSearch) { + public DialogsSearchAdapter(Context context, int messagesSearch, int type) { mContext = context; needMessagesSearch = messagesSearch; + dialogsType = type; + loadRecentSearch(); } public void setDelegate(MessagesActivitySearchAdapterDelegate delegate) { @@ -160,7 +173,194 @@ public class DialogsSearchAdapter extends BaseSearchAdapterRecycler { }, true, RPCRequest.RPCRequestClassGeneric | RPCRequest.RPCRequestClassFailOnServerErrors); } - private void searchDialogsInternal(final String query, final int dialogsType, final int searchId) { + public boolean hasRecentRearch() { + return !recentSearchObjects.isEmpty(); + } + + public boolean isRecentSearchDisplayed() { + return (lastSearchText == null || lastSearchText.length() == 0) && !recentSearchObjects.isEmpty(); + } + + private void loadRecentSearch() { + MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() { + @Override + public void run() { + try { + SQLiteCursor cursor = MessagesStorage.getInstance().getDatabase().queryFinalized("SELECT did, date FROM search_recent WHERE 1"); + + ArrayList usersToLoad = new ArrayList<>(); + ArrayList chatsToLoad = new ArrayList<>(); + ArrayList encryptedToLoad = new ArrayList<>(); + ArrayList encUsers = new ArrayList<>(); + + final ArrayList arrayList = new ArrayList<>(); + final HashMap hashMap = new HashMap<>(); + while (cursor.next()) { + long did = cursor.longValue(0); + + boolean add = false; + int lower_id = (int) did; + int high_id = (int) (did >> 32); + if (lower_id != 0) { + if (high_id == 1) { + if (dialogsType == 0 && !chatsToLoad.contains(lower_id)) { + chatsToLoad.add(lower_id); + add = true; + } + } else { + if (lower_id > 0) { + if (dialogsType != 2 && !usersToLoad.contains(lower_id)) { + usersToLoad.add(lower_id); + add = true; + } + } else { + if (!chatsToLoad.contains(-lower_id)) { + chatsToLoad.add(-lower_id); + add = true; + } + } + } + } else if (dialogsType == 0) { + if (!encryptedToLoad.contains(high_id)) { + encryptedToLoad.add(high_id); + add = true; + } + } + if (add) { + RecentSearchObject recentSearchObject = new RecentSearchObject(); + recentSearchObject.did = did; + recentSearchObject.date = cursor.intValue(1); + arrayList.add(recentSearchObject); + hashMap.put(recentSearchObject.did, recentSearchObject); + } + } + cursor.dispose(); + + + ArrayList users = new ArrayList<>(); + + if (!encryptedToLoad.isEmpty()) { + ArrayList encryptedChats = new ArrayList<>(); + MessagesStorage.getInstance().getEncryptedChatsInternal(TextUtils.join(",", encryptedToLoad), encryptedChats, usersToLoad); + for (int a = 0; a < encryptedChats.size(); a++) { + hashMap.get((long) encryptedChats.get(a).id << 32).object = encryptedChats.get(a); + } + } + + if (!chatsToLoad.isEmpty()) { + ArrayList chats = new ArrayList<>(); + MessagesStorage.getInstance().getChatsInternal(TextUtils.join(",", chatsToLoad), chats); + for (int a = 0; a < chats.size(); a++) { + TLRPC.Chat chat = chats.get(a); + long did; + if (chat.id > 0) { + did = -chat.id; + } else { + did = AndroidUtilities.makeBroadcastId(chat.id); + } + hashMap.get(did).object = chat; + } + } + + if (!usersToLoad.isEmpty()) { + MessagesStorage.getInstance().getUsersInternal(TextUtils.join(",", usersToLoad), users); + for (int a = 0; a < users.size(); a++) { + TLRPC.User user = users.get(a); + RecentSearchObject recentSearchObject = hashMap.get((long) user.id); + if (recentSearchObject != null) { + recentSearchObject.object = user; + } + } + } + + Collections.sort(arrayList, new Comparator() { + @Override + public int compare(RecentSearchObject lhs, RecentSearchObject rhs) { + if (lhs.date < rhs.date) { + return 1; + } else if (lhs.date > rhs.date) { + return -1; + } else { + return 0; + } + } + }); + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + setRecentSearch(arrayList, hashMap); + } + }); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + }); + } + + public void putRecentSearch(final long did, TLObject object) { + RecentSearchObject recentSearchObject = recentSearchObjectsById.get(did); + if (recentSearchObject == null) { + recentSearchObject = new RecentSearchObject(); + recentSearchObjectsById.put(did, recentSearchObject); + } else { + recentSearchObjects.remove(recentSearchObject); + } + recentSearchObjects.add(0, recentSearchObject); + recentSearchObject.did = did; + recentSearchObject.object = object; + recentSearchObject.date = (int) System.currentTimeMillis() / 1000; + notifyDataSetChanged(); + MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() { + @Override + public void run() { + try { + SQLitePreparedStatement state = MessagesStorage.getInstance().getDatabase().executeFast("REPLACE INTO search_recent VALUES(?, ?)"); + state.requery(); + state.bindLong(1, did); + state.bindInteger(2, (int) System.currentTimeMillis() / 1000); + state.step(); + state.dispose(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + }); + } + + public void clearRecentSearch() { + recentSearchObjectsById = new HashMap<>(); + recentSearchObjects = new ArrayList<>(); + notifyDataSetChanged(); + MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() { + @Override + public void run() { + try { + MessagesStorage.getInstance().getDatabase().executeFast("DELETE FROM search_recent WHERE 1").stepThis().dispose(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + }); + } + + private void setRecentSearch(ArrayList arrayList, HashMap hashMap) { + recentSearchObjects = arrayList; + recentSearchObjectsById = hashMap; + for (int a = 0; a < recentSearchObjects.size(); a++) { + RecentSearchObject recentSearchObject = recentSearchObjects.get(a); + if (recentSearchObject.object instanceof TLRPC.User) { + MessagesController.getInstance().putUser((TLRPC.User) recentSearchObject.object, true); + } else if (recentSearchObject.object instanceof TLRPC.Chat) { + MessagesController.getInstance().putChat((TLRPC.Chat) recentSearchObject.object, true); + } else if (recentSearchObject.object instanceof TLRPC.EncryptedChat) { + MessagesController.getInstance().putEncryptedChat((TLRPC.EncryptedChat) recentSearchObject.object, true); + } + } + notifyDataSetChanged(); + } + + private void searchDialogsInternal(final String query, final int searchId) { if (needMessagesSearch == 2) { return; } @@ -477,10 +677,6 @@ public class DialogsSearchAdapter extends BaseSearchAdapterRecycler { }); } - public String getLastSearchText() { - return lastSearchText; - } - public boolean isGlobalSearch(int i) { return i > searchResult.size() && i <= globalSearch.size() + searchResult.size(); } @@ -504,10 +700,11 @@ public class DialogsSearchAdapter extends BaseSearchAdapterRecycler { notifyDataSetChanged(); } - public void searchDialogs(final String query, final int dialogsType) { + public void searchDialogs(final String query) { if (query != null && lastSearchText != null && query.equals(lastSearchText)) { return; } + lastSearchText = query; try { if (searchTimer != null) { searchTimer.cancel(); @@ -562,7 +759,7 @@ public class DialogsSearchAdapter extends BaseSearchAdapterRecycler { } catch (Exception e) { FileLog.e("tmessages", e); } - searchDialogsInternal(query, dialogsType, searchId); + searchDialogsInternal(query, searchId); AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { @@ -579,6 +776,9 @@ public class DialogsSearchAdapter extends BaseSearchAdapterRecycler { @Override public int getItemCount() { + if ((lastSearchText == null || lastSearchText.length() == 0) && !recentSearchObjects.isEmpty()) { + return recentSearchObjects.size() + 1; + } if (!searchResultHashtags.isEmpty()) { return searchResultHashtags.size() + 1; } @@ -595,8 +795,19 @@ public class DialogsSearchAdapter extends BaseSearchAdapterRecycler { } public Object getItem(int i) { + if ((lastSearchText == null || lastSearchText.length() == 0) && !recentSearchObjects.isEmpty()) { + if (i > 0 && i - 1 < recentSearchObjects.size()) { + return recentSearchObjects.get(i - 1).object; + } else { + return null; + } + } if (!searchResultHashtags.isEmpty()) { - return searchResultHashtags.get(i - 1); + if (i > 0) { + return searchResultHashtags.get(i - 1); + } else { + return null; + } } int localCount = searchResult.size(); int globalCount = globalSearch.isEmpty() ? 0 : globalSearch.size() + 1; @@ -649,12 +860,11 @@ public class DialogsSearchAdapter extends BaseSearchAdapterRecycler { TLRPC.User user = null; TLRPC.Chat chat = null; TLRPC.EncryptedChat encryptedChat = null; - - int localCount = searchResult.size(); - int globalCount = globalSearch.isEmpty() ? 0 : globalSearch.size() + 1; - - cell.useSeparator = (position != getItemCount() - 1 && position != localCount - 1 && position != localCount + globalCount - 1); + CharSequence username = null; + CharSequence name = null; + boolean isRecent = false; Object obj = getItem(position); + if (obj instanceof TLRPC.User) { user = (TLRPC.User) obj; } else if (obj instanceof TLRPC.Chat) { @@ -664,37 +874,45 @@ public class DialogsSearchAdapter extends BaseSearchAdapterRecycler { user = MessagesController.getInstance().getUser(encryptedChat.user_id); } - CharSequence username = null; - CharSequence name = null; - if (position < searchResult.size()) { - name = searchResultNames.get(position); - if (name != null && user != null && user.username != null && user.username.length() > 0) { - if (name.toString().startsWith("@" + user.username)) { - username = name; - name = null; + if ((lastSearchText == null || lastSearchText.length() == 0) && !recentSearchObjects.isEmpty()) { + isRecent = true; + cell.useSeparator = position != getItemCount() - 1; + } else { + int localCount = searchResult.size(); + int globalCount = globalSearch.isEmpty() ? 0 : globalSearch.size() + 1; + cell.useSeparator = (position != getItemCount() - 1 && position != localCount - 1 && position != localCount + globalCount - 1); + + if (position < searchResult.size()) { + name = searchResultNames.get(position); + if (name != null && user != null && user.username != null && user.username.length() > 0) { + if (name.toString().startsWith("@" + user.username)) { + username = name; + name = null; + } + } + } else if (position > searchResult.size() && user != null && user.username != null) { + String foundUserName = lastFoundUsername; + if (foundUserName.startsWith("@")) { + foundUserName = foundUserName.substring(1); + } + try { + username = AndroidUtilities.replaceTags(String.format("@%s%s", user.username.substring(0, foundUserName.length()), user.username.substring(foundUserName.length()))); + } catch (Exception e) { + username = user.username; + FileLog.e("tmessages", e); } } - } else if (position > searchResult.size() && user != null && user.username != null) { - String foundUserName = lastFoundUsername; - if (foundUserName.startsWith("@")) { - foundUserName = foundUserName.substring(1); - } - try { - username = AndroidUtilities.replaceTags(String.format("@%s%s", user.username.substring(0, foundUserName.length()), user.username.substring(foundUserName.length()))); - } catch (Exception e) { - username = user.username; - FileLog.e("tmessages", e); - } } - - cell.setData(user, chat, encryptedChat, name, username); + cell.setData(user, chat, encryptedChat, name, username, isRecent); break; } case 1: { GreySectionCell cell = (GreySectionCell) holder.itemView; - if (!searchResultHashtags.isEmpty()) { + if ((lastSearchText == null || lastSearchText.length() == 0) && !recentSearchObjects.isEmpty()) { + cell.setText(LocaleController.getString("Recent", R.string.Recent).toUpperCase()); + } else if (!searchResultHashtags.isEmpty()) { cell.setText(LocaleController.getString("Hashtags", R.string.Hashtags).toUpperCase()); - } else if (!globalSearch.isEmpty() && position == searchResult.size()) { + } else if (!globalSearch.isEmpty() && position == searchResult.size()) { cell.setText(LocaleController.getString("GlobalSearch", R.string.GlobalSearch)); } else { cell.setText(LocaleController.getString("SearchMessages", R.string.SearchMessages)); @@ -722,6 +940,9 @@ public class DialogsSearchAdapter extends BaseSearchAdapterRecycler { @Override public int getItemViewType(int i) { + if ((lastSearchText == null || lastSearchText.length() == 0) && !recentSearchObjects.isEmpty()) { + return i == 0 ? 1 : 0; + } if (!searchResultHashtags.isEmpty()) { return i == 0 ? 1 : 4; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/PhotoAttachAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/PhotoAttachAdapter.java index 102a170fc..7c8c550eb 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/PhotoAttachAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/PhotoAttachAdapter.java @@ -13,7 +13,6 @@ import android.view.View; import android.view.ViewGroup; import org.telegram.android.MediaController; -import org.telegram.android.NotificationCenter; import org.telegram.android.support.widget.RecyclerView; import org.telegram.ui.Cells.PhotoAttachPhotoCell; @@ -73,24 +72,27 @@ public class PhotoAttachAdapter extends RecyclerView.Adapter { view = new PhotoAttachCameraCell(mContext); } else {*/ PhotoAttachPhotoCell cell = new PhotoAttachPhotoCell(mContext); - cell.setOnCheckClickLisnener(new View.OnClickListener() { + /*cell.setOnCheckClickLisnener(new View.OnClickListener() { @Override public void onClick(View v) { - PhotoAttachPhotoCell cell = (PhotoAttachPhotoCell) v.getParent(); - MediaController.PhotoEntry photoEntry = cell.getPhotoEntry(); - if (selectedPhotos.containsKey(photoEntry.imageId)) { - selectedPhotos.remove(photoEntry.imageId); - cell.setChecked(false, true); - } else { - selectedPhotos.put(photoEntry.imageId, photoEntry); - cell.setChecked(true, true); - } - delegate.selectedPhotosChanged(); + onItemClick((PhotoAttachPhotoCell) v.getParent()); } }); - view = cell; + view = cell;*/ //} - return new Holder(view); + return new Holder(cell); + } + + public void onItemClick(PhotoAttachPhotoCell cell) { + MediaController.PhotoEntry photoEntry = cell.getPhotoEntry(); + if (selectedPhotos.containsKey(photoEntry.imageId)) { + selectedPhotos.remove(photoEntry.imageId); + cell.setChecked(false, true); + } else { + selectedPhotos.put(photoEntry.imageId, photoEntry); + cell.setChecked(true, true); + } + delegate.selectedPhotosChanged(); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/SearchAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/SearchAdapter.java index 9894b9592..47ba5d218 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/SearchAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/SearchAdapter.java @@ -269,7 +269,7 @@ public class SearchAdapter extends BaseSearchAdapter { ((UserCell) view).setChecked(checkedMap.containsKey(user.id), false); } } else { - ((ProfileSearchCell) view).setData(user, null, null, name, username); + ((ProfileSearchCell) view).setData(user, null, null, name, username, false); ((ProfileSearchCell) view).useSeparator = (i != getCount() - 1 && i != searchResult.size() - 1); if (ignoreUsers != null) { if (ignoreUsers.containsKey(user.id)) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/BaseCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/BaseCell.java index df37101dd..92505cdb2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/BaseCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/BaseCell.java @@ -56,7 +56,9 @@ public class BaseCell extends View { } protected void setDrawableBounds(Drawable drawable, int x, int y, int w, int h) { - drawable.setBounds(x, y, x + w, y + h); + if (drawable != null) { + drawable.setBounds(x, y, x + w, y + h); + } } protected void startCheckLongPress() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/BotHelpCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/BotHelpCell.java index 32d9c6baa..b2283feb9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/BotHelpCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/BotHelpCell.java @@ -42,7 +42,6 @@ public class BotHelpCell extends View { private int height; private int textX; private int textY; - private int textXOffset; private ClickableSpan pressedLink; private LinkPath urlPath = new LinkPath(); @@ -102,10 +101,8 @@ public class BotHelpCell extends View { width = 0; height = textLayout.getHeight() + AndroidUtilities.dp(4 + 18); int count = textLayout.getLineCount(); - textXOffset = Integer.MAX_VALUE; for (int a = 0; a < count; a++) { - textXOffset = (int) Math.ceil(Math.min(textXOffset, textLayout.getLineLeft(a))); - width = (int) Math.ceil(Math.max(width, textLayout.getLineWidth(a) - textLayout.getLineLeft(a))); + width = (int) Math.ceil(Math.max(width, textLayout.getLineWidth(a) + textLayout.getLineLeft(a))); } width += AndroidUtilities.dp(4 + 18); } @@ -188,7 +185,7 @@ public class BotHelpCell extends View { ResourceLoader.backgroundMediaDrawableIn.setBounds(x, y, width + x, height + y); ResourceLoader.backgroundMediaDrawableIn.draw(canvas); canvas.save(); - canvas.translate(textX = AndroidUtilities.dp(2 + 9) + x - textXOffset, textY = AndroidUtilities.dp(2 + 9) + y); + canvas.translate(textX = AndroidUtilities.dp(2 + 9) + x, textY = AndroidUtilities.dp(2 + 9) + y); if (pressedLink != null) { canvas.drawPath(urlPath, urlPaint); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatAudioCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatAudioCell.java index 77f5069e2..bce938c21 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatAudioCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatAudioCell.java @@ -22,16 +22,18 @@ import org.telegram.android.AndroidUtilities; import org.telegram.android.ImageLoader; import org.telegram.android.MessagesController; import org.telegram.android.SendMessagesHelper; +import org.telegram.messenger.BuildVars; import org.telegram.messenger.FileLoader; import org.telegram.android.MediaController; import org.telegram.android.MessageObject; +import org.telegram.messenger.FileLog; import org.telegram.ui.Components.RadialProgress; import org.telegram.ui.Components.ResourceLoader; import org.telegram.ui.Components.SeekBar; import java.io.File; -public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelegate, MediaController.FileDownloadProgressListener { +public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelegate { private static TextPaint timePaint; private static Paint circlePaint; @@ -51,11 +53,8 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega private int timeWidth; private String lastTimeString = null; - private int TAG; - public ChatAudioCell(Context context) { super(context); - TAG = MediaController.getInstance().generateObserverTag(); seekBar = new SeekBar(context); seekBar.delegate = this; @@ -217,6 +216,9 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega if (cacheFile == null) { cacheFile = FileLoader.getPathToMessage(currentMessageObject.messageOwner); } + if (BuildVars.DEBUG_VERSION) { + FileLog.d("tmessages", "looking for audio in " + cacheFile); + } if (cacheFile.exists()) { MediaController.getInstance().removeLoadingFileObserver(this); boolean playing = MediaController.getInstance().isPlayingAudio(currentMessageObject); @@ -272,11 +274,6 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega radialProgress.setProgress(progress, true); } - @Override - public int getObserverTag() { - return TAG; - } - @Override public void onSeekBarDrag(float progress) { if (currentMessageObject == null) { @@ -369,7 +366,7 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega timePaint.setColor(0xffa1aab3); circlePaint.setColor(0xff4195e5); } - radialProgress.onDraw(canvas); + radialProgress.draw(canvas); canvas.save(); canvas.translate(timeX, AndroidUtilities.dp(42) + namesOffset); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatBaseCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatBaseCell.java index dda180c9a..12ffdd918 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatBaseCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatBaseCell.java @@ -25,6 +25,7 @@ import android.view.SoundEffectConstants; import org.telegram.android.AndroidUtilities; import org.telegram.android.Emoji; import org.telegram.android.LocaleController; +import org.telegram.android.MediaController; import org.telegram.android.UserObject; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.FileLoader; @@ -39,7 +40,7 @@ import org.telegram.ui.Components.LinkPath; import org.telegram.ui.Components.ResourceLoader; import org.telegram.ui.Components.StaticLayoutEx; -public class ChatBaseCell extends BaseCell { +public class ChatBaseCell extends BaseCell implements MediaController.FileDownloadProgressListener { public interface ChatBaseCellDelegate { void didPressedUserAvatar(ChatBaseCell cell, TLRPC.User user); @@ -48,6 +49,7 @@ public class ChatBaseCell extends BaseCell { void didPressReplyMessage(ChatBaseCell cell, int id); void didPressUrl(MessageObject messageObject, String url); void needOpenWebView(String url, String title, String originalUrl, int w, int h); + void didClickedImage(ChatBaseCell cell); boolean canPerformActions(); } @@ -55,6 +57,7 @@ public class ChatBaseCell extends BaseCell { protected boolean linkPreviewPressed; protected LinkPath urlPath = new LinkPath(); protected static Paint urlPaint; + private int TAG; public boolean isChat = false; protected boolean isPressed = false; @@ -170,6 +173,7 @@ public class ChatBaseCell extends BaseCell { avatarImage.setRoundRadius(AndroidUtilities.dp(21)); avatarDrawable = new AvatarDrawable(); replyImageReceiver = new ImageReceiver(this); + TAG = MediaController.getInstance().generateObserverTag(); } @Override @@ -600,6 +604,10 @@ public class ChatBaseCell extends BaseCell { } + public ImageReceiver getPhotoImage() { + return null; + } + @Override protected void onLongPress() { if (delegate != null) { @@ -658,7 +666,7 @@ public class ChatBaseCell extends BaseCell { setDrawableBounds(currentBackgroundDrawable, (!media ? 0 : AndroidUtilities.dp(9)), AndroidUtilities.dp(1), backgroundWidth, layoutHeight - AndroidUtilities.dp(2)); } } - if (drawBackground) { + if (drawBackground && currentBackgroundDrawable != null) { currentBackgroundDrawable.draw(canvas); } @@ -858,4 +866,29 @@ public class ChatBaseCell extends BaseCell { } } } + + @Override + public void onFailedDownload(String fileName) { + + } + + @Override + public void onSuccessDownload(String fileName) { + + } + + @Override + public void onProgressDownload(String fileName, float progress) { + + } + + @Override + public void onProgressUpload(String fileName, float progress, boolean isEncrypted) { + + } + + @Override + public int getObserverTag() { + return TAG; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMediaCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMediaCell.java index 0c704b956..3fc8394b1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMediaCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMediaCell.java @@ -46,11 +46,9 @@ import org.telegram.android.ImageReceiver; import java.io.File; import java.util.Locale; -public class ChatMediaCell extends ChatBaseCell implements MediaController.FileDownloadProgressListener { +public class ChatMediaCell extends ChatBaseCell { public interface ChatMediaCellDelegate { - void didClickedImage(ChatMediaCell cell); - void didPressedOther(ChatMediaCell cell); } @@ -78,8 +76,6 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD private boolean allowedToSetPhoto = true; - private int TAG; - private int buttonState = 0; private int buttonPressed = 0; private boolean imagePressed = false; @@ -127,8 +123,6 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD locationAddressPaint.setTextSize(AndroidUtilities.dp(14)); } - TAG = MediaController.getInstance().generateObserverTag(); - photoImage = new ImageReceiver(this); radialProgress = new RadialProgress(this); } @@ -343,8 +337,8 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD private void didClickedImage() { if (currentMessageObject.type == 1) { if (buttonState == -1) { - if (mediaDelegate != null) { - mediaDelegate.didClickedImage(this); + if (delegate != null) { + delegate.didClickedImage(this); } } else if (buttonState == 0) { didPressedButton(false); @@ -365,13 +359,13 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD didPressedButton(false); } } else if (currentMessageObject.type == 4) { - if (mediaDelegate != null) { - mediaDelegate.didClickedImage(this); + if (delegate != null) { + delegate.didClickedImage(this); } } else if (currentMessageObject.type == 9) { if (buttonState == -1) { - if (mediaDelegate != null) { - mediaDelegate.didClickedImage(this); + if (delegate != null) { + delegate.didClickedImage(this); } } } @@ -447,8 +441,8 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD radialProgress.setBackground(getDrawableForCurrentState(), false, animated); } } else if (buttonState == 3) { - if (mediaDelegate != null) { - mediaDelegate.didClickedImage(this); + if (delegate != null) { + delegate.didClickedImage(this); } } } @@ -828,6 +822,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD updateButtonState(dataChanged); } + @Override public ImageReceiver getPhotoImage() { return photoImage; } @@ -1066,7 +1061,7 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD } } - radialProgress.onDraw(canvas); + radialProgress.draw(canvas); if (currentMessageObject.type == 1 || currentMessageObject.type == 3) { if (nameLayout != null) { @@ -1155,9 +1150,4 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD public void onProgressUpload(String fileName, float progress, boolean isEncrypted) { radialProgress.setProgress(progress, true); } - - @Override - public int getObserverTag() { - return TAG; - } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java index 14159564f..f3cdfdf43 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java @@ -24,8 +24,10 @@ import android.text.TextPaint; import android.text.TextUtils; import android.text.style.ClickableSpan; import android.view.MotionEvent; +import android.view.SoundEffectConstants; import org.telegram.android.AndroidUtilities; +import org.telegram.android.ImageLoader; import org.telegram.android.ImageReceiver; import org.telegram.android.MediaController; import org.telegram.messenger.FileLoader; @@ -33,6 +35,7 @@ import org.telegram.messenger.FileLog; import org.telegram.android.MessageObject; import org.telegram.messenger.R; import org.telegram.messenger.TLRPC; +import org.telegram.ui.Components.RadialProgress; import org.telegram.ui.Components.ResourceLoader; import org.telegram.ui.Components.StaticLayoutEx; import org.telegram.ui.Components.URLSpanNoUnderline; @@ -50,8 +53,10 @@ public class ChatMessageCell extends ChatBaseCell { private int firstVisibleBlockNum = 0; private int totalVisibleBlocksCount = 0; + private RadialProgress radialProgress; private ImageReceiver linkImageView; private boolean isSmallImage; + private boolean drawImageButton; private boolean drawLinkImageView; private boolean hasLinkPreview; private int linkPreviewHeight; @@ -68,19 +73,31 @@ public class ChatMessageCell extends ChatBaseCell { private StaticLayout authorLayout; private static TextPaint durationPaint; + private int buttonX; + private int buttonY; + private int buttonState; + private boolean buttonPressed; + private boolean photoNotSet; + private TLRPC.PhotoSize currentPhotoObject; + private TLRPC.PhotoSize currentPhotoObjectThumb; + private String currentPhotoFilter; + private String currentPhotoFilterThumb; + private boolean cancelLoading; + private static Drawable igvideoDrawable; public ChatMessageCell(Context context) { super(context); drawForwardedName = true; linkImageView = new ImageReceiver(this); + radialProgress = new RadialProgress(this); } @Override public boolean onTouchEvent(MotionEvent event) { boolean result = false; if (currentMessageObject != null && currentMessageObject.textLayoutBlocks != null && !currentMessageObject.textLayoutBlocks.isEmpty() && currentMessageObject.messageText instanceof Spannable && delegate.canPerformActions()) { - if (event.getAction() == MotionEvent.ACTION_DOWN || (linkPreviewPressed || pressedLink != null) && event.getAction() == MotionEvent.ACTION_UP) { + if (event.getAction() == MotionEvent.ACTION_DOWN || (linkPreviewPressed || pressedLink != null || buttonPressed) && event.getAction() == MotionEvent.ACTION_UP) { int x = (int) event.getX(); int y = (int) event.getY(); if (x >= textX && y >= textY && x <= textX + currentMessageObject.textWidth && y <= textY + currentMessageObject.textHeight) { @@ -148,8 +165,13 @@ public class ChatMessageCell extends ChatBaseCell { if (event.getAction() == MotionEvent.ACTION_DOWN) { resetPressedLink(); if (drawLinkImageView && linkImageView.isInsideImage(x, y)) { - linkPreviewPressed = true; - result = true; + if (drawImageButton && buttonState != -1 && x >= buttonX && x <= buttonX + AndroidUtilities.dp(48) && y >= buttonY && y <= buttonY + AndroidUtilities.dp(48)) { + buttonPressed = true; + result = true; + } else { + linkPreviewPressed = true; + result = true; + } } else { if (descriptionLayout != null && y >= descriptionY) { try { @@ -192,14 +214,18 @@ public class ChatMessageCell extends ChatBaseCell { if (pressedLink != null) { pressedLink.onClick(this); } else { - TLRPC.WebPage webPage = currentMessageObject.messageOwner.media.webpage; - if (Build.VERSION.SDK_INT >= 19 && webPage.embed_url != null && webPage.embed_url.length() != 0) { - delegate.needOpenWebView(webPage.embed_url, webPage.site_name, webPage.url, webPage.embed_width, webPage.embed_height); + if (drawImageButton && delegate != null) { + delegate.didClickedImage(this); } else { - Uri uri = Uri.parse(webPage.url); - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - intent.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName()); - getContext().startActivity(intent); + TLRPC.WebPage webPage = currentMessageObject.messageOwner.media.webpage; + if (Build.VERSION.SDK_INT >= 16 && webPage.embed_url != null && webPage.embed_url.length() != 0) { + delegate.needOpenWebView(webPage.embed_url, webPage.site_name, webPage.url, webPage.embed_width, webPage.embed_height); + } else { + Uri uri = Uri.parse(webPage.url); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + intent.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName()); + getContext().startActivity(intent); + } } } } catch (Exception e) { @@ -207,6 +233,21 @@ public class ChatMessageCell extends ChatBaseCell { } resetPressedLink(); result = true; + } else if (buttonPressed) { + if (event.getAction() == MotionEvent.ACTION_UP) { + buttonPressed = false; + playSoundEffect(SoundEffectConstants.CLICK); + didPressedButton(false); + invalidate(); + } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { + buttonPressed = false; + invalidate(); + } else if (event.getAction() == MotionEvent.ACTION_MOVE) { + if (!(x >= buttonX && x <= buttonX + AndroidUtilities.dp(48) && y >= buttonY && y <= buttonY + AndroidUtilities.dp(48))) { + buttonPressed = false; + invalidate(); + } + } } } else { resetPressedLink(); @@ -261,7 +302,7 @@ public class ChatMessageCell extends ChatBaseCell { return left1 <= right2; } - private StaticLayout generateStaticLayout(CharSequence text, TextPaint paint, int maxWidth, int smallWidth, int linesCount, int maxLines) { + public static StaticLayout generateStaticLayout(CharSequence text, TextPaint paint, int maxWidth, int smallWidth, int linesCount, int maxLines) { SpannableStringBuilder stringBuilder = new SpannableStringBuilder(text); int addedChars = 0; StaticLayout layout = new StaticLayout(text, paint, smallWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); @@ -296,21 +337,30 @@ public class ChatMessageCell extends ChatBaseCell { return super.isUserDataChanged(); } + @Override + public ImageReceiver getPhotoImage() { + return linkImageView; + } + @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); linkImageView.onDetachedFromWindow(); + MediaController.getInstance().removeLoadingFileObserver(this); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - linkImageView.onAttachedToWindow(); + if (linkImageView.onAttachedToWindow()) { + updateButtonState(false); + } } @Override public void setMessageObject(MessageObject messageObject) { - if (currentMessageObject != messageObject || isUserDataChanged()) { + boolean dataChanged = currentMessageObject == messageObject && (isUserDataChanged() || photoNotSet); + if (currentMessageObject != messageObject || dataChanged) { if (currentMessageObject != messageObject) { firstVisibleBlockNum = 0; lastVisibleBlockNum = 0; @@ -319,6 +369,7 @@ public class ChatMessageCell extends ChatBaseCell { hasLinkPreview = false; resetPressedLink(); linkPreviewPressed = false; + buttonPressed = false; linkPreviewHeight = 0; isInstagram = false; durationLayout = null; @@ -326,6 +377,10 @@ public class ChatMessageCell extends ChatBaseCell { titleLayout = null; siteNameLayout = null; authorLayout = null; + drawImageButton = false; + currentPhotoObject = null; + currentPhotoObjectThumb = null; + currentPhotoFilter = null; int maxWidth; if (AndroidUtilities.isTablet()) { @@ -540,9 +595,10 @@ public class ChatMessageCell extends ChatBaseCell { smallImage = false; isSmallImage = false; } + drawImageButton = webPage.type != null && webPage.type.equals("photo"); int maxPhotoWidth = smallImage ? AndroidUtilities.dp(48) : linkPreviewMaxWidth; - TLRPC.PhotoSize currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, maxPhotoWidth, true); - TLRPC.PhotoSize currentPhotoObjectThumb = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, 80); + currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, drawImageButton ? AndroidUtilities.getPhotoSize() : maxPhotoWidth, !drawImageButton); + currentPhotoObjectThumb = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, 80); if (currentPhotoObjectThumb == currentPhotoObject) { currentPhotoObjectThumb = null; } @@ -595,11 +651,14 @@ public class ChatMessageCell extends ChatBaseCell { photoExist = false; } - String filter = String.format(Locale.US, "%d_%d", width, height); + currentPhotoFilter = String.format(Locale.US, "%d_%d", width, height); + currentPhotoFilterThumb = String.format(Locale.US, "%d_%d_b", width, height); if (photoExist || MediaController.getInstance().canDownloadMedia(MediaController.AUTODOWNLOAD_MASK_PHOTO) || FileLoader.getInstance().isLoadingFile(fileName)) { - linkImageView.setImage(currentPhotoObject.location, filter, currentPhotoObjectThumb != null ? currentPhotoObjectThumb.location : null, String.format(Locale.US, "%d_%d_b", width, height), 0, null, false); + photoNotSet = false; + linkImageView.setImage(currentPhotoObject.location, currentPhotoFilter, currentPhotoObjectThumb != null ? currentPhotoObjectThumb.location : null, currentPhotoFilterThumb, 0, null, false); } else { + photoNotSet = true; if (currentPhotoObjectThumb != null) { linkImageView.setImage(null, null, currentPhotoObjectThumb.location, String.format(Locale.US, "%d_%d_b", width, height), 0, null, false); } else { @@ -648,6 +707,7 @@ public class ChatMessageCell extends ChatBaseCell { } } } + updateButtonState(dataChanged); } @Override @@ -776,8 +836,17 @@ public class ChatMessageCell extends ChatBaseCell { linkImageView.setImageCoords(textX + backgroundWidth - AndroidUtilities.dp(77), smallImageStartY, linkImageView.getImageWidth(), linkImageView.getImageHeight()); } else { linkImageView.setImageCoords(textX + AndroidUtilities.dp(10), linkPreviewY, linkImageView.getImageWidth(), linkImageView.getImageHeight()); + if (drawImageButton) { + int size = AndroidUtilities.dp(48); + buttonX = (int) (linkImageView.getImageX() + (linkImageView.getImageWidth() - size) / 2.0f); + buttonY = (int) (linkImageView.getImageY() + (linkImageView.getImageHeight() - size) / 2.0f) + namesOffset; + radialProgress.setProgressRect(buttonX, buttonY, buttonX + AndroidUtilities.dp(48), buttonY + AndroidUtilities.dp(48)); + } } linkImageView.draw(canvas); + if (drawImageButton) { + radialProgress.draw(canvas); + } if (isInstagram && igvideoDrawable != null) { int x = linkImageView.getImageX() + linkImageView.getImageWidth() - igvideoDrawable.getIntrinsicWidth() - AndroidUtilities.dp(4); @@ -800,4 +869,99 @@ public class ChatMessageCell extends ChatBaseCell { } } } + + private Drawable getDrawableForCurrentState() { + if (buttonState >= 0 && buttonState < 4) { + if (buttonState == 1) { + return ResourceLoader.buttonStatesDrawables[4]; + } else { + return ResourceLoader.buttonStatesDrawables[buttonState]; + } + } + return null; + } + + public void updateButtonState(boolean animated) { + if (currentPhotoObject == null || !drawImageButton) { + return; + } + String fileName = FileLoader.getAttachFileName(currentPhotoObject); + File cacheFile = FileLoader.getPathToAttach(currentPhotoObject, true); + if (fileName == null) { + radialProgress.setBackground(null, false, false); + return; + } + if (!cacheFile.exists()) { + MediaController.getInstance().addLoadingFileObserver(fileName, this); + float setProgress = 0; + boolean progressVisible = false; + if (!FileLoader.getInstance().isLoadingFile(fileName)) { + if (cancelLoading || !MediaController.getInstance().canDownloadMedia(MediaController.AUTODOWNLOAD_MASK_PHOTO)) { + buttonState = 0; + } else { + progressVisible = true; + buttonState = 1; + } + } else { + progressVisible = true; + buttonState = 1; + Float progress = ImageLoader.getInstance().getFileProgress(fileName); + setProgress = progress != null ? progress : 0; + } + radialProgress.setProgress(setProgress, false); + radialProgress.setBackground(getDrawableForCurrentState(), progressVisible, animated); + invalidate(); + } else { + MediaController.getInstance().removeLoadingFileObserver(this); + buttonState = -1; + radialProgress.setBackground(getDrawableForCurrentState(), false, animated); + invalidate(); + } + } + + private void didPressedButton(boolean animated) { + if (buttonState == 0) { + cancelLoading = false; + radialProgress.setProgress(0, false); + linkImageView.setImage(currentPhotoObject.location, currentPhotoFilter, currentPhotoObjectThumb != null ? currentPhotoObjectThumb.location : null, currentPhotoFilterThumb, 0, null, false); + buttonState = 1; + radialProgress.setBackground(getDrawableForCurrentState(), true, animated); + invalidate(); + } else if (buttonState == 1) { + if (currentMessageObject.isOut() && currentMessageObject.isSending()) { + if (delegate != null) { + delegate.didPressedCancelSendButton(this); + } + } else { + cancelLoading = true; + linkImageView.cancelLoadImage(); + buttonState = 0; + radialProgress.setBackground(getDrawableForCurrentState(), false, animated); + invalidate(); + } + } + } + + @Override + public void onFailedDownload(String fileName) { + updateButtonState(false); + } + + @Override + public void onSuccessDownload(String fileName) { + radialProgress.setProgress(1, true); + if (!photoNotSet) { + updateButtonState(true); + } else { + setMessageObject(currentMessageObject); + } + } + + @Override + public void onProgressDownload(String fileName, float progress) { + radialProgress.setProgress(progress, true); + if (buttonState != 1) { + updateButtonState(false); + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMusicCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMusicCell.java index d85fe7747..bfc9aa8f3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMusicCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMusicCell.java @@ -32,7 +32,7 @@ import org.telegram.ui.Components.SeekBar; import java.io.File; -public class ChatMusicCell extends ChatBaseCell implements SeekBar.SeekBarDelegate, MediaController.FileDownloadProgressListener { +public class ChatMusicCell extends ChatBaseCell implements SeekBar.SeekBarDelegate { public interface ChatMusicCellDelegate { boolean needPlayMusic(MessageObject messageObject); @@ -62,13 +62,10 @@ public class ChatMusicCell extends ChatBaseCell implements SeekBar.SeekBarDelega private StaticLayout authorLayout; private int authorX; - private int TAG; - private ChatMusicCellDelegate musicDelegate; public ChatMusicCell(Context context) { super(context); - TAG = MediaController.getInstance().generateObserverTag(); seekBar = new SeekBar(context); seekBar.delegate = this; @@ -299,11 +296,6 @@ public class ChatMusicCell extends ChatBaseCell implements SeekBar.SeekBarDelega radialProgress.setProgress(progress, true); } - @Override - public int getObserverTag() { - return TAG; - } - @Override public void onSeekBarDrag(float progress) { if (currentMessageObject == null) { @@ -403,7 +395,7 @@ public class ChatMusicCell extends ChatBaseCell implements SeekBar.SeekBarDelega } else { timePaint.setColor(0xffa1aab3); } - radialProgress.onDraw(canvas); + radialProgress.draw(canvas); canvas.save(); canvas.translate(timeX + titleX, AndroidUtilities.dp(12) + namesOffset); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java index 8aaa67bea..d32a08b13 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java @@ -37,9 +37,11 @@ public class ProfileSearchCell extends BaseCell { private static TextPaint nameEncryptedPaint; private static TextPaint onlinePaint; private static TextPaint offlinePaint; + private static TextPaint countPaint; private static Drawable lockDrawable; private static Drawable broadcastDrawable; private static Drawable groupDrawable; + private static Drawable countDrawable; private static Paint linePaint; private CharSequence currentName; @@ -50,6 +52,7 @@ public class ProfileSearchCell extends BaseCell { private TLRPC.User user = null; private TLRPC.Chat chat = null; private TLRPC.EncryptedChat encryptedChat = null; + long dialog_id; private String lastName = null; private int lastStatus = 0; @@ -67,6 +70,13 @@ public class ProfileSearchCell extends BaseCell { private int nameLockLeft; private int nameLockTop; + private boolean drawCount; + private int lastUnreadCount; + private int countTop = AndroidUtilities.dp(25); + private int countLeft; + private int countWidth; + private StaticLayout countLayout; + private int onlineLeft; private StaticLayout onlineLayout; @@ -95,9 +105,15 @@ public class ProfileSearchCell extends BaseCell { linePaint = new Paint(); linePaint.setColor(0xffdcdcdc); + countPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG); + countPaint.setTextSize(AndroidUtilities.dp(13)); + countPaint.setColor(0xffffffff); + countPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + broadcastDrawable = getResources().getDrawable(R.drawable.list_broadcast); lockDrawable = getResources().getDrawable(R.drawable.list_secret); groupDrawable = getResources().getDrawable(R.drawable.list_group); + countDrawable = getResources().getDrawable(R.drawable.dialogs_badge); } avatarImage = new ImageReceiver(this); @@ -115,12 +131,13 @@ public class ProfileSearchCell extends BaseCell { return super.onTouchEvent(event); } - public void setData(TLRPC.User u, TLRPC.Chat c, TLRPC.EncryptedChat ec, CharSequence n, CharSequence s) { + public void setData(TLRPC.User u, TLRPC.Chat c, TLRPC.EncryptedChat ec, CharSequence n, CharSequence s, boolean needCount) { currentName = n; user = u; chat = c; encryptedChat = ec; subLabel = s; + drawCount = needCount; update(0); } @@ -162,6 +179,7 @@ public class ProfileSearchCell extends BaseCell { if (encryptedChat != null) { drawNameLock = true; + dialog_id = ((long) encryptedChat.id) << 32; if (!LocaleController.isRTL) { nameLockLeft = AndroidUtilities.dp(AndroidUtilities.leftBaseline); nameLeft = AndroidUtilities.dp(AndroidUtilities.leftBaseline + 4) + lockDrawable.getIntrinsicWidth(); @@ -173,9 +191,11 @@ public class ProfileSearchCell extends BaseCell { } else { if (chat != null) { if (chat.id < 0) { + dialog_id = AndroidUtilities.makeBroadcastId(chat.id); drawNameBroadcast = true; nameLockTop = AndroidUtilities.dp(28.5f); } else { + dialog_id = -chat.id; drawNameGroup = true; nameLockTop = AndroidUtilities.dp(30); } @@ -187,6 +207,7 @@ public class ProfileSearchCell extends BaseCell { nameLeft = AndroidUtilities.dp(11); } } else { + dialog_id = user.id; if (!LocaleController.isRTL) { nameLeft = AndroidUtilities.dp(AndroidUtilities.leftBaseline); } else { @@ -234,6 +255,30 @@ public class ProfileSearchCell extends BaseCell { nameWidth -= AndroidUtilities.dp(6) + groupDrawable.getIntrinsicWidth(); } + if (drawCount) { + TLRPC.TL_dialog dialog = MessagesController.getInstance().dialogs_dict.get(dialog_id); + if (dialog != null && dialog.unread_count != 0) { + lastUnreadCount = dialog.unread_count; + String countString = String.format("%d", dialog.unread_count); + countWidth = Math.max(AndroidUtilities.dp(12), (int) Math.ceil(countPaint.measureText(countString))); + countLayout = new StaticLayout(countString, countPaint, countWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); + int w = countWidth + AndroidUtilities.dp(18); + nameWidth -= w; + if (!LocaleController.isRTL) { + countLeft = getMeasuredWidth() - countWidth - AndroidUtilities.dp(19); + } else { + countLeft = AndroidUtilities.dp(19); + nameLeft += w; + } + } else { + lastUnreadCount = 0; + countLayout = null; + } + } else { + lastUnreadCount = 0; + countLayout = null; + } + CharSequence nameStringFinal = TextUtils.ellipsize(nameString, currentNamePaint, nameWidth - AndroidUtilities.dp(12), TextUtils.TruncateAt.END); nameLayout = new StaticLayout(nameStringFinal, currentNamePaint, nameWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); @@ -244,12 +289,12 @@ public class ProfileSearchCell extends BaseCell { onlineLeft = AndroidUtilities.dp(11); } - CharSequence onlineString; + CharSequence onlineString = ""; TextPaint currentOnlinePaint = offlinePaint; if (subLabel != null) { onlineString = subLabel; - } else { + } else if (user != null) { if ((user.flags & TLRPC.USER_FLAG_BOT) != 0) { onlineString = LocaleController.getString("Bot", R.string.Bot); } else { @@ -364,6 +409,12 @@ public class ProfileSearchCell extends BaseCell { continueUpdate = true; } } + if (!continueUpdate && drawCount && (mask & MessagesController.UPDATE_MASK_READ_DIALOG_MESSAGE) != 0) { + TLRPC.TL_dialog dialog = MessagesController.getInstance().dialogs_dict.get(dialog_id); + if (dialog != null && dialog.unread_count != lastUnreadCount) { + continueUpdate = true; + } + } if (!continueUpdate) { return; @@ -422,10 +473,12 @@ public class ProfileSearchCell extends BaseCell { broadcastDrawable.draw(canvas); } - canvas.save(); - canvas.translate(nameLeft, nameTop); - nameLayout.draw(canvas); - canvas.restore(); + if (nameLayout != null) { + canvas.save(); + canvas.translate(nameLeft, nameTop); + nameLayout.draw(canvas); + canvas.restore(); + } if (onlineLayout != null) { canvas.save(); @@ -434,6 +487,15 @@ public class ProfileSearchCell extends BaseCell { canvas.restore(); } + if (countLayout != null) { + setDrawableBounds(countDrawable, countLeft - AndroidUtilities.dp(5.5f), countTop, countWidth + AndroidUtilities.dp(11), countDrawable.getIntrinsicHeight()); + countDrawable.draw(canvas); + canvas.save(); + canvas.translate(countLeft, countTop + AndroidUtilities.dp(4)); + countLayout.draw(canvas); + canvas.restore(); + } + avatarImage.draw(canvas); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedLinkCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedLinkCell.java new file mode 100644 index 000000000..f69c00d9c --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedLinkCell.java @@ -0,0 +1,247 @@ +/* + * This is the source code of Telegram for Android v. 2.x.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2015. + */ + +package org.telegram.ui.Cells; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.text.TextUtils; +import android.view.View; + +import org.telegram.android.AndroidUtilities; +import org.telegram.android.ImageReceiver; +import org.telegram.android.MediaController; +import org.telegram.android.MessageObject; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.TLRPC; + +import java.io.File; +import java.util.Locale; + +public class SharedLinkCell extends View { + + private ImageReceiver linkImageView; + private boolean drawLinkImageView; + + private boolean needDivider; + + private int linkX; + private int linkY; + private StaticLayout linkLayout; + + private int titleX; + private int titleY; + private StaticLayout titleLayout; + + private int descriptionX; + private int descriptionY; + private StaticLayout descriptionLayout; + + private MessageObject message; + + private static TextPaint titleTextPaint; + private static TextPaint descriptionTextPaint; + private static Paint paint; + + public SharedLinkCell(Context context) { + super(context); + + if (titleTextPaint == null) { + titleTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + titleTextPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + titleTextPaint.setColor(0xff212121); + titleTextPaint.setTextSize(AndroidUtilities.dp(16)); + + descriptionTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + descriptionTextPaint.setTextSize(AndroidUtilities.dp(16)); + + paint = new Paint(); + paint.setColor(0xffd9d9d9); + paint.setStrokeWidth(1); + } + linkImageView = new ImageReceiver(this); + } + + @SuppressLint("DrawAllocation") + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + drawLinkImageView = false; + descriptionLayout = null; + titleLayout = null; + linkLayout = null; + descriptionX = 0; + titleX = 0; + linkX = 0; + + int maxWidth = MeasureSpec.getSize(widthMeasureSpec) - AndroidUtilities.dp(32); + + String title = ""; + String description = ""; + String link = ""; + boolean hasPhoto = false; + + if (message.messageOwner.media instanceof TLRPC.TL_messageMediaWebPage && message.messageOwner.media.webpage instanceof TLRPC.TL_webPage) { + TLRPC.TL_webPage webPage = (TLRPC.TL_webPage) message.messageOwner.media.webpage; + if (message.photoThumbs == null && webPage.photo != null) { + message.generateThumbs(true); + } + hasPhoto = webPage.photo != null && message.photoThumbs != null; + title = webPage.title; + if (title == null) { + title = webPage.site_name; + } + description = webPage.description; + link = webPage.url; + } + + if (title != null) { + try { + int width = (int) Math.ceil(titleTextPaint.measureText(title)); + CharSequence titleFinal = TextUtils.ellipsize(title.replace("\n", " "), titleTextPaint, Math.min(width, maxWidth - (hasPhoto ? AndroidUtilities.dp(56) : 0)), TextUtils.TruncateAt.END); + titleLayout = new StaticLayout(titleFinal, titleTextPaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + titleX = (int) Math.ceil(-titleLayout.getLineLeft(0)) + AndroidUtilities.dp(16); + titleY = AndroidUtilities.dp(8); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + + if (description != null) { + try { + descriptionLayout = ChatMessageCell.generateStaticLayout(description, descriptionTextPaint, maxWidth, maxWidth - (hasPhoto ? AndroidUtilities.dp(56) : 0), 2, 6); + int height = descriptionLayout.getLineBottom(descriptionLayout.getLineCount() - 1); + for (int a = 0; a < descriptionLayout.getLineCount(); a++) { + int lineLeft = (int) Math.ceil(descriptionLayout.getLineLeft(a)); + if (descriptionX == 0) { + descriptionX = -lineLeft; + } else { + descriptionX = Math.max(descriptionX, -lineLeft); + } + } + descriptionX += AndroidUtilities.dp(16); + descriptionY = AndroidUtilities.dp(28); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + + if (link != null) { + try { + int width = (int) Math.ceil(descriptionTextPaint.measureText(link)); + CharSequence linkFinal = TextUtils.ellipsize(link.replace("\n", " "), descriptionTextPaint, Math.min(width, maxWidth), TextUtils.TruncateAt.END); + linkLayout = new StaticLayout(linkFinal, descriptionTextPaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + linkX = (int) Math.ceil(-linkLayout.getLineLeft(0)) + AndroidUtilities.dp(16); + linkY = descriptionY; + if (descriptionLayout != null && descriptionLayout.getLineCount() != 0) { + linkY += descriptionLayout.getLineBottom(descriptionLayout.getLineCount() - 1) + AndroidUtilities.dp(1); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + + if (hasPhoto) { + int maxPhotoWidth = AndroidUtilities.dp(48); + TLRPC.PhotoSize currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(message.photoThumbs, maxPhotoWidth, true); + TLRPC.PhotoSize currentPhotoObjectThumb = FileLoader.getClosestPhotoSizeWithSize(message.photoThumbs, 80); + if (currentPhotoObjectThumb == currentPhotoObject) { + currentPhotoObjectThumb = null; + } + currentPhotoObject.size = -1; + if (currentPhotoObjectThumb != null) { + currentPhotoObjectThumb.size = -1; + } + linkImageView.setImageCoords(maxWidth - AndroidUtilities.dp(32), AndroidUtilities.dp(8), maxPhotoWidth, maxPhotoWidth); + String fileName = FileLoader.getAttachFileName(currentPhotoObject); + boolean photoExist = true; + File cacheFile = FileLoader.getPathToAttach(currentPhotoObject, true); + if (!cacheFile.exists()) { + photoExist = false; + } + String filter = String.format(Locale.US, "%d_%d", maxPhotoWidth, maxPhotoWidth); + if (photoExist || MediaController.getInstance().canDownloadMedia(MediaController.AUTODOWNLOAD_MASK_PHOTO) || FileLoader.getInstance().isLoadingFile(fileName)) { + linkImageView.setImage(currentPhotoObject.location, filter, currentPhotoObjectThumb != null ? currentPhotoObjectThumb.location : null, String.format(Locale.US, "%d_%d_b", maxPhotoWidth, maxPhotoWidth), 0, null, false); + } else { + if (currentPhotoObjectThumb != null) { + linkImageView.setImage(null, null, currentPhotoObjectThumb.location, String.format(Locale.US, "%d_%d_b", maxPhotoWidth, maxPhotoWidth), 0, null, false); + } else { + linkImageView.setImageBitmap((Drawable) null); + } + } + drawLinkImageView = true; + } + + int height = 0; + if (titleLayout != null && titleLayout.getLineCount() != 0) { + height += titleLayout.getLineBottom(titleLayout.getLineCount() - 1); + } + if (descriptionLayout != null && descriptionLayout.getLineCount() != 0) { + height += descriptionLayout.getLineBottom(descriptionLayout.getLineCount() - 1); + } + if (linkLayout != null && linkLayout.getLineCount() != 0) { + height += linkLayout.getLineBottom(linkLayout.getLineCount() - 1); + } + if (hasPhoto) { + height = Math.max(AndroidUtilities.dp(48), height); + } + + setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.makeMeasureSpec(height + AndroidUtilities.dp(16) + (needDivider ? 1 : 0), MeasureSpec.EXACTLY)); + } + + public void setLink(MessageObject messageObject, boolean divider) { + needDivider = divider; + message = messageObject; + + requestLayout(); + } + + public MessageObject getMessage() { + return message; + } + + @Override + protected void onDraw(Canvas canvas) { + if (titleLayout != null) { + canvas.save(); + canvas.translate(titleX, titleY); + titleLayout.draw(canvas); + canvas.restore(); + } + + if (descriptionLayout != null) { + descriptionTextPaint.setColor(0xff212121); + canvas.save(); + canvas.translate(descriptionX, descriptionY); + descriptionLayout.draw(canvas); + canvas.restore(); + } + + if (linkLayout != null) { + descriptionTextPaint.setColor(0xff316f9f); + canvas.save(); + canvas.translate(linkX, linkY); + linkLayout.draw(canvas); + canvas.restore(); + } + + if (drawLinkImageView) { + linkImageView.draw(canvas); + } + + if (needDivider) { + canvas.drawLine(0, getHeight() - 1, getWidth(), getHeight() - 1, paint); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerEmojiCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerEmojiCell.java index 1f5c47eeb..45fdd65cd 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerEmojiCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerEmojiCell.java @@ -9,8 +9,12 @@ package org.telegram.ui.Cells; import android.content.Context; +import android.graphics.Canvas; +import android.os.Build; import android.util.TypedValue; import android.view.Gravity; +import android.view.View; +import android.view.animation.AccelerateInterpolator; import android.widget.FrameLayout; import android.widget.TextView; @@ -26,6 +30,13 @@ public class StickerEmojiCell extends FrameLayout { private BackupImageView imageView; private TLRPC.Document sticker; private TextView emojiTextView; + private float alpha = 1; + private boolean changingAlpha; + private long lastUpdateTime; + private boolean scaled; + private float scale; + private long time = 0; + private AccelerateInterpolator interpolator = new AccelerateInterpolator(0.5f); public StickerEmojiCell(Context context) { super(context); @@ -39,15 +50,6 @@ public class StickerEmojiCell extends FrameLayout { addView(emojiTextView, LayoutHelper.createFrame(28, 28, Gravity.BOTTOM | Gravity.RIGHT)); } - @Override - public void setPressed(boolean pressed) { - if (imageView.getImageReceiver().getPressed() != pressed) { - imageView.getImageReceiver().setPressed(pressed); - imageView.invalidate(); - } - super.setPressed(pressed); - } - public TLRPC.Document getSticker() { return sticker; } @@ -57,7 +59,6 @@ public class StickerEmojiCell extends FrameLayout { sticker = document; imageView.setImage(document.thumb.location, null, "webp", null); - if (showEmoji) { boolean set = false; for (TLRPC.DocumentAttribute attribute : document.attributes) { @@ -78,4 +79,67 @@ public class StickerEmojiCell extends FrameLayout { } } } + + public void disable() { + changingAlpha = true; + alpha = 0.5f; + time = 0; + imageView.getImageReceiver().setAlpha(alpha); + imageView.invalidate(); + lastUpdateTime = System.currentTimeMillis(); + invalidate(); + } + + public void setScaled(boolean value) { + scaled = value; + lastUpdateTime = System.currentTimeMillis(); + invalidate(); + } + + public boolean isDisabled() { + return changingAlpha; + } + + public boolean showingBitmap() { + return imageView.getImageReceiver().getBitmap() != null; + } + + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + boolean result = super.drawChild(canvas, child, drawingTime); + if (child == imageView && (changingAlpha || scaled && scale != 0.8f || !scaled && scale != 1.0f)) { + long newTime = System.currentTimeMillis(); + long dt = (newTime - lastUpdateTime); + lastUpdateTime = newTime; + if (changingAlpha) { + time += dt; + if (time > 1050) { + time = 1050; + } + alpha = 0.5f + interpolator.getInterpolation(time / 1050.0f) * 0.5f; + if (alpha >= 1.0f) { + changingAlpha = false; + alpha = 1.0f; + } + imageView.getImageReceiver().setAlpha(alpha); + } else if (scaled && scale != 0.8f) { + scale -= dt / 400.0f; + if (scale < 0.8f) { + scale = 0.8f; + } + } else { + scale += dt / 400.0f; + if (scale > 1.0f) { + scale = 1.0f; + } + } + if (Build.VERSION.SDK_INT >= 11) { + imageView.setScaleX(scale); + imageView.setScaleY(scale); + } + imageView.invalidate(); + invalidate(); + } + return result; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java index 0077cde01..697d5c06b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java @@ -812,22 +812,30 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not @Override public void onRevealAnimationStart(boolean open) { - chatAttachView.onRevealAnimationStart(open); + if (chatAttachView != null) { + chatAttachView.onRevealAnimationStart(open); + } } @Override public void onRevealAnimationProgress(boolean open, float radius, int x, int y) { - chatAttachView.onRevealAnimationProgress(open, radius, x, y); + if (chatAttachView != null) { + chatAttachView.onRevealAnimationProgress(open, radius, x, y); + } } @Override public void onRevealAnimationEnd(boolean open) { - chatAttachView.onRevealAnimationEnd(open); + if (chatAttachView != null) { + chatAttachView.onRevealAnimationEnd(open); + } } @Override public void onOpenAnimationEnd() { - chatAttachView.onRevealAnimationEnd(true); + if (chatAttachView != null) { + chatAttachView.onRevealAnimationEnd(true); + } } @Override @@ -1379,17 +1387,15 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } catch (Exception e) { FileLog.e("tmessages", e); } - } else { - if (SecretPhotoViewer.getInstance().isVisible()) { - AndroidUtilities.runOnUIThread(new Runnable() { - @Override - public void run() { - chatListView.setOnItemLongClickListener(onItemLongClickListener); - chatListView.setLongClickable(true); - } - }); - SecretPhotoViewer.getInstance().closePhoto(); - } + } else if (SecretPhotoViewer.getInstance().isVisible()) { + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + chatListView.setOnItemLongClickListener(onItemLongClickListener); + chatListView.setLongClickable(true); + } + }); + SecretPhotoViewer.getInstance().closePhoto(); } } else if (event.getAction() != MotionEvent.ACTION_DOWN) { if (SecretPhotoViewer.getInstance().isVisible()) { @@ -1420,12 +1426,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not int x = (int) event.getX(); int y = (int) event.getY(); int count = chatListView.getChildCount(); - Rect rect = new Rect(); for (int a = 0; a < count; a++) { View view = chatListView.getChildAt(a); int top = view.getTop(); int bottom = view.getBottom(); - view.getLocalVisibleRect(rect); if (top > y || bottom < y) { continue; } @@ -3195,8 +3199,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (extractUriFrom.contains("com.google.android.apps.photos.contentprovider")) { try { String firstExtraction = extractUriFrom.split("/1/")[1]; - if (firstExtraction.contains("/ACTUAL")) { - firstExtraction = firstExtraction.replace("/ACTUAL", ""); + int index = firstExtraction.indexOf("/ACTUAL"); + if (index != -1) { + firstExtraction = firstExtraction.substring(0, index); String secondExtraction = URLDecoder.decode(firstExtraction, "UTF-8"); uri = Uri.parse(secondExtraction); } @@ -4067,9 +4072,11 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (cell.getMessageObject() != null && cell.getMessageObject().getId() == mid) { MessageObject playing = cell.getMessageObject(); MessageObject player = MediaController.getInstance().getPlayingMessageObject(); - playing.audioProgress = player.audioProgress; - playing.audioProgressSec = player.audioProgressSec; - cell.updateProgress(); + if (player != null) { + playing.audioProgress = player.audioProgress; + playing.audioProgressSec = player.audioProgressSec; + cell.updateProgress(); + } break; } } @@ -4823,6 +4830,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not moveScrollToLastMessage(); } } else if (option == 1) { + if (getParentActivity() == null) { + return; + } final MessageObject finalSelectedObject = selectedObject; AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); builder.setMessage(LocaleController.formatString("AreYouSureDeleteMessages", R.string.AreYouSureDeleteMessages, LocaleController.formatPluralString("messages", 1))); @@ -5125,8 +5135,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not MessageObject messageToOpen = null; ImageReceiver imageReceiver = null; View view = chatListView.getChildAt(a); - if (view instanceof ChatMediaCell) { - ChatMediaCell cell = (ChatMediaCell) view; + if (view instanceof ChatBaseCell) { + ChatBaseCell cell = (ChatBaseCell) view; MessageObject message = cell.getMessageObject(); if (message != null && message.getId() == messageObject.getId()) { messageToOpen = message; @@ -5359,96 +5369,96 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not public void didPressReplyMessage(ChatBaseCell cell, int id) { scrollToMessageId(id, cell.getMessageObject().getId(), true); } - }); - if (view instanceof ChatMediaCell) { - ((ChatMediaCell) view).setAllowedToSetPhoto(openAnimationEnded); - ((ChatMediaCell) view).setMediaDelegate(new ChatMediaCell.ChatMediaCellDelegate() { - @Override - public void didClickedImage(ChatMediaCell cell) { - MessageObject message = cell.getMessageObject(); - if (message.isSendError()) { - createMenu(cell, false); - return; - } else if (message.isSending()) { - return; - } - if (message.type == 1) { - PhotoViewer.getInstance().setParentActivity(getParentActivity()); - PhotoViewer.getInstance().openPhoto(message, ChatActivity.this); - } else if (message.type == 3) { - sendSecretMessageRead(message); - try { - File f = null; - if (message.messageOwner.attachPath != null && message.messageOwner.attachPath.length() != 0) { - f = new File(message.messageOwner.attachPath); - } - if (f == null || f != null && !f.exists()) { - f = FileLoader.getPathToMessage(message.messageOwner); - } - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setDataAndType(Uri.fromFile(f), "video/mp4"); - getParentActivity().startActivityForResult(intent, 500); - } catch (Exception e) { - alertUserOpenError(message); - } - } else if (message.type == 4) { - if (!isGoogleMapsInstalled()) { - return; - } - LocationActivity fragment = new LocationActivity(); - fragment.setMessageObject(message); - presentFragment(fragment); - } else if (message.type == 9) { + + @Override + public void didClickedImage(ChatBaseCell cell) { + MessageObject message = cell.getMessageObject(); + if (message.isSendError()) { + createMenu(cell, false); + return; + } else if (message.isSending()) { + return; + } + if (message.type == 1 || message.type == 0) { + PhotoViewer.getInstance().setParentActivity(getParentActivity()); + PhotoViewer.getInstance().openPhoto(message, ChatActivity.this); + } else if (message.type == 3) { + sendSecretMessageRead(message); + try { File f = null; - String fileName = message.getFileName(); if (message.messageOwner.attachPath != null && message.messageOwner.attachPath.length() != 0) { f = new File(message.messageOwner.attachPath); } if (f == null || f != null && !f.exists()) { f = FileLoader.getPathToMessage(message.messageOwner); } - if (f != null && f.exists()) { - String realMimeType = null; - try { - Intent intent = new Intent(Intent.ACTION_VIEW); - if (message.type == 8 || message.type == 9) { - MimeTypeMap myMime = MimeTypeMap.getSingleton(); - int idx = fileName.lastIndexOf("."); - if (idx != -1) { - String ext = fileName.substring(idx + 1); - realMimeType = myMime.getMimeTypeFromExtension(ext.toLowerCase()); - if (realMimeType == null) { - realMimeType = message.messageOwner.media.document.mime_type; - if (realMimeType == null || realMimeType.length() == 0) { - realMimeType = null; - } - } - if (realMimeType != null) { - intent.setDataAndType(Uri.fromFile(f), realMimeType); - } else { - intent.setDataAndType(Uri.fromFile(f), "text/plain"); + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setDataAndType(Uri.fromFile(f), "video/mp4"); + getParentActivity().startActivityForResult(intent, 500); + } catch (Exception e) { + alertUserOpenError(message); + } + } else if (message.type == 4) { + if (!isGoogleMapsInstalled()) { + return; + } + LocationActivity fragment = new LocationActivity(); + fragment.setMessageObject(message); + presentFragment(fragment); + } else if (message.type == 9) { + File f = null; + String fileName = message.getFileName(); + if (message.messageOwner.attachPath != null && message.messageOwner.attachPath.length() != 0) { + f = new File(message.messageOwner.attachPath); + } + if (f == null || f != null && !f.exists()) { + f = FileLoader.getPathToMessage(message.messageOwner); + } + if (f != null && f.exists()) { + String realMimeType = null; + try { + Intent intent = new Intent(Intent.ACTION_VIEW); + if (message.type == 8 || message.type == 9) { + MimeTypeMap myMime = MimeTypeMap.getSingleton(); + int idx = fileName.lastIndexOf("."); + if (idx != -1) { + String ext = fileName.substring(idx + 1); + realMimeType = myMime.getMimeTypeFromExtension(ext.toLowerCase()); + if (realMimeType == null) { + realMimeType = message.messageOwner.media.document.mime_type; + if (realMimeType == null || realMimeType.length() == 0) { + realMimeType = null; } + } + if (realMimeType != null) { + intent.setDataAndType(Uri.fromFile(f), realMimeType); } else { intent.setDataAndType(Uri.fromFile(f), "text/plain"); } - } - if (realMimeType != null) { - try { - getParentActivity().startActivityForResult(intent, 500); - } catch (Exception e) { - intent.setDataAndType(Uri.fromFile(f), "text/plain"); - getParentActivity().startActivityForResult(intent, 500); - } } else { + intent.setDataAndType(Uri.fromFile(f), "text/plain"); + } + } + if (realMimeType != null) { + try { + getParentActivity().startActivityForResult(intent, 500); + } catch (Exception e) { + intent.setDataAndType(Uri.fromFile(f), "text/plain"); getParentActivity().startActivityForResult(intent, 500); } - } catch (Exception e) { - alertUserOpenError(message); + } else { + getParentActivity().startActivityForResult(intent, 500); } + } catch (Exception e) { + alertUserOpenError(message); } } } - + } + }); + if (view instanceof ChatMediaCell) { + ((ChatMediaCell) view).setAllowedToSetPhoto(openAnimationEnded); + ((ChatMediaCell) view).setMediaDelegate(new ChatMediaCell.ChatMediaCellDelegate() { @Override public void didPressedOther(ChatMediaCell cell) { createMenu(cell, true); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachView.java index 3b175e09a..bbfbf049a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachView.java @@ -33,6 +33,7 @@ import org.telegram.android.NotificationCenter; import org.telegram.android.support.widget.LinearLayoutManager; import org.telegram.messenger.R; import org.telegram.ui.Adapters.PhotoAttachAdapter; +import org.telegram.ui.Cells.PhotoAttachPhotoCell; import org.telegram.ui.ChatActivity; import java.util.ArrayList; @@ -130,6 +131,12 @@ public class ChatAttachView extends FrameLayout implements NotificationCenter.No updatePhotosButton(); } }); + attachPhotoRecyclerView.setOnItemClickListener(new RecyclerListView.OnItemClickListener() { + @Override + public void onItemClick(View view, int position) { + photoAttachAdapter.onItemClick((PhotoAttachPhotoCell) view); + } + }); views[9] = progressView = new EmptyTextProgressView(context); progressView.setText(LocaleController.getString("NoPhotos", R.string.NoPhotos)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java index 683ad8e31..8a150ea8c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java @@ -15,7 +15,6 @@ import android.database.DataSetObserver; import android.os.Build; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; -import android.text.TextUtils; import android.util.TypedValue; import android.view.Gravity; import android.view.HapticFeedbackConstants; @@ -23,6 +22,7 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; +import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.FrameLayout; import android.widget.GridView; @@ -41,6 +41,7 @@ import org.telegram.messenger.R; import org.telegram.messenger.TLRPC; import org.telegram.ui.Cells.EmptyCell; import org.telegram.ui.Cells.StickerEmojiCell; +import org.telegram.ui.StickerPreviewViewer; import java.util.ArrayList; import java.util.Collections; @@ -56,8 +57,11 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific } private ArrayList adapters = new ArrayList<>(); + private HashMap emojiUseHistory = new HashMap<>(); + private ArrayList recentEmoji = new ArrayList<>(); private HashMap stickersUseHistory = new HashMap<>(); private ArrayList recentStickers = new ArrayList<>(); + private HashMap stickerSetsUseCount = new HashMap<>(); private ArrayList stickerSets = new ArrayList<>(); private int[] icons = { @@ -78,6 +82,13 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific private StickersGridAdapter stickersGridAdapter; private LinearLayout pagerSlidingTabStripContainer; private ScrollSlidingTabStrip scrollSlidingTabStrip; + private GridView stickersGridView; + private AdapterView.OnItemClickListener stickersOnItemClickListener; + private Runnable openStickerPreviewRunnable; + private StickerEmojiCell currentStickerPreviewCell; + + private int startX; + private int startY; private int oldWidth; private int lastNotifyWidth; @@ -91,7 +102,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific showStickers = needStickers; - for (int i = 0; i < Emoji.data.length; i++) { + for (int i = 0; i < Emoji.data.length + 1; i++) { GridView gridView = new GridView(context); if (AndroidUtilities.isTablet()) { gridView.setColumnWidth(AndroidUtilities.dp(60)); @@ -101,7 +112,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific gridView.setNumColumns(-1); views.add(gridView); - EmojiGridAdapter emojiGridAdapter = new EmojiGridAdapter(Emoji.data[i]); + EmojiGridAdapter emojiGridAdapter = new EmojiGridAdapter(i - 1); gridView.setAdapter(emojiGridAdapter); AndroidUtilities.setListViewEdgeEffectColor(gridView, 0xfff5f6f7); adapters.add(emojiGridAdapter); @@ -109,25 +120,187 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific if (showStickers) { StickersQuery.checkStickers(); - GridView gridView = new GridView(context); - gridView.setColumnWidth(AndroidUtilities.dp(72)); - gridView.setNumColumns(-1); - gridView.setPadding(0, AndroidUtilities.dp(4), 0, 0); - gridView.setClipToPadding(false); - views.add(gridView); + stickersGridView = new GridView(context) { + @Override + public boolean onInterceptTouchEvent(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + int x = (int) event.getX(); + int y = (int) event.getY(); + int count = stickersGridView.getChildCount(); + for (int a = 0; a < count; a++) { + View view = stickersGridView.getChildAt(a); + int top = view.getTop(); + int bottom = view.getBottom(); + int left = view.getLeft(); + int right = view.getRight(); + if (top > y || bottom < y || left > x || right < x) { + continue; + } + if (!(view instanceof StickerEmojiCell) || !((StickerEmojiCell) view).showingBitmap()) { + return super.onInterceptTouchEvent(event); + } + startX = x; + startY = y; + currentStickerPreviewCell = (StickerEmojiCell) view; + openStickerPreviewRunnable = new Runnable() { + @Override + public void run() { + if (openStickerPreviewRunnable == null) { + return; + } + stickersGridView.setOnItemClickListener(null); + stickersGridView.requestDisallowInterceptTouchEvent(true); + openStickerPreviewRunnable = null; + StickerPreviewViewer.getInstance().setParentActivity((Activity) getContext()); + StickerPreviewViewer.getInstance().setKeyboardHeight(EmojiView.this.getMeasuredHeight()); + StickerPreviewViewer.getInstance().open(currentStickerPreviewCell.getSticker()); + currentStickerPreviewCell.setScaled(true); + } + }; + AndroidUtilities.runOnUIThread(openStickerPreviewRunnable, 200); + return true; + } + } + return false; + } + }; + stickersGridView.setSelector(R.drawable.transparent); + stickersGridView.setColumnWidth(AndroidUtilities.dp(72)); + stickersGridView.setNumColumns(-1); + stickersGridView.setPadding(0, AndroidUtilities.dp(4), 0, 0); + stickersGridView.setClipToPadding(false); + views.add(stickersGridView); stickersGridAdapter = new StickersGridAdapter(context); - gridView.setAdapter(stickersGridAdapter); - AndroidUtilities.setListViewEdgeEffectColor(gridView, 0xfff5f6f7); + stickersGridView.setAdapter(stickersGridAdapter); + stickersGridView.setOnTouchListener(new OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + if (openStickerPreviewRunnable != null || StickerPreviewViewer.getInstance().isVisible()) { + if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_POINTER_UP) { + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + stickersGridView.setOnItemClickListener(stickersOnItemClickListener); + } + }, 150); + if (openStickerPreviewRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(openStickerPreviewRunnable); + openStickerPreviewRunnable = null; + } else if (StickerPreviewViewer.getInstance().isVisible()) { + StickerPreviewViewer.getInstance().close(); + if (currentStickerPreviewCell != null) { + currentStickerPreviewCell.setScaled(false); + currentStickerPreviewCell = null; + } + } + } else if (event.getAction() != MotionEvent.ACTION_DOWN) { + if (StickerPreviewViewer.getInstance().isVisible()) { + if (event.getAction() == MotionEvent.ACTION_MOVE) { + int x = (int) event.getX(); + int y = (int) event.getY(); + int count = stickersGridView.getChildCount(); + for (int a = 0; a < count; a++) { + View view = stickersGridView.getChildAt(a); + int top = view.getTop(); + int bottom = view.getBottom(); + int left = view.getLeft(); + int right = view.getRight(); + if (top > y || bottom < y || left > x || right < x) { + continue; + } + if (!(view instanceof StickerEmojiCell) || view == currentStickerPreviewCell) { + break; + } + if (currentStickerPreviewCell != null) { + currentStickerPreviewCell.setScaled(false); + } + currentStickerPreviewCell = (StickerEmojiCell) view; + StickerPreviewViewer.getInstance().setKeyboardHeight(EmojiView.this.getMeasuredHeight()); + StickerPreviewViewer.getInstance().open(currentStickerPreviewCell.getSticker()); + currentStickerPreviewCell.setScaled(true); + return true; + } + } + return true; + } else if (openStickerPreviewRunnable != null) { + if (event.getAction() == MotionEvent.ACTION_MOVE) { + if (Math.hypot(startX - event.getX(), startY - event.getY()) > AndroidUtilities.dp(10)) { + AndroidUtilities.cancelRunOnUIThread(openStickerPreviewRunnable); + openStickerPreviewRunnable = null; + } + } else { + AndroidUtilities.cancelRunOnUIThread(openStickerPreviewRunnable); + openStickerPreviewRunnable = null; + } + } + } + } + return false; + } + }); + stickersOnItemClickListener = new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long i) { + if (!(view instanceof StickerEmojiCell)) { + return; + } + if (openStickerPreviewRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(openStickerPreviewRunnable); + openStickerPreviewRunnable = null; + } + if (currentStickerPreviewCell != null) { + currentStickerPreviewCell.setScaled(false); + currentStickerPreviewCell = null; + } + StickerEmojiCell cell = (StickerEmojiCell) view; + if (cell.isDisabled()) { + return; + } + cell.disable(); + TLRPC.Document document = cell.getSticker(); + Integer count = stickersUseHistory.get(document.id); + if (count == null) { + count = 0; + } + if (count == 0 && stickersUseHistory.size() > 19) { + for (int a = recentStickers.size() - 1; a >= 0; a--) { + TLRPC.Document sticker = recentStickers.get(a); + stickersUseHistory.remove(sticker.id); + recentStickers.remove(a); + if (stickersUseHistory.size() <= 19) { + break; + } + } + } + stickersUseHistory.put(document.id, ++count); + + long id = StickersQuery.getStickerSetId(document); + if (id != -1) { + count = stickerSetsUseCount.get(id); + if (count == null) { + count = 0; + } + stickerSetsUseCount.put(id, ++count); + } + + saveRecentStickers(); + if (listener != null) { + listener.onStickerSelected(document); + } + } + }; + stickersGridView.setOnItemClickListener(stickersOnItemClickListener); + AndroidUtilities.setListViewEdgeEffectColor(stickersGridView, 0xfff5f6f7); stickersWrap = new FrameLayout(context); - stickersWrap.addView(gridView); + stickersWrap.addView(stickersGridView); TextView textView = new TextView(context); textView.setText(LocaleController.getString("NoStickers", R.string.NoStickers)); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); textView.setTextColor(0xff888888); stickersWrap.addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); - gridView.setEmptyView(textView); + stickersGridView.setEmptyView(textView); scrollSlidingTabStrip = new ScrollSlidingTabStrip(context) { @@ -218,7 +391,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific } }); - gridView.setOnScrollListener(new AbsListView.OnScrollListener() { + stickersGridView.setOnScrollListener(new AbsListView.OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { @@ -304,7 +477,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific } else if (event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_UP) { backspacePressed = false; if (!backspaceOnce) { - if (EmojiView.this.listener != null && EmojiView.this.listener.onBackspace()) { + if (listener != null && listener.onBackspace()) { backspaceButton.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); } } @@ -383,7 +556,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific if (!backspacePressed) { return; } - if (EmojiView.this.listener != null && EmojiView.this.listener.onBackspace()) { + if (listener != null && listener.onBackspace()) { backspaceButton.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); } backspaceOnce = true; @@ -392,33 +565,6 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific }, time); } - private void addToRecent(long code) { - if (pager.getCurrentItem() == 0) { - return; - } - ArrayList recent = new ArrayList<>(); - long[] currentRecent = Emoji.data[0]; - boolean was = false; - for (long aCurrentRecent : currentRecent) { - if (code == aCurrentRecent) { - recent.add(0, code); - was = true; - } else { - recent.add(aCurrentRecent); - } - } - if (!was) { - recent.add(0, code); - } - Emoji.data[0] = new long[Math.min(recent.size(), 50)]; - for (int q = 0; q < Emoji.data[0].length; q++) { - Emoji.data[0][q] = recent.get(q); - } - adapters.get(0).data = Emoji.data[0]; - adapters.get(0).notifyDataSetChanged(); - saveRecents(); - } - private String convert(long paramLong) { String str = ""; for (int i = 0; ; i++) { @@ -432,16 +578,22 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific } } - private void saveRecents() { - ArrayList arrayList = new ArrayList<>(Emoji.data[0].length); - for (int j = 0; j < Emoji.data[0].length; j++) { - arrayList.add(Emoji.data[0][j]); + private void saveRecentEmoji() { + SharedPreferences preferences = getContext().getSharedPreferences("emoji", Activity.MODE_PRIVATE); + StringBuilder stringBuilder = new StringBuilder(); + for (HashMap.Entry entry : emojiUseHistory.entrySet()) { + if (stringBuilder.length() != 0) { + stringBuilder.append(","); + } + stringBuilder.append(entry.getKey()); + stringBuilder.append("="); + stringBuilder.append(entry.getValue()); } - getContext().getSharedPreferences("emoji", 0).edit().putString("recents", TextUtils.join(",", arrayList)).commit(); + preferences.edit().putString("emojis", stringBuilder.toString()).commit(); } private void saveRecentStickers() { - SharedPreferences preferences = getContext().getSharedPreferences("emoji", Activity.MODE_PRIVATE); + SharedPreferences.Editor editor = getContext().getSharedPreferences("emoji", Activity.MODE_PRIVATE).edit(); StringBuilder stringBuilder = new StringBuilder(); for (HashMap.Entry entry : stickersUseHistory.entrySet()) { if (stringBuilder.length() != 0) { @@ -451,7 +603,64 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific stringBuilder.append("="); stringBuilder.append(entry.getValue()); } - preferences.edit().putString("stickers", stringBuilder.toString()).commit(); + editor.putString("stickers", stringBuilder.toString()); + + ArrayList toRemove = null; + for (HashMap.Entry entry : stickerSetsUseCount.entrySet()) { + if (!StickersQuery.isStickerPackInstalled(entry.getKey())) { + if (toRemove == null) { + toRemove = new ArrayList<>(); + } + toRemove.add(entry.getKey()); + } + } + if (toRemove != null) { + for (int a = 0; a < toRemove.size(); a++) { + stickerSetsUseCount.remove(toRemove.get(a)); + } + } + + stringBuilder.setLength(0); + for (HashMap.Entry entry : stickerSetsUseCount.entrySet()) { + if (stringBuilder.length() != 0) { + stringBuilder.append(","); + } + stringBuilder.append(entry.getKey()); + stringBuilder.append("="); + stringBuilder.append(entry.getValue()); + } + editor.putString("sets", stringBuilder.toString()); + + editor.commit(); + } + + private void sortEmoji() { + recentEmoji.clear(); + for (HashMap.Entry entry : emojiUseHistory.entrySet()) { + recentEmoji.add(entry.getKey()); + } + Collections.sort(recentEmoji, new Comparator() { + @Override + public int compare(Long lhs, Long rhs) { + Integer count1 = emojiUseHistory.get(lhs); + Integer count2 = emojiUseHistory.get(rhs); + if (count1 == null) { + count1 = 0; + } + if (count2 == null) { + count2 = 0; + } + if (count1 > count2) { + return -1; + } else if (count1 < count2) { + return 1; + } + return 0; + } + }); + while (recentEmoji.size() > 50) { + recentEmoji.remove(recentEmoji.size() - 1); + } } private void sortStickers() { @@ -468,11 +677,9 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific hashMap.put(sticker.id, entry.getValue()); } } - if (!stickersUseHistory.isEmpty()) { - stickersUseHistory = hashMap; - saveRecents(); - } else { + if (stickersUseHistory.size() != hashMap.size()) { stickersUseHistory = hashMap; + saveRecentStickers(); } Collections.sort(recentStickers, new Comparator() { @Override @@ -512,33 +719,80 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific continue; } stickerSets.add(pack); - scrollSlidingTabStrip.addStickerTab(pack.documents.get(0)); + } + Collections.sort(stickerSets, new Comparator() { + @Override + public int compare(TLRPC.TL_messages_stickerSet lhs, TLRPC.TL_messages_stickerSet rhs) { + Integer count1 = stickerSetsUseCount.get(lhs.set.id); + Integer count2 = stickerSetsUseCount.get(rhs.set.id); + if (count1 == null) { + count1 = 0; + } + if (count2 == null) { + count2 = 0; + } + if (count1 > count2) { + return -1; + } else if (count1 < count2) { + return 1; + } + return 0; + } + }); + for (int a = 0; a < stickerSets.size(); a++) { + scrollSlidingTabStrip.addStickerTab(stickerSets.get(a).documents.get(0)); } scrollSlidingTabStrip.updateTabStyles(); } public void loadRecents() { + String str; SharedPreferences preferences = getContext().getSharedPreferences("emoji", Activity.MODE_PRIVATE); - String str = preferences.getString("recents", ""); - try { - if (str != null && str.length() > 0) { - String[] args = str.split(","); - Emoji.data[0] = new long[args.length]; - for (int i = 0; i < args.length; i++) { - Emoji.data[0][i] = Long.parseLong(args[i]); + + if (preferences.contains("recents")) { + try { + str = preferences.getString("recents", ""); + if (str != null && str.length() > 0) { + String[] args = str.split(","); + for (int i = 0; i < args.length; i++) { + emojiUseHistory.put(Long.parseLong(args[i]), args.length - i); + } } - } else { - Emoji.data[0] = new long[]{0x00000000D83DDE02L, 0x00000000D83DDE18L, 0x0000000000002764L, 0x00000000D83DDE0DL, 0x00000000D83DDE0AL, 0x00000000D83DDE01L, - 0x00000000D83DDC4DL, 0x000000000000263AL, 0x00000000D83DDE14L, 0x00000000D83DDE04L, 0x00000000D83DDE2DL, 0x00000000D83DDC8BL, - 0x00000000D83DDE12L, 0x00000000D83DDE33L, 0x00000000D83DDE1CL, 0x00000000D83DDE48L, 0x00000000D83DDE09L, 0x00000000D83DDE03L, - 0x00000000D83DDE22L, 0x00000000D83DDE1DL, 0x00000000D83DDE31L, 0x00000000D83DDE21L, 0x00000000D83DDE0FL, 0x00000000D83DDE1EL, - 0x00000000D83DDE05L, 0x00000000D83DDE1AL, 0x00000000D83DDE4AL, 0x00000000D83DDE0CL, 0x00000000D83DDE00L, 0x00000000D83DDE0BL, - 0x00000000D83DDE06L, 0x00000000D83DDC4CL, 0x00000000D83DDE10L, 0x00000000D83DDE15L}; + } catch (Exception e) { + FileLog.e("tmessages", e); } - adapters.get(0).data = Emoji.data[0]; + sortEmoji(); + preferences.edit().remove("recents").commit(); + saveRecentEmoji(); adapters.get(0).notifyDataSetChanged(); - } catch (Exception e) { - FileLog.e("tmessages", e); + } else { + try { + emojiUseHistory.clear(); + str = preferences.getString("emojis", ""); + if (str != null && str.length() > 0) { + String[] args = str.split(","); + for (String arg : args) { + String[] args2 = arg.split("="); + emojiUseHistory.put(Long.parseLong(args2[0]), Integer.parseInt(args2[1])); + } + } + if (emojiUseHistory.isEmpty()) { + long[] newRecent = new long[]{0x00000000D83DDE02L, 0x00000000D83DDE18L, 0x0000000000002764L, 0x00000000D83DDE0DL, 0x00000000D83DDE0AL, 0x00000000D83DDE01L, + 0x00000000D83DDC4DL, 0x000000000000263AL, 0x00000000D83DDE14L, 0x00000000D83DDE04L, 0x00000000D83DDE2DL, 0x00000000D83DDC8BL, + 0x00000000D83DDE12L, 0x00000000D83DDE33L, 0x00000000D83DDE1CL, 0x00000000D83DDE48L, 0x00000000D83DDE09L, 0x00000000D83DDE03L, + 0x00000000D83DDE22L, 0x00000000D83DDE1DL, 0x00000000D83DDE31L, 0x00000000D83DDE21L, 0x00000000D83DDE0FL, 0x00000000D83DDE1EL, + 0x00000000D83DDE05L, 0x00000000D83DDE1AL, 0x00000000D83DDE4AL, 0x00000000D83DDE0CL, 0x00000000D83DDE00L, 0x00000000D83DDE0BL, + 0x00000000D83DDE06L, 0x00000000D83DDC4CL, 0x00000000D83DDE10L, 0x00000000D83DDE15L}; + for (int i = 0; i < newRecent.length; i++) { + emojiUseHistory.put(newRecent[i], newRecent.length - i); + } + saveRecentEmoji(); + } + sortEmoji(); + adapters.get(0).notifyDataSetChanged(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } } if (showStickers) { @@ -552,6 +806,16 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific stickersUseHistory.put(Long.parseLong(args2[0]), Integer.parseInt(args2[1])); } } + + stickerSetsUseCount.clear(); + str = preferences.getString("sets", ""); + if (str != null && str.length() > 0) { + String[] args = str.split(","); + for (String arg : args) { + String[] args2 = arg.split("="); + stickerSetsUseCount.put(Long.parseLong(args2[0]), Integer.parseInt(args2[1])); + } + } sortStickers(); updateStickerTabs(); } catch (Exception e) { @@ -586,13 +850,28 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific protected void onLayout(boolean changed, int left, int top, int right, int bottom) { if (lastNotifyWidth != right - left) { lastNotifyWidth = right - left; - if (stickersGridAdapter != null) { - stickersGridAdapter.notifyDataSetChanged(); - } + reloadStickersAdapter(); } super.onLayout(changed, left, top, right, bottom); } + private void reloadStickersAdapter() { + if (stickersGridAdapter != null) { + stickersGridAdapter.notifyDataSetChanged(); + } + if (StickerPreviewViewer.getInstance().isVisible()) { + StickerPreviewViewer.getInstance().close(); + } + if (openStickerPreviewRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(openStickerPreviewRunnable); + openStickerPreviewRunnable = null; + } + if (currentStickerPreviewCell != null) { + currentStickerPreviewCell.setScaled(false); + currentStickerPreviewCell = null; + } + } + public void setListener(Listener value) { listener = value; } @@ -616,11 +895,15 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific @Override public void setVisibility(int visibility) { super.setVisibility(visibility); - if (visibility != GONE && stickersGridAdapter != null) { - NotificationCenter.getInstance().addObserver(this, NotificationCenter.stickersDidLoaded); - sortStickers(); - updateStickerTabs(); - stickersGridAdapter.notifyDataSetChanged(); + if (visibility != GONE) { + sortEmoji(); + adapters.get(0).notifyDataSetChanged(); + if (stickersGridAdapter != null) { + NotificationCenter.getInstance().addObserver(this, NotificationCenter.stickersDidLoaded); + sortStickers(); + updateStickerTabs(); + reloadStickersAdapter(); + } } } @@ -636,7 +919,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific public void didReceivedNotification(int id, Object... args) { if (id == NotificationCenter.stickersDidLoaded) { updateStickerTabs(); - stickersGridAdapter.notifyDataSetChanged(); + reloadStickersAdapter(); } } @@ -717,31 +1000,6 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(82), MeasureSpec.EXACTLY)); } }; - view.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - if (listener != null) { - TLRPC.Document document = ((StickerEmojiCell) v).getSticker(); - Integer count = stickersUseHistory.get(document.id); - if (count == null) { - count = 0; - } - if (count == 0 && stickersUseHistory.size() > 19) { - for (int a = recentStickers.size() - 1; a >= 0; a--) { - TLRPC.Document sticker = recentStickers.get(a); - stickersUseHistory.remove(sticker.id); - recentStickers.remove(a); - if (stickersUseHistory.size() <= 19) { - break; - } - } - } - stickersUseHistory.put(document.id, ++count); - saveRecentStickers(); - listener.onStickerSelected(document); - } - } - }); } ((StickerEmojiCell) view).setSticker(sticker, false); } else { @@ -811,45 +1069,76 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific } private class EmojiGridAdapter extends BaseAdapter { - long[] data; - public EmojiGridAdapter(long[] arg2) { - this.data = arg2; + private int emojiPage; + + public EmojiGridAdapter(int page) { + emojiPage = page; } public int getCount() { - return data.length; + if (emojiPage == -1) { + return recentEmoji.size(); + } + return Emoji.data[emojiPage].length; } public Object getItem(int i) { return null; } - public long getItemId(int i) { - return data[i]; + @Override + public long getItemId(int position) { + return position; } public View getView(int i, View view, ViewGroup paramViewGroup) { ImageView imageView = (ImageView)view; if (imageView == null) { - imageView = new ImageView(EmojiView.this.getContext()) { - public void onMeasure(int paramAnonymousInt1, int paramAnonymousInt2) { - setMeasuredDimension(View.MeasureSpec.getSize(paramAnonymousInt1), View.MeasureSpec.getSize(paramAnonymousInt1)); + imageView = new ImageView(getContext()) { + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(View.MeasureSpec.getSize(widthMeasureSpec), View.MeasureSpec.getSize(widthMeasureSpec)); } }; imageView.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { - if (EmojiView.this.listener != null) { - EmojiView.this.listener.onEmojiSelected(EmojiView.this.convert((Long)view.getTag())); + Long code = (Long) view.getTag(); + Integer count = emojiUseHistory.get(code); + if (count == null) { + count = 0; + } + if (count == 0 && emojiUseHistory.size() > 50) { + for (int a = recentEmoji.size() - 1; a >= 0; a--) { + Long emoji = recentEmoji.get(a); + emojiUseHistory.remove(emoji); + recentEmoji.remove(a); + if (emojiUseHistory.size() <= 50) { + break; + } + } + } + emojiUseHistory.put(code, ++count); + if (pager.getCurrentItem() != 0) { + sortEmoji(); + } + saveRecentEmoji(); + adapters.get(0).notifyDataSetChanged(); + if (listener != null) { + listener.onEmojiSelected(convert((Long)view.getTag())); } - EmojiView.this.addToRecent((Long)view.getTag()); } }); imageView.setBackgroundResource(R.drawable.list_selector); imageView.setScaleType(ImageView.ScaleType.CENTER); } - imageView.setImageDrawable(Emoji.getEmojiBigDrawable(data[i])); - imageView.setTag(data[i]); + long code; + if (emojiPage == -1) { + code = recentEmoji.get(i); + } else { + code = Emoji.data[emojiPage][i]; + } + imageView.setImageDrawable(Emoji.getEmojiBigDrawable(code)); + imageView.setTag(code); return imageView; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/RadialProgress.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/RadialProgress.java index 8826b64a2..ced401ddc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/RadialProgress.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RadialProgress.java @@ -135,7 +135,7 @@ public class RadialProgress { return previousDrawable != null || currentDrawable != null ? animatedAlphaValue : 0.0f; } - public void onDraw(Canvas canvas) { + public void draw(Canvas canvas) { if (previousDrawable != null) { previousDrawable.setAlpha((int)(255 * animatedAlphaValue)); previousDrawable.setBounds((int)progressRect.left, (int)progressRect.top, (int)progressRect.right, (int)progressRect.bottom); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java index 3362c6714..eded5c252 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java @@ -52,6 +52,7 @@ import org.telegram.ui.Adapters.DialogsAdapter; import org.telegram.ui.Adapters.DialogsSearchAdapter; import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; import org.telegram.android.AnimationCompat.ViewProxy; +import org.telegram.ui.Cells.ProfileSearchCell; import org.telegram.ui.Cells.UserCell; import org.telegram.ui.Cells.DialogCell; import org.telegram.ui.ActionBar.ActionBar; @@ -226,7 +227,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. } } if (dialogsSearchAdapter != null) { - dialogsSearchAdapter.searchDialogs(null, dialogsType); + dialogsSearchAdapter.searchDialogs(null); } updatePasscodeButton(); } @@ -234,7 +235,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. @Override public void onTextChanged(EditText editText) { String text = editText.getText().toString(); - if (text.length() != 0) { + if (text.length() != 0 || dialogsSearchAdapter != null && dialogsSearchAdapter.hasRecentRearch()) { searchWas = true; if (dialogsSearchAdapter != null) { listView.setAdapter(dialogsSearchAdapter); @@ -248,7 +249,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. } } if (dialogsSearchAdapter != null) { - dialogsSearchAdapter.searchDialogs(text, dialogsType); + dialogsSearchAdapter.searchDialogs(text); } } }); @@ -329,14 +330,17 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. MessagesController.getInstance().putUsers(users, false); MessagesStorage.getInstance().putUsersAndChats(users, null, false, true); } + dialogsSearchAdapter.putRecentSearch(dialog_id, (TLRPC.User) obj); } else if (obj instanceof TLRPC.Chat) { if (((TLRPC.Chat) obj).id > 0) { dialog_id = -((TLRPC.Chat) obj).id; } else { dialog_id = AndroidUtilities.makeBroadcastId(((TLRPC.Chat) obj).id); } + dialogsSearchAdapter.putRecentSearch(dialog_id, (TLRPC.Chat) obj); } else if (obj instanceof TLRPC.EncryptedChat) { dialog_id = ((long) ((TLRPC.EncryptedChat) obj).id) << 32; + dialogsSearchAdapter.putRecentSearch(dialog_id, (TLRPC.EncryptedChat) obj); } else if (obj instanceof MessageObject) { MessageObject messageObject = (MessageObject) obj; dialog_id = messageObject.getDialogId(); @@ -399,18 +403,22 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. @Override public void onItemClick(View view, int position) { if (onlySelect || searching && searchWas || getParentActivity() == null) { - if (searchWas && searching) { + if (searchWas && searching || dialogsSearchAdapter.isRecentSearchDisplayed()) { RecyclerView.Adapter adapter = listView.getAdapter(); if (adapter == dialogsSearchAdapter) { Object item = dialogsSearchAdapter.getItem(position); - if (item instanceof String) { + if (item instanceof String || dialogsSearchAdapter.isRecentSearchDisplayed()) { AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); builder.setMessage(LocaleController.getString("ClearSearch", R.string.ClearSearch)); builder.setPositiveButton(LocaleController.getString("ClearButton", R.string.ClearButton).toUpperCase(), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { - dialogsSearchAdapter.clearRecentHashtags(); + if (dialogsSearchAdapter.isRecentSearchDisplayed()) { + dialogsSearchAdapter.clearRecentSearch(); + } else { + dialogsSearchAdapter.clearRecentHashtags(); + } } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); @@ -620,7 +628,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. } else if (!onlySelect) { type = 1; } - dialogsSearchAdapter = new DialogsSearchAdapter(context, type); + dialogsSearchAdapter = new DialogsSearchAdapter(context, type, dialogsType); dialogsSearchAdapter.setDelegate(new DialogsSearchAdapter.MessagesActivitySearchAdapterDelegate() { @Override public void searchStateChanged(boolean search) { @@ -816,6 +824,8 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. } } else if (child instanceof UserCell) { ((UserCell) child).update(mask); + } else if (child instanceof ProfileSearchCell) { + ((ProfileSearchCell) child).update(mask); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DocumentSelectActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/DocumentSelectActivity.java index cee01daf9..442af2927 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DocumentSelectActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DocumentSelectActivity.java @@ -29,6 +29,7 @@ import android.widget.ListView; import android.widget.TextView; import org.telegram.android.AndroidUtilities; +import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.FileLog; import org.telegram.android.LocaleController; import org.telegram.messenger.R; @@ -48,6 +49,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; +import java.util.StringTokenizer; public class DocumentSelectActivity extends BaseFragment { @@ -116,7 +119,7 @@ public class DocumentSelectActivity extends BaseFragment { public void onFragmentDestroy() { try { if (receiverRegistered) { - getParentActivity().unregisterReceiver(receiver); + ApplicationLoader.applicationContext.unregisterReceiver(receiver); } } catch (Exception e) { FileLog.e("tmessages", e); @@ -139,7 +142,7 @@ public class DocumentSelectActivity extends BaseFragment { filter.addAction(Intent.ACTION_MEDIA_UNMOUNTABLE); filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED); filter.addDataScheme("file"); - getParentActivity().registerReceiver(receiver, filter); + ApplicationLoader.applicationContext.registerReceiver(receiver, filter); } actionBar.setBackButtonImage(R.drawable.ic_ab_back); @@ -308,7 +311,7 @@ public class DocumentSelectActivity extends BaseFragment { } else { if (!file.canRead()) { showErrorBox(LocaleController.getString("AccessError", R.string.AccessError)); - return; + file = new File("/mnt/sdcard"); } if (sizeLimit != 0) { if (file.length() > sizeLimit) { @@ -488,59 +491,79 @@ public class DocumentSelectActivity extends BaseFragment { private void listRoots() { currentDir = null; items.clear(); - String extStorage = Environment.getExternalStorageDirectory().getAbsolutePath(); - ListItem ext = new ListItem(); - if (Build.VERSION.SDK_INT < 9 || Environment.isExternalStorageRemovable()) { - ext.title = LocaleController.getString("SdCard", R.string.SdCard); - } else { - ext.title = LocaleController.getString("InternalStorage", R.string.InternalStorage); - } - ext.icon = Build.VERSION.SDK_INT < 9 || Environment.isExternalStorageRemovable() ? R.drawable.ic_external_storage : R.drawable.ic_storage; - ext.subtitle = getRootSubtitle(extStorage); - ext.file = Environment.getExternalStorageDirectory(); - items.add(ext); - try { - BufferedReader reader = new BufferedReader(new FileReader("/proc/mounts")); - String line; - HashMap> aliases = new HashMap<>(); - ArrayList result = new ArrayList<>(); - String extDevice = null; - while ((line = reader.readLine()) != null) { - if ((!line.contains("/mnt") && !line.contains("/storage") && !line.contains("/sdcard")) || line.contains("asec") || line.contains("tmpfs") || line.contains("none")) { - continue; - } - String[] info = line.split(" "); - if (!aliases.containsKey(info[0])) { - aliases.put(info[0], new ArrayList()); - } - aliases.get(info[0]).add(info[1]); - if (info[1].equals(extStorage)) { - extDevice=info[0]; - } - result.add(info[1]); + + HashSet 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(); + if (Build.VERSION.SDK_INT < 9 || Environment.isExternalStorageRemovable()) { + ext.title = LocaleController.getString("SdCard", R.string.SdCard); + ext.icon = R.drawable.ic_external_storage; + } else { + ext.title = LocaleController.getString("InternalStorage", R.string.InternalStorage); + ext.icon = R.drawable.ic_storage; } - reader.close(); - if (extDevice != null) { - result.removeAll(aliases.get(extDevice)); - for (String path : result) { - try { - ListItem item = new ListItem(); - if (path.toLowerCase().contains("sd")) { - ext.title = LocaleController.getString("SdCard", R.string.SdCard); - } else { - ext.title = LocaleController.getString("ExternalStorage", R.string.ExternalStorage); + ext.subtitle = getRootSubtitle(defaultPath); + ext.file = Environment.getExternalStorageDirectory(); + items.add(ext); + paths.add(defaultPath); + } + + BufferedReader bufferedReader = null; + try { + bufferedReader = new BufferedReader(new FileReader("/proc/mounts")); + String line; + while ((line = bufferedReader.readLine()) != null) { + if (line.contains("vfat") || line.contains("/mnt")) { + FileLog.e("tmessages", line); + StringTokenizer tokens = new StringTokenizer(line, " "); + String unused = tokens.nextToken(); + String path = tokens.nextToken(); + if (paths.contains(path)) { + continue; + } + if (line.contains("/dev/block/vold")) { + if (!line.contains("/mnt/secure") && !line.contains("/mnt/asec") && !line.contains("/mnt/obb") && !line.contains("/dev/mapper") && !line.contains("tmpfs")) { + 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; + } + } + } + paths.add(path); + try { + ListItem item = new ListItem(); + if (path.toLowerCase().contains("sd")) { + item.title = LocaleController.getString("SdCard", R.string.SdCard); + } else { + item.title = LocaleController.getString("ExternalStorage", R.string.ExternalStorage); + } + item.icon = R.drawable.ic_external_storage; + item.subtitle = getRootSubtitle(path); + item.file = new File(path); + items.add(item); + } catch (Exception e) { + FileLog.e("tmessages", e); + } } - item.icon = R.drawable.ic_external_storage; - item.subtitle = getRootSubtitle(path); - item.file = new File(path); - items.add(item); - } catch (Exception e) { - FileLog.e("tmessages", e); } } } } catch (Exception e) { FileLog.e("tmessages", e); + } finally { + if (bufferedReader != null) { + try { + bufferedReader.close(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } } ListItem fs = new ListItem(); fs.title = "/"; @@ -576,13 +599,18 @@ public class DocumentSelectActivity extends BaseFragment { } private String getRootSubtitle(String path) { - StatFs stat = new StatFs(path); - long total = (long)stat.getBlockCount() * (long)stat.getBlockSize(); - long free = (long)stat.getAvailableBlocks() * (long)stat.getBlockSize(); - if (total == 0) { - return ""; + try { + StatFs stat = new StatFs(path); + long total = (long)stat.getBlockCount() * (long)stat.getBlockSize(); + long free = (long)stat.getAvailableBlocks() * (long)stat.getBlockSize(); + if (total == 0) { + return ""; + } + return LocaleController.formatString("FreeOfTotal", R.string.FreeOfTotal, AndroidUtilities.formatFileSize(free), AndroidUtilities.formatFileSize(total)); + } catch (Exception e) { + FileLog.e("tmessages", e); } - return LocaleController.formatString("FreeOfTotal", R.string.FreeOfTotal, AndroidUtilities.formatFileSize(free), AndroidUtilities.formatFileSize(total)); + return path; } private class ListAdapter extends BaseFragmentAdapter { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java index 7e9980499..d7c08d4fc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java @@ -702,19 +702,23 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa String sticker = null; String botUser = null; String botChat = null; + String message = null; String scheme = data.getScheme(); if (scheme != null) { if ((scheme.equals("http") || scheme.equals("https"))) { String host = data.getHost().toLowerCase(); if (host.equals("telegram.me")) { String path = data.getPath(); - if (path != null && path.length() >= 6) { + if (path != null) { path = path.substring(1); if (path.startsWith("joinchat/")) { group = path.replace("joinchat/", ""); } else if (path.startsWith("addstickers/")) { sticker = path.replace("addstickers/", ""); - } else { + } else if (path.startsWith("msg/")) { + message = data.getQueryParameter("text"); + message += " " + data.getQueryParameter("url"); + } else if (path.length() >= 5) { username = data.getLastPathSegment(); botUser = data.getQueryParameter("start"); botChat = data.getQueryParameter("startgroup"); @@ -737,11 +741,16 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa url = url.replace("tg:addstickers", "tg://telegram.org").replace("tg://addstickers", "tg://telegram.org"); data = Uri.parse(url); sticker = data.getQueryParameter("set"); + } else if (url.startsWith("tg:msg") || url.startsWith("tg://msg")) { + url = url.replace("tg:msg", "tg://telegram.org").replace("tg://msg", "tg://telegram.org"); + data = Uri.parse(url); + message = data.getQueryParameter("text"); + message += " " + data.getQueryParameter("url"); } } } - if (username != null || group != null || sticker != null) { - runLinkRequest(username, group, sticker, botUser, botChat, 0); + if (username != null || group != null || sticker != null || message != null) { + runLinkRequest(username, group, sticker, botUser, botChat, message, 0); } else { try { Cursor cursor = getContentResolver().query(intent.getData(), null, null, null, null); @@ -919,7 +928,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa return false; } - private void runLinkRequest(final String username, final String group, final String sticker, final String botUser, final String botChat, final int state) { + private void runLinkRequest(final String username, final String group, final String sticker, final String botUser, final String botChat, final String message, final int state) { final ProgressDialog progressDialog = new ProgressDialog(this); progressDialog.setMessage(LocaleController.getString("Loading", R.string.Loading)); progressDialog.setCanceledOnTouchOutside(false); @@ -1030,7 +1039,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { - runLinkRequest(username, group, sticker, botUser, botChat, 1); + runLinkRequest(username, group, sticker, botUser, botChat, message, 1); } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); @@ -1104,21 +1113,56 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa StickersQuery.loadStickers(mainFragmentsStack.get(0), stickerset); } return; + } else if (message != null) { + Bundle args = new Bundle(); + args.putBoolean("onlySelect", true); + DialogsActivity fragment = new DialogsActivity(args); + fragment.setDelegate(new DialogsActivity.MessagesActivityDelegate() { + @Override + public void didSelectDialog(DialogsActivity fragment, long did, boolean param) { + NotificationCenter.getInstance().postNotificationName(NotificationCenter.closeChats); + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); + SharedPreferences.Editor editor = preferences.edit(); + editor.putString("dialog_" + did, message); + editor.commit(); + Bundle args = new Bundle(); + args.putBoolean("scrollToTopOnResume", true); + int lower_part = (int) did; + int high_id = (int) (did >> 32); + if (lower_part != 0) { + if (high_id == 1) { + args.putInt("chat_id", lower_part); + } else { + if (lower_part > 0) { + args.putInt("user_id", lower_part); + } else if (lower_part < 0) { + args.putInt("chat_id", -lower_part); + } + } + } else { + args.putInt("enc_id", high_id); + } + actionBarLayout.presentFragment(new ChatActivity(args), true, false, true); + } + }); + presentFragment(fragment); } - final long reqId = requestId; - progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, LocaleController.getString("Cancel", R.string.Cancel), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - ConnectionsManager.getInstance().cancelRpc(reqId, true); - try { - dialog.dismiss(); - } catch (Exception e) { - FileLog.e("tmessages", e); + if (requestId != 0) { + final long reqId = requestId; + progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, LocaleController.getString("Cancel", R.string.Cancel), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + ConnectionsManager.getInstance().cancelRpc(reqId, true); + try { + dialog.dismiss(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } } - } - }); - progressDialog.show(); + }); + progressDialog.show(); + } } public AlertDialog showAlertDialog(AlertDialog.Builder builder) { @@ -1385,6 +1429,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa protected void onDestroy() { PhotoViewer.getInstance().destroyPhotoViewer(); SecretPhotoViewer.getInstance().destroyPhotoViewer(); + StickerPreviewViewer.getInstance().destroy(); try { if (visibleDialog != null) { visibleDialog.dismiss(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/MediaActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/MediaActivity.java index b52d4b1ad..9ce96818c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/MediaActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/MediaActivity.java @@ -18,6 +18,7 @@ import android.graphics.Bitmap; import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.provider.Browser; import android.text.TextUtils; import android.util.TypedValue; import android.view.Gravity; @@ -57,6 +58,7 @@ import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; import org.telegram.ui.ActionBar.ActionBarPopupWindow; import org.telegram.ui.ActionBar.ActionBar; +import org.telegram.ui.ActionBar.BottomSheet; import org.telegram.ui.Adapters.BaseFragmentAdapter; import org.telegram.ui.Adapters.BaseSectionsAdapter; import org.telegram.android.AnimationCompat.AnimatorSetProxy; @@ -64,12 +66,14 @@ import org.telegram.android.AnimationCompat.ObjectAnimatorProxy; import org.telegram.ui.Cells.GreySectionCell; import org.telegram.ui.Cells.LoadingCell; import org.telegram.ui.Cells.SharedDocumentCell; +import org.telegram.ui.Cells.SharedLinkCell; import org.telegram.ui.Cells.SharedMediaSectionCell; import org.telegram.ui.Cells.SharedPhotoVideoCell; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.SectionsListView; +import org.telegram.ui.Components.WebFrameLayout; import java.io.File; import java.util.ArrayList; @@ -81,8 +85,10 @@ import java.util.TimerTask; public class MediaActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate, PhotoViewer.PhotoViewerProvider { private SharedPhotoVideoAdapter photoVideoAdapter; + private SharedLinksAdapter linksAdapter; private SharedDocumentsAdapter documentsAdapter; - private DocumentsSearchAdapter documentsSearchAdapter; + private MediaSearchAdapter documentsSearchAdapter; + private MediaSearchAdapter linksSearchAdapter; private SectionsListView listView; private LinearLayout progressView; private TextView emptyTextView; @@ -179,10 +185,11 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No } } - private SharedMediaData sharedMediaData[] = new SharedMediaData[3]; + private SharedMediaData sharedMediaData[] = new SharedMediaData[4]; private final static int shared_media_item = 1; private final static int files_item = 2; + private final static int links_item = 5; private final static int forward = 3; private final static int delete = 4; @@ -230,6 +237,7 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No listView = null; photoVideoAdapter = null; documentsAdapter = null; + linksAdapter = null; } finishFragment(); } else if (id == -2) { @@ -248,6 +256,12 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No } selectedMode = 1; switchToCurrentSelectedMode(); + } else if (id == links_item) { + if (selectedMode == 3) { + return; + } + selectedMode = 3; + switchToCurrentSelectedMode(); } else if (id == delete) { if (getParentActivity() == null) { return; @@ -348,7 +362,11 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No @Override public void onSearchCollapse() { dropDownContainer.setVisibility(View.VISIBLE); - documentsSearchAdapter.searchDocuments(null); + if (selectedMode == 1) { + documentsSearchAdapter.search(null); + } else if (selectedMode == 3) { + linksSearchAdapter.search(null); + } searching = false; searchWas = false; switchToCurrentSelectedMode(); @@ -356,15 +374,22 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No @Override public void onTextChanged(EditText editText) { - if (documentsSearchAdapter == null) { - return; - } String text = editText.getText().toString(); if (text.length() != 0) { searchWas = true; switchToCurrentSelectedMode(); } - documentsSearchAdapter.searchDocuments(text); + if (selectedMode == 1) { + if (documentsSearchAdapter == null) { + return; + } + documentsSearchAdapter.search(text); + } else if (selectedMode == 3) { + if (linksSearchAdapter == null) { + return; + } + linksSearchAdapter.search(text); + } } }); searchItem.getSearchField().setHint(LocaleController.getString("Search", R.string.Search)); @@ -374,6 +399,9 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No dropDownContainer.setSubMenuOpenSide(1); dropDownContainer.addSubItem(shared_media_item, LocaleController.getString("SharedMediaTitle", R.string.SharedMediaTitle), 0); dropDownContainer.addSubItem(files_item, LocaleController.getString("DocumentsTitle", R.string.DocumentsTitle), 0); + //if ((int) dialog_id != 0) { + // dropDownContainer.addSubItem(links_item, LocaleController.getString("LinksTitle", R.string.LinksTitle), 0); + //} actionBar.addView(dropDownContainer, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT, AndroidUtilities.isTablet() ? 64 : 56, 0, 40, 0)); dropDownContainer.setOnClickListener(new View.OnClickListener() { @Override @@ -422,7 +450,9 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No photoVideoAdapter = new SharedPhotoVideoAdapter(context); documentsAdapter = new SharedDocumentsAdapter(context); - documentsSearchAdapter = new DocumentsSearchAdapter(context); + documentsSearchAdapter = new MediaSearchAdapter(context, 1); + linksSearchAdapter = new MediaSearchAdapter(context, 3); + linksAdapter = new SharedLinksAdapter(context); FrameLayout frameLayout; fragmentView = frameLayout = new FrameLayout(context); @@ -437,9 +467,9 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No @Override public void onItemClick(AdapterView adapterView, View view, final int i, long l) { if (selectedMode == 1 && view instanceof SharedDocumentCell) { - SharedDocumentCell cell = (SharedDocumentCell) view; - MessageObject message = cell.getDocument(); - MediaActivity.this.onItemClick(i, view, message, 0); + MediaActivity.this.onItemClick(i, view, ((SharedDocumentCell) view).getDocument(), 0); + } else if (selectedMode == 3 && view instanceof SharedLinkCell) { + MediaActivity.this.onItemClick(i, view, ((SharedLinkCell) view).getMessage(), 0); } } }); @@ -464,8 +494,10 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No type = SharedMediaQuery.MEDIA_PHOTOVIDEO; } else if (selectedMode == 1) { type = SharedMediaQuery.MEDIA_FILE; - } else { + } else if (selectedMode == 2) { type = SharedMediaQuery.MEDIA_AUDIO; + } else { + type = SharedMediaQuery.MEDIA_URL; } SharedMediaQuery.loadMedia(dialog_id, 0, 50, sharedMediaData[selectedMode].max_id, type, !sharedMediaData[selectedMode].cacheEndReached, classGuid); } @@ -564,8 +596,12 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No if (documentsAdapter != null) { documentsAdapter.notifyDataSetChanged(); } + } else if (selectedMode == 3 && type == 3) { + if (linksAdapter != null) { + linksAdapter.notifyDataSetChanged(); + } } - if (selectedMode == 1) { + if (selectedMode == 1 || selectedMode == 3) { searchItem.setVisibility(!sharedMediaData[selectedMode].messages.isEmpty() && !searching ? View.VISIBLE : View.GONE); } } @@ -587,7 +623,10 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No if (documentsAdapter != null) { documentsAdapter.notifyDataSetChanged(); } - if (selectedMode == 1) { + if (linksAdapter != null) { + linksAdapter.notifyDataSetChanged(); + } + if (selectedMode == 1 || selectedMode == 3) { searchItem.setVisibility(!sharedMediaData[selectedMode].messages.isEmpty() && !searching ? View.VISIBLE : View.GONE); } } @@ -617,7 +656,10 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No if (documentsAdapter != null) { documentsAdapter.notifyDataSetChanged(); } - if (selectedMode == 1) { + if (linksAdapter != null) { + linksAdapter.notifyDataSetChanged(); + } + if (selectedMode == 1 || selectedMode == 3) { searchItem.setVisibility(!sharedMediaData[selectedMode].messages.isEmpty() && !searching ? View.VISIBLE : View.GONE); } } @@ -641,6 +683,9 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No if (documentsAdapter != null) { documentsAdapter.notifyDataSetChanged(); } + if (linksAdapter != null) { + linksAdapter.notifyDataSetChanged(); + } fixLayoutInternal(); } @@ -728,8 +773,13 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No private void switchToCurrentSelectedMode() { if (searching && searchWas) { if (listView != null) { - listView.setAdapter(documentsSearchAdapter); - documentsSearchAdapter.notifyDataSetChanged(); + if (selectedMode == 1) { + listView.setAdapter(documentsSearchAdapter); + documentsSearchAdapter.notifyDataSetChanged(); + } else if (selectedMode == 3) { + listView.setAdapter(linksSearchAdapter); + linksSearchAdapter.notifyDataSetChanged(); + } } if (emptyTextView != null) { emptyTextView.setText(LocaleController.getString("NoResult", R.string.NoResult)); @@ -760,8 +810,8 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No dropDown.setText(LocaleController.getString("DocumentsTitle", R.string.DocumentsTitle)); emptyImageView.setImageResource(R.drawable.tip2); emptyTextView.setText(LocaleController.getString("NoSharedFiles", R.string.NoSharedFiles)); - searchItem.setVisibility(!sharedMediaData[1].messages.isEmpty() ? View.VISIBLE : View.GONE); - if (!sharedMediaData[1].loading && !sharedMediaData[1].endReached && sharedMediaData[1].messages.isEmpty()) { + searchItem.setVisibility(!sharedMediaData[selectedMode].messages.isEmpty() ? View.VISIBLE : View.GONE); + if (!sharedMediaData[selectedMode].loading && !sharedMediaData[selectedMode].endReached && sharedMediaData[selectedMode].messages.isEmpty()) { sharedMediaData[selectedMode].loading = true; SharedMediaQuery.loadMedia(dialog_id, 0, 50, 0, SharedMediaQuery.MEDIA_FILE, true, classGuid); } @@ -775,6 +825,26 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No listView.setEmptyView(emptyView); } listView.setPadding(0, 0, 0, AndroidUtilities.dp(4)); + } else if (selectedMode == 3) { + listView.setAdapter(linksAdapter); + dropDown.setText(LocaleController.getString("LinksTitle", R.string.LinksTitle)); + emptyImageView.setImageResource(R.drawable.tip2); + emptyTextView.setText(LocaleController.getString("NoSharedLinks", R.string.NoSharedLinks)); + searchItem.setVisibility(!sharedMediaData[3].messages.isEmpty() ? View.VISIBLE : View.GONE); + if (!sharedMediaData[selectedMode].loading && !sharedMediaData[selectedMode].endReached && sharedMediaData[selectedMode].messages.isEmpty()) { + sharedMediaData[selectedMode].loading = true; + SharedMediaQuery.loadMedia(dialog_id, 0, 50, 0, SharedMediaQuery.MEDIA_URL, false, classGuid); + } + listView.setVisibility(View.VISIBLE); + if (sharedMediaData[selectedMode].loading && sharedMediaData[selectedMode].messages.isEmpty()) { + progressView.setVisibility(View.VISIBLE); + listView.setEmptyView(null); + emptyView.setVisibility(View.GONE); + } else { + progressView.setVisibility(View.GONE); + listView.setEmptyView(emptyView); + } + listView.setPadding(0, 0, 0, AndroidUtilities.dp(4)); } } } @@ -905,6 +975,23 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No cell.updateFileExistIcon(); } } + } else if (selectedMode == 3) { + try { + TLRPC.WebPage webPage = message.messageOwner.media.webpage; + if (Build.VERSION.SDK_INT >= 16 && webPage.embed_url != null && webPage.embed_url.length() != 0) { + BottomSheet.Builder builder = new BottomSheet.Builder(getParentActivity()); + builder.setCustomView(new WebFrameLayout(getParentActivity(), builder.create(), webPage.title, webPage.url, webPage.embed_url, webPage.embed_width, webPage.embed_height)); + builder.setUseFullWidth(true); + showDialog(builder.create()); + } else { + Uri uri = Uri.parse(webPage.url); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + intent.putExtra(Browser.EXTRA_APPLICATION_ID, getParentActivity().getPackageName()); + getParentActivity().startActivity(intent); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } } } } @@ -945,6 +1032,100 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No } } + private class SharedLinksAdapter extends BaseSectionsAdapter { + private Context mContext; + + public SharedLinksAdapter(Context context) { + mContext = context; + } + + @Override + public Object getItem(int section, int position) { + return null; + } + + @Override + public boolean isRowEnabled(int section, int row) { + return row != 0; + } + + @Override + public int getSectionCount() { + return sharedMediaData[3].sections.size() + (sharedMediaData[3].sections.isEmpty() || sharedMediaData[3].endReached ? 0 : 1); + } + + @Override + public int getCountForSection(int section) { + if (section < sharedMediaData[3].sections.size()) { + return sharedMediaData[3].sectionArrays.get(sharedMediaData[3].sections.get(section)).size() + 1; + } + return 1; + } + + @Override + public View getSectionHeaderView(int section, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = new GreySectionCell(mContext); + } + if (section < sharedMediaData[3].sections.size()) { + String name = sharedMediaData[3].sections.get(section); + ArrayList messageObjects = sharedMediaData[3].sectionArrays.get(name); + MessageObject messageObject = messageObjects.get(0); + ((GreySectionCell) convertView).setText(LocaleController.formatterMonthYear.format((long) messageObject.messageOwner.date * 1000).toUpperCase()); + } + return convertView; + } + + @Override + public View getItemView(int section, int position, View convertView, ViewGroup parent) { + if (section < sharedMediaData[3].sections.size()) { + String name = sharedMediaData[3].sections.get(section); + ArrayList messageObjects = sharedMediaData[3].sectionArrays.get(name); + if (position == 0) { + if (convertView == null) { + convertView = new GreySectionCell(mContext); + } + MessageObject messageObject = messageObjects.get(0); + ((GreySectionCell) convertView).setText(LocaleController.formatterMonthYear.format((long) messageObject.messageOwner.date * 1000).toUpperCase()); + } else { + if (convertView == null) { + convertView = new SharedLinkCell(mContext); + } + SharedLinkCell sharedDocumentCell = (SharedLinkCell) convertView; + MessageObject messageObject = messageObjects.get(position - 1); + sharedDocumentCell.setLink(messageObject, position != messageObjects.size() || section == sharedMediaData[3].sections.size() - 1 && sharedMediaData[3].loading); + /*if (actionBar.isActionModeShowed()) { + sharedDocumentCell.setChecked(selectedFiles.containsKey(messageObject.getId()), !scrolling); + } else { + sharedDocumentCell.setChecked(false, !scrolling); + }*/ + } + } else { + if (convertView == null) { + convertView = new LoadingCell(mContext); + } + } + return convertView; + } + + @Override + public int getItemViewType(int section, int position) { + if (section < sharedMediaData[3].sections.size()) { + if (position == 0) { + return 0; + } else { + return 1; + } + } + return 2; + } + + @Override + public int getViewTypeCount() { + return 3; + } + } + private class SharedDocumentsAdapter extends BaseSectionsAdapter { private Context mContext; @@ -1164,16 +1345,18 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No } } - public class DocumentsSearchAdapter extends BaseFragmentAdapter { + public class MediaSearchAdapter extends BaseFragmentAdapter { private Context mContext; private ArrayList searchResult = new ArrayList<>(); private Timer searchTimer; protected ArrayList globalSearch = new ArrayList<>(); private long reqId = 0; private int lastReqId; + private int currentType; - public DocumentsSearchAdapter(Context context) { + public MediaSearchAdapter(Context context, int type) { mContext = context; + currentType = type; } public void queryServerSearch(final String query, final int max_id) { @@ -1195,7 +1378,11 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No req.offset = 0; req.limit = 50; req.max_id = max_id; - req.filter = new TLRPC.TL_inputMessagesFilterDocument(); + if (currentType == 1) { + req.filter = new TLRPC.TL_inputMessagesFilterDocument(); + } else if (currentType == 3) { + req.filter = new TLRPC.TL_inputMessagesFilterUrl(); + } req.q = query; if (uid < 0) { req.peer = new TLRPC.TL_inputPeerChat(); @@ -1239,7 +1426,7 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No ConnectionsManager.getInstance().bindRequestToGuid(reqId, classGuid); } - public void searchDocuments(final String query) { + public void search(final String query) { try { if (searchTimer != null) { searchTimer.cancel(); @@ -1271,49 +1458,55 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { - if (!sharedMediaData[1].messages.isEmpty()) { - MessageObject messageObject = sharedMediaData[1].messages.get(sharedMediaData[1].messages.size() - 1); - queryServerSearch(query, messageObject.getId()); + if (!sharedMediaData[currentType].messages.isEmpty()) { + if (currentType == 1) { + MessageObject messageObject = sharedMediaData[currentType].messages.get(sharedMediaData[currentType].messages.size() - 1); + queryServerSearch(query, messageObject.getId()); + } else if (currentType == 3) { + queryServerSearch(query, 0); + } } - final ArrayList copy = new ArrayList<>(); - copy.addAll(sharedMediaData[1].messages); - Utilities.searchQueue.postRunnable(new Runnable() { - @Override - public void run() { - String search1 = query.trim().toLowerCase(); - if (search1.length() == 0) { - updateSearchResults(new ArrayList()); - return; - } - String search2 = LocaleController.getInstance().getTranslitString(search1); - if (search1.equals(search2) || search2.length() == 0) { - search2 = null; - } - String search[] = new String[1 + (search2 != null ? 1 : 0)]; - search[0] = search1; - if (search2 != null) { - search[1] = search2; - } + if (currentType == 1) { + final ArrayList copy = new ArrayList<>(); + copy.addAll(sharedMediaData[1].messages); + Utilities.searchQueue.postRunnable(new Runnable() { + @Override + public void run() { + String search1 = query.trim().toLowerCase(); + if (search1.length() == 0) { + updateSearchResults(new ArrayList()); + return; + } + String search2 = LocaleController.getInstance().getTranslitString(search1); + if (search1.equals(search2) || search2.length() == 0) { + search2 = null; + } + String search[] = new String[1 + (search2 != null ? 1 : 0)]; + search[0] = search1; + if (search2 != null) { + search[1] = search2; + } - ArrayList resultArray = new ArrayList<>(); + ArrayList resultArray = new ArrayList<>(); - for (MessageObject messageObject : copy) { - for (String q : search) { - String name = messageObject.getDocumentName(); - if (name == null || name.length() == 0) { - continue; - } - name = name.toLowerCase(); - if (name.contains(q)) { - resultArray.add(messageObject); - break; + for (MessageObject messageObject : copy) { + for (String q : search) { + String name = messageObject.getDocumentName(); + if (name == null || name.length() == 0) { + continue; + } + name = name.toLowerCase(); + if (name.contains(q)) { + resultArray.add(messageObject); + break; + } } } - } - updateSearchResults(resultArray); - } - }); + updateSearchResults(resultArray); + } + }); + } } }); } @@ -1380,16 +1573,30 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No @Override public View getView(int i, View view, ViewGroup viewGroup) { - if (view == null) { - view = new SharedDocumentCell(mContext); - } - SharedDocumentCell sharedDocumentCell = (SharedDocumentCell) view; - MessageObject messageObject = getItem(i); - sharedDocumentCell.setDocument(messageObject, i != getCount() - 1); - if (actionBar.isActionModeShowed()) { - sharedDocumentCell.setChecked(selectedFiles.containsKey(messageObject.getId()), !scrolling); - } else { - sharedDocumentCell.setChecked(false, !scrolling); + if (currentType == 1) { + if (view == null) { + view = new SharedDocumentCell(mContext); + } + SharedDocumentCell sharedDocumentCell = (SharedDocumentCell) view; + MessageObject messageObject = getItem(i); + sharedDocumentCell.setDocument(messageObject, i != getCount() - 1); + if (actionBar.isActionModeShowed()) { + sharedDocumentCell.setChecked(selectedFiles.containsKey(messageObject.getId()), !scrolling); + } else { + sharedDocumentCell.setChecked(false, !scrolling); + } + } else if (currentType == 3) { + if (view == null) { + view = new SharedLinkCell(mContext); + } + SharedLinkCell sharedLinkCell = (SharedLinkCell) view; + MessageObject messageObject = getItem(i); + sharedLinkCell.setLink(messageObject, i != getCount() - 1); + /*if (actionBar.isActionModeShowed()) { + sharedDocumentCell.setChecked(selectedFiles.containsKey(messageObject.getId()), !scrolling); + } else { + sharedDocumentCell.setChecked(false, !scrolling); + }*/ } return view; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoPickerActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoPickerActivity.java index 4c0f327a4..3ae8f8052 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PhotoPickerActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoPickerActivity.java @@ -780,6 +780,7 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen updateSearchInterface(); } }); + jsonObjReq.setShouldCache(false); jsonObjReq.setTag("search"); requestQueue.add(jsonObjReq); } catch (Exception e) { @@ -866,6 +867,7 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen return headers; } }; + jsonObjReq.setShouldCache(false); jsonObjReq.setTag("search"); requestQueue.add(jsonObjReq); } catch (Exception e) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java index bffc3f51a..e3dfcca2e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java @@ -26,6 +26,7 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.provider.Browser; import android.text.TextUtils; import android.util.TypedValue; import android.view.GestureDetector; @@ -1159,6 +1160,13 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat File f = null; if (currentMessageObject != null) { + if (currentMessageObject.messageOwner.media instanceof TLRPC.TL_messageMediaWebPage) { + Uri uri = Uri.parse(currentMessageObject.messageOwner.media.webpage.url); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + intent.putExtra(Browser.EXTRA_APPLICATION_ID, parentActivity.getPackageName()); + parentActivity.startActivity(intent); + return; + } f = FileLoader.getPathToMessage(currentMessageObject.messageOwner); } else if (currentFileLocation != null) { f = FileLoader.getPathToAttach(currentFileLocation, avatarsUserId != 0); @@ -2020,7 +2028,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } else if (message.messageOwner.media != null) { if (message.messageOwner.media instanceof TLRPC.TL_messageMediaVideo) { return file.volume_id + "_" + file.id + ".mp4"; - } else if (message.messageOwner.media instanceof TLRPC.TL_messageMediaPhoto) { + } else if (message.messageOwner.media instanceof TLRPC.TL_messageMediaPhoto || message.messageOwner.media instanceof TLRPC.TL_messageMediaWebPage) { return file.volume_id + "_" + file.local_id + ".jpg"; } } @@ -2076,7 +2084,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat size[0] = -1; } } - } else if (message.messageOwner.media instanceof TLRPC.TL_messageMediaPhoto && message.messageOwner.media.photo != null) { + } else if (message.messageOwner.media instanceof TLRPC.TL_messageMediaPhoto && message.messageOwner.media.photo != null || message.messageOwner.media instanceof TLRPC.TL_messageMediaWebPage && message.messageOwner.media.webpage != null) { TLRPC.PhotoSize sizeFull = FileLoader.getClosestPhotoSizeWithSize(message.photoThumbs, AndroidUtilities.getPhotoSize()); if (sizeFull != null) { size[0] = sizeFull.size; @@ -2138,7 +2146,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat return location; } } - } else if (message.messageOwner.media instanceof TLRPC.TL_messageMediaPhoto) { + } else if (message.messageOwner.media instanceof TLRPC.TL_messageMediaPhoto || message.messageOwner.media instanceof TLRPC.TL_messageMediaWebPage) { TLRPC.PhotoSize sizeFull = FileLoader.getClosestPhotoSizeWithSize(message.photoThumbs, AndroidUtilities.getPhotoSize()); if (sizeFull != null) { TLRPC.TL_inputFileLocation location = new TLRPC.TL_inputFileLocation(); @@ -2237,7 +2245,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (messageObject != null && messages == null) { imagesArr.add(messageObject); - if (messageObject.messageOwner.action == null || messageObject.messageOwner.action instanceof TLRPC.TL_messageActionEmpty) { + if (!(messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaWebPage) && (messageObject.messageOwner.action == null || messageObject.messageOwner.action instanceof TLRPC.TL_messageActionEmpty)) { needSearchImageInArr = true; imagesByIds.put(messageObject.getId(), messageObject); if (messageObject.messageOwner.dialog_id != 0) { @@ -2397,6 +2405,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } actionBar.setTitle(LocaleController.formatString("Of", R.string.Of, (totalImagesCount - imagesArr.size()) + currentIndex + 1, totalImagesCount)); } + } else if (currentMessageObject.messageOwner.media instanceof TLRPC.TL_messageMediaWebPage) { + actionBar.setTitle(LocaleController.getString("AttachPhoto", R.string.AttachPhoto)); } if (currentMessageObject.messageOwner.ttl != 0) { menuItem.hideSubItem(gallery_menu_save); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SecretPhotoViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/SecretPhotoViewer.java index 368898919..77b8ef7f9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SecretPhotoViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SecretPhotoViewer.java @@ -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 String currentInfoString; private int infoWidth; @@ -147,7 +135,7 @@ public class SecretPhotoViewer implements NotificationCenter.NotificationCenterD private Activity parentActivity; private WindowManager.LayoutParams windowLayoutParams; - private FrameLayoutTouchListener windowView; + private FrameLayout windowView; private FrameLayoutDrawer containerView; private ImageReceiver centerImage = new ImageReceiver(); private SecretDeleteTimer secretDeleteTimer; @@ -205,7 +193,7 @@ public class SecretPhotoViewer implements NotificationCenter.NotificationCenterD } parentActivity = activity; - windowView = new FrameLayoutTouchListener(activity); + windowView = new FrameLayout(activity); windowView.setBackgroundColor(0xff000000); windowView.setFocusable(true); windowView.setFocusableInTouchMode(true); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/StickerPreviewViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/StickerPreviewViewer.java new file mode 100644 index 000000000..7446cd43a --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/StickerPreviewViewer.java @@ -0,0 +1,221 @@ +/* + * This is the source code of Telegram for Android v. 3.x.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2015. + */ + +package org.telegram.ui; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.PixelFormat; +import android.graphics.drawable.ColorDrawable; +import android.os.Build; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.WindowManager; +import android.widget.FrameLayout; + +import org.telegram.android.AndroidUtilities; +import org.telegram.android.ImageReceiver; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.TLRPC; +import org.telegram.ui.Components.LayoutHelper; + +public class StickerPreviewViewer { + + private class FrameLayoutDrawer extends FrameLayout { + public FrameLayoutDrawer(Context context) { + super(context); + setWillNotDraw(false); + } + + @Override + protected void onDraw(Canvas canvas) { + getInstance().onDraw(canvas); + } + } + + private ColorDrawable backgroundDrawable = new ColorDrawable(0x71000000); + private Activity parentActivity; + private WindowManager.LayoutParams windowLayoutParams; + private FrameLayout windowView; + private FrameLayoutDrawer containerView; + private ImageReceiver centerImage = new ImageReceiver(); + private boolean isVisible = false; + private float showProgress; + private long lastUpdateTime; + private int keyboardHeight = AndroidUtilities.dp(200); + + private TLRPC.Document currentSticker = null; + + private static volatile StickerPreviewViewer Instance = null; + public static StickerPreviewViewer getInstance() { + StickerPreviewViewer localInstance = Instance; + if (localInstance == null) { + synchronized (PhotoViewer.class) { + localInstance = Instance; + if (localInstance == null) { + Instance = localInstance = new StickerPreviewViewer(); + } + } + } + return localInstance; + } + + public void setParentActivity(Activity activity) { + if (parentActivity == activity) { + return; + } + parentActivity = activity; + + windowView = new FrameLayout(activity); + windowView.setFocusable(true); + windowView.setFocusableInTouchMode(true); + + containerView = new FrameLayoutDrawer(activity); + containerView.setFocusable(false); + windowView.addView(containerView); + FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) containerView.getLayoutParams(); + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = LayoutHelper.MATCH_PARENT; + layoutParams.gravity = Gravity.TOP | Gravity.LEFT; + containerView.setLayoutParams(layoutParams); + containerView.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_POINTER_UP || event.getAction() == MotionEvent.ACTION_CANCEL) { + close(); + } + return true; + } + }); + + windowLayoutParams = new WindowManager.LayoutParams(); + windowLayoutParams.height = WindowManager.LayoutParams.MATCH_PARENT; + windowLayoutParams.format = PixelFormat.TRANSLUCENT; + windowLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT; + windowLayoutParams.gravity = Gravity.TOP; + windowLayoutParams.type = WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; + if (Build.VERSION.SDK_INT >= 21) { + windowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + } else { + windowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + } + + centerImage.setAspectFit(true); + centerImage.setInvalidateAll(true); + centerImage.setParentView(containerView); + } + + public void setKeyboardHeight(int height) { + keyboardHeight = height; + } + + public void open(TLRPC.Document sticker) { + if (parentActivity == null || sticker == null) { + return; + } + + centerImage.setImage(sticker, null, sticker.thumb.location, null, "webp", true); + currentSticker = sticker; + containerView.invalidate(); + + if (!isVisible) { + AndroidUtilities.lockOrientation(parentActivity); + try { + if (windowView.getParent() != null) { + WindowManager wm = (WindowManager) parentActivity.getSystemService(Context.WINDOW_SERVICE); + wm.removeView(windowView); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + WindowManager wm = (WindowManager) parentActivity.getSystemService(Context.WINDOW_SERVICE); + wm.addView(windowView, windowLayoutParams); + isVisible = true; + showProgress = 0.0f; + lastUpdateTime = System.currentTimeMillis(); + } + } + + public boolean isVisible() { + return isVisible; + } + + public void close() { + if (parentActivity == null) { + return; + } + showProgress = 1.0f; + currentSticker = null; + isVisible = false; + AndroidUtilities.unlockOrientation(parentActivity); + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + centerImage.setImageBitmap((Bitmap)null); + } + }); + try { + if (windowView.getParent() != null) { + WindowManager wm = (WindowManager) parentActivity.getSystemService(Context.WINDOW_SERVICE); + wm.removeView(windowView); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + + public void destroy() { + isVisible = false; + currentSticker = null; + if (parentActivity == null || windowView == null) { + return; + } + try { + if (windowView.getParent() != null) { + WindowManager wm = (WindowManager) parentActivity.getSystemService(Context.WINDOW_SERVICE); + wm.removeViewImmediate(windowView); + } + windowView = null; + } catch (Exception e) { + FileLog.e("tmessages", e); + } + Instance = null; + } + + private void onDraw(Canvas canvas) { + backgroundDrawable.setAlpha((int) (180 * showProgress)); + backgroundDrawable.setBounds(0, 0, containerView.getWidth(), containerView.getHeight()); + backgroundDrawable.draw(canvas); + + canvas.save(); + int size = (int) (Math.min(containerView.getWidth(), containerView.getHeight()) / 1.8f); + canvas.translate(containerView.getWidth() / 2, Math.max(size / 2 + AndroidUtilities.statusBarHeight, (containerView.getHeight() - keyboardHeight) / 2)); + Bitmap bitmap = centerImage.getBitmap(); + if (bitmap != null) { + float scale = 0.8f * showProgress / 0.8f; + size = (int) (size * scale); + centerImage.setAlpha(showProgress); + centerImage.setImageCoords(-size / 2, -size / 2, size, size); + centerImage.draw(canvas); + } + canvas.restore(); + if (showProgress != 1) { + long newTime = System.currentTimeMillis(); + long dt = newTime - lastUpdateTime; + lastUpdateTime = newTime; + showProgress += dt / 150.0f; + containerView.invalidate(); + if (showProgress > 1.0f) { + showProgress = 1.0f; + } + } + } +} diff --git a/TMessagesProj/src/main/res/values-ar/strings.xml b/TMessagesProj/src/main/res/values-ar/strings.xml index 5562767b2..c9ea54895 100644 --- a/TMessagesProj/src/main/res/values-ar/strings.xml +++ b/TMessagesProj/src/main/res/values-ar/strings.xml @@ -59,6 +59,7 @@ خلال %1$s تعطيل الأوسمة + حديث رسالة جماعية جديدة أدخل اسم القائمة @@ -391,9 +392,11 @@ معطّل شارك المقاطع المرئية والصور في هذه المحادثة لتستطيع الوصول إليها من أية جهاز من أجهزتك - ملفات + الملفات المشاركة الوسائط المشتركة + الروابط المشاركة شارك الملفات والمستندات في هذه المحادثة لتستطيع الوصول إليها من أية جهاز من أجهزتك + شارك الروابط في هذه المحادثة لتستطيع الوصول إليها من أية جهاز من أجهزتك الخريطة قمر صناعي @@ -834,6 +837,6 @@ h:mm a %1$s الساعة %2$s - تم تحديث تيليجرام نسخة الأندرويد. الجديد في النسخة رقم 3.1:\n\n- بحث عن الرسائل داخل محادثات محددة. \n- إعادة تصميم كاملة لشاشة ارفاق الملفات. إرسال جهات اتصال وملفات صوتية مباشرة من خيار المرفقات. \n- تطوير لتشغيل الوسائط داخل التطبيق (يوتيوب, ڤيميو, ساوندكلاود وغيرها.),.\n\nللاستزادة، اطلع هنا:\nhttps://telegram.org/blog/search-and-media - 577 + تم تحديث تيليجرام نسخة الاندرويد. الجديد في نسخة ٣.١.٢: \n\n- كلمات البحث الحديثة. \n- اضغط باستمرار لاستعراض الملصق قبل إرساله. + 583 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-de/strings.xml b/TMessagesProj/src/main/res/values-de/strings.xml index d171f0ee5..0f7e23337 100644 --- a/TMessagesProj/src/main/res/values-de/strings.xml +++ b/TMessagesProj/src/main/res/values-de/strings.xml @@ -59,6 +59,7 @@ In %1$s Dauerhaft Stumm HASHTAGS + LETZTE Neue Broadcast Liste Listenname @@ -234,7 +235,7 @@ Geheimer Schlüssel Selbstzerstörungs-Timer Aus - Das ist eine Darstellung des Schlüssels für den Geheimen Chat mit ]]>%1$s]]>.
]]>Wenn dieses Bild auf ]]>%2$s\s]]>s Telefon genau so aussieht, ist euer Chat zu 200%% sicher.
]]>Erfahre mehr auf telegram.org
+ Die Abbildung zeigt den aktuellen Schlüssel dieses geheimen Chats mit ]]>%1$s]]>.
]]>Wenn dieses Bild auf dem Gerät von ]]>%2$s]]> genau so aussieht, ist euer Chat zu 200%% sicher.
]]>Erfahre mehr auf telegram.org
Unbekannt Info Telefon @@ -391,9 +392,11 @@ Deaktiviert Die hier geteilten Bilder und Videos kannst du von jedem deiner Geräte aufrufen. - Dateien + Geteilte Dateien Geteilte Medien + Geteilte Links Die hier geteilten Dateien kannst du von jedem deiner Geräte aufrufen. + Die hier geteilten Links kannst du von jedem deiner Geräte aufrufen. Karte Satellit @@ -834,6 +837,6 @@ h:mm a %1$s um %2$s - Telegram für Android wurde aktualisiert. Neu in Version 3.1:\n\n- Direkte Suche in Chats.\n- In Chats versteckt sich ein komplett neues Menü hinter der Büroklammer. Nun kann man dort auch Musik und Kontakte versenden.\n- In-App Medienwiedergabe für YouTube, Vimeo, SoundCloud, etc. optimiert und neuer Player für Audiodateien.\n\nMehr dazu unter:\nhttps://telegram.org/blog/search-and-media - 577 + Telegram für Android wurde aktualisiert. Neu in Version 3.1.2:\n\n- Die letzte Suche wird gespeichert\n- Stickervorschau: Sticker vor dem Senden antippen und halten + 583 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-es/strings.xml b/TMessagesProj/src/main/res/values-es/strings.xml index 3c328ef1b..b80c0fced 100644 --- a/TMessagesProj/src/main/res/values-es/strings.xml +++ b/TMessagesProj/src/main/res/values-es/strings.xml @@ -59,6 +59,7 @@ En %1$s Desactivar HASHTAGS + RECIENTES Nueva difusión Nombre de la lista @@ -393,7 +394,9 @@ Comparte fotos y vídeos en este chat y accede a ellos desde cualquier dispositivo. Archivos Multimedia + Enlaces Comparte archivos en este chat y accede a ellos desde cualquier dispositivo. + Comparte enlaces en este chat y accede a ellos desde cualquiera de tus dispositivos. Mapa Satélite @@ -834,6 +837,6 @@ h:mm a %1$s a las %2$s - Telegram para Android fue actualizada. Novedades en la versión 3.1:\n\n- Busca mensajes dentro de un chat en específico.\n- Menú para adjuntar completamente rediseñado. Envía contactos y archivos de audio directamente desde el menú para adjuntar.\n- Reproducción de multimedia dentro de la aplicación mejorada (YouTube, Vimeo, SoundCloud etc.), nuevo reproductor para archivos de audio largos.\n\nMás sobre esta actualización:\nhttps://telegram.org/blog/search-and-media - 577 + Telegram para Android fue actualizada. Novedades en la versión 3.1.2:\n\n- Búsquedas recientes\n- Mantén pulsado sobre un sticker para obtener una vista previa antes de enviarlo + 583 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-it/strings.xml b/TMessagesProj/src/main/res/values-it/strings.xml index d826f248b..ce4cf1766 100644 --- a/TMessagesProj/src/main/res/values-it/strings.xml +++ b/TMessagesProj/src/main/res/values-it/strings.xml @@ -59,6 +59,7 @@ Tra %1$s Disabilita HASHTAG + RECENTI Nuova lista broadcast Immetti il nome della lista @@ -164,7 +165,7 @@ %1$s ha modificato il nome del gruppo %2$s %1$s ha modificato la foto del gruppo %2$s %1$s ha invitato %3$s nel gruppo %2$s - %1$s è rientrato nel gruppo %2$s + %1$s è tornato nel gruppo %2$s %1$s ha rimosso %3$s dal gruppo %2$s %1$s ti ha rimosso dal gruppo %2$s %1$s ha lasciato il gruppo %2$s @@ -391,9 +392,11 @@ Disabilitato Condividi foto e video in questa chat e accedi ad essi da ogni tuo dispositivo. - File + File condivisi Media condivisi + Link condivisi Condividi file e documenti in questa chat e accedi ad essi da ogni tuo dispositivo. + Condividi link in questa chat ed accedi ad essi da ogni tuo dispositivo. Mappa Satellite @@ -586,8 +589,8 @@ Hai creato il gruppo un1 ti ha rimosso un1 ti ha aggiunto - un1 è rientrato nel gruppo - Sei rientrato nel gruppo + un1 è tornato nel gruppo + Sei tornato nel gruppo Questo messaggio non è supportato sulla tua versione di Telegram. Aggiorna l\'applicazione per visualizzarlo: https://telegram.org/update Foto Video @@ -834,6 +837,6 @@ h:mm a %1$s alle %2$s - Telegram per Android si è aggiornato. Nuovo nella versione 3.1:\n\n- Cerca messaggi all\'interno di una specifica chat.\n- Menu degli allegati completamente ridisegnato. Invia contatti e audio direttamente dal menu degli allegati.\n- Riproduzione in-app migliorata (YouTube, Vimeo, SoundCloud etc.), nuovo player per i file audio.\n\nMaggiori informazioni su questo aggiornamento:\nhttps://telegram.org/blog/search-and-media - 577 + Telegram per Android si è aggiornato. Nuovo nella versione 3.1.2:\n\n- Risultati recenti nella ricerca\n- Tieni premuto su uno sticker per visualizzare l\'anteprima prima di inviarlo + 583 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-ko/strings.xml b/TMessagesProj/src/main/res/values-ko/strings.xml index f2fc30ddf..9640fd27e 100644 --- a/TMessagesProj/src/main/res/values-ko/strings.xml +++ b/TMessagesProj/src/main/res/values-ko/strings.xml @@ -59,6 +59,7 @@ %1$s 후 비활성화 해시태그 + 최신 새 단체 메시지 리스트 리스트 이름을 입력하세요 @@ -391,9 +392,11 @@ 비활성화됨 이 채팅방에서 사진이나 동영상을 공유하면 다른 기기에서도 보실 수 있습니다. - 파일 + 공유한 파일 공유된 미디어 + 공유한 링크 이 채팅방에서 파일이나 문서를 공유하면 다른 기기에서도 보실 수 있습니다. + 이 채팅방에서 파일이나 문서를 공유하면 다른 기기에서도 보실 수 있습니다. 지도 위성 @@ -834,6 +837,6 @@ a h:mm %1$s %2$s - 텔레그램 안드로이드 버전이 업데이트 되었습니다. 새로운 버전은 3.1 입니다:\n\n- 특정 대화창에서 검색. \n- 첨부 메뉴 개선. 해당 메뉴에서 바로 연락처, 오디오를 전송 가능. \n- 인앱 미디어 재생 기능 향상 (YouTube, Vimeo, SoundCloud etc). 대용량 오디오 파일 별도 플레이어 기능. \n\nhttps://telegram.org/blog/search-and-media에서 자세한 사항을 알아보세요. - 577 + 텔레그램 안드로이드 버전이 업데이트 되었습니다. 새로운 버전은 3.1.2 입니다:\n\n- 최신 검색 결과\n- 스티커를 꾹 누를 경우 미리보기 기능 + 583 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-nl/strings.xml b/TMessagesProj/src/main/res/values-nl/strings.xml index 487122018..be14001e0 100644 --- a/TMessagesProj/src/main/res/values-nl/strings.xml +++ b/TMessagesProj/src/main/res/values-nl/strings.xml @@ -59,6 +59,7 @@ Over %1$s Uitschakelen HASHTAGS + RECENT Nieuwe verzendlijst Naam van lijst @@ -391,9 +392,11 @@ Uitgeschakeld Deel foto\'s en video\'s in deze chat om ze op al je apparaten te kunnen benaderen. - Bestanden + Gedeelde bestanden Gedeelde media + Gedeelde links Deel bestanden en documenten in deze chat om ze op al je apparaten te kunnen benaderen. + Deel links in deze chat om ze op al je apparaten te kunnen benaderen. Kaart Satelliet @@ -834,6 +837,6 @@ h:mm a %1$s om %2$s - Telegram voor Android is bijgewerkt. Nieuw in versie 3.1:\n\n- Zoek naar berichten in een specifieke chat.\n- Volledig opnieuw ontworpen bijlagemenu. Verstuur contacten en audiobestanden rechtstreeks vanuit het bijlagemenu.\n- Afspelen van media in de app verbeterd (YouTube, Vimeo, SoundCloud, etc.), nieuwe speler voor grote audiobestanden.\n\nMeer weten? Kijk op:\nhttps://telegram.org/blog/search-and-media - 577 + Telegram voor Android is bijgewerkt. Nieuw in versie 3.1.2:\n\n- Recente zoekresultaten\n- Stickers aantikken en vasthouden om een voorbeeld weer te geven voor het versturen. + 583 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-pt-rBR/strings.xml b/TMessagesProj/src/main/res/values-pt-rBR/strings.xml index d9e826d5b..e5d5d4454 100644 --- a/TMessagesProj/src/main/res/values-pt-rBR/strings.xml +++ b/TMessagesProj/src/main/res/values-pt-rBR/strings.xml @@ -59,6 +59,7 @@ Em %1$s Desativar HASHTAGS + RECENTE Nova Lista de Transmissão Digite o nome da lista @@ -391,9 +392,11 @@ Desativado Compartilhar fotos e vídeos no chat e acessá-los em qualquer um de seus dispositivos. - Arquivos + Arquivos Compartilhados Mídia Compartilhada + Links Compartilhados Compartilhar arquivos e documentos no chat e acessá-los de qualquer um de seus dispositivos. + Compartilhe links nesse chat e os acesse de qualquer um de seus dispositivos Mapa Satélite @@ -834,6 +837,6 @@ h:mm a %1$s às %2$s - Telegram para Android foi atualizado. Novo na versão 3.1:\n\n- Busca por mensagens dentro de um chat específico.\n-Menu de anexo totalmente redesenhado. Envie contatos e arquivos de áudio diretamente do menu de anexo.\n- Reprodução melhorada de mídia dentro do aplicativo (YouTube, Vimeo, SoundCloud, etc.), novo player para grandes arquivos de áudio.\n\nMais sobre a atualização:\nhttps://telegram.org/blog/search-and-media - 577 + Seu Telegram para Android acaba de ser atualizado. Novo na versão 3.1.2\n\n- Resultados das buscas recentes\n- Pressione e mantenha em um sticker para pré-visualizar antes do envio + 583 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-pt-rPT/strings.xml b/TMessagesProj/src/main/res/values-pt-rPT/strings.xml index 02883ee2d..3f82bb00e 100644 --- a/TMessagesProj/src/main/res/values-pt-rPT/strings.xml +++ b/TMessagesProj/src/main/res/values-pt-rPT/strings.xml @@ -59,6 +59,7 @@ Em %1$s Desativar HASHTAGS + RECENTE Nova Lista de Transmissão Digite o nome da lista @@ -391,9 +392,11 @@ Desativado Compartilhar fotos e vídeos no chat e acessá-los em qualquer um de seus dispositivos. - Arquivos + Arquivos Compartilhados Mídia Compartilhada + Links Compartilhados Compartilhar arquivos e documentos no chat e acessá-los de qualquer um de seus dispositivos. + Compartilhe links nesse chat e os acesse de qualquer um de seus dispositivos Mapa Satélite @@ -834,6 +837,6 @@ h:mm a %1$s às %2$s - Telegram para Android foi atualizado. Novo na versão 3.1:\n\n- Busca por mensagens dentro de um chat específico.\n-Menu de anexo totalmente redesenhado. Envie contatos e arquivos de áudio diretamente do menu de anexo.\n- Reprodução melhorada de mídia dentro do aplicativo (YouTube, Vimeo, SoundCloud, etc.), novo player para grandes arquivos de áudio.\n\nMais sobre a atualização:\nhttps://telegram.org/blog/search-and-media - 577 + Seu Telegram para Android acaba de ser atualizado. Novo na versão 3.1.2\n\n- Resultados das buscas recentes\n- Pressione e mantenha em um sticker para pré-visualizar antes do envio + 583 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values/strings.xml b/TMessagesProj/src/main/res/values/strings.xml index 782c27627..0562882b0 100644 --- a/TMessagesProj/src/main/res/values/strings.xml +++ b/TMessagesProj/src/main/res/values/strings.xml @@ -59,6 +59,7 @@ In %1$s Disable HASHTAGS + RECENT New Broadcast List Enter list name @@ -391,9 +392,11 @@ Disabled Share photos and videos in this chat and access them on any of your devices. - Files + Shared Files Shared Media + Shared Links Share files and documents in this chat and access them on any of your devices. + Share links in this chat and access them on any of your devices. Map Satellite @@ -834,6 +837,6 @@ h:mm a %1$s at %2$s - Telegram for Android has been updated. New in version 3.1:\n\n- Search for messages inside a specific chat.\n- Fully redesigned attachment menu. Send contacts and audio files straight from the attachment menu.\n- Improved in-app media playback (YouTube, Vimeo, SoundCloud etc.), new player for large audio files.\n\nMore about this update:\nhttps://telegram.org/blog/search-and-media - 577 + Telegram for Android has been updated. New in version 3.1.2:\n\n- Recent search results\n- Tap and hold sticker to preview before sending + 583 \ No newline at end of file diff --git a/build.gradle b/build.gradle index 712b03d91..bd99b8d76 100644 --- a/build.gradle +++ b/build.gradle @@ -4,6 +4,6 @@ buildscript { mavenCentral() } 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