1
0
mirror of https://github.com/MGislv/NekoX.git synced 2024-06-30 22:22:57 +00:00

Update to 3.1.2

This commit is contained in:
DrKLO 2015-08-13 12:23:31 +03:00
parent 82f9be238d
commit 3e90177277
85 changed files with 2856 additions and 909 deletions

View File

@ -73,7 +73,7 @@ android {
defaultConfig {
minSdkVersion 8
targetSdkVersion 22
versionCode 580
versionName "3.1.1"
versionCode 586
versionName "3.1.2"
}
}

View File

@ -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()) {

View File

@ -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();

View File

@ -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;

View File

@ -1445,6 +1445,9 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
}
private void buildShuffledPlayList() {
if (playlist.isEmpty()) {
return;
}
ArrayList<MessageObject> all = new ArrayList<>(playlist);
shuffledPlaylist.clear();
@ -2492,7 +2495,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
albumEntry.addPhoto(photoEntry);
}
}
} catch (Exception e) {
} catch (Throwable e) {
FileLog.e("tmessages", e);
} finally {
if (cursor != null) {
@ -2551,7 +2554,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
albumEntry.addPhoto(photoEntry);
}
}
} catch (Exception e) {
} catch (Throwable e) {
FileLog.e("tmessages", e);
} finally {
if (cursor != null) {

View File

@ -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();

View File

@ -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;

View File

@ -110,8 +110,9 @@ public class StickersQuery {
ArrayList<TLRPC.TL_messages_stickerSet> newStickerArray = null;
int date = 0;
String hash = null;
SQLiteCursor cursor = null;
try {
SQLiteCursor cursor = MessagesStorage.getInstance().getDatabase().queryFinalized("SELECT data, date, hash FROM stickers_v2 WHERE 1");
cursor = MessagesStorage.getInstance().getDatabase().queryFinalized("SELECT data, date, hash FROM stickers_v2 WHERE 1");
if (cursor.next()) {
ByteBufferDesc data = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(cursor.byteArrayLength(0));
if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) {
@ -128,9 +129,12 @@ public class StickersQuery {
hash = cursor.stringValue(2);
MessagesStorage.getInstance().getBuffersStorage().reuseFreeBuffer(data);
}
cursor.dispose();
} catch (Exception e) {
} catch (Throwable e) {
FileLog.e("tmessages", e);
} finally {
if (cursor != null) {
cursor.dispose();
}
}
processLoadedStickers(newStickerArray, true, date, hash);
}
@ -229,7 +233,7 @@ public class StickersQuery {
});
}
private static long getStickerSetId(TLRPC.Document document) {
public static long getStickerSetId(TLRPC.Document document) {
for (TLRPC.DocumentAttribute attribute : document.attributes) {
if (attribute instanceof TLRPC.TL_documentAttributeSticker) {
if (attribute.stickerset instanceof TLRPC.TL_inputStickerSetID) {

View File

@ -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;

View File

@ -151,6 +151,7 @@ public class CacheDispatcher extends Thread {
if (mQuit) {
return;
}
continue;
}
}
}

View File

@ -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;
}

View File

@ -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. */

View File

@ -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 {
/**

View File

@ -18,9 +18,13 @@ package org.telegram.android.volley;
import android.net.TrafficStats;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import android.text.TextUtils;
import org.telegram.android.volley.VolleyLog.MarkerLog;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Collections;
@ -53,6 +57,9 @@ public abstract class Request<T> implements Comparable<Request<T>> {
int PATCH = 7;
}
/** An event log tracing the lifetime of this request; for debugging. */
private final MarkerLog mEventLog = MarkerLog.ENABLED ? new MarkerLog() : null;
/**
* Request method of this request. Currently supports GET, POST, PUT, DELETE, HEAD, OPTIONS,
* TRACE, and PATCH.
@ -83,12 +90,6 @@ public abstract class Request<T> implements Comparable<Request<T>> {
/** Whether or not a response has been delivered for this request yet. */
private boolean mResponseDelivered = false;
// A cheap variant of request tracing used to dump slow requests.
private long mRequestBirthTime = 0;
/** Threshold at which we should log the request (even when debug logging is not enabled). */
private static final long SLOW_REQUEST_THRESHOLD_MS = 3000;
/** The retry policy for this request. */
private RetryPolicy mRetryPolicy;
@ -108,6 +109,7 @@ public abstract class Request<T> implements Comparable<Request<T>> {
* is provided by subclasses, who have a better idea of how to deliver an
* already-parsed response.
*
* @deprecated Use {@link #Request(int, String, com.android.volley.Response.ErrorListener)}.
*/
@Deprecated
public Request(String url, Response.ErrorListener listener) {
@ -155,6 +157,9 @@ public abstract class Request<T> implements Comparable<Request<T>> {
return mTag;
}
/**
* @return this request's {@link com.android.volley.Response.ErrorListener}.
*/
public Response.ErrorListener getErrorListener() {
return mErrorListener;
}
@ -196,8 +201,8 @@ public abstract class Request<T> implements Comparable<Request<T>> {
* Adds an event to this request's event log; for debugging.
*/
public void addMarker(String tag) {
if (mRequestBirthTime == 0) {
mRequestBirthTime = SystemClock.elapsedRealtime();
if (MarkerLog.ENABLED) {
mEventLog.add(tag, Thread.currentThread().getId());
}
}
@ -210,9 +215,24 @@ public abstract class Request<T> implements Comparable<Request<T>> {
if (mRequestQueue != null) {
mRequestQueue.finish(this);
}
long requestTime = SystemClock.elapsedRealtime() - mRequestBirthTime;
if (requestTime >= SLOW_REQUEST_THRESHOLD_MS) {
VolleyLog.d("%d ms: %s", requestTime, this.toString());
if (MarkerLog.ENABLED) {
final long threadId = Thread.currentThread().getId();
if (Looper.myLooper() != Looper.getMainLooper()) {
// If we finish marking off of the main thread, we need to
// actually do it on the main thread to ensure correct ordering.
Handler mainThread = new Handler(Looper.getMainLooper());
mainThread.post(new Runnable() {
@Override
public void run() {
mEventLog.add(tag, threadId);
mEventLog.finish(this.toString());
}
});
return;
}
mEventLog.add(tag, threadId);
mEventLog.finish(this.toString());
}
}

View File

@ -19,9 +19,11 @@ package org.telegram.android.volley;
import android.os.Handler;
import android.os.Looper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
@ -31,12 +33,18 @@ import java.util.concurrent.atomic.AtomicInteger;
/**
* A request dispatch queue with a thread pool of dispatchers.
*
* Calling {@link #add(org.telegram.android.volley.Request)} will enqueue the given Request for dispatch,
* Calling {@link #add(Request)} will enqueue the given Request for dispatch,
* resolving from either cache or network on a worker thread, and then delivering
* a parsed response on the main thread.
*/
public class RequestQueue {
/** Callback interface for completed requests. */
public static interface RequestFinishedListener<T> {
/** Called when a request has finished processing. */
public void onRequestFinished(Request<T> request);
}
/** Used for generating monotonically-increasing sequence numbers for requests. */
private AtomicInteger mSequenceGenerator = new AtomicInteger();
@ -86,6 +94,9 @@ public class RequestQueue {
/** The cache dispatcher. */
private CacheDispatcher mCacheDispatcher;
private List<RequestFinishedListener> mFinishedListeners =
new ArrayList<RequestFinishedListener>();
/**
* Creates the worker pool. Processing will not begin until {@link #start()} is called.
*
@ -149,9 +160,9 @@ public class RequestQueue {
if (mCacheDispatcher != null) {
mCacheDispatcher.quit();
}
for (NetworkDispatcher mDispatcher : mDispatchers) {
if (mDispatcher != null) {
mDispatcher.quit();
for (int i = 0; i < mDispatchers.length; i++) {
if (mDispatchers[i] != null) {
mDispatchers[i].quit();
}
}
}
@ -175,7 +186,7 @@ public class RequestQueue {
* {@link RequestQueue#cancelAll(RequestFilter)}.
*/
public interface RequestFilter {
boolean apply(Request<?> request);
public boolean apply(Request<?> request);
}
/**
@ -261,11 +272,16 @@ public class RequestQueue {
* <p>Releases waiting requests for <code>request.getCacheKey()</code> if
* <code>request.shouldCache()</code>.</p>
*/
void finish(Request<?> request) {
<T> void finish(Request<T> request) {
// Remove from the set of requests currently being processed.
synchronized (mCurrentRequests) {
mCurrentRequests.remove(request);
}
synchronized (mFinishedListeners) {
for (RequestFinishedListener<T> listener : mFinishedListeners) {
listener.onRequestFinished(request);
}
}
if (request.shouldCache()) {
synchronized (mWaitingRequests) {
@ -283,4 +299,19 @@ public class RequestQueue {
}
}
}
public <T> void addRequestFinishedListener(RequestFinishedListener<T> listener) {
synchronized (mFinishedListeners) {
mFinishedListeners.add(listener);
}
}
/**
* Remove a RequestFinishedListener. Has no effect if listener was not previously added.
*/
public <T> void removeRequestFinishedListener(RequestFinishedListener<T> listener) {
synchronized (mFinishedListeners) {
mFinishedListeners.remove(listener);
}
}
}

View File

@ -26,7 +26,7 @@ public class Response<T> {
/** Callback interface for delivering parsed responses. */
public interface Listener<T> {
/** Called when a response is received. */
void onResponse(T response);
public void onResponse(T response);
}
/** Callback interface for delivering error responses. */
@ -35,7 +35,7 @@ public class Response<T> {
* Callback method that an error has been occurred with the
* provided error code and optional user-readable message.
*/
void onErrorResponse(VolleyError error);
public void onErrorResponse(VolleyError error);
}
/** Returns a successful response containing the parsed result. */

View File

@ -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);
}

View File

@ -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;
}

View File

@ -29,3 +29,4 @@ public class ServerError extends VolleyError {
super();
}
}

View File

@ -23,7 +23,12 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/** Logging helper class. */
/**
* Logging helper class.
* <p/>
* to see Volley logs call:<br/>
* {@code <android-sdk>/platform-tools/adb shell setprop log.tag.Volley VERBOSE}
*/
public class VolleyLog {
public static String TAG = "Volley";
@ -89,7 +94,7 @@ public class VolleyLog {
callingClass = callingClass.substring(callingClass.lastIndexOf('.') + 1);
callingClass = callingClass.substring(callingClass.lastIndexOf('$') + 1);
caller = callingClass + "" + trace[i].getMethodName();
caller = callingClass + "." + trace[i].getMethodName();
break;
}
}

View File

@ -16,6 +16,8 @@
package org.telegram.android.volley.toolbox;
import org.telegram.android.volley.AuthFailureError;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerFuture;
@ -23,14 +25,12 @@ import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import org.telegram.android.volley.AuthFailureError;
/**
* An Authenticator that uses {@link AccountManager} to get auth
* tokens of a specified type for a specified account.
*/
public class AndroidAuthenticator implements Authenticator {
private final Context mContext;
private final AccountManager mAccountManager;
private final Account mAccount;
private final String mAuthTokenType;
private final boolean mNotifyAuthFailure;
@ -54,7 +54,13 @@ public class AndroidAuthenticator implements Authenticator {
*/
public AndroidAuthenticator(Context context, Account account, String authTokenType,
boolean notifyAuthFailure) {
mContext = context;
this(AccountManager.get(context), account, authTokenType, notifyAuthFailure);
}
// Visible for testing. Allows injection of a mock AccountManager.
AndroidAuthenticator(AccountManager accountManager, Account account,
String authTokenType, boolean notifyAuthFailure) {
mAccountManager = accountManager;
mAccount = account;
mAuthTokenType = authTokenType;
mNotifyAuthFailure = notifyAuthFailure;
@ -71,8 +77,7 @@ public class AndroidAuthenticator implements Authenticator {
@SuppressWarnings("deprecation")
@Override
public String getAuthToken() throws AuthFailureError {
final AccountManager accountManager = AccountManager.get(mContext);
AccountManagerFuture<Bundle> future = accountManager.getAuthToken(mAccount,
AccountManagerFuture<Bundle> future = mAccountManager.getAuthToken(mAccount,
mAuthTokenType, mNotifyAuthFailure, null, null);
Bundle result;
try {
@ -97,6 +102,6 @@ public class AndroidAuthenticator implements Authenticator {
@Override
public void invalidateAuthToken(String authToken) {
AccountManager.get(mContext).invalidateAuthToken(mAccount.type, authToken);
mAccountManager.invalidateAuthToken(mAccount.type, authToken);
}
}

View File

@ -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);
}

View File

@ -18,15 +18,9 @@ package org.telegram.android.volley.toolbox;
import android.os.SystemClock;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.impl.cookie.DateUtils;
import org.telegram.android.volley.AuthFailureError;
import org.telegram.android.volley.Cache;
import org.telegram.android.volley.Cache.Entry;
import org.telegram.android.volley.Network;
import org.telegram.android.volley.NetworkError;
import org.telegram.android.volley.NetworkResponse;
@ -38,6 +32,14 @@ import org.telegram.android.volley.TimeoutError;
import org.telegram.android.volley.VolleyError;
import org.telegram.android.volley.VolleyLog;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.impl.cookie.DateUtils;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
@ -49,14 +51,14 @@ import java.util.Map;
import java.util.TreeMap;
/**
* A network performing Volley requests over an {@link org.telegram.android.volley.toolbox.HttpStack}.
* A network performing Volley requests over an {@link HttpStack}.
*/
public class BasicNetwork implements Network {
protected static final boolean DEBUG = VolleyLog.DEBUG;
private static final int SLOW_REQUEST_THRESHOLD_MS = 3000;
private static int SLOW_REQUEST_THRESHOLD_MS = 3000;
private static final int DEFAULT_POOL_SIZE = 4096;
private static int DEFAULT_POOL_SIZE = 4096;
protected final HttpStack mHttpStack;
@ -99,7 +101,7 @@ public class BasicNetwork implements Network {
// Handle cache validation.
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
Cache.Entry entry = request.getCacheEntry();
Entry entry = request.getCacheEntry();
if (entry == null) {
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
responseHeaders, true,
@ -210,8 +212,8 @@ public class BasicNetwork implements Network {
headers.put("If-None-Match", entry.etag);
}
if (entry.serverDate > 0) {
Date refTime = new Date(entry.serverDate);
if (entry.lastModified > 0) {
Date refTime = new Date(entry.lastModified);
headers.put("If-Modified-Since", DateUtils.formatDate(refTime));
}
}
@ -256,8 +258,8 @@ public class BasicNetwork implements Network {
*/
protected static Map<String, String> convertHeaders(Header[] headers) {
Map<String, String> result = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
for (Header header : headers) {
result.put(header.getName(), header.getValue());
for (int i = 0; i < headers.length; i++) {
result.put(headers[i].getName(), headers[i].getValue());
}
return result;
}

View File

@ -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.
*/

View File

@ -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<String, CacheHeader> mEntries =
new LinkedHashMap<String, CacheHeader>(16, .75f, true);
/**
* Total amount of space currently used by the cache in bytes.
*/
/** Total amount of space currently used by the cache in bytes. */
private long mTotalSize = 0;
/**
* 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<String, String> 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;
}
}

View File

@ -16,6 +16,10 @@
package org.telegram.android.volley.toolbox;
import org.telegram.android.volley.AuthFailureError;
import org.telegram.android.volley.Request;
import org.telegram.android.volley.Request.Method;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
@ -33,8 +37,6 @@ import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.telegram.android.volley.AuthFailureError;
import org.telegram.android.volley.Request;
import java.io.IOException;
import java.net.URI;
@ -92,7 +94,7 @@ public class HttpClientStack implements HttpStack {
/* protected */ static HttpUriRequest createHttpRequest(Request<?> request,
Map<String, String> additionalHeaders) throws AuthFailureError {
switch (request.getMethod()) {
case Request.Method.DEPRECATED_GET_OR_POST: {
case Method.DEPRECATED_GET_OR_POST: {
// This is the deprecated way that needs to be handled for backwards compatibility.
// If the request's post body is null, then the assumption is that the request is
// GET. Otherwise, it is assumed that the request is a POST.
@ -108,29 +110,29 @@ public class HttpClientStack implements HttpStack {
return new HttpGet(request.getUrl());
}
}
case Request.Method.GET:
case Method.GET:
return new HttpGet(request.getUrl());
case Request.Method.DELETE:
case Method.DELETE:
return new HttpDelete(request.getUrl());
case Request.Method.POST: {
case Method.POST: {
HttpPost postRequest = new HttpPost(request.getUrl());
postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(postRequest, request);
return postRequest;
}
case Request.Method.PUT: {
case Method.PUT: {
HttpPut putRequest = new HttpPut(request.getUrl());
putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(putRequest, request);
return putRequest;
}
case Request.Method.HEAD:
case Method.HEAD:
return new HttpHead(request.getUrl());
case Request.Method.OPTIONS:
case Method.OPTIONS:
return new HttpOptions(request.getUrl());
case Request.Method.TRACE:
case Method.TRACE:
return new HttpTrace(request.getUrl());
case Request.Method.PATCH: {
case Method.PATCH: {
HttpPatch patchRequest = new HttpPatch(request.getUrl());
patchRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(patchRequest, request);

View File

@ -16,11 +16,12 @@
package org.telegram.android.volley.toolbox;
import org.telegram.android.volley.Cache;
import org.telegram.android.volley.NetworkResponse;
import org.apache.http.impl.cookie.DateParseException;
import org.apache.http.impl.cookie.DateUtils;
import org.apache.http.protocol.HTTP;
import org.telegram.android.volley.Cache;
import org.telegram.android.volley.NetworkResponse;
import java.util.Map;
@ -41,10 +42,14 @@ public class HttpHeaderParser {
Map<String, String> headers = response.headers;
long serverDate = 0;
long lastModified = 0;
long serverExpires = 0;
long softExpire = 0;
long finalExpire = 0;
long maxAge = 0;
long staleWhileRevalidate = 0;
boolean hasCacheControl = false;
boolean mustRevalidate = false;
String serverEtag = null;
String headerValue;
@ -58,18 +63,22 @@ public class HttpHeaderParser {
if (headerValue != null) {
hasCacheControl = true;
String[] tokens = headerValue.split(",");
for (String token1 : tokens) {
String token = token1.trim();
for (int i = 0; i < tokens.length; i++) {
String token = tokens[i].trim();
if (token.equals("no-cache") || token.equals("no-store")) {
return null;
} else if (token.startsWith("max-age=")) {
try {
maxAge = Long.parseLong(token.substring(8));
} catch (Exception e) {
/**/
}
} else if (token.startsWith("stale-while-revalidate=")) {
try {
staleWhileRevalidate = Long.parseLong(token.substring(23));
} catch (Exception e) {
}
} else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) {
maxAge = 0;
mustRevalidate = true;
}
}
}
@ -79,23 +88,33 @@ public class HttpHeaderParser {
serverExpires = parseDateAsEpoch(headerValue);
}
headerValue = headers.get("Last-Modified");
if (headerValue != null) {
lastModified = parseDateAsEpoch(headerValue);
}
serverEtag = headers.get("ETag");
// Cache-Control takes precedence over an Expires header, even if both exist and Expires
// is more restrictive.
if (hasCacheControl) {
softExpire = now + maxAge * 1000;
finalExpire = mustRevalidate
? softExpire
: softExpire + staleWhileRevalidate * 1000;
} else if (serverDate > 0 && serverExpires >= serverDate) {
// Default semantic for Expire header in HTTP specification is softExpire.
softExpire = now + (serverExpires - serverDate);
finalExpire = softExpire;
}
Cache.Entry entry = new Cache.Entry();
entry.data = response.data;
entry.etag = serverEtag;
entry.softTtl = softExpire;
entry.ttl = entry.softTtl;
entry.ttl = finalExpire;
entry.serverDate = serverDate;
entry.lastModified = lastModified;
entry.responseHeaders = headers;
return entry;
@ -115,10 +134,14 @@ public class HttpHeaderParser {
}
/**
* Returns the charset specified in the Content-Type of this header,
* or the HTTP default (ISO-8859-1) if none can be found.
* Retrieve a charset from headers
*
* @param headers An {@link java.util.Map} of headers
* @param defaultCharset Charset to return if none can be found
* @return Returns the charset specified in the Content-Type of this header,
* or the defaultCharset if none can be found.
*/
public static String parseCharset(Map<String, String> headers) {
public static String parseCharset(Map<String, String> headers, String defaultCharset) {
String contentType = headers.get(HTTP.CONTENT_TYPE);
if (contentType != null) {
String[] params = contentType.split(";");
@ -132,6 +155,14 @@ public class HttpHeaderParser {
}
}
return HTTP.DEFAULT_CONTENT_CHARSET;
return defaultCharset;
}
/**
* Returns the charset specified in the Content-Type of this header,
* or the HTTP default (ISO-8859-1) if none can be found.
*/
public static String parseCharset(Map<String, String> headers) {
return parseCharset(headers, HTTP.DEFAULT_CONTENT_CHARSET);
}
}

View File

@ -16,10 +16,11 @@
package org.telegram.android.volley.toolbox;
import org.apache.http.HttpResponse;
import org.telegram.android.volley.AuthFailureError;
import org.telegram.android.volley.Request;
import org.apache.http.HttpResponse;
import java.io.IOException;
import java.util.Map;
@ -38,7 +39,7 @@ public interface HttpStack {
* {@link Request#getHeaders()}
* @return the HTTP response
*/
HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError;
}

View File

@ -16,17 +16,20 @@
package org.telegram.android.volley.toolbox;
import org.telegram.android.volley.AuthFailureError;
import org.telegram.android.volley.Request;
import org.telegram.android.volley.Request.Method;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.ProtocolVersion;
import org.apache.http.StatusLine;
import org.apache.http.entity.BasicHttpEntity;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.message.BasicStatusLine;
import org.telegram.android.volley.AuthFailureError;
import org.telegram.android.volley.Request;
import java.io.DataOutputStream;
import java.io.IOException;
@ -42,7 +45,7 @@ import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSocketFactory;
/**
* An {@link org.telegram.android.volley.toolbox.HttpStack} based on {@link HttpURLConnection}.
* An {@link HttpStack} based on {@link HttpURLConnection}.
*/
public class HurlStack implements HttpStack {
@ -56,7 +59,7 @@ public class HurlStack implements HttpStack {
* Returns a URL to use instead of the provided one, or null to indicate
* this URL should not be used at all.
*/
String rewriteUrl(String originalUrl);
public String rewriteUrl(String originalUrl);
}
private final UrlRewriter mUrlRewriter;
@ -113,7 +116,9 @@ public class HurlStack implements HttpStack {
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
response.setEntity(entityFromConnection(connection));
if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) {
response.setEntity(entityFromConnection(connection));
}
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
@ -123,6 +128,20 @@ public class HurlStack implements HttpStack {
return response;
}
/**
* Checks if a response message contains a body.
* @see <a href="https://tools.ietf.org/html/rfc7230#section-3.3">RFC 7230 section 3.3</a>
* @param requestMethod request method
* @param responseCode response status code
* @return whether the response has a body
*/
private static boolean hasResponseBody(int requestMethod, int responseCode) {
return requestMethod != Request.Method.HEAD
&& !(HttpStatus.SC_CONTINUE <= responseCode && responseCode < HttpStatus.SC_OK)
&& responseCode != HttpStatus.SC_NO_CONTENT
&& responseCode != HttpStatus.SC_NOT_MODIFIED;
}
/**
* Initializes an {@link HttpEntity} from the given {@link HttpURLConnection}.
* @param connection
@ -177,7 +196,7 @@ public class HurlStack implements HttpStack {
/* package */ static void setConnectionParametersForRequest(HttpURLConnection connection,
Request<?> request) throws IOException, AuthFailureError {
switch (request.getMethod()) {
case Request.Method.DEPRECATED_GET_OR_POST:
case Method.DEPRECATED_GET_OR_POST:
// This is the deprecated way that needs to be handled for backwards compatibility.
// If the request's post body is null, then the assumption is that the request is
// GET. Otherwise, it is assumed that the request is a POST.
@ -195,32 +214,32 @@ public class HurlStack implements HttpStack {
out.close();
}
break;
case Request.Method.GET:
case Method.GET:
// Not necessary to set the request method because connection defaults to GET but
// being explicit here.
connection.setRequestMethod("GET");
break;
case Request.Method.DELETE:
case Method.DELETE:
connection.setRequestMethod("DELETE");
break;
case Request.Method.POST:
case Method.POST:
connection.setRequestMethod("POST");
addBodyIfExists(connection, request);
break;
case Request.Method.PUT:
case Method.PUT:
connection.setRequestMethod("PUT");
addBodyIfExists(connection, request);
break;
case Request.Method.HEAD:
case Method.HEAD:
connection.setRequestMethod("HEAD");
break;
case Request.Method.OPTIONS:
case Method.OPTIONS:
connection.setRequestMethod("OPTIONS");
break;
case Request.Method.TRACE:
case Method.TRACE:
connection.setRequestMethod("TRACE");
break;
case Request.Method.PATCH:
case Method.PATCH:
connection.setRequestMethod("PATCH");
addBodyIfExists(connection, request);
break;

View File

@ -20,10 +20,11 @@ import android.graphics.Bitmap.Config;
import android.os.Handler;
import android.os.Looper;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import org.telegram.android.volley.Request;
import org.telegram.android.volley.RequestQueue;
import org.telegram.android.volley.Response;
import org.telegram.android.volley.Response.ErrorListener;
import org.telegram.android.volley.Response.Listener;
import org.telegram.android.volley.VolleyError;
import java.util.HashMap;
@ -71,8 +72,8 @@ public class ImageLoader {
* must not block. Implementation with an LruCache is recommended.
*/
public interface ImageCache {
Bitmap getBitmap(String url);
void putBitmap(String url, Bitmap bitmap);
public Bitmap getBitmap(String url);
public void putBitmap(String url, Bitmap bitmap);
}
/**
@ -127,7 +128,7 @@ public class ImageLoader {
* or
* - onErrorResponse will be called if there was an error loading the image.
*/
public interface ImageListener extends Response.ErrorListener {
public interface ImageListener extends ErrorListener {
/**
* Listens for non-error changes to the loading of the image request.
*
@ -138,7 +139,7 @@ public class ImageLoader {
* image loading in order to, for example, run an animation to fade in network loaded
* images.
*/
void onResponse(ImageContainer response, boolean isImmediate);
public void onResponse(ImageContainer response, boolean isImmediate);
}
/**
@ -149,9 +150,22 @@ public class ImageLoader {
* @return True if the item exists in cache, false otherwise.
*/
public boolean isCached(String requestUrl, int maxWidth, int maxHeight) {
return isCached(requestUrl, maxWidth, maxHeight, ScaleType.CENTER_INSIDE);
}
/**
* Checks if the item is available in the cache.
*
* @param requestUrl The url of the remote image
* @param maxWidth The maximum width of the returned image.
* @param maxHeight The maximum height of the returned image.
* @param scaleType The scaleType of the imageView.
* @return True if the item exists in cache, false otherwise.
*/
public boolean isCached(String requestUrl, int maxWidth, int maxHeight, ScaleType scaleType) {
throwIfNotOnMainThread();
String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight);
String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType);
return mCache.getBitmap(cacheKey) != null;
}
@ -168,6 +182,15 @@ public class ImageLoader {
return get(requestUrl, listener, 0, 0);
}
/**
* Equivalent to calling {@link #get(String, ImageListener, int, int, ScaleType)} with
* {@code Scaletype == ScaleType.CENTER_INSIDE}.
*/
public ImageContainer get(String requestUrl, ImageListener imageListener,
int maxWidth, int maxHeight) {
return get(requestUrl, imageListener, maxWidth, maxHeight, ScaleType.CENTER_INSIDE);
}
/**
* Issues a bitmap request with the given URL if that image is not available
* in the cache, and returns a bitmap container that contains all of the data
@ -177,15 +200,17 @@ public class ImageLoader {
* @param imageListener The listener to call when the remote image is loaded
* @param maxWidth The maximum width of the returned image.
* @param maxHeight The maximum height of the returned image.
* @param scaleType The ImageViews ScaleType used to calculate the needed image size.
* @return A container object that contains all of the properties of the request, as well as
* the currently available image (default if remote is not loaded).
*/
public ImageContainer get(String requestUrl, ImageListener imageListener,
int maxWidth, int maxHeight) {
int maxWidth, int maxHeight, ScaleType scaleType) {
// only fulfill requests that were initiated from the main thread.
throwIfNotOnMainThread();
final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight);
final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType);
// Try to look up the request in the cache of remote images.
Bitmap cachedBitmap = mCache.getBitmap(cacheKey);
@ -213,7 +238,8 @@ public class ImageLoader {
// The request is not already in flight. Send the new request to the network and
// track it.
Request<Bitmap> newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, cacheKey);
Request<Bitmap> newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, scaleType,
cacheKey);
mRequestQueue.add(newRequest);
mInFlightRequests.put(cacheKey,
@ -221,14 +247,14 @@ public class ImageLoader {
return imageContainer;
}
protected Request<Bitmap> makeImageRequest(String requestUrl, int maxWidth, int maxHeight, final String cacheKey) {
return new ImageRequest(requestUrl, new Response.Listener<Bitmap>() {
protected Request<Bitmap> makeImageRequest(String requestUrl, int maxWidth, int maxHeight,
ScaleType scaleType, final String cacheKey) {
return new ImageRequest(requestUrl, new Listener<Bitmap>() {
@Override
public void onResponse(Bitmap response) {
onGetImageSuccess(cacheKey, response);
}
}, maxWidth, maxHeight,
Config.RGB_565, new Response.ErrorListener() {
}, maxWidth, maxHeight, scaleType, Config.RGB_565, new ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
onGetImageError(cacheKey, error);
@ -471,8 +497,11 @@ public class ImageLoader {
* @param url The URL of the request.
* @param maxWidth The max-width of the output.
* @param maxHeight The max-height of the output.
* @param scaleType The scaleType of the imageView.
*/
private static String getCacheKey(String url, int maxWidth, int maxHeight) {
return "#W" + maxWidth + "#H" + maxHeight + url;
private static String getCacheKey(String url, int maxWidth, int maxHeight, ScaleType scaleType) {
return new StringBuilder(url.length() + 12).append("#W").append(maxWidth)
.append("#H").append(maxHeight).append("#S").append(scaleType.ordinal()).append(url)
.toString();
}
}

View File

@ -16,10 +16,6 @@
package org.telegram.android.volley.toolbox;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import org.telegram.android.volley.DefaultRetryPolicy;
import org.telegram.android.volley.NetworkResponse;
import org.telegram.android.volley.ParseError;
@ -27,6 +23,11 @@ import org.telegram.android.volley.Request;
import org.telegram.android.volley.Response;
import org.telegram.android.volley.VolleyLog;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.widget.ImageView.ScaleType;
/**
* A canned request for getting an image at a given URL and calling
* back with a decoded Bitmap.
@ -45,6 +46,7 @@ public class ImageRequest extends Request<Bitmap> {
private final Config mDecodeConfig;
private final int mMaxWidth;
private final int mMaxHeight;
private ScaleType mScaleType;
/** Decoding lock so that we don't decode more than one image at a time (to avoid OOM's) */
private static final Object sDecodeLock = new Object();
@ -63,20 +65,32 @@ public class ImageRequest extends Request<Bitmap> {
* @param maxWidth Maximum width to decode this bitmap to, or zero for none
* @param maxHeight Maximum height to decode this bitmap to, or zero for
* none
* @param scaleType The ImageViews ScaleType used to calculate the needed image size.
* @param decodeConfig Format to decode the bitmap to
* @param errorListener Error listener, or null to ignore errors
*/
public ImageRequest(String url, Response.Listener<Bitmap> listener, int maxWidth, int maxHeight,
Config decodeConfig, Response.ErrorListener errorListener) {
super(Method.GET, url, errorListener);
ScaleType scaleType, Config decodeConfig, Response.ErrorListener errorListener) {
super(Method.GET, url, errorListener);
setRetryPolicy(
new DefaultRetryPolicy(IMAGE_TIMEOUT_MS, IMAGE_MAX_RETRIES, IMAGE_BACKOFF_MULT));
mListener = listener;
mDecodeConfig = decodeConfig;
mMaxWidth = maxWidth;
mMaxHeight = maxHeight;
mScaleType = scaleType;
}
/**
* For API compatibility with the pre-ScaleType variant of the constructor. Equivalent to
* the normal constructor with {@code ScaleType.CENTER_INSIDE}.
*/
@Deprecated
public ImageRequest(String url, Response.Listener<Bitmap> listener, int maxWidth, int maxHeight,
Config decodeConfig, Response.ErrorListener errorListener) {
this(url, listener, maxWidth, maxHeight,
ScaleType.CENTER_INSIDE, decodeConfig, errorListener);
}
@Override
public Priority getPriority() {
return Priority.LOW;
@ -92,14 +106,24 @@ public class ImageRequest extends Request<Bitmap> {
* maintain aspect ratio with primary dimension
* @param actualPrimary Actual size of the primary dimension
* @param actualSecondary Actual size of the secondary dimension
* @param scaleType The ScaleType used to calculate the needed image size.
*/
private static int getResizedDimension(int maxPrimary, int maxSecondary, int actualPrimary,
int actualSecondary) {
int actualSecondary, ScaleType scaleType) {
// If no dominant value at all, just return the actual.
if (maxPrimary == 0 && maxSecondary == 0) {
if ((maxPrimary == 0) && (maxSecondary == 0)) {
return actualPrimary;
}
// If ScaleType.FIT_XY fill the whole rectangle, ignore ratio.
if (scaleType == ScaleType.FIT_XY) {
if (maxPrimary == 0) {
return actualPrimary;
}
return maxPrimary;
}
// If primary is unspecified, scale primary to match secondary's scaling ratio.
if (maxPrimary == 0) {
double ratio = (double) maxSecondary / (double) actualSecondary;
@ -112,7 +136,16 @@ public class ImageRequest extends Request<Bitmap> {
double ratio = (double) actualSecondary / (double) actualPrimary;
int resized = maxPrimary;
if (resized * ratio > maxSecondary) {
// If ScaleType.CENTER_CROP fill the whole rectangle, preserve aspect ratio.
if (scaleType == ScaleType.CENTER_CROP) {
if ((resized * ratio) < maxSecondary) {
resized = (int) (maxSecondary / ratio);
}
return resized;
}
if ((resized * ratio) > maxSecondary) {
resized = (int) (maxSecondary / ratio);
}
return resized;
@ -150,9 +183,9 @@ public class ImageRequest extends Request<Bitmap> {
// Then compute the dimensions we would ideally like to decode to.
int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight,
actualWidth, actualHeight);
actualWidth, actualHeight, mScaleType);
int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth,
actualHeight, actualWidth);
actualHeight, actualWidth, mScaleType);
// Decode to the nearest power of two scaling factor.
decodeOptions.inJustDecodeBounds = false;

View File

@ -16,11 +16,14 @@
package org.telegram.android.volley.toolbox;
import org.json.JSONArray;
import org.json.JSONException;
import org.telegram.android.volley.NetworkResponse;
import org.telegram.android.volley.ParseError;
import org.telegram.android.volley.Response;
import org.telegram.android.volley.Response.ErrorListener;
import org.telegram.android.volley.Response.Listener;
import org.json.JSONArray;
import org.json.JSONException;
import java.io.UnsupportedEncodingException;
@ -35,15 +38,30 @@ public class JsonArrayRequest extends JsonRequest<JSONArray> {
* @param listener Listener to receive the JSON response
* @param errorListener Error listener, or null to ignore errors.
*/
public JsonArrayRequest(String url, Response.Listener<JSONArray> listener, Response.ErrorListener errorListener) {
public JsonArrayRequest(String url, Listener<JSONArray> listener, ErrorListener errorListener) {
super(Method.GET, url, null, listener, errorListener);
}
/**
* Creates a new request.
* @param method the HTTP method to use
* @param url URL to fetch the JSON from
* @param jsonRequest A {@link JSONArray} to post with the request. Null is allowed and
* indicates no parameters will be posted along with request.
* @param listener Listener to receive the JSON response
* @param errorListener Error listener, or null to ignore errors.
*/
public JsonArrayRequest(int method, String url, JSONArray jsonRequest,
Listener<JSONArray> listener, ErrorListener errorListener) {
super(method, url, (jsonRequest == null) ? null : jsonRequest.toString(), listener,
errorListener);
}
@Override
protected Response<JSONArray> parseNetworkResponse(NetworkResponse response) {
try {
String jsonString =
new String(response.data, HttpHeaderParser.parseCharset(response.headers));
String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
return Response.success(new JSONArray(jsonString),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {

View File

@ -16,11 +16,14 @@
package org.telegram.android.volley.toolbox;
import org.json.JSONException;
import org.json.JSONObject;
import org.telegram.android.volley.NetworkResponse;
import org.telegram.android.volley.ParseError;
import org.telegram.android.volley.Response;
import org.telegram.android.volley.Response.ErrorListener;
import org.telegram.android.volley.Response.Listener;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.UnsupportedEncodingException;
@ -40,7 +43,7 @@ public class JsonObjectRequest extends JsonRequest<JSONObject> {
* @param errorListener Error listener, or null to ignore errors.
*/
public JsonObjectRequest(int method, String url, JSONObject jsonRequest,
Response.Listener<JSONObject> listener, Response.ErrorListener errorListener) {
Listener<JSONObject> listener, ErrorListener errorListener) {
super(method, url, (jsonRequest == null) ? null : jsonRequest.toString(), listener,
errorListener);
}
@ -49,9 +52,10 @@ public class JsonObjectRequest extends JsonRequest<JSONObject> {
* Constructor which defaults to <code>GET</code> if <code>jsonRequest</code> is
* <code>null</code>, <code>POST</code> otherwise.
*
* @see #JsonObjectRequest(int, String, JSONObject, Listener, ErrorListener)
*/
public JsonObjectRequest(String url, JSONObject jsonRequest, Response.Listener<JSONObject> listener,
Response.ErrorListener errorListener) {
public JsonObjectRequest(String url, JSONObject jsonRequest, Listener<JSONObject> listener,
ErrorListener errorListener) {
this(jsonRequest == null ? Method.GET : Method.POST, url, jsonRequest,
listener, errorListener);
}
@ -59,8 +63,8 @@ public class JsonObjectRequest extends JsonRequest<JSONObject> {
@Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
try {
String jsonString =
new String(response.data, HttpHeaderParser.parseCharset(response.headers));
String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
return Response.success(new JSONObject(jsonString),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {

View File

@ -19,6 +19,8 @@ package org.telegram.android.volley.toolbox;
import org.telegram.android.volley.NetworkResponse;
import org.telegram.android.volley.Request;
import org.telegram.android.volley.Response;
import org.telegram.android.volley.Response.ErrorListener;
import org.telegram.android.volley.Response.Listener;
import org.telegram.android.volley.VolleyLog;
import java.io.UnsupportedEncodingException;
@ -30,28 +32,29 @@ import java.io.UnsupportedEncodingException;
* @param <T> JSON type of response expected
*/
public abstract class JsonRequest<T> extends Request<T> {
/** Charset for request. */
private static final String PROTOCOL_CHARSET = "utf-8";
/** Default charset for JSON request. */
protected static final String PROTOCOL_CHARSET = "utf-8";
/** Content type for request. */
private static final String PROTOCOL_CONTENT_TYPE =
String.format("application/json; charset=%s", PROTOCOL_CHARSET);
private final Response.Listener<T> mListener;
private final Listener<T> mListener;
private final String mRequestBody;
/**
* Deprecated constructor for a JsonRequest which defaults to GET unless {@link #getPostBody()}
* or {@link #getPostParams()} is overridden (which defaults to POST).
*
* @deprecated Use {@link #JsonRequest(int, String, String, Listener, ErrorListener)}.
*/
public JsonRequest(String url, String requestBody, Response.Listener<T> listener,
Response.ErrorListener errorListener) {
public JsonRequest(String url, String requestBody, Listener<T> listener,
ErrorListener errorListener) {
this(Method.DEPRECATED_GET_OR_POST, url, requestBody, listener, errorListener);
}
public JsonRequest(int method, String url, String requestBody, Response.Listener<T> listener,
Response.ErrorListener errorListener) {
public JsonRequest(int method, String url, String requestBody, Listener<T> listener,
ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
mRequestBody = requestBody;

View File

@ -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;

View File

@ -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;

View File

@ -126,7 +126,10 @@ public class RequestFuture<T> implements Future<T>, Response.Listener<T>,
@Override
public boolean isCancelled() {
return mRequest != null && mRequest.isCanceled();
if (mRequest == null) {
return false;
}
return mRequest.isCanceled();
}
@Override

View File

@ -19,6 +19,8 @@ package org.telegram.android.volley.toolbox;
import org.telegram.android.volley.NetworkResponse;
import org.telegram.android.volley.Request;
import org.telegram.android.volley.Response;
import org.telegram.android.volley.Response.ErrorListener;
import org.telegram.android.volley.Response.Listener;
import java.io.UnsupportedEncodingException;
@ -26,7 +28,7 @@ import java.io.UnsupportedEncodingException;
* A canned request for retrieving the response body at a given URL as a String.
*/
public class StringRequest extends Request<String> {
private final Response.Listener<String> mListener;
private final Listener<String> mListener;
/**
* Creates a new request with the given method.
@ -36,8 +38,8 @@ public class StringRequest extends Request<String> {
* @param listener Listener to receive the String response
* @param errorListener Error listener, or null to ignore errors
*/
public StringRequest(int method, String url, Response.Listener<String> listener,
Response.ErrorListener errorListener) {
public StringRequest(int method, String url, Listener<String> listener,
ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
}
@ -49,7 +51,7 @@ public class StringRequest extends Request<String> {
* @param listener Listener to receive the String response
* @param errorListener Error listener, or null to ignore errors
*/
public StringRequest(String url, Response.Listener<String> listener, Response.ErrorListener errorListener) {
public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
this(Method.GET, url, listener, errorListener);
}

View File

@ -48,7 +48,6 @@ public class Volley {
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
/**/
}
if (stack == null) {

View File

@ -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;

View File

@ -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";

View File

@ -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);
}

View File

@ -573,6 +573,14 @@ public class FileLoader {
return getPathToAttach(sizeFull);
}
}
} else if (message.media instanceof TLRPC.TL_messageMediaWebPage && message.media.webpage.photo != null) {
ArrayList<TLRPC.PhotoSize> sizes = message.media.webpage.photo.sizes;
if (sizes.size() > 0) {
TLRPC.PhotoSize sizeFull = getClosestPhotoSizeWithSize(sizes, AndroidUtilities.getPhotoSize());
if (sizeFull != null) {
return getPathToAttach(sizeFull);
}
}
}
}
return new File("");
@ -617,7 +625,7 @@ public class FileLoader {
}
} else if (attach instanceof TLRPC.PhotoSize) {
TLRPC.PhotoSize photoSize = (TLRPC.PhotoSize) attach;
if (photoSize.location == null || photoSize.location.key != null || photoSize.location.volume_id == Integer.MIN_VALUE && photoSize.location.local_id < 0) {
if (photoSize.location == null || photoSize.location.key != null || photoSize.location.volume_id == Integer.MIN_VALUE && photoSize.location.local_id < 0 || photoSize.size < 0) {
dir = getInstance().getDirectory(MEDIA_DIR_CACHE);
} else {
dir = getInstance().getDirectory(MEDIA_DIR_IMAGE);

View File

@ -89,6 +89,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
final Object lock = new Object();
static ArrayList<HashMap<String, Object>> serverPublicKeys = null;
HashMap<String, Object> selectPublicKey(ArrayList<Long> fingerprints) {
synchronized (lock) {
if (serverPublicKeys == null) {
@ -150,7 +151,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
}
for (HashMap<String, Object> keyDesc : serverPublicKeys) {
long keyFingerprint = (Long)keyDesc.get("fingerprint");
long keyFingerprint = (Long) keyDesc.get("fingerprint");
for (long nFingerprint : fingerprints) {
if (nFingerprint == keyFingerprint) {
return keyDesc;
@ -161,7 +162,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
}
long generateMessageId() {
long messageId = (long)((((double)System.currentTimeMillis()) * 4294967296.0) / 1000.0);
long messageId = (long) ((((double) System.currentTimeMillis()) * 4294967296.0) / 1000.0);
if (messageId <= lastOutgoingMessageId) {
messageId = lastOutgoingMessageId + 1;
}
@ -197,7 +198,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
}
processedPQRes = true;
final TLRPC.TL_resPQ resPq = (TLRPC.TL_resPQ)message;
final TLRPC.TL_resPQ resPq = (TLRPC.TL_resPQ) message;
if (Utilities.arraysEquals(authNonce, 0, resPq.nonce, 0)) {
final HashMap<String, Object> publicKey = selectPublicKey(resPq.server_public_key_fingerprints);
if (publicKey == null) {
@ -221,11 +222,11 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
@Override
public void run() {
ByteBuffer pBytes = ByteBuffer.allocate(4);
pBytes.putInt((int)factorizedPq.p);
pBytes.putInt((int) factorizedPq.p);
byte[] pData = pBytes.array();
ByteBuffer qBytes = ByteBuffer.allocate(4);
qBytes.putInt((int)factorizedPq.q);
qBytes.putInt((int) factorizedPq.q);
byte[] qData = qBytes.array();
TLRPC.TL_req_DH_params reqDH = new TLRPC.TL_req_DH_params();
@ -233,7 +234,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
reqDH.server_nonce = authServerNonce;
reqDH.p = pData;
reqDH.q = qData;
reqDH.public_key_fingerprint = (Long)publicKey.get("fingerprint");
reqDH.public_key_fingerprint = (Long) publicKey.get("fingerprint");
SerializedData os = new SerializedData();
@ -261,7 +262,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
dataWithHash.writeByte(b[0]);
}
byte[] encryptedBytes = Utilities.encryptWithRSA((BigInteger[])publicKey.get("key"), dataWithHash.toByteArray());
byte[] encryptedBytes = Utilities.encryptWithRSA((BigInteger[]) publicKey.get("key"), dataWithHash.toByteArray());
dataWithHash.cleanup();
SerializedData encryptedData = new SerializedData();
encryptedData.writeRaw(encryptedBytes);
@ -300,7 +301,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
if (authNewNonce == null) {
return;
}
TLRPC.TL_server_DH_params_ok serverDhParams = (TLRPC.TL_server_DH_params_ok)message;
TLRPC.TL_server_DH_params_ok serverDhParams = (TLRPC.TL_server_DH_params_ok) message;
SerializedData tmpAesKey = new SerializedData();
@ -423,17 +424,17 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
for (int i = 7; i >= 0; i--) {
byte a_ = authNewNonce[i];
byte b_ = authServerNonce[i];
byte x = (byte)(a_ ^ b_);
byte x = (byte) (a_ ^ b_);
serverSaltData.writeByte(x);
}
ByteBuffer saltBuffer = ByteBuffer.wrap(serverSaltData.toByteArray());
serverSaltData.cleanup();
timeDifference = dhInnerData.server_time - (int)(System.currentTimeMillis() / 1000);
timeDifference = dhInnerData.server_time - (int) (System.currentTimeMillis() / 1000);
serverSalt = new ServerSalt();
serverSalt.validSince = (int)(System.currentTimeMillis() / 1000) + timeDifference;
serverSalt.validUntil = (int)(System.currentTimeMillis() / 1000) + timeDifference + 30 * 60;
serverSalt.validSince = (int) (System.currentTimeMillis() / 1000) + timeDifference;
serverSalt.validUntil = (int) (System.currentTimeMillis() / 1000) + timeDifference + 30 * 60;
serverSalt.value = saltBuffer.getLong();
FileLog.d("tmessages", String.format(Locale.US, "===== Time difference: %d", timeDifference));
@ -489,7 +490,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
setClientDHParamsMsgData = null;
}
TLRPC.Set_client_DH_params_answer dhAnswer = (TLRPC.Set_client_DH_params_answer)message;
TLRPC.Set_client_DH_params_answer dhAnswer = (TLRPC.Set_client_DH_params_answer) message;
if (!Utilities.arraysEquals(authNonce, 0, dhAnswer.nonce, 0)) {
FileLog.e("tmessages", "***** Invalid DH answer nonce");
@ -544,7 +545,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
System.arraycopy(newNonceHash3Full, newNonceHash3Full.length - 16, newNonceHash3, 0, 16);
if (message instanceof TLRPC.TL_dh_gen_ok) {
TLRPC.TL_dh_gen_ok dhGenOk = (TLRPC.TL_dh_gen_ok)message;
TLRPC.TL_dh_gen_ok dhGenOk = (TLRPC.TL_dh_gen_ok) message;
if (!Utilities.arraysEquals(newNonceHash1, 0, dhGenOk.new_nonce_hash1, 0)) {
FileLog.e("tmessages", "***** Invalid DH answer nonce hash 1");
beginHandshake(false);
@ -569,7 +570,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
}
});
} else if (message instanceof TLRPC.TL_dh_gen_retry) {
TLRPC.TL_dh_gen_retry dhRetry = (TLRPC.TL_dh_gen_retry)message;
TLRPC.TL_dh_gen_retry dhRetry = (TLRPC.TL_dh_gen_retry) message;
if (!Utilities.arraysEquals(newNonceHash2, 0, dhRetry.new_nonce_hash2, 0)) {
FileLog.e("tmessages", "***** Invalid DH answer nonce hash 2");
beginHandshake(false);
@ -578,7 +579,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
FileLog.d("tmessages", "***** Retry DH");
beginHandshake(false);
} else if (message instanceof TLRPC.TL_dh_gen_fail) {
TLRPC.TL_dh_gen_fail dhFail = (TLRPC.TL_dh_gen_fail)message;
TLRPC.TL_dh_gen_fail dhFail = (TLRPC.TL_dh_gen_fail) message;
if (!Utilities.arraysEquals(newNonceHash3, 0, dhFail.new_nonce_hash3, 0)) {
FileLog.e("tmessages", "***** Invalid DH answer nonce hash 3");
beginHandshake(false);

View File

@ -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;

View File

@ -300,10 +300,6 @@ public class TcpConnection extends ConnectionContext {
}
}
public void resumeConnection() {
}
private void reconnect() {
suspendConnection(false);
connectionState = TcpConnectionState.TcpConnectionStageReconnecting;

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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) {

View File

@ -208,6 +208,7 @@ public class BaseLocationAdapter extends BaseFragmentAdapter {
}
}
});
jsonObjReq.setShouldCache(false);
jsonObjReq.setTag("search");
requestQueue.add(jsonObjReq);
} catch (Exception e) {

View File

@ -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<RecentSearchObject> recentSearchObjects = new ArrayList<>();
private HashMap<Long, RecentSearchObject> 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<Integer> usersToLoad = new ArrayList<>();
ArrayList<Integer> chatsToLoad = new ArrayList<>();
ArrayList<Integer> encryptedToLoad = new ArrayList<>();
ArrayList<TLRPC.User> encUsers = new ArrayList<>();
final ArrayList<RecentSearchObject> arrayList = new ArrayList<>();
final HashMap<Long, RecentSearchObject> 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<TLRPC.User> users = new ArrayList<>();
if (!encryptedToLoad.isEmpty()) {
ArrayList<TLRPC.EncryptedChat> 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<TLRPC.Chat> 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<RecentSearchObject>() {
@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<RecentSearchObject> arrayList, HashMap<Long, RecentSearchObject> 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("<c#ff4d83b3>@%s</c>%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("<c#ff4d83b3>@%s</c>%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;
}

View File

@ -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

View File

@ -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)) {

View File

@ -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() {

View File

@ -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);
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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));

View File

@ -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<EmojiGridAdapter> adapters = new ArrayList<>();
private HashMap<Long, Integer> emojiUseHistory = new HashMap<>();
private ArrayList<Long> recentEmoji = new ArrayList<>();
private HashMap<Long, Integer> stickersUseHistory = new HashMap<>();
private ArrayList<TLRPC.Document> recentStickers = new ArrayList<>();
private HashMap<Long, Integer> stickerSetsUseCount = new HashMap<>();
private ArrayList<TLRPC.TL_messages_stickerSet> 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<Long> 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<Long> 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<Long, Integer> 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<Long, Integer> 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<Long> toRemove = null;
for (HashMap.Entry<Long, Integer> 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<Long, Integer> 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<Long, Integer> entry : emojiUseHistory.entrySet()) {
recentEmoji.add(entry.getKey());
}
Collections.sort(recentEmoji, new Comparator<Long>() {
@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<TLRPC.Document>() {
@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<TLRPC.TL_messages_stickerSet>() {
@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;
}

View File

@ -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);

View File

@ -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);
}
}
}

View File

@ -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<String, ArrayList<String>> aliases = new HashMap<>();
ArrayList<String> 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<String>());
}
aliases.get(info[0]).add(info[1]);
if (info[1].equals(extStorage)) {
extDevice=info[0];
}
result.add(info[1]);
HashSet<String> paths = new HashSet<>();
String defaultPath = Environment.getExternalStorageDirectory().getPath();
boolean isDefaultPathRemovable = Build.VERSION.SDK_INT >= 9 && Environment.isExternalStorageRemovable();
String defaultPathState = Environment.getExternalStorageState();
if (defaultPathState.equals(Environment.MEDIA_MOUNTED) || defaultPathState.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
ListItem ext = new ListItem();
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 {

View File

@ -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();

View File

@ -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<MessageObject> 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<MessageObject> 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<MessageObject> searchResult = new ArrayList<>();
private Timer searchTimer;
protected ArrayList<MessageObject> 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<MessageObject> 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<MessageObject>());
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<MessageObject> 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<MessageObject>());
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<MessageObject> resultArray = new ArrayList<>();
ArrayList<MessageObject> 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;
}

View File

@ -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) {

View File

@ -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);

View File

@ -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);

View File

@ -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;
}
}
}
}

View File

@ -59,6 +59,7 @@
<string name="WillUnmuteIn">خلال %1$s</string>
<string name="MuteDisable">تعطيل</string>
<string name="Hashtags">الأوسمة</string>
<string name="Recent">حديث</string>
<!--broadcasts-->
<string name="NewBroadcastList">رسالة جماعية جديدة</string>
<string name="EnterListName">أدخل اسم القائمة</string>
@ -391,9 +392,11 @@
<string name="AutoLockDisabled">معطّل</string>
<!--media view-->
<string name="NoMedia">شارك المقاطع المرئية والصور في هذه المحادثة لتستطيع الوصول إليها من أية جهاز من أجهزتك</string>
<string name="DocumentsTitle">ملفات</string>
<string name="DocumentsTitle">الملفات المشاركة</string>
<string name="SharedMediaTitle">الوسائط المشتركة</string>
<string name="LinksTitle">الروابط المشاركة</string>
<string name="NoSharedFiles">شارك الملفات والمستندات في هذه المحادثة لتستطيع الوصول إليها من أية جهاز من أجهزتك</string>
<string name="NoSharedLinks">شارك الروابط في هذه المحادثة لتستطيع الوصول إليها من أية جهاز من أجهزتك</string>
<!--map view-->
<string name="Map">الخريطة</string>
<string name="Satellite">قمر صناعي</string>
@ -834,6 +837,6 @@
<string name="formatterDay12H">h:mm a</string>
<string name="formatDateAtTime">%1$s الساعة %2$s</string>
<!--update text-->
<string name="updateText">تم تحديث تيليجرام نسخة الأندرويد. الجديد في النسخة رقم 3.1:\n\n- بحث عن الرسائل داخل محادثات محددة. \n- إعادة تصميم كاملة لشاشة ارفاق الملفات. إرسال جهات اتصال وملفات صوتية مباشرة من خيار المرفقات. \n- تطوير لتشغيل الوسائط داخل التطبيق (يوتيوب, ڤيميو, ساوندكلاود وغيرها.),.\n\nللاستزادة، اطلع هنا:\nhttps://telegram.org/blog/search-and-media</string>
<string name="updateBuild">577</string>
<string name="updateText">تم تحديث تيليجرام نسخة الاندرويد. الجديد في نسخة ٣.١.٢: \n\n- كلمات البحث الحديثة. \n- اضغط باستمرار لاستعراض الملصق قبل إرساله.</string>
<string name="updateBuild">583</string>
</resources>

View File

@ -59,6 +59,7 @@
<string name="WillUnmuteIn">In %1$s</string>
<string name="MuteDisable">Dauerhaft Stumm</string>
<string name="Hashtags">HASHTAGS</string>
<string name="Recent">LETZTE</string>
<!--broadcasts-->
<string name="NewBroadcastList">Neue Broadcast Liste</string>
<string name="EnterListName">Listenname</string>
@ -234,7 +235,7 @@
<string name="EncryptionKey">Geheimer Schlüssel</string>
<string name="MessageLifetime">Selbstzerstörungs-Timer</string>
<string name="ShortMessageLifetimeForever">Aus</string>
<string name="EncryptionKeyDescription">Das ist eine Darstellung des Schlüssels für den Geheimen Chat mit <![CDATA[<b>]]>%1$s<![CDATA[</b>]]>.<![CDATA[<br><br>]]>Wenn dieses Bild auf <![CDATA[<b>]]>%2$s\s<![CDATA[</b>]]>s Telefon genau so aussieht, ist euer Chat zu 200%% sicher.<![CDATA[<br><br>]]>Erfahre mehr auf telegram.org</string>
<string name="EncryptionKeyDescription">Die Abbildung zeigt den aktuellen Schlüssel dieses geheimen Chats mit <![CDATA[<b>]]>%1$s<![CDATA[</b>]]>.<![CDATA[<br><br>]]>Wenn dieses Bild auf dem Gerät von <![CDATA[<b>]]>%2$s<![CDATA[</b>]]> genau so aussieht, ist euer Chat zu 200%% sicher.<![CDATA[<br><br>]]>Erfahre mehr auf telegram.org</string>
<string name="NumberUnknown">Unbekannt</string>
<string name="Info">Info</string>
<string name="Phone">Telefon</string>
@ -391,9 +392,11 @@
<string name="AutoLockDisabled">Deaktiviert</string>
<!--media view-->
<string name="NoMedia">Die hier geteilten Bilder und Videos kannst du von jedem deiner Geräte aufrufen.</string>
<string name="DocumentsTitle">Dateien</string>
<string name="DocumentsTitle">Geteilte Dateien</string>
<string name="SharedMediaTitle">Geteilte Medien</string>
<string name="LinksTitle">Geteilte Links</string>
<string name="NoSharedFiles">Die hier geteilten Dateien kannst du von jedem deiner Geräte aufrufen.</string>
<string name="NoSharedLinks">Die hier geteilten Links kannst du von jedem deiner Geräte aufrufen.</string>
<!--map view-->
<string name="Map">Karte</string>
<string name="Satellite">Satellit</string>
@ -834,6 +837,6 @@
<string name="formatterDay12H">h:mm a</string>
<string name="formatDateAtTime">%1$s um %2$s</string>
<!--update text-->
<string name="updateText">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</string>
<string name="updateBuild">577</string>
<string name="updateText">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</string>
<string name="updateBuild">583</string>
</resources>

View File

@ -59,6 +59,7 @@
<string name="WillUnmuteIn">En %1$s</string>
<string name="MuteDisable">Desactivar</string>
<string name="Hashtags">HASHTAGS</string>
<string name="Recent">RECIENTES</string>
<!--broadcasts-->
<string name="NewBroadcastList">Nueva difusión</string>
<string name="EnterListName">Nombre de la lista</string>
@ -393,7 +394,9 @@
<string name="NoMedia">Comparte fotos y vídeos en este chat y accede a ellos desde cualquier dispositivo.</string>
<string name="DocumentsTitle">Archivos</string>
<string name="SharedMediaTitle">Multimedia</string>
<string name="LinksTitle">Enlaces</string>
<string name="NoSharedFiles">Comparte archivos en este chat y accede a ellos desde cualquier dispositivo.</string>
<string name="NoSharedLinks">Comparte enlaces en este chat y accede a ellos desde cualquiera de tus dispositivos.</string>
<!--map view-->
<string name="Map">Mapa</string>
<string name="Satellite">Satélite</string>
@ -834,6 +837,6 @@
<string name="formatterDay12H">h:mm a</string>
<string name="formatDateAtTime">%1$s a las %2$s</string>
<!--update text-->
<string name="updateText">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</string>
<string name="updateBuild">577</string>
<string name="updateText">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</string>
<string name="updateBuild">583</string>
</resources>

View File

@ -59,6 +59,7 @@
<string name="WillUnmuteIn">Tra %1$s</string>
<string name="MuteDisable">Disabilita</string>
<string name="Hashtags">HASHTAG</string>
<string name="Recent">RECENTI</string>
<!--broadcasts-->
<string name="NewBroadcastList">Nuova lista broadcast</string>
<string name="EnterListName">Immetti il nome della lista</string>
@ -164,7 +165,7 @@
<string name="NotificationEditedGroupName">%1$s ha modificato il nome del gruppo %2$s</string>
<string name="NotificationEditedGroupPhoto">%1$s ha modificato la foto del gruppo %2$s</string>
<string name="NotificationGroupAddMember">%1$s ha invitato %3$s nel gruppo %2$s</string>
<string name="NotificationGroupAddSelf">%1$s è rientrato nel gruppo %2$s</string>
<string name="NotificationGroupAddSelf">%1$s è tornato nel gruppo %2$s</string>
<string name="NotificationGroupKickMember">%1$s ha rimosso %3$s dal gruppo %2$s</string>
<string name="NotificationGroupKickYou">%1$s ti ha rimosso dal gruppo %2$s</string>
<string name="NotificationGroupLeftMember">%1$s ha lasciato il gruppo %2$s</string>
@ -391,9 +392,11 @@
<string name="AutoLockDisabled">Disabilitato</string>
<!--media view-->
<string name="NoMedia">Condividi foto e video in questa chat e accedi ad essi da ogni tuo dispositivo.</string>
<string name="DocumentsTitle">File</string>
<string name="DocumentsTitle">File condivisi</string>
<string name="SharedMediaTitle">Media condivisi</string>
<string name="LinksTitle">Link condivisi</string>
<string name="NoSharedFiles">Condividi file e documenti in questa chat e accedi ad essi da ogni tuo dispositivo.</string>
<string name="NoSharedLinks">Condividi link in questa chat ed accedi ad essi da ogni tuo dispositivo.</string>
<!--map view-->
<string name="Map">Mappa</string>
<string name="Satellite">Satellite</string>
@ -586,8 +589,8 @@
<string name="ActionYouCreateGroup">Hai creato il gruppo</string>
<string name="ActionKickUserYou">un1 ti ha rimosso</string>
<string name="ActionAddUserYou">un1 ti ha aggiunto</string>
<string name="ActionAddUserSelf">un1 è rientrato nel gruppo</string>
<string name="ActionAddUserSelfYou">Sei rientrato nel gruppo</string>
<string name="ActionAddUserSelf">un1 è tornato nel gruppo</string>
<string name="ActionAddUserSelfYou">Sei tornato nel gruppo</string>
<string name="UnsuppotedMedia">Questo messaggio non è supportato sulla tua versione di Telegram. Aggiorna l\'applicazione per visualizzarlo: https://telegram.org/update</string>
<string name="AttachPhoto">Foto</string>
<string name="AttachVideo">Video</string>
@ -834,6 +837,6 @@
<string name="formatterDay12H">h:mm a</string>
<string name="formatDateAtTime">%1$s alle %2$s</string>
<!--update text-->
<string name="updateText">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</string>
<string name="updateBuild">577</string>
<string name="updateText">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</string>
<string name="updateBuild">583</string>
</resources>

View File

@ -59,6 +59,7 @@
<string name="WillUnmuteIn">%1$s 후</string>
<string name="MuteDisable">비활성화</string>
<string name="Hashtags">해시태그</string>
<string name="Recent">최신</string>
<!--broadcasts-->
<string name="NewBroadcastList">새 단체 메시지 리스트</string>
<string name="EnterListName">리스트 이름을 입력하세요</string>
@ -391,9 +392,11 @@
<string name="AutoLockDisabled">비활성화됨</string>
<!--media view-->
<string name="NoMedia">이 채팅방에서 사진이나 동영상을 공유하면 다른 기기에서도 보실 수 있습니다.</string>
<string name="DocumentsTitle">파일</string>
<string name="DocumentsTitle">공유한 파일</string>
<string name="SharedMediaTitle">공유된 미디어</string>
<string name="LinksTitle">공유한 링크</string>
<string name="NoSharedFiles">이 채팅방에서 파일이나 문서를 공유하면 다른 기기에서도 보실 수 있습니다.</string>
<string name="NoSharedLinks">이 채팅방에서 파일이나 문서를 공유하면 다른 기기에서도 보실 수 있습니다.</string>
<!--map view-->
<string name="Map">지도</string>
<string name="Satellite">위성</string>
@ -834,6 +837,6 @@
<string name="formatterDay12H">a h:mm</string>
<string name="formatDateAtTime">%1$s %2$s</string>
<!--update text-->
<string name="updateText">텔레그램 안드로이드 버전이 업데이트 되었습니다. 새로운 버전은 3.1 입니다:\n\n- 특정 대화창에서 검색. \n- 첨부 메뉴 개선. 해당 메뉴에서 바로 연락처, 오디오를 전송 가능. \n- 인앱 미디어 재생 기능 향상 (YouTube, Vimeo, SoundCloud etc). 대용량 오디오 파일 별도 플레이어 기능. \n\nhttps://telegram.org/blog/search-and-media에서 자세한 사항을 알아보세요.</string>
<string name="updateBuild">577</string>
<string name="updateText">텔레그램 안드로이드 버전이 업데이트 되었습니다. 새로운 버전은 3.1.2 입니다:\n\n- 최신 검색 결과\n- 스티커를 꾹 누를 경우 미리보기 기능</string>
<string name="updateBuild">583</string>
</resources>

View File

@ -59,6 +59,7 @@
<string name="WillUnmuteIn">Over %1$s</string>
<string name="MuteDisable">Uitschakelen</string>
<string name="Hashtags">HASHTAGS</string>
<string name="Recent">RECENT</string>
<!--broadcasts-->
<string name="NewBroadcastList">Nieuwe verzendlijst</string>
<string name="EnterListName">Naam van lijst</string>
@ -391,9 +392,11 @@
<string name="AutoLockDisabled">Uitgeschakeld</string>
<!--media view-->
<string name="NoMedia">Deel foto\'s en video\'s in deze chat om ze op al je apparaten te kunnen benaderen.</string>
<string name="DocumentsTitle">Bestanden</string>
<string name="DocumentsTitle">Gedeelde bestanden</string>
<string name="SharedMediaTitle">Gedeelde media</string>
<string name="LinksTitle">Gedeelde links</string>
<string name="NoSharedFiles">Deel bestanden en documenten in deze chat om ze op al je apparaten te kunnen benaderen.</string>
<string name="NoSharedLinks">Deel links in deze chat om ze op al je apparaten te kunnen benaderen.</string>
<!--map view-->
<string name="Map">Kaart</string>
<string name="Satellite">Satelliet</string>
@ -834,6 +837,6 @@
<string name="formatterDay12H">h:mm a</string>
<string name="formatDateAtTime">%1$s om %2$s</string>
<!--update text-->
<string name="updateText">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</string>
<string name="updateBuild">577</string>
<string name="updateText">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.</string>
<string name="updateBuild">583</string>
</resources>

View File

@ -59,6 +59,7 @@
<string name="WillUnmuteIn">Em %1$s</string>
<string name="MuteDisable">Desativar</string>
<string name="Hashtags">HASHTAGS</string>
<string name="Recent">RECENTE</string>
<!--broadcasts-->
<string name="NewBroadcastList">Nova Lista de Transmissão</string>
<string name="EnterListName">Digite o nome da lista</string>
@ -391,9 +392,11 @@
<string name="AutoLockDisabled">Desativado</string>
<!--media view-->
<string name="NoMedia">Compartilhar fotos e vídeos no chat e acessá-los em qualquer um de seus dispositivos.</string>
<string name="DocumentsTitle">Arquivos</string>
<string name="DocumentsTitle">Arquivos Compartilhados</string>
<string name="SharedMediaTitle">Mídia Compartilhada</string>
<string name="LinksTitle">Links Compartilhados</string>
<string name="NoSharedFiles">Compartilhar arquivos e documentos no chat e acessá-los de qualquer um de seus dispositivos.</string>
<string name="NoSharedLinks">Compartilhe links nesse chat e os acesse de qualquer um de seus dispositivos</string>
<!--map view-->
<string name="Map">Mapa</string>
<string name="Satellite">Satélite</string>
@ -834,6 +837,6 @@
<string name="formatterDay12H">h:mm a</string>
<string name="formatDateAtTime">%1$s às %2$s</string>
<!--update text-->
<string name="updateText">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</string>
<string name="updateBuild">577</string>
<string name="updateText">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</string>
<string name="updateBuild">583</string>
</resources>

View File

@ -59,6 +59,7 @@
<string name="WillUnmuteIn">Em %1$s</string>
<string name="MuteDisable">Desativar</string>
<string name="Hashtags">HASHTAGS</string>
<string name="Recent">RECENTE</string>
<!--broadcasts-->
<string name="NewBroadcastList">Nova Lista de Transmissão</string>
<string name="EnterListName">Digite o nome da lista</string>
@ -391,9 +392,11 @@
<string name="AutoLockDisabled">Desativado</string>
<!--media view-->
<string name="NoMedia">Compartilhar fotos e vídeos no chat e acessá-los em qualquer um de seus dispositivos.</string>
<string name="DocumentsTitle">Arquivos</string>
<string name="DocumentsTitle">Arquivos Compartilhados</string>
<string name="SharedMediaTitle">Mídia Compartilhada</string>
<string name="LinksTitle">Links Compartilhados</string>
<string name="NoSharedFiles">Compartilhar arquivos e documentos no chat e acessá-los de qualquer um de seus dispositivos.</string>
<string name="NoSharedLinks">Compartilhe links nesse chat e os acesse de qualquer um de seus dispositivos</string>
<!--map view-->
<string name="Map">Mapa</string>
<string name="Satellite">Satélite</string>
@ -834,6 +837,6 @@
<string name="formatterDay12H">h:mm a</string>
<string name="formatDateAtTime">%1$s às %2$s</string>
<!--update text-->
<string name="updateText">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</string>
<string name="updateBuild">577</string>
<string name="updateText">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</string>
<string name="updateBuild">583</string>
</resources>

View File

@ -59,6 +59,7 @@
<string name="WillUnmuteIn">In %1$s</string>
<string name="MuteDisable">Disable</string>
<string name="Hashtags">HASHTAGS</string>
<string name="Recent">RECENT</string>
<!--broadcasts-->
<string name="NewBroadcastList">New Broadcast List</string>
<string name="EnterListName">Enter list name</string>
@ -391,9 +392,11 @@
<string name="AutoLockDisabled">Disabled</string>
<!--media view-->
<string name="NoMedia">Share photos and videos in this chat and access them on any of your devices.</string>
<string name="DocumentsTitle">Files</string>
<string name="DocumentsTitle">Shared Files</string>
<string name="SharedMediaTitle">Shared Media</string>
<string name="LinksTitle">Shared Links</string>
<string name="NoSharedFiles">Share files and documents in this chat and access them on any of your devices.</string>
<string name="NoSharedLinks">Share links in this chat and access them on any of your devices.</string>
<!--map view-->
<string name="Map">Map</string>
<string name="Satellite">Satellite</string>
@ -834,6 +837,6 @@
<string name="formatterDay12H">h:mm a</string>
<string name="formatDateAtTime">%1$s at %2$s</string>
<!--update text-->
<string name="updateText">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</string>
<string name="updateBuild">577</string>
<string name="updateText">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</string>
<string name="updateBuild">583</string>
</resources>

View File

@ -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'
}
}