Compare commits

...

26 Commits

Author SHA1 Message Date
Björn Petersen 6c625d078f Update CHANGELOG.md 2017-04-14 14:29:47 +02:00
B. Petersen a69afe11f4 Pimp notifications. 2017-04-13 23:55:51 +02:00
B. Petersen 4ff15e433b Use 'white' as default LED-color, show text 'Default' if the default LED-color is selected (instead of a white circle on white background). 2017-04-13 23:03:02 +02:00
B. Petersen 6c81d03c50 Use delta-chat default sound if choosing 'default' in the chat-sound-settings. 2017-04-13 22:37:15 +02:00
B. Petersen 9f0b322249 Remove unsued strings that have no default. 2017-04-13 16:26:41 +02:00
B. Petersen e8831b0dfc Remove bad libEtPan version declaration. 2017-04-13 13:58:53 +02:00
B. Petersen d6e9ba2ea9 Rewording the 'Mute for' options - thanks for the hint, Tobi. 2017-04-13 12:33:41 +02:00
B. Petersen 2c8c5a7558 Adapt copyright note. 2017-04-13 11:05:30 +02:00
B. Petersen d2e76a5e61 Pimp 'Notifications and sounds' dialog. 2017-04-12 23:02:54 +02:00
B. Petersen 2263c24019 Typo 2017-04-12 14:13:35 +02:00
B. Petersen 19e414e65c Update messenger-backend submodule. 2017-04-12 14:11:22 +02:00
B. Petersen f5a52c1aa2 Changelog. 2017-04-12 14:10:09 +02:00
B. Petersen 1cf5039c81 Additional heartbeat call. 2017-04-12 11:36:07 +02:00
B. Petersen 90a5e39872 Personalize needed foreground notification. 2017-04-11 14:44:24 +02:00
B. Petersen 30868b107e Remove dead code. 2017-04-11 12:37:16 +02:00
B. Petersen 546d4a3bda Remove dead code. 2017-04-11 12:23:11 +02:00
B. Petersen 33eaf71e5b Remove currently unused AOSP Volley. 2017-04-11 12:08:15 +02:00
B. Petersen a61e73b91b Improve color selector, selection is still a little bit weird, however, much better. 2017-04-10 22:51:10 +02:00
B. Petersen f03a3a9b50 New background-active-icon. 2017-04-10 21:09:56 +02:00
B. Petersen e2225be331 Installing an permanent foreground service to satisfy Googles battery optimization stuff, hopefully, *REQUEST_IGNORE_BATTERY_OPTIMIZATIONS (which is only possible with the mercy of Google) is no longer needed. 2017-04-10 18:11:52 +02:00
B. Petersen f1f23a5099 Additionally log to a file to fix temporary notification problems. 2017-04-10 13:30:13 +02:00
B. Petersen db3bcb017c Remove void FileLog commands. 2017-04-10 12:39:50 +02:00
B. Petersen ab0ce1393e Remove void FileLog commands. 2017-04-10 12:01:56 +02:00
B. Petersen b941b9e21a Request the user to ignore the battery optimizations which avoid running Delta Chat properly. 2017-04-07 19:36:05 +02:00
B. Petersen b838e0d69e If possible, use Java's Log-class instead of JNI's __android_log_print() which is not available on some devices (eg. LG X Cam). 2017-04-05 16:11:56 +02:00
B. Petersen 8459f5552d Avoid global JNI-namespace pollution. 2017-04-05 14:35:28 +02:00
153 changed files with 1175 additions and 6536 deletions
+13
View File
@@ -1,5 +1,18 @@
# Delta Chat Changelog
## v0.1.28
2017-04-14
* Pimp notifications
* Bug fixes
## v0.1.27
2017-04-12
* Use a permanent foreground service for reliable notifications
* Monitor the IMAP-IDLE thread and reconnect if IMAP-IDLE seems to hang
* Various battery and background optimizations
## v0.1.25
2017-04-04
+2 -2
View File
@@ -76,7 +76,7 @@ android {
}
}
defaultConfig.versionCode = 24
defaultConfig.versionCode = 28
sourceSets.main {
jniLibs.srcDir 'libs'
@@ -117,6 +117,6 @@ android {
defaultConfig {
minSdkVersion 14 // 14: Android 4.0 Ice Cream Sandwich 2011 (Telegram default), 21: Android 5.0 Lollipop 2014 (recommended for InstantRun)
targetSdkVersion 25 // 25: Nougat. CAVE: Do NOT target "Andoid O" without checking the background tasks carefully, see https://developer.android.com/preview/behavior-changes.html#back-all . As long as we target "Nougat", everything works as expected even for "Andoid O" or later
versionName "0.1.25" // do NOT forget to increase defaultConfig.versionCode!
versionName "0.1.28" // do NOT forget to increase defaultConfig.versionCode!
}
}
+1 -2
View File
@@ -1598,9 +1598,8 @@ ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
endif
LOCAL_SRC_FILES += \
./jni.c \
./mrjnimain.c \
./audio.c \
./utils.c \
./image.c \
./video.c \
./gifvideo.cpp \
+64 -40
View File
@@ -1,3 +1,26 @@
/*******************************************************************************
*
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
#include <jni.h>
#include <ogg/ogg.h>
#include <stdio.h>
@@ -6,7 +29,7 @@
#include <time.h>
#include <opusfile.h>
#include <math.h>
#include "utils.h"
#include "mrjnimain.h"
typedef struct {
int version;
@@ -115,7 +138,7 @@ static int read_chars(ROPacket *p, unsigned char *str, int nb_chars)
return 1;
}
int opus_header_to_packet(const OpusHeader *h, unsigned char *packet, int len) {
static int opus_header_to_packet(const OpusHeader *h, unsigned char *packet, int len) {
int i;
Packet p;
unsigned char ch;
@@ -219,34 +242,34 @@ static int writeOggPage(ogg_page *page, FILE *os) {
return written;
}
const opus_int32 bitrate = 16000;
const opus_int32 rate = 16000;
const opus_int32 frame_size = 960;
const int with_cvbr = 1;
const int max_ogg_delay = 0;
const int comment_padding = 512;
static const opus_int32 bitrate = 16000;
static const opus_int32 rate = 16000;
static const opus_int32 frame_size = 960;
static const int with_cvbr = 1;
static const int max_ogg_delay = 0;
static const int comment_padding = 512;
opus_int32 coding_rate = 16000;
ogg_int32_t _packetId;
OpusEncoder *_encoder = 0;
uint8_t *_packet = 0;
ogg_stream_state os;
FILE *_fileOs = 0;
oe_enc_opt inopt;
OpusHeader header;
opus_int32 min_bytes;
int max_frame_bytes;
ogg_packet op;
ogg_page og;
opus_int64 bytes_written;
opus_int64 pages_out;
opus_int64 total_samples;
ogg_int64_t enc_granulepos;
ogg_int64_t last_granulepos;
int size_segments;
int last_segments;
static opus_int32 coding_rate = 16000;
static ogg_int32_t _packetId;
static OpusEncoder *_encoder = 0;
static uint8_t *_packet = 0;
static ogg_stream_state os;
static FILE *_fileOs = 0;
static oe_enc_opt inopt;
static OpusHeader header;
static opus_int32 min_bytes;
static int max_frame_bytes;
static ogg_packet op;
static ogg_page og;
static opus_int64 bytes_written;
static opus_int64 pages_out;
static opus_int64 total_samples;
static ogg_int64_t enc_granulepos;
static ogg_int64_t last_granulepos;
static int size_segments;
static int last_segments;
void cleanupRecorder() {
static void cleanupRecorder() {
ogg_stream_flush(&os, &og);
@@ -282,7 +305,7 @@ void cleanupRecorder() {
memset(&og, 0, sizeof(ogg_page));
}
int initRecorder(const char *path) {
static int initRecorder(const char *path) {
cleanupRecorder();
if (!path) {
@@ -421,7 +444,7 @@ int initRecorder(const char *path) {
return 1;
}
int writeFrame(uint8_t *framePcmBytes, unsigned int frameByteCount) {
static int writeFrame(uint8_t *framePcmBytes, unsigned int frameByteCount) {
int cur_frame_size = frame_size;
_packetId++;
@@ -527,15 +550,15 @@ JNIEXPORT void Java_com_b44t_messenger_MediaController_stopRecord(JNIEnv *env, j
}
//player
OggOpusFile *_opusFile;
int _isSeekable = 0;
int64_t _totalPcmDuration = 0;
int64_t _currentPcmOffset = 0;
int _finished = 0;
static OggOpusFile *_opusFile;
static int _isSeekable = 0;
static int64_t _totalPcmDuration = 0;
static int64_t _currentPcmOffset = 0;
static int _finished = 0;
static const int playerBuffersCount = 3;
static const int playerSampleRate = 48000;
void cleanupPlayer() {
static void cleanupPlayer() {
if (_opusFile) {
op_free(_opusFile);
_opusFile = 0;
@@ -546,7 +569,7 @@ void cleanupPlayer() {
_finished = 0;
}
int seekPlayer(float position) {
static int seekPlayer(float position) {
if (!_opusFile || !_isSeekable || position < 0) {
return 0;
}
@@ -559,7 +582,7 @@ int seekPlayer(float position) {
return result == OPUS_OK;
}
int initPlayer(const char *path) {
static int initPlayer(const char *path) {
cleanupPlayer();
int openError = OPUS_OK;
@@ -576,7 +599,7 @@ int initPlayer(const char *path) {
return 1;
}
void fillBuffer(uint8_t *buffer, int capacity, int *args) {
static void fillBuffer(uint8_t *buffer, int capacity, int *args) {
if (_opusFile) {
args[1] = max(0, op_pcm_tell(_opusFile));
@@ -735,7 +758,7 @@ JNIEXPORT jbyteArray Java_com_b44t_messenger_MediaController_getWaveform2(JNIEnv
return result;
}
int16_t *sampleBuffer = NULL;
static int16_t *sampleBuffer = NULL;
JNIEXPORT jbyteArray Java_com_b44t_messenger_MediaController_getWaveform(JNIEnv *env, jclass class, jstring path) {
@@ -819,3 +842,4 @@ JNIEXPORT jbyteArray Java_com_b44t_messenger_MediaController_getWaveform(JNIEnv
return result;
}
+30 -11
View File
@@ -1,5 +1,28 @@
/*******************************************************************************
*
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
#include <jni.h>
#include <utils.h>
#include "mrjnimain.h"
#include <libyuv.h>
#include <android/bitmap.h>
#include <cstdint>
@@ -53,19 +76,14 @@ typedef struct VideoInfo {
AVPacket orig_pkt;
};
jobject makeGlobarRef(JNIEnv *env, jobject object) {
if (object) {
return env->NewGlobalRef(object);
}
return 0;
}
int gifvideoOnJNILoad(JavaVM *vm, JNIEnv *env) {
int gifvideoOnJNILoad(JavaVM *vm, JNIEnv *env) { /* called from JNI_OnLoad() */
av_register_all();
return 0;
}
int open_codec_context(int *stream_idx, AVFormatContext *fmt_ctx, enum AVMediaType type) {
static int open_codec_context(int *stream_idx, AVFormatContext *fmt_ctx, enum AVMediaType type) {
int ret;
AVStream *st;
AVCodecContext *dec_ctx = NULL;
@@ -97,7 +115,7 @@ int open_codec_context(int *stream_idx, AVFormatContext *fmt_ctx, enum AVMediaTy
return 0;
}
int decode_packet(VideoInfo *info, int *got_frame) {
static int decode_packet(VideoInfo *info, int *got_frame) {
int ret = 0;
int decoded = info->pkt.size;
@@ -263,4 +281,5 @@ jint Java_com_b44t_ui_Components_AnimatedFileDrawable_getVideoFrame(JNIEnv *env,
}
return 0;
}
}
} /* extern "C" */
@@ -21,11 +21,13 @@
******************************************************************************/
package com.b44t.messenger;
#ifndef __GIFVIDEO_H__
#define __GIFVIDEO_H__
public class UserObject {
public static String getUserName(TLRPC.User user) {
return "ErrName"; // use MrContact.getName() instead
}
}
#include <jni.h>
int gifvideoOnJNILoad(JavaVM *vm, JNIEnv *env);
#endif /* __GIFVIDEO_H__ */
+52 -15
View File
@@ -1,3 +1,26 @@
/*******************************************************************************
*
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
#include <jni.h>
#include <stdio.h>
#include <setjmp.h>
@@ -5,21 +28,35 @@
#include <android/bitmap.h>
#include <libwebp/webp/decode.h>
#include <libwebp/webp/encode.h>
#include "utils.h"
#include "mrjnimain.h"
#include "image.h"
jclass jclass_NullPointerException;
jclass jclass_RuntimeException;
jclass jclass_Options;
jfieldID jclass_Options_inJustDecodeBounds;
jfieldID jclass_Options_outHeight;
jfieldID jclass_Options_outWidth;
static jclass jclass_NullPointerException;
static jclass jclass_RuntimeException;
const uint32_t PGPhotoEnhanceHistogramBins = 256;
const uint32_t PGPhotoEnhanceSegments = 4;
static jclass jclass_Options;
static jfieldID jclass_Options_inJustDecodeBounds;
static jfieldID jclass_Options_outHeight;
static jfieldID jclass_Options_outWidth;
jclass createGlobarRef(JNIEnv *env, jclass class) {
static const uint32_t PGPhotoEnhanceHistogramBins = 256;
static const uint32_t PGPhotoEnhanceSegments = 4;
static void throwException(JNIEnv *env, char *format, ...) {
jclass exClass = (*env)->FindClass(env, "java/lang/UnsupportedOperationException");
if (!exClass) {
return;
}
char dest[256];
va_list argptr;
va_start(argptr, format);
vsprintf(dest, format, argptr);
va_end(argptr);
(*env)->ThrowNew(env, exClass, dest);
}
static jclass createGlobalRef(JNIEnv *env, jclass class) {
if (class) {
return (*env)->NewGlobalRef(env, class);
}
@@ -27,16 +64,16 @@ jclass createGlobarRef(JNIEnv *env, jclass class) {
}
jint imageOnJNILoad(JavaVM *vm, void *reserved, JNIEnv *env) {
jclass_NullPointerException = createGlobarRef(env, (*env)->FindClass(env, "java/lang/NullPointerException"));
jclass_NullPointerException = createGlobalRef(env, (*env)->FindClass(env, "java/lang/NullPointerException"));
if (jclass_NullPointerException == 0) {
return -1;
}
jclass_RuntimeException = createGlobarRef(env, (*env)->FindClass(env, "java/lang/RuntimeException"));
jclass_RuntimeException = createGlobalRef(env, (*env)->FindClass(env, "java/lang/RuntimeException"));
if (jclass_RuntimeException == 0) {
return -1;
}
jclass_Options = createGlobarRef(env, (*env)->FindClass(env, "android/graphics/BitmapFactory$Options"));
jclass_Options = createGlobalRef(env, (*env)->FindClass(env, "android/graphics/BitmapFactory$Options"));
if (jclass_Options == 0) {
return -1;
}
@@ -262,7 +299,7 @@ typedef struct my_error_mgr {
} *my_error_ptr;
METHODDEF(void) my_error_exit(j_common_ptr cinfo) {
METHODDEF(void) my_jpeglib_error_exit(j_common_ptr cinfo) {
my_error_ptr myerr = (my_error_ptr) cinfo->err;
(*cinfo->err->output_message) (cinfo);
longjmp(myerr->setjmp_buffer, 1);
@@ -424,7 +461,7 @@ JNIEXPORT void Java_com_b44t_messenger_Utilities_loadBitmap(JNIEnv *env, jclass
struct jpeg_decompress_struct cinfo;
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;
jerr.pub.error_exit = my_jpeglib_error_exit;
if (!setjmp(jerr.setjmp_buffer)) {
jpeg_create_decompress(&cinfo);
+29 -3
View File
@@ -1,8 +1,34 @@
#ifndef image_h
#define image_h
/*******************************************************************************
*
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
#ifndef __IMAGE_H__
#define __IMAGE_H__
#include <jni.h>
jint imageOnJNILoad(JavaVM *vm, void *reserved, JNIEnv *env);
#endif
#endif /* __IMAGE_H__ */
-67
View File
@@ -1,67 +0,0 @@
#include <stdio.h>
#include <string.h>
#include <jni.h>
#include <sys/types.h>
#include <inttypes.h>
#include <stdlib.h>
#include <openssl/aes.h>
#include <unistd.h>
#include "utils.h"
#include "image.h"
int gifvideoOnJNILoad(JavaVM *vm, JNIEnv *env);
jint JNI_OnLoad(JavaVM *vm, void *reserved) { /* this function is called automatically by the JNI when the library gets loaded */
JNIEnv *env = 0;
srand(time(NULL));
if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
if (imageOnJNILoad(vm, reserved, env) == -1) {
return -1;
}
if (gifvideoOnJNILoad(vm, env) == -1) {
return -1;
}
return JNI_VERSION_1_6;
}
void JNI_OnUnload(JavaVM *vm, void *reserved) {
}
/*
JNIEXPORT void Java_com_b44t_messenger_Utilities_aesIgeEncryption(JNIEnv *env, jclass class, jobject buffer, jbyteArray key, jbyteArray iv, jboolean encrypt, int offset, int length) {
jbyte *what = (*env)->GetDirectBufferAddress(env, buffer) + offset;
unsigned char *keyBuff = (unsigned char *)(*env)->GetByteArrayElements(env, key, NULL);
unsigned char *ivBuff = (unsigned char *)(*env)->GetByteArrayElements(env, iv, NULL);
AES_KEY akey;
if (!encrypt) {
AES_set_decrypt_key(keyBuff, 32 * 8, &akey);
AES_ige_encrypt(what, what, length, &akey, ivBuff, AES_DECRYPT);
} else {
AES_set_encrypt_key(keyBuff, 32 * 8, &akey);
AES_ige_encrypt(what, what, length, &akey, ivBuff, AES_ENCRYPT);
}
(*env)->ReleaseByteArrayElements(env, key, keyBuff, JNI_ABORT);
(*env)->ReleaseByteArrayElements(env, iv, ivBuff, 0);
}
*/
JNIEXPORT jstring Java_com_b44t_messenger_Utilities_readlink(JNIEnv *env, jclass class, jstring path) {
static char buf[1000];
char *fileName = (*env)->GetStringUTFChars(env, path, NULL);
int result = readlink(fileName, buf, 999);
jstring value = 0;
if (result != -1) {
buf[result] = '\0';
value = (*env)->NewStringUTF(env, buf);
}
(*env)->ReleaseStringUTFChars(env, path, fileName);
return value;
}
@@ -122,13 +122,13 @@
#define LIBETPAN_REENTRANT 1
/* Define this to the version of libEtPan */
#define LIBETPAN_VERSION "1.2-dev-20141203"
//#define LIBETPAN_VERSION "1.2-dev-20141203"
/* Define this to the major version of libEtPan */
#define LIBETPAN_VERSION_MAJOR 1
//#define LIBETPAN_VERSION_MAJOR 1
/* Define this to the minor version of libEtPan */
#define LIBETPAN_VERSION_MINOR 2
//#define LIBETPAN_VERSION_MINOR 2
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
@@ -144,7 +144,7 @@
#define PACKAGE_NAME "libetpan"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "libetpan 1.2"
//#define PACKAGE_STRING "libetpan 1.2"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "libetpan"
@@ -153,7 +153,7 @@
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION "1.2"
//#define PACKAGE_VERSION "1.2"
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
@@ -171,7 +171,7 @@
#define USE_SSL 1
/* Version number of package */
#define VERSION "1.2"
//#define VERSION "1.2"
/* Define to `__inline__' or `__inline' if that's what the C compiler
calls it, or to nothing if 'inline' is not supported under any name. */
+77
View File
@@ -0,0 +1,77 @@
/*******************************************************************************
*
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
#include <stdio.h>
#include <string.h>
#include <jni.h>
#include <sys/types.h>
#include <inttypes.h>
#include <stdlib.h>
#include <openssl/aes.h>
#include <unistd.h>
#include "mrjnimain.h"
#include "image.h"
#include "gifvideo.h"
jint JNI_OnLoad(JavaVM *vm, void *reserved) { /* this function is called automatically by the JNI when the library gets loaded */
JNIEnv *env = 0;
srand(time(NULL));
__android_log_print(ANDROID_LOG_INFO, "DeltaChat", "JNI_OnLoad() ..."); /* please note, that __android_log_print() may not work (eg. on LG X Cam), however, we don't have an option here. */
if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
if (imageOnJNILoad(vm, reserved, env) == -1) {
return -1;
}
if (gifvideoOnJNILoad(vm, env) == -1) {
return -1;
}
__android_log_print(ANDROID_LOG_INFO, "DeltaChat", "JNI_OnLoad() succeeded.");
return JNI_VERSION_1_6;
}
void JNI_OnUnload(JavaVM *vm, void *reserved) {
}
JNIEXPORT jstring Java_com_b44t_messenger_Utilities_readlink(JNIEnv *env, jclass class, jstring path) {
static char buf[1000];
char *fileName = (*env)->GetStringUTFChars(env, path, NULL);
int result = readlink(fileName, buf, 999);
jstring value = 0;
if (result != -1) {
buf[result] = '\0';
value = (*env)->NewStringUTF(env, buf);
}
(*env)->ReleaseStringUTFChars(env, path, fileName);
return value;
}
@@ -1,6 +1,7 @@
/*******************************************************************************
*
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
@@ -20,8 +21,34 @@
******************************************************************************/
package com.b44t.messenger;
#ifndef __MRJNIMAIN_H__
#define __MRJNIMAIN_H__
#include <android/log.h>
#include <jni.h>
#define LOG_TAG "DeltaChat"
#ifndef LOG_DISABLED
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
#else
#define LOGI(...)
#define LOGD(...)
#define LOGE(...)
#define LOGV(...)
#endif
#ifndef max
#define max(x, y) ((x) > (y)) ? (x) : (y)
#endif
#ifndef min
#define min(x, y) ((x) < (y)) ? (x) : (y)
#endif
#endif /* __MRJNIMAIN_H__*/
public class BuildVars {
public static boolean DEBUG_VERSION = false;
}
+13 -3
View File
@@ -23,8 +23,8 @@
* Purpose: The C part of the Java<->C Wrapper, see also MrMailbox.java
*
******************************************************************************/
#include <jni.h>
#include <android/log.h>
#include "messenger-backend/src/mrmailbox.h"
@@ -81,6 +81,8 @@ static void s_init_globals(JNIEnv *env, jclass MrMailbox_class)
s_global_init_done = 1;
/* prepare calling back a Java function */
__android_log_print(ANDROID_LOG_INFO, "DeltaChat", "JNI: s_init_globals()..."); /* low-level logging, mrmailbox_log_*() may not be yet available. However, please note that __android_log_print() may not work (eg. on LG X Cam) */
(*env)->GetJavaVM(env, &s_jvm); /* JNIEnv cannot be shared between threads, so we share the JavaVM object */
s_MrMailbox_class = (*env)->NewGlobalRef(env, MrMailbox_class);
s_MrCallback_methodID = (*env)->GetStaticMethodID(env, MrMailbox_class, "MrCallback","(IJJ)J" /*signature as "(param)ret" with I=int, J=long*/ );
@@ -108,7 +110,7 @@ void mrosnative_unsetup_thread(mrmailbox_t* mailbox)
{
mrmailbox_log_info(mailbox, 0, "Detaching C-thread from Java VM...");
(*s_jvm)->DetachCurrentThread(s_jvm);
mrmailbox_log_info(mailbox, 0, "Detaching done.");
mrmailbox_log_info(mailbox, 0, "DeltaChat", "Detaching done.");
}
@@ -193,6 +195,7 @@ static uintptr_t s_mailbox_callback_(mrmailbox_t* mailbox, int event, uintptr_t
jlong l;
JNIEnv* env;
#if 0 /* -- __android_log_print() does not log eg. on LG X Cam - but Javas Log.i() etc. do. So, we do not optimize these calls and just use the Java logging. */
if( event==MR_EVENT_INFO || event==MR_EVENT_WARNING ) {
__android_log_print(event==MR_EVENT_INFO? ANDROID_LOG_INFO : ANDROID_LOG_WARN, "DeltaChat", "%s", (char*)data2); /* on problems, add `-llog` to `Android.mk` */
return 0; /* speed up things for info/warning */
@@ -201,6 +204,7 @@ static uintptr_t s_mailbox_callback_(mrmailbox_t* mailbox, int event, uintptr_t
__android_log_print(ANDROID_LOG_ERROR, "DeltaChat", "%s", (char*)data2);
/* errors are also forwarded to Java to show them in a bubble or so */
}
#endif
if( s_jvm==NULL || s_MrMailbox_class==NULL || s_MrCallback_methodID==NULL ) {
return 0; /* may happen on startup */
@@ -518,6 +522,12 @@ JNIEXPORT jstring Java_com_b44t_messenger_MrMailbox_cmdline(JNIEnv *env, jclass
}
JNIEXPORT void Java_com_b44t_messenger_MrMailbox_heartbeat(JNIEnv *env, jclass cls)
{
mrmailbox_heartbeat(get_mrmailbox_t(env, cls));
}
JNIEXPORT jint Java_com_b44t_messenger_MrMailbox_MrMailboxAddAddressBook(JNIEnv *env, jclass c, jlong hMailbox, jstring adrbook)
{
CHAR_REF(adrbook);
-14
View File
@@ -1,14 +0,0 @@
#include "utils.h"
void throwException(JNIEnv *env, char *format, ...) {
jclass exClass = (*env)->FindClass(env, "java/lang/UnsupportedOperationException");
if (!exClass) {
return;
}
char dest[256];
va_list argptr;
va_start(argptr, format);
vsprintf(dest, format, argptr);
va_end(argptr);
(*env)->ThrowNew(env, exClass, dest);
}
-29
View File
@@ -1,29 +0,0 @@
#ifndef log_h
#define log_h
#include <android/log.h>
#include <jni.h>
#define LOG_TAG "DeltaChat"
#ifndef LOG_DISABLED
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
#else
#define LOGI(...)
#define LOGD(...)
#define LOGE(...)
#define LOGV(...)
#endif
#ifndef max
#define max(x, y) ((x) > (y)) ? (x) : (y)
#endif
#ifndef min
#define min(x, y) ((x) < (y)) ? (x) : (y)
#endif
void throwException(JNIEnv *env, char *format, ...);
#endif
+26 -2
View File
@@ -1,6 +1,30 @@
/*******************************************************************************
*
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
#include <jni.h>
#include <libyuv.h>
#include <utils.h>
#include "mrjnimain.h"
enum COLOR_FORMATTYPE {
COLOR_FormatMonochrome = 1,
@@ -52,7 +76,7 @@ enum COLOR_FORMATTYPE {
COLOR_QCOM_FormatYUV420SemiPlanar = 0x7fa30c00
};
int isSemiPlanarYUV(int colorFormat) {
static int isSemiPlanarYUV(int colorFormat) {
switch (colorFormat) {
case COLOR_FormatYUV420Planar:
case COLOR_FormatYUV420PackedPlanar:
@@ -51,6 +51,7 @@ import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.StateSet;
import android.view.Display;
import android.view.Surface;
@@ -113,7 +114,7 @@ public class AndroidUtilities {
final String HOST_NAME = "(" + IRI + "\\.)+" + GTLD;
final Pattern DOMAIN_NAME = Pattern.compile("(" + HOST_NAME + "|" + IP_ADDRESS + ")");
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -193,7 +194,7 @@ public class AndroidUtilities {
}
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -207,7 +208,7 @@ public class AndroidUtilities {
prevOrientation = -10;
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -219,7 +220,7 @@ public class AndroidUtilities {
InputMethodManager inputManager = (InputMethodManager)view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
inputManager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -234,7 +235,7 @@ public class AndroidUtilities {
}
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -244,7 +245,7 @@ public class AndroidUtilities {
try {
state = Environment.getExternalStorageState();
} catch (Exception e) {
FileLog.e("messenger", e);
}
if (state == null || state.startsWith(Environment.MEDIA_MOUNTED)) {
try {
@@ -253,7 +254,7 @@ public class AndroidUtilities {
return file;
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
try {
@@ -262,7 +263,7 @@ public class AndroidUtilities {
return file;
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
return new File("");
}
@@ -301,11 +302,10 @@ public class AndroidUtilities {
if (display != null) {
display.getMetrics(displayMetrics);
display.getSize(displaySize);
FileLog.e("messenger", "display size = " + displaySize.x + " " + displaySize.y + " " + displayMetrics.xdpi + "x" + displayMetrics.ydpi);
Log.i("DeltaChat", "Display size: " + displaySize.x + " x " + displaySize.y + ", DPI: " + displayMetrics.xdpi + " x " + displayMetrics.ydpi);
}
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -380,7 +380,7 @@ public class AndroidUtilities {
mCursorDrawableRes.setAccessible(true);
mCursorDrawableRes.setInt(editText, 0);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -410,7 +410,7 @@ public class AndroidUtilities {
addIntent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
ApplicationLoader.applicationContext.sendBroadcast(addIntent);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -423,7 +423,7 @@ public class AndroidUtilities {
addIntent.setAction("com.android.launcher.action.UNINSTALL_SHORTCUT");
ApplicationLoader.applicationContext.sendBroadcast(addIntent);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}*/
@@ -442,7 +442,7 @@ public class AndroidUtilities {
return insets.bottom;
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
return 0;
}
@@ -460,11 +460,11 @@ public class AndroidUtilities {
size.set((Integer) mGetRawW.invoke(windowManager.getDefaultDisplay()), (Integer) mGetRawH.invoke(windowManager.getDefaultDisplay()));
} catch (Exception e) {
size.set(windowManager.getDefaultDisplay().getWidth(), windowManager.getDefaultDisplay().getHeight());
FileLog.e("messenger", e);
}
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
return size;
}
@@ -560,7 +560,7 @@ public class AndroidUtilities {
}
return spannableStringBuilder;
} catch (Exception e) {
FileLog.e("messenger", e);
}
return new SpannableStringBuilder(str);
}
@@ -617,7 +617,7 @@ public class AndroidUtilities {
android.content.ClipData clip = android.content.ClipData.newPlainText("label", str);
clipboard.setPrimaryClip(clip);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -639,7 +639,7 @@ public class AndroidUtilities {
mediaScanIntent.setData(uri);
ApplicationLoader.applicationContext.sendBroadcast(mediaScanIntent);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -652,12 +652,12 @@ public class AndroidUtilities {
storageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "Delta Chat");
if (!storageDir.mkdirs()) {
if (!storageDir.exists()){
FileLog.d("messenger", "failed to create directory");
Log.w("DeltaChat", "failed to create directory");
return null;
}
}
} else {
FileLog.d("messenger", "External storage is not mounted READ/WRITE.");
Log.w("DeltaChat", "External storage is not mounted READ/WRITE.");
}
return storageDir;
@@ -710,7 +710,7 @@ public class AndroidUtilities {
return uri.getPath();
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
return null;
}
@@ -734,7 +734,7 @@ public class AndroidUtilities {
return value;
}
} catch (Exception e) {
FileLog.e("messenger", e);
} finally {
if (cursor != null) {
cursor.close();
@@ -761,7 +761,7 @@ public class AndroidUtilities {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
return new File(storageDir, "IMG_" + timeStamp + ".jpg");
} catch (Exception e) {
FileLog.e("messenger", e);
}
return null;
}
@@ -772,7 +772,7 @@ public class AndroidUtilities {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
return new File(storageDir, "VID_" + timeStamp + ".mp4");
} catch (Exception e) {
FileLog.e("messenger", e);
}
return null;
}
@@ -802,7 +802,7 @@ public class AndroidUtilities {
final int l = Character.digit((char) bytes[++i], 16);
buffer.write((char) ((u << 4) + l));
} catch (Exception e) {
FileLog.e("messenger", e);
return null;
}
} else {
@@ -813,7 +813,7 @@ public class AndroidUtilities {
try {
buffer.close();
} catch (Exception e) {
FileLog.e("messenger", e);
}
return array;
}
@@ -841,7 +841,7 @@ public class AndroidUtilities {
destination = new FileOutputStream(destFile);
destination.getChannel().transferFrom(source.getChannel(), 0, source.getChannel().size());
} catch (Exception e) {
FileLog.e("messenger", e);
return false;
} finally {
if (source != null) {
@@ -133,17 +133,17 @@ public class ApplicationLoader extends Application {
path.mkdirs();
return path;
} catch (Exception e) {
FileLog.e("messenger", e);
}
return new File("/data/data/com.b44t.messenger/files");
}
@Override
public void onCreate() {
Log.i("DeltaChat", "*************** ApplicationLoader.onCreate() ***************");
super.onCreate();
applicationContext = getApplicationContext(); // should be set very first as it may be needed eg. for logging
applicationContext = getApplicationContext();
MrMailbox.log_i("DeltaChat", "*************** ApplicationLoader.onCreate() ***************");
System.loadLibrary("messenger.1");
new ForegroundDetector(this);
applicationHandler = new Handler(applicationContext.getMainLooper());
@@ -195,28 +195,15 @@ public class ApplicationLoader extends Application {
try {
PowerManager pm = (PowerManager)ApplicationLoader.applicationContext.getSystemService(Context.POWER_SERVICE);
isScreenOn = pm.isScreenOn();
FileLog.e("messenger", "screen state = " + isScreenOn);
//Log.i("DeltaChat", "screen state = " + isScreenOn);
} catch (Exception e) {
FileLog.e("messenger", e);
}
UserConfig.loadConfig();
// create a timer that wakes up the CPU from time to time
try
{
Intent intent = new Intent(applicationContext, TimerReceiver.class);
PendingIntent alarmIntent = PendingIntent.getBroadcast(applicationContext, 0, intent, 0);
AlarmManager alarmManager = (AlarmManager)applicationContext.getSystemService(Activity.ALARM_SERVICE);
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime()+60*1000,
60*1000,
alarmIntent);
}
catch( Exception e) {
Log.e("DeltaChat", "Cannot create alarm.");
}
TimerReceiver.scheduleNextAlarm();
// make sure, the notifications for the "deaddrop" dialog are muted by default
SharedPreferences notificationPreferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE);
@@ -273,6 +260,6 @@ public class ApplicationLoader extends Application {
public static void stayAwakeForAMoment()
{
stayAwakeWakeLock.acquire(10*60*1000); // 10 Minutes to wait for "after chat" messages, after that, we sleep most time, see wakeupWakeLock
stayAwakeWakeLock.acquire(1*60*1000); // 1 Minute to wait for "after chat" messages, after that, we sleep most time, see wakeupWakeLock
}
}
@@ -30,7 +30,7 @@ import android.util.Log;
public class BootCompletedReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
Log.i("DeltaChat", "*** BootCompletedReceiver.onReceive()");
MrMailbox.log_i("DeltaChat", "*** BootCompletedReceiver.onReceive()");
// there's nothing more to do here as all initialisation stuff is already done in
// ApplicationLoader.onCreate() which is called before this broadcast is sended.
}
@@ -92,7 +92,7 @@ public class ContactsController {
pCur.close();
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
return allContacts;
}
@@ -48,7 +48,7 @@ public class DispatchQueue extends Thread {
handler.sendMessageDelayed(msg, delay);
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
*/
@@ -58,7 +58,7 @@ public class DispatchQueue extends Thread {
syncLatch.await();
handler.removeCallbacks(runnable);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -75,7 +75,7 @@ public class DispatchQueue extends Thread {
handler.postDelayed(runnable, delay);
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -85,7 +85,7 @@ public class DispatchQueue extends Thread {
syncLatch.await();
handler.removeCallbacksAndMessages(null);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
*/
@@ -117,7 +117,7 @@ public class Emoji {
}
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
@@ -158,7 +158,7 @@ public class Emoji {
}
});
} catch (Throwable x) {
FileLog.e("messenger", "Error loading emoji", x);
//Log.i("DeltaChat", "Error loading emoji", x);
}
}
@@ -209,7 +209,7 @@ public class Emoji {
public static EmojiDrawable getEmojiDrawable(CharSequence code) {
DrawableInfo info = rects.get(code);
if (info == null) {
FileLog.e("messenger", "No drawable for emoji " + code);
//Log.i("DeltaChat", "No drawable for emoji " + code);
return null;
}
EmojiDrawable ed = new EmojiDrawable(info);
@@ -434,7 +434,7 @@ public class Emoji {
// }
// }
// } catch (Exception e) {
// FileLog.e("messenger", e);
//
// return cs;
// }
return s;
@@ -23,8 +23,6 @@
package com.b44t.messenger;
import android.util.Log;
import com.b44t.messenger.time.FastDateFormat;
import java.io.File;
@@ -37,7 +35,6 @@ public class FileLog {
private FastDateFormat dateFormat = null;
private DispatchQueue logQueue = null;
private File currentFile = null;
private File networkFile = null;
private static volatile FileLog Instance = null;
public static FileLog getInstance() {
@@ -54,10 +51,7 @@ public class FileLog {
}
public FileLog() {
if (!BuildVars.DEBUG_VERSION) {
return;
}
dateFormat = FastDateFormat.getInstance("dd_MM_yyyy_HH_mm_ss", Locale.US);
dateFormat = FastDateFormat.getInstance("yyyyMMdd_HHmmss", Locale.US);
try {
File sdCard = ApplicationLoader.applicationContext.getExternalFilesDir(null);
if (sdCard == null) {
@@ -81,37 +75,14 @@ public class FileLog {
}
}
public static String getNetworkLogPath() {
if (!BuildVars.DEBUG_VERSION) {
return "";
}
try {
File sdCard = ApplicationLoader.applicationContext.getExternalFilesDir(null);
if (sdCard == null) {
return "";
}
File dir = new File(sdCard.getAbsolutePath() + "/logs");
dir.mkdirs();
getInstance().networkFile = new File(dir, getInstance().dateFormat.format(System.currentTimeMillis()) + "_net.txt");
return getInstance().networkFile.getAbsolutePath();
} catch (Throwable e) {
e.printStackTrace();
}
return "";
}
public static void e(final String tag, final String message, final Throwable exception) {
if (!BuildVars.DEBUG_VERSION) {
return;
}
Log.e(tag, message, exception);
private static void log(final String what, final String tag, final String message)
{
if (getInstance().streamWriter != null) {
getInstance().logQueue.postRunnable(new Runnable() {
@Override
public void run() {
try {
getInstance().streamWriter.write(getInstance().dateFormat.format(System.currentTimeMillis()) + " E/" + tag + " " + message + "\n");
getInstance().streamWriter.write(exception.toString());
getInstance().streamWriter.write(getInstance().dateFormat.format(System.currentTimeMillis()) + " " + what + "/" + tag + ": " + message + "\n");
getInstance().streamWriter.flush();
} catch (Exception e) {
e.printStackTrace();
@@ -122,109 +93,14 @@ public class FileLog {
}
public static void e(final String tag, final String message) {
if (!BuildVars.DEBUG_VERSION) {
return;
}
Log.e(tag, message);
if (getInstance().streamWriter != null) {
getInstance().logQueue.postRunnable(new Runnable() {
@Override
public void run() {
try {
getInstance().streamWriter.write(getInstance().dateFormat.format(System.currentTimeMillis()) + " E/" + tag + "" + message + "\n");
getInstance().streamWriter.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
public static void e(final String tag, final Throwable e) {
if (!BuildVars.DEBUG_VERSION) {
return;
}
e.printStackTrace();
if (getInstance().streamWriter != null) {
getInstance().logQueue.postRunnable(new Runnable() {
@Override
public void run() {
try {
getInstance().streamWriter.write(getInstance().dateFormat.format(System.currentTimeMillis()) + " E/" + tag + "" + e + "\n");
StackTraceElement[] stack = e.getStackTrace();
for (int a = 0; a < stack.length; a++) {
getInstance().streamWriter.write(getInstance().dateFormat.format(System.currentTimeMillis()) + " E/" + tag + "" + stack[a] + "\n");
}
getInstance().streamWriter.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
});
} else {
e.printStackTrace();
}
}
public static void d(final String tag, final String message) {
if (!BuildVars.DEBUG_VERSION) {
return;
}
Log.d(tag, message);
if (getInstance().streamWriter != null) {
getInstance().logQueue.postRunnable(new Runnable() {
@Override
public void run() {
try {
getInstance().streamWriter.write(getInstance().dateFormat.format(System.currentTimeMillis()) + " D/" + tag + "" + message + "\n");
getInstance().streamWriter.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
log("E", tag, message);
}
public static void w(final String tag, final String message) {
if (!BuildVars.DEBUG_VERSION) {
return;
}
Log.w(tag, message);
if (getInstance().streamWriter != null) {
getInstance().logQueue.postRunnable(new Runnable() {
@Override
public void run() {
try {
getInstance().streamWriter.write(getInstance().dateFormat.format(System.currentTimeMillis()) + " W/" + tag + ": " + message + "\n");
getInstance().streamWriter.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
log("W", tag, message);
}
public static void cleanupLogs() {
File sdCard = ApplicationLoader.applicationContext.getExternalFilesDir(null);
if (sdCard == null) {
return;
}
File dir = new File (sdCard.getAbsolutePath() + "/logs");
File[] files = dir.listFiles();
if (files != null) {
for (int a = 0; a < files.length; a++) {
File file = files[a];
if (getInstance().currentFile != null && file.getAbsolutePath().equals(getInstance().currentFile.getAbsolutePath())) {
continue;
}
if (getInstance().networkFile != null && file.getAbsolutePath().equals(getInstance().networkFile.getAbsolutePath())) {
continue;
}
file.delete();
}
}
public static void i(final String tag, final String message) {
log("I", tag, message);
}
}
@@ -37,6 +37,7 @@ import android.media.ThumbnailUtils;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;
import android.util.Log;
import com.b44t.ui.Components.AnimatedFileDrawable;
@@ -158,7 +159,7 @@ public class ImageLoader {
try {
stream.close();
} catch (Exception e) {
FileLog.e("messenger", e);
}
final BitmapDrawable bitmapDrawable = new BitmapDrawable(originalBitmap);
AndroidUtilities.runOnUIThread(new Runnable() {
@@ -175,7 +176,7 @@ public class ImageLoader {
}
});
} catch (Throwable e) {
FileLog.e("messenger", e);
removeTask();
}
}
@@ -237,13 +238,13 @@ public class ImageLoader {
}
randomAccessFile.close();
} catch (Exception e) {
FileLog.e("messenger", e);
} finally {
if (randomAccessFile != null) {
try {
randomAccessFile.close();
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
}
@@ -328,7 +329,7 @@ public class ImageLoader {
}
}
} catch (Throwable e) {
FileLog.e("messenger", e);
}
} else {
try {
@@ -577,7 +578,7 @@ public class ImageLoader {
trackAllocation = cl.getMethod("trackExternalAllocation", new Class[]{long.class});
trackFree = cl.getMethod("trackExternalFree", new Class[]{long.class});
} catch (Exception e) {
FileLog.e("messenger", e);
runtime = null;
trackAllocation = null;
trackFree = null;
@@ -752,7 +753,6 @@ public class ImageLoader {
BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context arg0, Intent intent) {
FileLog.e("messenger", "file system changed");
Runnable r = new Runnable() {
public void run() {
checkMediaPaths();
@@ -1170,7 +1170,7 @@ public class ImageLoader {
try {
path = AndroidUtilities.getPath(uri);
} catch (Throwable e) {
FileLog.e("messenger", e);
}
}
}
@@ -1184,7 +1184,7 @@ public class ImageLoader {
inputStream.close();
inputStream = ApplicationLoader.applicationContext.getContentResolver().openInputStream(uri);
} catch (Throwable e) {
FileLog.e("messenger", e);
return null;
}
}
@@ -1225,7 +1225,7 @@ public class ImageLoader {
break;
}
} catch (Throwable e) {
FileLog.e("messenger", e);
}
}
@@ -1244,7 +1244,7 @@ public class ImageLoader {
}
}
} catch (Throwable e) {
FileLog.e("messenger", e);
ImageLoader.getInstance().clearMemory();
try {
if (b == null) {
@@ -1261,7 +1261,6 @@ public class ImageLoader {
}
}
} catch (Throwable e2) {
FileLog.e("messenger", e2);
}
}
} else if (uri != null) {
@@ -1278,12 +1277,12 @@ public class ImageLoader {
}
}
} catch (Throwable e) {
FileLog.e("messenger", e);
} finally {
try {
inputStream.close();
} catch (Throwable e) {
FileLog.e("messenger", e);
}
}
}
@@ -1386,13 +1385,12 @@ public class ImageLoader {
try {
return scaleAndSaveImageInternal(cacheFile, bitmap, w, h, photoW, photoH, scaleFactor, quality, cache, scaleAnyway);
} catch (Throwable e) {
FileLog.e("messenger", e);
ImageLoader.getInstance().clearMemory();
System.gc();
try {
return scaleAndSaveImageInternal(cacheFile, bitmap, w, h, photoW, photoH, scaleFactor, quality, cache, scaleAnyway);
} catch (Throwable e2) {
FileLog.e("messenger", e2);
return null;
}
}
@@ -1450,7 +1448,7 @@ public class ImageLoader {
writeFile.write(photoSize.bytes);
writeFile.close();
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
TLRPC.TL_photoSize newPhotoSize = new TLRPC.TL_photoSize();
@@ -483,7 +483,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
currentThumbKey = null;
}
setImage(currentImageLocation, currentHttpUrl, currentFilter, currentThumb, currentThumbLocation, currentThumbFilter, currentSize, currentExt, currentCacheOnly);
FileLog.e("messenger", e);
}
canvas.restore();
} else {
@@ -528,7 +528,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
currentThumbKey = null;
}
setImage(currentImageLocation, currentHttpUrl, currentFilter, currentThumb, currentThumbLocation, currentThumbFilter, currentSize, currentExt, currentCacheOnly);
FileLog.e("messenger", e);
}
}
@@ -565,7 +565,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
currentThumbKey = null;
}
setImage(currentImageLocation, currentHttpUrl, currentFilter, currentThumb, currentThumbLocation, currentThumbFilter, currentSize, currentExt, currentCacheOnly);
FileLog.e("messenger", e);
}
}
canvas.restore();
@@ -580,7 +580,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
drawable.setAlpha(alpha);
drawable.draw(canvas);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
}
@@ -662,7 +662,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
checkAlphaAnimation(animationNotReady);
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
return false;
}
@@ -22,19 +22,30 @@
package com.b44t.messenger;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import java.util.Locale;
public class KeepAliveService extends Service {
static KeepAliveService s_this = null;
@Override
public void onCreate() {
Log.i("DeltaChat", "*** KeepAliveService.onCreate()");
MrMailbox.log_i("DeltaChat", "*** KeepAliveService.onCreate()");
// there's nothing more to do here as all initialisation stuff is already done in
// ApplicationLoader.onCreate() which is called before this broadcast is sended.
s_this = this;
setSelfAsForeground();
}
@Override
@@ -42,7 +53,7 @@ public class KeepAliveService extends Service {
// START_STICKY ensured, the service is recreated as soon it is terminted for any reasons.
// as ApplicationLoader.onCreate() is called before a service starts, there is no more to do here,
// the app is just running fine.
Log.i("DeltaChat", "*** KeepAliveService.onStartCommand()");
MrMailbox.log_i("DeltaChat", "*** KeepAliveService.onStartCommand()");
return START_STICKY;
}
@@ -52,7 +63,52 @@ public class KeepAliveService extends Service {
}
public void onDestroy() {
Log.i("DeltaChat", "*** KeepAliveService.onDestroy()");
MrMailbox.log_i("DeltaChat", "*** KeepAliveService.onDestroy()");
// the service will be restarted due to START_STICKY automatically, there's nothing more to do.
}
private void setSelfAsForeground() {
stopForeground(true);
startForeground(FG_NOTIFICATION_ID, createNotification()); // TODO: if we target Android O, we should use startServiceInForeground()
}
static public KeepAliveService getInstance()
{
return s_this; // may be null
}
/* The notification
* A notification is required for a foreground service; and without a foreground service,
* Delta Chat won't get new messages reliable
**********************************************************************************************/
public static final int FG_NOTIFICATION_ID = 4142;
private Notification createNotification()
{
// a notification _must_ contain a small icon, a title and a text, see https://developer.android.com/guide/topics/ui/notifiers/notifications.html#Required
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
if( MrMailbox.isConfigured()!=0) {
builder.setContentTitle(LocaleController.formatString("", R.string.PermNotificationTitle, MrMailbox.getConfig("addr", "")));
builder.setContentText(getString(R.string.PermNotificationText));
}
else {
builder.setContentTitle(getString(R.string.AppName));
builder.setContentText(getString(R.string.AccountNotConfigured));
}
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN ) {
builder.setPriority(NotificationCompat.PRIORITY_MIN);
}
builder.setWhen(0);
builder.setSmallIcon(R.drawable.notification_permanent);
return builder.build();
}
public void updateForegroundNotification()
{
// update the notification by simply creating a new notification with the same ID, see https://developer.android.com/training/notify-user/managing.html#Updating
NotificationManager nm = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(FG_NOTIFICATION_ID, createNotification());
}
}
@@ -182,14 +182,14 @@ public class LocaleController {
}
applyLanguage(currentInfo, override);
} catch (Exception e) {
FileLog.e("messenger", e);
}
try {
IntentFilter timezoneFilter = new IntentFilter(Intent.ACTION_TIMEZONE_CHANGED);
ApplicationLoader.applicationContext.registerReceiver(new TimeZoneChangedReceiver(), timezoneFilter);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -271,7 +271,7 @@ public class LocaleController {
changingConfiguration = false;
}
} catch (Exception e) {
FileLog.e("messenger", e);
changingConfiguration = false;
}
recreateFormatters();
@@ -305,7 +305,7 @@ public class LocaleController {
return String.format(value, args);
}
} catch (Exception e) {
FileLog.e("messenger", e);
return "LOC_ERR: " + key;
}
}
@@ -318,7 +318,7 @@ public class LocaleController {
return String.format(string, args);
}
} catch (Exception e) {
FileLog.e("messenger", e);
return "LOC_ERR: " + string;
}
}
@@ -359,7 +359,7 @@ public class LocaleController {
}
return getInstance().chatFullDate.format(date * 1000);
} catch (Exception e) {
FileLog.e("messenger", e);
}
return "LOC_ERR: formatDateChat";
}
@@ -421,7 +421,7 @@ public class LocaleController {
}
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
return "LOC_ERR";
}
@@ -368,7 +368,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
samplesCount = newSamplesCount;
} catch (Exception e) {
FileLog.e("messenger", e);
}
buffer.position(0);
final double amplitude = Math.sqrt(sum / len / 2);
@@ -497,14 +497,14 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
freePlayerBuffers.add(new AudioBuffer(playerBufferSize));
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
try {
sensorManager = (SensorManager) ApplicationLoader.applicationContext.getSystemService(Context.SENSOR_SERVICE);
linearSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
gravitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
if (linearSensor == null || gravitySensor == null) {
FileLog.e("messenger", "gravity or linear sensor not found");
//Log.i("DeltaChat", "gravity or linear sensor not found");
accelerometerSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
linearSensor = null;
gravitySensor = null;
@@ -513,7 +513,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
PowerManager powerManager = (PowerManager) ApplicationLoader.applicationContext.getSystemService(Context.POWER_SERVICE);
proximityWakeLock = powerManager.newWakeLock(0x00000020, "proximity");
} catch (Exception e) {
FileLog.e("messenger", e);
}
fileBuffer = ByteBuffer.allocateDirect(1920);
recordQueue = new DispatchQueue("recordQueue");
@@ -562,12 +562,12 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
try {
ApplicationLoader.applicationContext.getContentResolver().registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, false, new GalleryObserverExternal());
} catch (Exception e) {
FileLog.e("messenger", e);
}
try {
ApplicationLoader.applicationContext.getContentResolver().registerContentObserver(MediaStore.Images.Media.INTERNAL_CONTENT_URI, false, new GalleryObserverInternal());
} catch (Exception e) {
FileLog.e("messenger", e);
}
try {
@@ -593,7 +593,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
mgr.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -639,7 +639,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
audioTrackPlayer.setStereoVolume(volume, volume);
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -650,7 +650,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
progressTimer.cancel();
progressTimer = null;
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
progressTimer = new Timer();
@@ -687,7 +687,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
currentPlayingMessageObject.audioProgressSec = lastProgress / 1000;
NotificationCenter.getInstance().postNotificationName(NotificationCenter.audioProgressDidChanged, currentPlayingMessageObject.getId(), value);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
}
@@ -705,7 +705,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
progressTimer.cancel();
progressTimer = null;
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
}
@@ -876,7 +876,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
try {
count = audioTrackPlayer.write(buffer.bufferBytes, 0, buffer.size);
} catch (Exception e) {
FileLog.e("messenger", e);
}
buffersWrited++;
@@ -931,7 +931,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
return;
}
if (event.sensor == proximitySensor) {
FileLog.e("messenger", "proximity changed to " + event.values[0]);
//Log.i("DeltaChat", "proximity changed to " + event.values[0]);
if (lastProximityValue == -100) {
lastProximityValue = event.values[0];
} else if (lastProximityValue != event.values[0]) {
@@ -996,7 +996,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
raisedToTop = 0;
countLess = 0;
timeSinceRaise = System.currentTimeMillis();
//FileLog.e("messenger", "motion detected");
//Log.i("DeltaChat", "motion detected");
}
}
} else {
@@ -1013,13 +1013,13 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
previousAccValue = val;
accelerometerVertical = gravityFast[1] > 2.5f && Math.abs(gravityFast[2]) < 4.0f && /*Math.abs(gravityFast[0]) < 9.0f &&*/ Math.abs(gravityFast[0]) > 1.5f;
//FileLog.e("messenger", accelerometerVertical + " val = " + val + " acc (" + linearAcceleration[0] + ", " + linearAcceleration[1] + ", " + linearAcceleration[2] + ") grav (" + gravityFast[0] + ", " + gravityFast[1] + ", " + gravityFast[2] + ")");
//Log.i("DeltaChat", accelerometerVertical + " val = " + val + " acc (" + linearAcceleration[0] + ", " + linearAcceleration[1] + ", " + linearAcceleration[2] + ") grav (" + gravityFast[0] + ", " + gravityFast[1] + ", " + gravityFast[2] + ")");
}
if (raisedToBack == minCount && accelerometerVertical && proximityTouched && !NotificationsController.getInstance().audioManager.isWiredHeadsetOn()) {
FileLog.e("messenger", "sensor values reached");
//Log.i("DeltaChat", "sensor values reached");
if (playingMessageObject == null && recordStartRunnable == null && recordingAudio == null && !PhotoViewer.getInstance().isVisible() && ApplicationLoader.isScreenOn && !inputFieldHasText && allowStartRecord && raiseChat != null && !callInProgress) {
if (!raiseToEarRecord) {
FileLog.e("messenger", "start record");
//Log.i("DeltaChat", "start record");
useFrontSpeaker = true;
if (!raiseChat.playFirstUnreadVoiceMessage() /*if there is nothing to play, the function checks for record permission*/ ) {
raiseToEarRecord = true;
@@ -1033,7 +1033,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
} /*else if (playingMessageObject != null && playingMessageObject.isVoice()) {
if (!useFrontSpeaker) {
FileLog.e("messenger", "start listen");
//Log.i("DeltaChat", "start listen");
if (proximityHasDifferentValues && proximityWakeLock != null && !proximityWakeLock.isHeld()) {
proximityWakeLock.acquire();
}
@@ -1048,7 +1048,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
} else if (proximityTouched) {
/*if (playingMessageObject != null && playingMessageObject.isVoice()) {
if (!useFrontSpeaker) {
FileLog.e("messenger", "start listen by proximity only");
//Log.i("DeltaChat", "start listen by proximity only");
if (proximityHasDifferentValues && proximityWakeLock != null && !proximityWakeLock.isHeld()) {
proximityWakeLock.acquire();
}
@@ -1059,7 +1059,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}*/
} else if (!proximityTouched) {
if (raiseToEarRecord) {
FileLog.e("messenger", "stop record");
//Log.i("DeltaChat", "stop record");
stopRecording(2);
raiseToEarRecord = false;
ignoreOnPause = false;
@@ -1067,7 +1067,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
proximityWakeLock.release();
}
} else if (useFrontSpeaker) {
FileLog.e("messenger", "stop listen");
//Log.i("DeltaChat", "stop listen");
useFrontSpeaker = false;
startAudioAgain(true);
ignoreOnPause = false;
@@ -1211,18 +1211,18 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
try {
audioPlayer.reset();
} catch (Exception e) {
FileLog.e("messenger", e);
}
try {
audioPlayer.stop();
} catch (Exception e) {
FileLog.e("messenger", e);
}
try {
audioPlayer.release();
audioPlayer = null;
} catch (Exception e) {
FileLog.e("messenger", e);
}
} else if (audioTrackPlayer != null) {
synchronized (playerObjectSync) {
@@ -1230,13 +1230,13 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
audioTrackPlayer.pause();
audioTrackPlayer.flush();
} catch (Exception e) {
FileLog.e("messenger", e);
}
try {
audioTrackPlayer.release();
audioTrackPlayer = null;
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
}
@@ -1323,7 +1323,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
seekOpusPlayer(progress);
}
} catch (Exception e) {
FileLog.e("messenger", e);
return false;
}
return true;
@@ -1457,7 +1457,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
});
audioTrackPlayer.play();
} catch (Exception e) {
FileLog.e("messenger", e);
if (audioTrackPlayer != null) {
audioTrackPlayer.release();
audioTrackPlayer = null;
@@ -1488,11 +1488,11 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
try {
audioInfo = AudioInfo.getAudioInfo(cacheFile);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
} catch (Exception e) {
FileLog.e("messenger", e);
NotificationCenter.getInstance().postNotificationName(NotificationCenter.audioPlayStateChanged, playingMessageObject != null ? playingMessageObject.getId() : 0);
if (audioPlayer != null) {
audioPlayer.release();
@@ -1526,7 +1526,6 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
playingMessageObject.audioProgress = 0;
playingMessageObject.audioProgressSec = 0;
NotificationCenter.getInstance().postNotificationName(NotificationCenter.audioProgressDidChanged, playingMessageObject.getId(), 0);
FileLog.e("messenger", e2);
}
} else if (audioTrackPlayer != null) {
if (playingMessageObject.audioProgress == 1) {
@@ -1541,7 +1540,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
seekOpusFile(playingMessageObject.audioProgress);
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
synchronized (playerSync) {
freePlayerBuffers.addAll(usedPlayerBuffers);
@@ -1569,7 +1568,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
try {
audioPlayer.reset();
} catch (Exception e) {
FileLog.e("messenger", e);
}
audioPlayer.stop();
} else if (audioTrackPlayer != null) {
@@ -1577,7 +1576,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
audioTrackPlayer.flush();
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
try {
if (audioPlayer != null) {
@@ -1590,7 +1589,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
stopProgressTimer();
playingMessageObject = null;
@@ -1618,7 +1617,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
isPaused = true;
NotificationCenter.getInstance().postNotificationName(NotificationCenter.audioPlayStateChanged, playingMessageObject.getId());
} catch (Exception e) {
FileLog.e("messenger", e);
isPaused = false;
return false;
}
@@ -1642,7 +1641,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
isPaused = false;
NotificationCenter.getInstance().postNotificationName(NotificationCenter.audioPlayStateChanged, playingMessageObject.getId());
} catch (Exception e) {
FileLog.e("messenger", e);
return false;
}
return true;
@@ -1671,7 +1670,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
v.vibrate(50);
//NotificationsController.getInstance().playRecordSound(); -- was also disabled in Telegram; playing a sound while recording may result in a feedback
} catch (Exception e) {
FileLog.e("messenger", e);
}
recordQueue.postRunnable(recordStartRunnable = new Runnable() {
@@ -1721,7 +1720,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
audioRecorder.startRecording();
} catch (Exception e) {
FileLog.e("messenger", e);
recordingAudio = null;
stopRecord();
recordingAudioFile.delete();
@@ -1730,7 +1729,6 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
audioRecorder.release();
audioRecorder = null;
} catch (Exception e2) {
FileLog.e("messenger", e2);
}
AndroidUtilities.runOnUIThread(new Runnable() {
@@ -1853,7 +1851,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
audioRecorder = null;
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
recordingAudio = null;
recordingAudioFile = null;
@@ -1874,7 +1872,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
sendAfterDone = send;
audioRecorder.stop();
} catch (Exception e) {
FileLog.e("messenger", e);
if (recordingAudioFile != null) {
recordingAudioFile.delete();
}
@@ -1886,7 +1884,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
Vibrator v = (Vibrator) ApplicationLoader.applicationContext.getSystemService(Context.VIBRATOR_SERVICE);
v.vibrate(50);
} catch (Exception e) {
FileLog.e("messenger", e);
}
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
@@ -1914,14 +1912,13 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
}
} catch (Exception e) {
FileLog.e("messenger", e);
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (Exception e2) {
FileLog.e("messenger", e2);
}
}
return false;
@@ -1939,14 +1936,13 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
}
} catch (Exception e) {
FileLog.e("messenger", e);
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (Exception e2) {
FileLog.e("messenger", e2);
}
}
return false;
@@ -1962,7 +1958,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
}
} catch (Exception e) {
FileLog.e("messenger", e);
} finally {
if (cursor != null) {
cursor.close();
@@ -2000,21 +1996,19 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
return f.getAbsolutePath();
} catch (Exception e) {
FileLog.e("messenger", e);
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (Exception e2) {
FileLog.e("messenger", e2);
}
try {
if (output != null) {
output.close();
}
} catch (Exception e2) {
FileLog.e("messenger", e2);
}
}
return null;
@@ -2121,13 +2115,13 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
}
} catch (Throwable e) {
FileLog.e("messenger", e);
} finally {
if (cursor != null) {
try {
cursor.close();
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
}
@@ -2182,13 +2176,13 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
}
} catch (Throwable e) {
FileLog.e("messenger", e);
} finally {
if (cursor != null) {
try {
cursor.close();
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
}
@@ -2435,7 +2429,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
th.start();
th.join();
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
}).start();
@@ -2569,11 +2563,11 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
} else if (codecName.equals("OMX.TI.DUCATI1.VIDEO.H264E")) {
processorType = PROCESSOR_TYPE_TI;
}
FileLog.e("messenger", "codec = " + codecInfo.getName() + " manufacturer = " + manufacturer + "device = " + Build.MODEL);
//Log.i("DeltaChat", "codec = " + codecInfo.getName() + " manufacturer = " + manufacturer + "device = " + Build.MODEL);
} else {
colorFormat = MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface;
}
FileLog.e("messenger", "colorFormat = " + colorFormat);
//Log.i("DeltaChat", "colorFormat = " + colorFormat);
int resultHeightAligned = resultHeight;
int padding = 0;
@@ -2765,7 +2759,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
} else if (decoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
MediaFormat newFormat = decoder.getOutputFormat();
FileLog.e("messenger", "newFormat = " + newFormat);
//Log.i("DeltaChat", "newFormat = " + newFormat);
} else if (decoderStatus < 0) {
throw new RuntimeException("unexpected result from decoder.dequeueOutputBuffer: " + decoderStatus);
} else {
@@ -2784,7 +2778,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
if (startTime > 0 && videoTime == -1) {
if (info.presentationTimeUs < startTime) {
doRender = false;
FileLog.e("messenger", "drop frame startTime = " + startTime + " present time = " + info.presentationTimeUs);
//Log.i("DeltaChat", "drop frame startTime = " + startTime + " present time = " + info.presentationTimeUs);
} else {
videoTime = info.presentationTimeUs;
}
@@ -2796,7 +2790,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
outputSurface.awaitNewImage();
} catch (Exception e) {
errorWait = true;
FileLog.e("messenger", e);
}
if (!errorWait) {
if (Build.VERSION.SDK_INT >= 18) {
@@ -2813,14 +2807,14 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
Utilities.convertVideoFrame(rgbBuf, yuvBuf, colorFormat, resultWidth, resultHeight, padding, swapUV);
encoder.queueInputBuffer(inputBufIndex, 0, bufferSize, info.presentationTimeUs, 0);
} else {
FileLog.e("messenger", "input buffer not available");
//Log.i("DeltaChat", "input buffer not available");
}
}
}
}
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
decoderOutputAvailable = false;
FileLog.e("messenger", "decoder stream end");
//Log.i("DeltaChat", "decoder stream end");
if (Build.VERSION.SDK_INT >= 18) {
encoder.signalEndOfInputStream();
} else {
@@ -2838,7 +2832,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
videoStartTime = videoTime;
}
} catch (Exception e) {
FileLog.e("messenger", e);
error = true;
}
@@ -2872,7 +2866,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
} catch (Exception e) {
error = true;
FileLog.e("messenger", e);
} finally {
if (extractor != null) {
extractor.release();
@@ -2881,10 +2875,10 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
try {
mediaMuxer.finishMovie(false);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
FileLog.e("messenger", "time = " + (System.currentTimeMillis() - time));
//Log.i("DeltaChat", "time = " + (System.currentTimeMillis() - time));
}
} else {
preferences.edit().putBoolean("isPreviousOk", true).apply();
@@ -309,7 +309,7 @@ public class MessageObject {
try {
Linkify.addLinks((Spannable) caption, Linkify.WEB_URLS | Linkify.PHONE_NUMBERS | Linkify.EMAIL_ADDRESSES);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
}
@@ -320,7 +320,7 @@ public class MessageObject {
try {
Linkify.addLinks((Spannable) messageText, Linkify.WEB_URLS | Linkify.PHONE_NUMBERS | Linkify.EMAIL_ADDRESSES);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
}
@@ -355,7 +355,7 @@ public class MessageObject {
try {
textLayout = new StaticLayout(messageText, textPaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
} catch (Exception e) {
FileLog.e("messenger", e);
return;
}
@@ -392,7 +392,7 @@ public class MessageObject {
block.height = Math.max(block.height, block.textLayout.getLineBottom(block.textLayout.getLineCount() - 1));
prevOffset = block.textYOffset;
} catch (Exception e) {
FileLog.e("messenger", e);
continue;
}
if (a == blocksCount - 1) {
@@ -400,7 +400,7 @@ public class MessageObject {
try {
textHeight = Math.max(textHeight, (int) (block.textYOffset + block.textLayout.getHeight()));
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
}
@@ -411,14 +411,14 @@ public class MessageObject {
try {
lastLeft = block.textXOffset = block.textLayout.getLineLeft(currentBlockLinesCount - 1);
} catch (Exception e) {
FileLog.e("messenger", e);
}
float lastLine = 0;
try {
lastLine = block.textLayout.getLineWidth(currentBlockLinesCount - 1);
} catch (Exception e) {
FileLog.e("messenger", e);
}
int linesMaxWidth = (int) Math.ceil(lastLine);
@@ -441,7 +441,7 @@ public class MessageObject {
try {
lineWidth = block.textLayout.getLineWidth(n);
} catch (Exception e) {
FileLog.e("messenger", e);
lineWidth = 0;
}
@@ -452,7 +452,7 @@ public class MessageObject {
try {
lineLeft = block.textLayout.getLineLeft(n);
} catch (Exception e) {
FileLog.e("messenger", e);
lineLeft = 0;
}
@@ -69,6 +69,7 @@ public class MrMailbox {
public native static String getInfo();
public native static String cmdline(String cmd);
public native static void heartbeat();
private static long m_hMailbox = 0; // do not rename this, is used in C-part
private native static long MrMailboxNew(); // returns hMailbox which must be unref'd after usage (Names as mrmailbox_new don't work due to the additional underscore)
@@ -165,6 +166,8 @@ public class MrMailbox {
/* receive events
**********************************************************************************************/
public final static int MR_EVENT_INFO = 100;
public final static int MR_EVENT_WARNING = 300;
public final static int MR_EVENT_ERROR = 400; // INFO and WARNING are blocked in the mrwrapper.c
public final static int MR_EVENT_MSGS_CHANGED = 2000;
@@ -262,7 +265,25 @@ public class MrMailbox {
});
return 0;
case MR_EVENT_INFO:
Log.i("DeltaChat", CPtr2String(data2));
if( BuildConfig.BUILD_TYPE.equals("debug") ) {
FileLog.i("DeltaChat", CPtr2String(data2)); // TODO: Can be removed as soon as notifications are reliable.
}
break;
case MR_EVENT_WARNING:
Log.w("DeltaChat", CPtr2String(data2));
if( BuildConfig.BUILD_TYPE.equals("debug") ) {
FileLog.w("DeltaChat", CPtr2String(data2)); // TODO: Can be removed as soon as notifications are reliable.
}
break;
case MR_EVENT_ERROR:
Log.e("DeltaChat", CPtr2String(data2));
if( BuildConfig.BUILD_TYPE.equals("debug") ) {
FileLog.e("DeltaChat", CPtr2String(data2)); // TODO: Can be removed as soon as notifications are reliable.
}
synchronized (m_lastErrorLock) {
m_lastErrorCode = (int)data1;
m_lastErrorString = CPtr2String(data2);
@@ -361,6 +382,14 @@ public class MrMailbox {
/* additional functions that are not 1:1 available in the backend
**********************************************************************************************/
public static void log_i(String tag, String msg)
{
Log.i(tag, msg);
if( BuildConfig.BUILD_TYPE.equals("debug") ) {
FileLog.i(tag, msg); // TODO: Can be removed as soon as notifications are reliable.
}
}
public static MrChatlist m_currChatlist = new MrChatlist(0);
public native static int getCurrentTime ();
public static void reloadMainChatlist()
@@ -96,7 +96,7 @@ public class MusicPlayerService extends Service implements NotificationCenter.No
remoteControlClient.setTransportControlFlags(RemoteControlClient.FLAG_KEY_MEDIA_PLAY | RemoteControlClient.FLAG_KEY_MEDIA_PAUSE | RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE |
RemoteControlClient.FLAG_KEY_MEDIA_STOP | RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS | RemoteControlClient.FLAG_KEY_MEDIA_NEXT);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
createNotification(messageObject);
@@ -182,7 +182,7 @@ public class MusicPlayerService extends Service implements NotificationCenter.No
try {
metadataEditor.putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, audioInfo.getCover());
} catch (Throwable e) {
FileLog.e("messenger", e);
}
}
metadataEditor.apply();
@@ -150,17 +150,18 @@ public class NotificationCenter {
}
public void postNotificationNameInternal(int id, boolean allowDuringAnimation, Object... args) {
if (BuildVars.DEBUG_VERSION) {
/*if (BuildConfig.BUILD_TYPE.equals("debug")) {
if (Thread.currentThread() != ApplicationLoader.applicationHandler.getLooper().getThread()) {
throw new RuntimeException("postNotificationName allowed only from MAIN thread");
}
}
}*/
if (!allowDuringAnimation && animationInProgress) {
DelayedPost delayedPost = new DelayedPost(id, args);
delayedPosts.add(delayedPost);
if (BuildVars.DEBUG_VERSION) {
FileLog.e("messenger", "delay post notification " + id + " with args count = " + args.length);
}
/*if (BuildConfig.BUILD_TYPE.equals("debug")) {
Log.i("DeltaChat", "delay post notification " + id + " with args count = " + args.length);
}*/
return;
}
broadcasting++;
@@ -197,11 +198,11 @@ public class NotificationCenter {
}
public void addObserver(Object observer, int id) {
if (BuildVars.DEBUG_VERSION) {
/*if (BuildConfig.BUILD_TYPE.equals("debug")) {
if (Thread.currentThread() != ApplicationLoader.applicationHandler.getLooper().getThread()) {
throw new RuntimeException("addObserver allowed only from MAIN thread");
}
}
}*/
if (broadcasting != 0) {
ArrayList<Object> arrayList = addAfterBroadcast.get(id);
if (arrayList == null) {
@@ -222,11 +223,11 @@ public class NotificationCenter {
}
public void removeObserver(Object observer, int id) {
if (BuildVars.DEBUG_VERSION) {
/*if (BuildConfig.BUILD_TYPE.equals("debug")) {
if (Thread.currentThread() != ApplicationLoader.applicationHandler.getLooper().getThread()) {
throw new RuntimeException("removeObserver allowed only from MAIN thread");
}
}
}*/
if (broadcasting != 0) {
ArrayList<Object> arrayList = removeAfterBroadcast.get(id);
if (arrayList == null) {
@@ -90,6 +90,8 @@ public class NotificationsController {
protected AudioManager audioManager;
private AlarmManager alarmManager;
public static final int DEF_LED_COLOR = 0xffffffff;
private static volatile NotificationsController Instance = null;
public static NotificationsController getInstance() {
NotificationsController localInstance = Instance;
@@ -113,12 +115,12 @@ public class NotificationsController {
try {
audioManager = (AudioManager) ApplicationLoader.applicationContext.getSystemService(Context.AUDIO_SERVICE);
} catch (Exception e) {
FileLog.e("messenger", e);
}
try {
alarmManager = (AlarmManager) ApplicationLoader.applicationContext.getSystemService(Context.ALARM_SERVICE);
} catch (Exception e) {
FileLog.e("messenger", e);
}
try {
@@ -126,13 +128,13 @@ public class NotificationsController {
notificationDelayWakelock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "notificationDelayWakelock" /*any name*/);
notificationDelayWakelock.setReferenceCounted(false);
} catch (Exception e) {
FileLog.e("messenger", e);
}
notificationDelayRunnable = new Runnable() {
@Override
public void run() {
FileLog.e("messenger", "delay reached");
//Log.i("DeltaChat", "delay reached");
if (!delayedPushMessages.isEmpty()) {
showOrUpdateNotification(true);
delayedPushMessages.clear();
@@ -142,7 +144,7 @@ public class NotificationsController {
notificationDelayWakelock.release();
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
};
@@ -170,7 +172,7 @@ public class NotificationsController {
notificationDelayWakelock.release();
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
setBadge(0);
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Context.MODE_PRIVATE);
@@ -769,12 +771,12 @@ public class NotificationsController {
intent.putExtra("badge_count_class_name", launcherClassName);
ApplicationLoader.applicationContext.sendBroadcast(intent);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
});
} catch (Throwable e) {
FileLog.e("messenger", e);
}
}
});
@@ -819,7 +821,7 @@ public class NotificationsController {
alarmManager.cancel(pintent);
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -838,7 +840,7 @@ public class NotificationsController {
}
}
} catch (Throwable e) {
FileLog.e("messenger", e);
}
return null;
}
@@ -876,7 +878,7 @@ public class NotificationsController {
total_unread_count = 0;
setBadge(0);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -889,7 +891,7 @@ public class NotificationsController {
return;
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
try {
@@ -925,23 +927,23 @@ public class NotificationsController {
soundPool.play(soundIn, 1.0f, 1.0f, 1, 0, 1.0f);
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
});
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
/*private void scheduleNotificationDelay(boolean onlineReason) {
try {
FileLog.e("messenger", "delay notification start, onlineReason = " + onlineReason);
//Log.i("DeltaChat", "delay notification start, onlineReason = " + onlineReason);
notificationDelayWakelock.acquire(10000);
AndroidUtilities.cancelRunOnUIThread(notificationDelayRunnable);
AndroidUtilities.runOnUIThread(notificationDelayRunnable, (onlineReason ? 3 * 1000 : 1000));
} catch (Exception e) {
FileLog.e("messenger", e);
showOrUpdateNotification(notifyCheck);
}
}*/
@@ -992,7 +994,7 @@ public class NotificationsController {
boolean notifyDisabled = false;
int needVibrate = 0;
String choosenSoundPath = null;
int ledColor = 0xff00ff00;
int ledColor = DEF_LED_COLOR;
boolean inAppSounds;
boolean inAppVibrate;
//boolean inAppPreview = false;
@@ -1046,7 +1048,7 @@ public class NotificationsController {
}
needVibrate = preferences.getInt("vibrate_group", 0);
priority = preferences.getInt("priority_group", 1);
ledColor = preferences.getInt("GroupLed", 0xff00ff00);
ledColor = preferences.getInt("GroupLed", DEF_LED_COLOR);
} else {
if (choosenSoundPath != null && choosenSoundPath.equals(defaultPath)) {
choosenSoundPath = null;
@@ -1055,10 +1057,10 @@ public class NotificationsController {
}
needVibrate = preferences.getInt("vibrate_messages", 0);
priority = preferences.getInt("priority_messages", 1);
ledColor = preferences.getInt("MessagesLed", 0xff00ff00);
ledColor = preferences.getInt("MessagesLed", DEF_LED_COLOR);
}
if (preferences.contains("color_" + dialog_id)) {
ledColor = preferences.getInt("color_" + dialog_id, 0);
ledColor = preferences.getInt("color_" + dialog_id, 0/*0=off*/);
}
if (priorityOverride != 3) {
@@ -1088,7 +1090,7 @@ public class NotificationsController {
needVibrate = 2;
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
}
@@ -1272,7 +1274,7 @@ public class NotificationsController {
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -1336,7 +1338,7 @@ public class NotificationsController {
if (chat != null) {
name = chat.title;
} else {
name = UserObject.getUserName(user);
name = "ErrName"; // use MrContact.getName(), if really needed
}
/*if (chat != null) {
if (chat.photo != null && chat.photo.photo_small != null && chat.photo.photo_small.volume_id != 0 && chat.photo.photo_small.local_id != 0) {
@@ -1471,7 +1473,7 @@ public class NotificationsController {
return;
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
notificationsQueue.postRunnable(new Runnable() {
@Override
@@ -1500,7 +1502,7 @@ public class NotificationsController {
soundPool.play(soundOut, 1.0f, 1.0f, 1, 0, 1.0f);
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
});
@@ -33,10 +33,10 @@ public class ScreenReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
Log.i("DeltaChat", "*** Screen off");
MrMailbox.log_i("DeltaChat", "*** Screen off");
ApplicationLoader.isScreenOn = false;
} else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
Log.i("DeltaChat", "*** Screen on");
MrMailbox.log_i("DeltaChat", "*** Screen on");
ApplicationLoader.isScreenOn = true;
}
//NotificationCenter.getInstance().postNotificationName(NotificationCenter.screenStateChanged);
@@ -358,7 +358,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
bitmap.recycle();
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
if (document.mime_type.equals("image/webp") && allowSticker) {
@@ -370,7 +370,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
Utilities.loadWebpImage(null, buffer, buffer.limit(), bmOptions, true);
file.close();
} catch (Exception e) {
FileLog.e("messenger", e);
}
if (bmOptions.outWidth != 0 && bmOptions.outHeight != 0 && bmOptions.outWidth <= 800 && bmOptions.outHeight <= 800) {
TLRPC.TL_documentAttributeSticker attributeSticker = new TLRPC.TL_documentAttributeSticker();
@@ -682,14 +682,14 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
}
infoObtained = true;
} catch (Exception e) {
FileLog.e("messenger", e);
} finally {
try {
if (mediaMetadataRetriever != null) {
mediaMetadataRetriever.release();
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
if (!infoObtained) {
@@ -702,7 +702,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
mp.release();
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
}
@@ -177,7 +177,6 @@ public class TLRPC {
public static class Chat extends TLObject {
public int flags;
public boolean creator;
public int id;
public String title;
public final ChatPhoto photo = null;
@@ -22,18 +22,51 @@
package com.b44t.messenger;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;
public class TimerReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
Log.i("DeltaChat", "*** TimerReceiver.onReceive()");
// acquire for 5 seconds, this should wake un the threads,
// acquire for 3 seconds, this should wake up the threads,
// _if_ there is more to do, the backend acquires an additional wakelock using MR_EVENT_WAKE_LOCK
ApplicationLoader.wakeupWakeLock.acquire(5*1000);
ApplicationLoader.wakeupWakeLock.acquire(3*1000); // do this first!
MrMailbox.log_i("DeltaChat", "*** TimerReceiver.onReceive()");
// we assume, the IMAP thread is alive. I cannot imagine, the thread was killed with the App being running.
// (if the whole App was killed, the IMAP thread is already started by MrMailbox.connect() if we're here)
// (the thread itself will reconnect to the IMAP server as needed)
// however, it seems as if the threads sleep longer than ususal, check this by calling heartbeat() manually
MrMailbox.heartbeat();
// create the next alarm in about a minute
scheduleNextAlarm();
}
public static void scheduleNextAlarm()
{
try {
Intent intent = new Intent(ApplicationLoader.applicationContext, TimerReceiver.class);
PendingIntent alarmIntent = PendingIntent.getBroadcast(ApplicationLoader.applicationContext, 0, intent, 0);
long triggerAtMillis = System.currentTimeMillis() + 60 * 1000;
AlarmManager alarmManager = (AlarmManager) ApplicationLoader.applicationContext.getSystemService(Activity.ALARM_SERVICE);
if( Build.VERSION.SDK_INT >= 23 ) {
// a simple AlarmManager.set() is no longer send in the new DOZE mode
alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, alarmIntent);
}
else {
alarmManager.set(AlarmManager.RTC_WAKEUP, triggerAtMillis, alarmIntent);
}
}
catch(Exception e) { Log.e("DeltaChat", "Cannot create alarm.", e); }
}
}
@@ -58,7 +58,7 @@ public class UserConfig {
editor.apply();
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
}
@@ -98,7 +98,7 @@ public class UserConfig {
passcodeHash = Utilities.bytesToHex(Utilities.computeSHA256(bytes, 0, bytes.length));
saveConfig();
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
return result;
@@ -112,7 +112,7 @@ public class UserConfig {
String hash = Utilities.bytesToHex(Utilities.computeSHA256(bytes, 0, bytes.length));
return passcodeHash.equals(hash);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
return false;
@@ -56,7 +56,7 @@ public class Utilities {
sUrandomIn.close();
random.setSeed(buffer);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -67,15 +67,8 @@ public class Utilities {
public native static void calcCDT(ByteBuffer hsvBuffer, int width, int height, ByteBuffer buffer);
public native static boolean loadWebpImage(Bitmap bitmap, ByteBuffer buffer, int len, BitmapFactory.Options options, boolean unpin);
public native static int convertVideoFrame(ByteBuffer src, ByteBuffer dest, int destFormat, int width, int height, int padding, int swap);
//private native static void aesIgeEncryption(ByteBuffer buffer, byte[] key, byte[] iv, boolean encrypt, int offset, int length);
public native static String readlink(String path);
/*
public static void aesIgeEncryption(ByteBuffer buffer, byte[] key, byte[] iv, boolean encrypt, boolean changeIv, int offset, int length) {
aesIgeEncryption(buffer, key, changeIv ? iv : iv.clone(), encrypt, offset, length);
}
*/
public static Integer parseInt(String value) {
if (value == null) {
return 0;
@@ -88,7 +81,7 @@ public class Utilities {
val = Integer.parseInt(num);
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
return val;
}
@@ -105,19 +98,11 @@ public class Utilities {
val = Long.parseLong(num);
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
return val;
}
public static String parseIntToString(String value) {
Matcher matcher = pattern.matcher(value);
if (matcher.find()) {
return matcher.group(0);
}
return null;
}
public static String bytesToHex(byte[] bytes) {
if (bytes == null) {
return "";
@@ -132,138 +117,17 @@ public class Utilities {
return new String(hexChars);
}
public static byte[] hexToBytes(String hex) {
if (hex == null) {
return null;
}
int len = hex.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + Character.digit(hex.charAt(i + 1), 16));
}
return data;
}
public static boolean isGoodPrime(byte[] prime, int g) {
if (!(g >= 2 && g <= 7)) {
return false;
}
if (prime.length != 256 || prime[0] >= 0) {
return false;
}
BigInteger dhBI = new BigInteger(1, prime);
if (g == 2) { // p mod 8 = 7 for g = 2;
BigInteger res = dhBI.mod(BigInteger.valueOf(8));
if (res.intValue() != 7) {
return false;
}
} else if (g == 3) { // p mod 3 = 2 for g = 3;
BigInteger res = dhBI.mod(BigInteger.valueOf(3));
if (res.intValue() != 2) {
return false;
}
} else if (g == 5) { // p mod 5 = 1 or 4 for g = 5;
BigInteger res = dhBI.mod(BigInteger.valueOf(5));
int val = res.intValue();
if (val != 1 && val != 4) {
return false;
}
} else if (g == 6) { // p mod 24 = 19 or 23 for g = 6;
BigInteger res = dhBI.mod(BigInteger.valueOf(24));
int val = res.intValue();
if (val != 19 && val != 23) {
return false;
}
} else if (g == 7) { // p mod 7 = 3, 5 or 6 for g = 7.
BigInteger res = dhBI.mod(BigInteger.valueOf(7));
int val = res.intValue();
if (val != 3 && val != 5 && val != 6) {
return false;
}
}
String hex = bytesToHex(prime);
if (hex.equals("C71CAEB9C6B1C9048E6C522F70F13F73980D40238E3E21C14934D037563D930F48198A0AA7C14058229493D22530F4DBFA336F6E0AC925139543AED44CCE7C3720FD51F69458705AC68CD4FE6B6B13ABDC9746512969328454F18FAF8C595F642477FE96BB2A941D5BCD1D4AC8CC49880708FA9B378E3C4F3A9060BEE67CF9A4A4A695811051907E162753B56B0F6B410DBA74D8A84B2A14B3144E0EF1284754FD17ED950D5965B4B9DD46582DB1178D169C6BC465B0D6FF9CA3928FEF5B9AE4E418FC15E83EBEA0F87FA9FF5EED70050DED2849F47BF959D956850CE929851F0D8115F635B105EE2E4E15D04B2454BF6F4FADF034B10403119CD8E3B92FCC5B")) {
return true;
}
BigInteger dhBI2 = dhBI.subtract(BigInteger.valueOf(1)).divide(BigInteger.valueOf(2));
return !(!dhBI.isProbablePrime(30) || !dhBI2.isProbablePrime(30));
}
public static boolean isGoodGaAndGb(BigInteger g_a, BigInteger p) {
return !(g_a.compareTo(BigInteger.valueOf(1)) != 1 || g_a.compareTo(p.subtract(BigInteger.valueOf(1))) != -1);
}
public static boolean arraysEquals(byte[] arr1, int offset1, byte[] arr2, int offset2) {
if (arr1 == null || arr2 == null || offset1 < 0 || offset2 < 0 || arr1.length - offset1 != arr2.length - offset2 || arr1.length - offset1 < 0 || arr2.length - offset2 < 0) {
return false;
}
boolean result = true;
for (int a = offset1; a < arr1.length; a++) {
if (arr1[a + offset1] != arr2[a + offset2]) {
result = false;
}
}
return result;
}
public static byte[] computeSHA1(byte[] convertme, int offset, int len) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(convertme, offset, len);
return md.digest();
} catch (Exception e) {
FileLog.e("messenger", e);
}
return new byte[20];
}
public static byte[] computeSHA1(ByteBuffer convertme, int offset, int len) {
int oldp = convertme.position();
int oldl = convertme.limit();
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
convertme.position(offset);
convertme.limit(len);
md.update(convertme);
return md.digest();
} catch (Exception e) {
FileLog.e("messenger", e);
} finally {
convertme.limit(oldl);
convertme.position(oldp);
}
return new byte[20];
}
public static byte[] computeSHA1(ByteBuffer convertme) {
return computeSHA1(convertme, 0, convertme.limit());
}
public static byte[] computeSHA1(byte[] convertme) {
return computeSHA1(convertme, 0, convertme.length);
}
public static byte[] computeSHA256(byte[] convertme, int offset, int len) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(convertme, offset, len);
return md.digest();
} catch (Exception e) {
FileLog.e("messenger", e);
}
return null;
}
public static long bytesToLong(byte[] bytes) {
return ((long) bytes[7] << 56) + (((long) bytes[6] & 0xFF) << 48) + (((long) bytes[5] & 0xFF) << 40) + (((long) bytes[4] & 0xFF) << 32)
+ (((long) bytes[3] & 0xFF) << 24) + (((long) bytes[2] & 0xFF) << 16) + (((long) bytes[1] & 0xFF) << 8) + ((long) bytes[0] & 0xFF);
}
public static String MD5(String md5) {
if (md5 == null) {
return null;
@@ -277,7 +141,7 @@ public class Utilities {
}
return sb.toString();
} catch (java.security.NoSuchAlgorithmException e) {
FileLog.e("messenger", e);
}
return null;
}
@@ -28,7 +28,6 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import com.b44t.messenger.FileLog;
import com.b44t.ui.LaunchActivity;
public class Browser {
@@ -55,7 +54,7 @@ public class Browser {
intent.putExtra(android.provider.Browser.EXTRA_APPLICATION_ID, context.getPackageName());
context.startActivity(intent);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -47,7 +47,6 @@ import android.support.v4.view.accessibility.AccessibilityRecordCompat;
import android.support.v4.widget.EdgeEffectCompat;
import android.support.v4.widget.ScrollerCompat;
import com.b44t.messenger.FileLog;
import com.b44t.messenger.support.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
import android.util.AttributeSet;
import android.util.Log;
@@ -2050,7 +2049,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView, NestedScro
edgeEffect.setColor(glowColor);
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
}
@@ -1,58 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley;
import android.content.Intent;
/**
* Error indicating that there was an authentication failure when performing a Request.
*/
@SuppressWarnings("serial")
public class AuthFailureError extends VolleyError {
/** An intent that can be used to resolve this exception. (Brings up the password dialog.) */
private Intent mResolutionIntent;
public AuthFailureError() { }
public AuthFailureError(Intent intent) {
mResolutionIntent = intent;
}
public AuthFailureError(NetworkResponse response) {
super(response);
}
public AuthFailureError(String message) {
super(message);
}
public AuthFailureError(String message, Exception reason) {
super(message, reason);
}
public Intent getResolutionIntent() {
return mResolutionIntent;
}
@Override
public String getMessage() {
if (mResolutionIntent != null) {
return "User needs to (re)enter credentials.";
}
return super.getMessage();
}
}
@@ -1,100 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley;
import java.util.Collections;
import java.util.Map;
/**
* An interface for a cache keyed by a String with a byte array as data.
*/
public interface Cache {
/**
* Retrieves an entry from the cache.
* @param key Cache key
* @return An {@link Entry} or null in the event of a cache miss
*/
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.
*/
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.
*/
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
*/
public void invalidate(String key, boolean fullExpire);
/**
* Removes an entry from the cache.
* @param key Cache key
*/
public void remove(String key);
/**
* Empties the cache.
*/
public void clear();
/**
* Data and metadata for an entry returned by the cache.
*/
public static class Entry {
/** The data returned from cache. */
public byte[] data;
/** ETag for cache coherency. */
public String etag;
/** 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;
/** Soft TTL for this record. */
public long softTtl;
/** Immutable response headers as received from server; must be non-null. */
public Map<String, String> responseHeaders = Collections.emptyMap();
/** True if the entry is expired. */
public boolean isExpired() {
return this.ttl < System.currentTimeMillis();
}
/** True if a refresh is needed from the original data source. */
public boolean refreshNeeded() {
return this.softTtl < System.currentTimeMillis();
}
}
}
@@ -1,158 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley;
import android.os.Process;
import java.util.concurrent.BlockingQueue;
/**
* Provides a thread for performing cache triage on a queue of requests.
*
* Requests added to the specified cache queue are resolved from cache.
* Any deliverable response is posted back to the caller via a
* {@link ResponseDelivery}. Cache misses and responses that require
* refresh are enqueued on the specified network queue for processing
* by a {@link NetworkDispatcher}.
*/
public class CacheDispatcher extends Thread {
private static final boolean DEBUG = VolleyLog.DEBUG;
/** The queue of requests coming in for triage. */
private final BlockingQueue<Request<?>> mCacheQueue;
/** The queue of requests going out to the network. */
private final BlockingQueue<Request<?>> mNetworkQueue;
/** The cache to read from. */
private final Cache mCache;
/** For posting responses. */
private final ResponseDelivery mDelivery;
/** Used for telling us to die. */
private volatile boolean mQuit = false;
/**
* Creates a new cache triage dispatcher thread. You must call {@link #start()}
* in order to begin processing.
*
* @param cacheQueue Queue of incoming requests for triage
* @param networkQueue Queue to post requests that require network to
* @param cache Cache interface to use for resolution
* @param delivery Delivery interface to use for posting responses
*/
public CacheDispatcher(
BlockingQueue<Request<?>> cacheQueue, BlockingQueue<Request<?>> networkQueue,
Cache cache, ResponseDelivery delivery) {
mCacheQueue = cacheQueue;
mNetworkQueue = networkQueue;
mCache = cache;
mDelivery = delivery;
}
/**
* Forces this dispatcher to quit immediately. If any requests are still in
* the queue, they are not guaranteed to be processed.
*/
public void quit() {
mQuit = true;
interrupt();
}
@Override
public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// Make a blocking call to initialize the cache.
mCache.initialize();
while (true) {
try {
// Get a request from the cache triage queue, blocking until
// at least one is available.
final Request<?> request = mCacheQueue.take();
request.addMarker("cache-queue-take");
// If the request has been canceled, don't bother dispatching it.
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
// Attempt to retrieve this item from cache.
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
// Cache miss; send off to the network dispatcher.
mNetworkQueue.put(request);
continue;
}
// If it is completely expired, just send it to the network.
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
}
// We have a cache hit; parse its data for delivery back to the request.
request.addMarker("cache-hit");
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
if (!entry.refreshNeeded()) {
// Completely unexpired cache hit. Just deliver the response.
mDelivery.postResponse(request, response);
} else {
// Soft-expired cache hit. We can deliver the cached response,
// but we need to also send the request to the network for
// refreshing.
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
// Mark the response as intermediate.
response.intermediate = true;
// Post the intermediate response back to the user and have
// the delivery then forward the request along to the network.
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
mNetworkQueue.put(request);
} catch (InterruptedException e) {
// Not much we can do about this.
}
}
});
}
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
continue;
}
}
}
}
@@ -1,105 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley;
/**
* Default retry policy for requests.
*/
public class DefaultRetryPolicy implements RetryPolicy {
/** The current timeout in milliseconds. */
private int mCurrentTimeoutMs;
/** The current retry count. */
private int mCurrentRetryCount;
/** The maximum number of attempts. */
private final int mMaxNumRetries;
/** The backoff multiplier for the policy. */
private final float mBackoffMultiplier;
/** The default socket timeout in milliseconds */
public static final int DEFAULT_TIMEOUT_MS = 2500;
/** The default number of retries */
public static final int DEFAULT_MAX_RETRIES = 1;
/** The default backoff multiplier */
public static final float DEFAULT_BACKOFF_MULT = 1f;
/**
* Constructs a new retry policy using the default timeouts.
*/
public DefaultRetryPolicy() {
this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT);
}
/**
* Constructs a new retry policy.
* @param initialTimeoutMs The initial timeout for the policy.
* @param maxNumRetries The maximum number of retries.
* @param backoffMultiplier Backoff multiplier for the policy.
*/
public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {
mCurrentTimeoutMs = initialTimeoutMs;
mMaxNumRetries = maxNumRetries;
mBackoffMultiplier = backoffMultiplier;
}
/**
* Returns the current timeout.
*/
@Override
public int getCurrentTimeout() {
return mCurrentTimeoutMs;
}
/**
* Returns the current retry count.
*/
@Override
public int getCurrentRetryCount() {
return mCurrentRetryCount;
}
/**
* Returns the backoff multiplier for the policy.
*/
public float getBackoffMultiplier() {
return mBackoffMultiplier;
}
/**
* Prepares for the next retry by applying a backoff to the timeout.
* @param error The error code of the last attempt.
*/
@Override
public void retry(VolleyError error) throws VolleyError {
mCurrentRetryCount++;
mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
if (!hasAttemptRemaining()) {
throw error;
}
}
/**
* Returns true if this policy has attempts remaining, false otherwise.
*/
protected boolean hasAttemptRemaining() {
return mCurrentRetryCount <= mMaxNumRetries;
}
}
@@ -1,118 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley;
import android.os.Handler;
import java.util.concurrent.Executor;
/**
* Delivers responses and errors.
*/
public class ExecutorDelivery implements ResponseDelivery {
/** Used for posting responses, typically to the main thread. */
private final Executor mResponsePoster;
/**
* Creates a new response delivery interface.
* @param handler {@link Handler} to post responses on
*/
public ExecutorDelivery(final Handler handler) {
// Make an Executor that just wraps the handler.
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
/**
* Creates a new response delivery interface, mockable version
* for testing.
* @param executor For running delivery tasks
*/
public ExecutorDelivery(Executor executor) {
mResponsePoster = executor;
}
@Override
public void postResponse(Request<?> request, Response<?> response) {
postResponse(request, response, null);
}
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
@Override
public void postError(Request<?> request, VolleyError error) {
request.addMarker("post-error");
Response<?> response = Response.error(error);
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
}
/**
* A Runnable used for delivering network responses to a listener on the
* main thread.
*/
@SuppressWarnings("rawtypes")
private class ResponseDeliveryRunnable implements Runnable {
private final Request mRequest;
private final Response mResponse;
private final Runnable mRunnable;
public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
mRequest = request;
mResponse = response;
mRunnable = runnable;
}
@SuppressWarnings("unchecked")
@Override
public void run() {
// If this request has canceled, finish it and don't deliver.
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
// Deliver a normal response or error, depending.
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
// If this is an intermediate response, add a marker, otherwise we're done
// and the request can be finished.
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
mRequest.finish("done");
}
// If we have been provided a post-delivery runnable, run it.
if (mRunnable != null) {
mRunnable.run();
}
}
}
}
@@ -1,30 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley;
/**
* An interface for performing requests.
*/
public interface Network {
/**
* Performs the specified request.
* @param request Request to process
* @return A {@link NetworkResponse} with data and caching metadata; will never be null
* @throws VolleyError on errors
*/
public NetworkResponse performRequest(Request<?> request) throws VolleyError;
}
@@ -1,152 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley;
import android.annotation.TargetApi;
import android.net.TrafficStats;
import android.os.Build;
import android.os.Process;
import android.os.SystemClock;
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 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. */
private final BlockingQueue<Request<?>> mQueue;
/** The network interface for processing requests. */
private final Network mNetwork;
/** The cache to write to. */
private final Cache mCache;
/** For posting responses and errors. */
private final ResponseDelivery mDelivery;
/** Used for telling us to die. */
private volatile boolean mQuit = false;
/**
* Creates a new network dispatcher thread. You must call {@link #start()}
* in order to begin processing.
*
* @param queue Queue of incoming requests for triage
* @param network Network interface to use for performing requests
* @param cache Cache interface to use for writing responses to cache
* @param delivery Delivery interface to use for posting responses
*/
public NetworkDispatcher(BlockingQueue<Request<?>> queue,
Network network, Cache cache,
ResponseDelivery delivery) {
mQueue = queue;
mNetwork = network;
mCache = cache;
mDelivery = delivery;
}
/**
* Forces this dispatcher to quit immediately. If any requests are still in
* the queue, they are not guaranteed to be processed.
*/
public void quit() {
mQuit = true;
interrupt();
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
private void addTrafficStatsTag(Request<?> request) {
// Tag the request (if API >= 14)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
}
}
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
long startTimeMs = SystemClock.elapsedRealtime();
Request<?> request;
try {
// Take a request from the queue.
request = mQueue.take();
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
continue;
}
try {
request.addMarker("network-queue-take");
// If the request was cancelled already, do not perform the
// network request.
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
addTrafficStatsTag(request);
// Perform the network request.
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
// If the server returned 304 AND we delivered a response already,
// we're done -- don't deliver a second identical response.
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
// Parse the response here on the worker thread.
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
// Write to cache if applicable.
// TODO: Only update cache metadata instead of entire record for 304s.
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
// Post the response back.
request.markDelivered();
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
parseAndDeliverNetworkError(request, volleyError);
} catch (Exception e) {
VolleyLog.e(e, "Unhandled exception %s", e.toString());
VolleyError volleyError = new VolleyError(e);
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
mDelivery.postError(request, volleyError);
}
}
}
private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) {
error = request.parseNetworkError(error);
mDelivery.postError(request, error);
}
}
@@ -1,35 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley;
/**
* Indicates that there was a network error when performing a Volley request.
*/
@SuppressWarnings("serial")
public class NetworkError extends VolleyError {
public NetworkError() {
super();
}
public NetworkError(Throwable cause) {
super(cause);
}
public NetworkError(NetworkResponse networkResponse) {
super(networkResponse);
}
}
@@ -1,73 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley;
import org.apache.http.HttpStatus;
import java.util.Collections;
import java.util.Map;
/**
* Data and headers returned from {@link Network#performRequest(Request)}.
*/
public class NetworkResponse {
/**
* Creates a new network response.
* @param statusCode the HTTP status code
* @param data Response body
* @param headers Headers returned with this response, or null for none
* @param notModified True if the server returned a 304 and the data was already in cache
* @param networkTimeMs Round-trip network time to receive network response
*/
public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers,
boolean notModified, long networkTimeMs) {
this.statusCode = statusCode;
this.data = data;
this.headers = headers;
this.notModified = notModified;
this.networkTimeMs = networkTimeMs;
}
public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers,
boolean notModified) {
this(statusCode, data, headers, notModified, 0);
}
public NetworkResponse(byte[] data) {
this(HttpStatus.SC_OK, data, Collections.<String, String>emptyMap(), false, 0);
}
public NetworkResponse(byte[] data, Map<String, String> headers) {
this(HttpStatus.SC_OK, data, headers, false, 0);
}
/** The HTTP status code. */
public final int statusCode;
/** Raw data from this response. */
public final byte[] data;
/** Response headers. */
public final Map<String, String> headers;
/** True if the server returned a 304 (Not Modified). */
public final boolean notModified;
/** Network roundtrip time in milliseconds. */
public final long networkTimeMs;
}
@@ -1,31 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley;
/**
* Error indicating that no connection could be established when performing a Volley request.
*/
@SuppressWarnings("serial")
public class NoConnectionError extends NetworkError {
public NoConnectionError() {
super();
}
public NoConnectionError(Throwable reason) {
super(reason);
}
}
@@ -1,33 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley;
/**
* Indicates that the server's response could not be parsed.
*/
@SuppressWarnings("serial")
public class ParseError extends VolleyError {
public ParseError() { }
public ParseError(NetworkResponse networkResponse) {
super(networkResponse);
}
public ParseError(Throwable cause) {
super(cause);
}
}
@@ -1,589 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley;
import android.net.TrafficStats;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import com.b44t.messenger.volley.VolleyLog.MarkerLog;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.Map;
/**
* Base class for all network requests.
*
* @param <T> The type of parsed response this request expects.
*/
public abstract class Request<T> implements Comparable<Request<T>> {
/**
* Default encoding for POST or PUT parameters. See {@link #getParamsEncoding()}.
*/
private static final String DEFAULT_PARAMS_ENCODING = "UTF-8";
/**
* Supported request methods.
*/
public interface Method {
int DEPRECATED_GET_OR_POST = -1;
int GET = 0;
int POST = 1;
int PUT = 2;
int DELETE = 3;
int HEAD = 4;
int OPTIONS = 5;
int TRACE = 6;
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.
*/
private final int mMethod;
/** URL of this request. */
private final String mUrl;
/** Default tag for {@link TrafficStats}. */
private final int mDefaultTrafficStatsTag;
/** Listener interface for errors. */
private final Response.ErrorListener mErrorListener;
/** Sequence number of this request, used to enforce FIFO ordering. */
private Integer mSequence;
/** The request queue this request is associated with. */
private RequestQueue mRequestQueue;
/** Whether or not responses to this request should be cached. */
private boolean mShouldCache = true;
/** Whether or not this request has been canceled. */
private boolean mCanceled = false;
/** Whether or not a response has been delivered for this request yet. */
private boolean mResponseDelivered = false;
/** The retry policy for this request. */
private RetryPolicy mRetryPolicy;
/**
* When a request can be retrieved from cache but must be refreshed from
* the network, the cache entry will be stored here so that in the event of
* a "Not Modified" response, we can be sure it hasn't been evicted from cache.
*/
private Cache.Entry mCacheEntry = null;
/** An opaque token tagging this request; used for bulk cancellation. */
private Object mTag;
/**
* Creates a new request with the given URL and error listener. Note that
* the normal response listener is not provided here as delivery of responses
* 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) {
this(Method.DEPRECATED_GET_OR_POST, url, listener);
}
/**
* Creates a new request with the given method (one of the values from {@link Method}),
* URL, and error listener. Note that the normal response listener is not provided here as
* delivery of responses is provided by subclasses, who have a better idea of how to deliver
* an already-parsed response.
*/
public Request(int method, String url, Response.ErrorListener listener) {
mMethod = method;
mUrl = url;
mErrorListener = listener;
setRetryPolicy(new DefaultRetryPolicy());
mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url);
}
/**
* Return the method for this request. Can be one of the values in {@link Method}.
*/
public int getMethod() {
return mMethod;
}
/**
* Set a tag on this request. Can be used to cancel all requests with this
* tag by {@link RequestQueue#cancelAll(Object)}.
*
* @return This Request object to allow for chaining.
*/
public Request<?> setTag(Object tag) {
mTag = tag;
return this;
}
/**
* Returns this request's tag.
* @see Request#setTag(Object)
*/
public Object getTag() {
return mTag;
}
/**
* @return this request's {@link com.android.volley.Response.ErrorListener}.
*/
public Response.ErrorListener getErrorListener() {
return mErrorListener;
}
/**
* @return A tag for use with {@link TrafficStats#setThreadStatsTag(int)}
*/
public int getTrafficStatsTag() {
return mDefaultTrafficStatsTag;
}
/**
* @return The hashcode of the URL's host component, or 0 if there is none.
*/
private static int findDefaultTrafficStatsTag(String url) {
if (!TextUtils.isEmpty(url)) {
Uri uri = Uri.parse(url);
if (uri != null) {
String host = uri.getHost();
if (host != null) {
return host.hashCode();
}
}
}
return 0;
}
/**
* Sets the retry policy for this request.
*
* @return This Request object to allow for chaining.
*/
public Request<?> setRetryPolicy(RetryPolicy retryPolicy) {
mRetryPolicy = retryPolicy;
return this;
}
/**
* Adds an event to this request's event log; for debugging.
*/
public void addMarker(String tag) {
if (MarkerLog.ENABLED) {
mEventLog.add(tag, Thread.currentThread().getId());
}
}
/**
* Notifies the request queue that this request has finished (successfully or with error).
*
* <p>Also dumps all events from this request's event log; for debugging.</p>
*/
void finish(final String tag) {
if (mRequestQueue != null) {
mRequestQueue.finish(this);
}
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());
}
}
/**
* Associates this request with the given queue. The request queue will be notified when this
* request has finished.
*
* @return This Request object to allow for chaining.
*/
public Request<?> setRequestQueue(RequestQueue requestQueue) {
mRequestQueue = requestQueue;
return this;
}
/**
* Sets the sequence number of this request. Used by {@link RequestQueue}.
*
* @return This Request object to allow for chaining.
*/
public final Request<?> setSequence(int sequence) {
mSequence = sequence;
return this;
}
/**
* Returns the sequence number of this request.
*/
public final int getSequence() {
if (mSequence == null) {
throw new IllegalStateException("getSequence called before setSequence");
}
return mSequence;
}
/**
* Returns the URL of this request.
*/
public String getUrl() {
return mUrl;
}
/**
* Returns the cache key for this request. By default, this is the URL.
*/
public String getCacheKey() {
return getUrl();
}
/**
* Annotates this request with an entry retrieved for it from cache.
* Used for cache coherency support.
*
* @return This Request object to allow for chaining.
*/
public Request<?> setCacheEntry(Cache.Entry entry) {
mCacheEntry = entry;
return this;
}
/**
* Returns the annotated cache entry, or null if there isn't one.
*/
public Cache.Entry getCacheEntry() {
return mCacheEntry;
}
/**
* Mark this request as canceled. No callback will be delivered.
*/
public void cancel() {
mCanceled = true;
}
/**
* Returns true if this request has been canceled.
*/
public boolean isCanceled() {
return mCanceled;
}
/**
* Returns a list of extra HTTP headers to go along with this request. Can
* throw {@link AuthFailureError} as authentication may be required to
* provide these values.
* @throws AuthFailureError In the event of auth failure
*/
public Map<String, String> getHeaders() throws AuthFailureError {
return Collections.emptyMap();
}
/**
* Returns a Map of POST parameters to be used for this request, or null if
* a simple GET should be used. Can throw {@link AuthFailureError} as
* authentication may be required to provide these values.
*
* <p>Note that only one of getPostParams() and getPostBody() can return a non-null
* value.</p>
* @throws AuthFailureError In the event of auth failure
*
* @deprecated Use {@link #getParams()} instead.
*/
@Deprecated
protected Map<String, String> getPostParams() throws AuthFailureError {
return getParams();
}
/**
* Returns which encoding should be used when converting POST parameters returned by
* {@link #getPostParams()} into a raw POST body.
*
* <p>This controls both encodings:
* <ol>
* <li>The string encoding used when converting parameter names and values into bytes prior
* to URL encoding them.</li>
* <li>The string encoding used when converting the URL encoded parameters into a raw
* byte array.</li>
* </ol>
*
* @deprecated Use {@link #getParamsEncoding()} instead.
*/
@Deprecated
protected String getPostParamsEncoding() {
return getParamsEncoding();
}
/**
* @deprecated Use {@link #getBodyContentType()} instead.
*/
@Deprecated
public String getPostBodyContentType() {
return getBodyContentType();
}
/**
* Returns the raw POST body to be sent.
*
* @throws AuthFailureError In the event of auth failure
*
* @deprecated Use {@link #getBody()} instead.
*/
@Deprecated
public byte[] getPostBody() throws AuthFailureError {
// Note: For compatibility with legacy clients of volley, this implementation must remain
// here instead of simply calling the getBody() function because this function must
// call getPostParams() and getPostParamsEncoding() since legacy clients would have
// overridden these two member functions for POST requests.
Map<String, String> postParams = getPostParams();
if (postParams != null && postParams.size() > 0) {
return encodeParameters(postParams, getPostParamsEncoding());
}
return null;
}
/**
* Returns a Map of parameters to be used for a POST or PUT request. Can throw
* {@link AuthFailureError} as authentication may be required to provide these values.
*
* <p>Note that you can directly override {@link #getBody()} for custom data.</p>
*
* @throws AuthFailureError in the event of auth failure
*/
protected Map<String, String> getParams() throws AuthFailureError {
return null;
}
/**
* Returns which encoding should be used when converting POST or PUT parameters returned by
* {@link #getParams()} into a raw POST or PUT body.
*
* <p>This controls both encodings:
* <ol>
* <li>The string encoding used when converting parameter names and values into bytes prior
* to URL encoding them.</li>
* <li>The string encoding used when converting the URL encoded parameters into a raw
* byte array.</li>
* </ol>
*/
protected String getParamsEncoding() {
return DEFAULT_PARAMS_ENCODING;
}
/**
* Returns the content type of the POST or PUT body.
*/
public String getBodyContentType() {
return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();
}
/**
* Returns the raw POST or PUT body to be sent.
*
* <p>By default, the body consists of the request parameters in
* application/x-www-form-urlencoded format. When overriding this method, consider overriding
* {@link #getBodyContentType()} as well to match the new body format.
*
* @throws AuthFailureError in the event of auth failure
*/
public byte[] getBody() throws AuthFailureError {
Map<String, String> params = getParams();
if (params != null && params.size() > 0) {
return encodeParameters(params, getParamsEncoding());
}
return null;
}
/**
* Converts <code>params</code> into an application/x-www-form-urlencoded encoded string.
*/
private byte[] encodeParameters(Map<String, String> params, String paramsEncoding) {
StringBuilder encodedParams = new StringBuilder();
try {
for (Map.Entry<String, String> entry : params.entrySet()) {
encodedParams.append(URLEncoder.encode(entry.getKey(), paramsEncoding));
encodedParams.append('=');
encodedParams.append(URLEncoder.encode(entry.getValue(), paramsEncoding));
encodedParams.append('&');
}
return encodedParams.toString().getBytes(paramsEncoding);
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException("Encoding not supported: " + paramsEncoding, uee);
}
}
/**
* Set whether or not responses to this request should be cached.
*
* @return This Request object to allow for chaining.
*/
public final Request<?> setShouldCache(boolean shouldCache) {
mShouldCache = shouldCache;
return this;
}
/**
* Returns true if responses to this request should be cached.
*/
public final boolean shouldCache() {
return mShouldCache;
}
/**
* Priority values. Requests will be processed from higher priorities to
* lower priorities, in FIFO order.
*/
public enum Priority {
LOW,
NORMAL,
HIGH,
IMMEDIATE
}
/**
* Returns the {@link Priority} of this request; {@link Priority#NORMAL} by default.
*/
public Priority getPriority() {
return Priority.NORMAL;
}
/**
* Returns the socket timeout in milliseconds per retry attempt. (This value can be changed
* per retry attempt if a backoff is specified via backoffTimeout()). If there are no retry
* attempts remaining, this will cause delivery of a {@link TimeoutError} error.
*/
public final int getTimeoutMs() {
return mRetryPolicy.getCurrentTimeout();
}
/**
* Returns the retry policy that should be used for this request.
*/
public RetryPolicy getRetryPolicy() {
return mRetryPolicy;
}
/**
* Mark this request as having a response delivered on it. This can be used
* later in the request's lifetime for suppressing identical responses.
*/
public void markDelivered() {
mResponseDelivered = true;
}
/**
* Returns true if this request has had a response delivered for it.
*/
public boolean hasHadResponseDelivered() {
return mResponseDelivered;
}
/**
* Subclasses must implement this to parse the raw network response
* and return an appropriate response type. This method will be
* called from a worker thread. The response will not be delivered
* if you return null.
* @param response Response from the network
* @return The parsed response, or null in the case of an error
*/
abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
/**
* Subclasses can override this method to parse 'networkError' and return a more specific error.
*
* <p>The default implementation just returns the passed 'networkError'.</p>
*
* @param volleyError the error retrieved from the network
* @return an NetworkError augmented with additional information
*/
protected VolleyError parseNetworkError(VolleyError volleyError) {
return volleyError;
}
/**
* Subclasses must implement this to perform delivery of the parsed
* response to their listeners. The given response is guaranteed to
* be non-null; responses that fail to parse are not delivered.
* @param response The parsed response returned by
* {@link #parseNetworkResponse(NetworkResponse)}
*/
abstract protected void deliverResponse(T response);
/**
* Delivers error message to the ErrorListener that the Request was
* initialized with.
*
* @param error Error details
*/
public void deliverError(VolleyError error) {
if (mErrorListener != null) {
mErrorListener.onErrorResponse(error);
}
}
/**
* Our comparator sorts from high to low priority, and secondarily by
* sequence number to provide FIFO ordering.
*/
@Override
public int compareTo(Request<T> other) {
Priority left = this.getPriority();
Priority right = other.getPriority();
// High-priority requests are "lesser" so they are sorted to the front.
// Equal priorities are sorted by sequence number to provide FIFO ordering.
return left == right ?
this.mSequence - other.mSequence :
right.ordinal() - left.ordinal();
}
@Override
public String toString() {
String trafficStatsTag = "0x" + Integer.toHexString(getTrafficStatsTag());
return (mCanceled ? "[X] " : "[ ] ") + getUrl() + " " + trafficStatsTag + " "
+ getPriority() + " " + mSequence;
}
}
@@ -1,317 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.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;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
/**
* A request dispatch queue with a thread pool of dispatchers.
*
* 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();
/**
* Staging area for requests that already have a duplicate request in flight.
*
* <ul>
* <li>containsKey(cacheKey) indicates that there is a request in flight for the given cache
* key.</li>
* <li>get(cacheKey) returns waiting requests for the given cache key. The in flight request
* is <em>not</em> contained in that list. Is null if no requests are staged.</li>
* </ul>
*/
private final Map<String, Queue<Request<?>>> mWaitingRequests =
new HashMap<String, Queue<Request<?>>>();
/**
* The set of all requests currently being processed by this RequestQueue. A Request
* will be in this set if it is waiting in any queue or currently being processed by
* any dispatcher.
*/
private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();
/** The cache triage queue. */
private final PriorityBlockingQueue<Request<?>> mCacheQueue =
new PriorityBlockingQueue<Request<?>>();
/** The queue of requests that are actually going out to the network. */
private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
new PriorityBlockingQueue<Request<?>>();
/** Number of network request dispatcher threads to start. */
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
/** Cache interface for retrieving and storing responses. */
private final Cache mCache;
/** Network interface for performing requests. */
private final Network mNetwork;
/** Response delivery mechanism. */
private final ResponseDelivery mDelivery;
/** The network dispatchers. */
private NetworkDispatcher[] mDispatchers;
/** 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.
*
* @param cache A Cache to use for persisting responses to disk
* @param network A Network interface for performing HTTP requests
* @param threadPoolSize Number of network dispatcher threads to create
* @param delivery A ResponseDelivery interface for posting responses and errors
*/
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
/**
* Creates the worker pool. Processing will not begin until {@link #start()} is called.
*
* @param cache A Cache to use for persisting responses to disk
* @param network A Network interface for performing HTTP requests
* @param threadPoolSize Number of network dispatcher threads to create
*/
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
/**
* Creates the worker pool. Processing will not begin until {@link #start()} is called.
*
* @param cache A Cache to use for persisting responses to disk
* @param network A Network interface for performing HTTP requests
*/
public RequestQueue(Cache cache, Network network) {
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}
/**
* Starts the dispatchers in this queue.
*/
public void start() {
stop(); // Make sure any currently running dispatchers are stopped.
// Create the cache dispatcher and start it.
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
// Create network dispatchers (and corresponding threads) up to the pool size.
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
/**
* Stops the cache and network dispatchers.
*/
public void stop() {
if (mCacheDispatcher != null) {
mCacheDispatcher.quit();
}
for (int i = 0; i < mDispatchers.length; i++) {
if (mDispatchers[i] != null) {
mDispatchers[i].quit();
}
}
}
/**
* Gets a sequence number.
*/
public int getSequenceNumber() {
return mSequenceGenerator.incrementAndGet();
}
/**
* Gets the {@link Cache} instance being used.
*/
public Cache getCache() {
return mCache;
}
/**
* A simple predicate or filter interface for Requests, for use by
* {@link RequestQueue#cancelAll(RequestFilter)}.
*/
public interface RequestFilter {
public boolean apply(Request<?> request);
}
/**
* Cancels all requests in this queue for which the given filter applies.
* @param filter The filtering function to use
*/
public void cancelAll(RequestFilter filter) {
synchronized (mCurrentRequests) {
for (Request<?> request : mCurrentRequests) {
if (filter.apply(request)) {
request.cancel();
}
}
}
}
/**
* Cancels all requests in this queue with the given tag. Tag must be non-null
* and equality is by identity.
*/
public void cancelAll(final Object tag) {
if (tag == null) {
throw new IllegalArgumentException("Cannot cancelAll with a null tag");
}
cancelAll(new RequestFilter() {
@Override
public boolean apply(Request<?> request) {
return request.getTag() == tag;
}
});
}
/**
* Adds a Request to the dispatch queue.
* @param request The request to service
* @return The passed-in request
*/
public <T> Request<T> add(Request<T> request) {
// Tag the request as belonging to this queue and add it to the set of current requests.
request.setRequestQueue(this);
synchronized (mCurrentRequests) {
mCurrentRequests.add(request);
}
// Process requests in the order they are added.
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
// If the request is uncacheable, skip the cache queue and go straight to the network.
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
// Insert request into stage if there's already a request with the same cache key in flight.
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
if (mWaitingRequests.containsKey(cacheKey)) {
// There is already a request in flight. Queue up.
Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
if (stagedRequests == null) {
stagedRequests = new LinkedList<Request<?>>();
}
stagedRequests.add(request);
mWaitingRequests.put(cacheKey, stagedRequests);
if (VolleyLog.DEBUG) {
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
}
} else {
// Insert 'null' queue for this cacheKey, indicating there is now a request in
// flight.
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
}
return request;
}
}
/**
* Called from {@link Request#finish(String)}, indicating that processing of the given request
* has finished.
*
* <p>Releases waiting requests for <code>request.getCacheKey()</code> if
* <code>request.shouldCache()</code>.</p>
*/
<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) {
String cacheKey = request.getCacheKey();
Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);
if (waitingRequests != null) {
if (VolleyLog.DEBUG) {
VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",
waitingRequests.size(), cacheKey);
}
// Process all queued up requests. They won't be considered as in flight, but
// that's not a problem as the cache has been primed by 'request'.
mCacheQueue.addAll(waitingRequests);
}
}
}
}
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);
}
}
}
@@ -1,85 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley;
/**
* Encapsulates a parsed response for delivery.
*
* @param <T> Parsed type of this response
*/
public class Response<T> {
/** Callback interface for delivering parsed responses. */
public interface Listener<T> {
/** Called when a response is received. */
public void onResponse(T response);
}
/** Callback interface for delivering error responses. */
public interface ErrorListener {
/**
* Callback method that an error has been occurred with the
* provided error code and optional user-readable message.
*/
public void onErrorResponse(VolleyError error);
}
/** Returns a successful response containing the parsed result. */
public static <T> Response<T> success(T result, Cache.Entry cacheEntry) {
return new Response<T>(result, cacheEntry);
}
/**
* Returns a failed response containing the given error code and an optional
* localized message displayed to the user.
*/
public static <T> Response<T> error(VolleyError error) {
return new Response<T>(error);
}
/** Parsed response, or null in the case of error. */
public final T result;
/** Cache metadata for this response, or null in the case of error. */
public final Cache.Entry cacheEntry;
/** Detailed error information if <code>errorCode != OK</code>. */
public final VolleyError error;
/** True if this response was a soft-expired one and a second one MAY be coming. */
public boolean intermediate = false;
/**
* Returns whether this response is considered successful.
*/
public boolean isSuccess() {
return error == null;
}
private Response(T result, Cache.Entry cacheEntry) {
this.result = result;
this.cacheEntry = cacheEntry;
this.error = null;
}
private Response(VolleyError error) {
this.result = null;
this.cacheEntry = null;
this.error = error;
}
}
@@ -1,35 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley;
public interface ResponseDelivery {
/**
* Parses a response from the network or cache and delivers it.
*/
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.
*/
public void postResponse(Request<?> request, Response<?> response, Runnable runnable);
/**
* Posts an error for the given request.
*/
public void postError(Request<?> request, VolleyError error);
}
@@ -1,41 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley;
/**
* Retry policy for a request.
*/
public interface RetryPolicy {
/**
* Returns the current timeout (used for logging).
*/
public int getCurrentTimeout();
/**
* Returns the current retry count (used for logging).
*/
public int getCurrentRetryCount();
/**
* Prepares for the next retry by applying a backoff to the timeout.
* @param error The error code of the last attempt.
* @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.
*/
public void retry(VolleyError error) throws VolleyError;
}
@@ -1,32 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley;
/**
* Indicates that the server responded with an error response.
*/
@SuppressWarnings("serial")
public class ServerError extends VolleyError {
public ServerError(NetworkResponse networkResponse) {
super(networkResponse);
}
public ServerError() {
super();
}
}
@@ -1,23 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley;
/**
* Indicates that the connection or the socket timed out.
*/
@SuppressWarnings("serial")
public class TimeoutError extends VolleyError { }
@@ -1,57 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley;
/**
* Exception style class encapsulating Volley errors
*/
@SuppressWarnings("serial")
public class VolleyError extends Exception {
public final NetworkResponse networkResponse;
private long networkTimeMs;
public VolleyError() {
networkResponse = null;
}
public VolleyError(NetworkResponse response) {
networkResponse = response;
}
public VolleyError(String exceptionMessage) {
super(exceptionMessage);
networkResponse = null;
}
public VolleyError(String exceptionMessage, Throwable reason) {
super(exceptionMessage, reason);
networkResponse = null;
}
public VolleyError(Throwable cause) {
super(cause);
networkResponse = null;
}
/* package */ void setNetworkTimeMs(long networkTimeMs) {
this.networkTimeMs = networkTimeMs;
}
public long getNetworkTimeMs() {
return networkTimeMs;
}
}
@@ -1,181 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley;
import android.os.SystemClock;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
* 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";
public static boolean DEBUG = Log.isLoggable(TAG, Log.VERBOSE);
/**
* Customize the log tag for your application, so that other apps
* using Volley don't mix their logs with yours.
* <br />
* Enable the log property for your tag before starting your app:
* <br />
* {@code adb shell setprop log.tag.&lt;tag&gt;}
*/
public static void setTag(String tag) {
d("Changing log tag to %s", tag);
TAG = tag;
// Reinitialize the DEBUG "constant"
DEBUG = Log.isLoggable(TAG, Log.VERBOSE);
}
public static void v(String format, Object... args) {
if (DEBUG) {
Log.v(TAG, buildMessage(format, args));
}
}
public static void d(String format, Object... args) {
Log.d(TAG, buildMessage(format, args));
}
public static void e(String format, Object... args) {
Log.e(TAG, buildMessage(format, args));
}
public static void e(Throwable tr, String format, Object... args) {
Log.e(TAG, buildMessage(format, args), tr);
}
public static void wtf(String format, Object... args) {
Log.wtf(TAG, buildMessage(format, args));
}
public static void wtf(Throwable tr, String format, Object... args) {
Log.wtf(TAG, buildMessage(format, args), tr);
}
/**
* Formats the caller's provided message and prepends useful info like
* calling thread ID and method name.
*/
private static String buildMessage(String format, Object... args) {
String msg = (args == null) ? format : String.format(Locale.US, format, args);
StackTraceElement[] trace = new Throwable().fillInStackTrace().getStackTrace();
String caller = "<unknown>";
// Walk up the stack looking for the first caller outside of VolleyLog.
// It will be at least two frames up, so start there.
for (int i = 2; i < trace.length; i++) {
Class<?> clazz = trace[i].getClass();
if (!clazz.equals(VolleyLog.class)) {
String callingClass = trace[i].getClassName();
callingClass = callingClass.substring(callingClass.lastIndexOf('.') + 1);
callingClass = callingClass.substring(callingClass.lastIndexOf('$') + 1);
caller = callingClass + "." + trace[i].getMethodName();
break;
}
}
return String.format(Locale.US, "[%d] %s: %s",
Thread.currentThread().getId(), caller, msg);
}
/**
* A simple event log with records containing a name, thread ID, and timestamp.
*/
static class MarkerLog {
public static final boolean ENABLED = VolleyLog.DEBUG;
/** Minimum duration from first marker to last in an marker log to warrant logging. */
private static final long MIN_DURATION_FOR_LOGGING_MS = 0;
private static class Marker {
public final String name;
public final long thread;
public final long time;
public Marker(String name, long thread, long time) {
this.name = name;
this.thread = thread;
this.time = time;
}
}
private final List<Marker> mMarkers = new ArrayList<Marker>();
private boolean mFinished = false;
/** Adds a marker to this log with the specified name. */
public synchronized void add(String name, long threadId) {
if (mFinished) {
throw new IllegalStateException("Marker added to finished log");
}
mMarkers.add(new Marker(name, threadId, SystemClock.elapsedRealtime()));
}
/**
* Closes the log, dumping it to logcat if the time difference between
* the first and last markers is greater than {@link #MIN_DURATION_FOR_LOGGING_MS}.
* @param header Header string to print above the marker log.
*/
public synchronized void finish(String header) {
mFinished = true;
long duration = getTotalDuration();
if (duration <= MIN_DURATION_FOR_LOGGING_MS) {
return;
}
long prevTime = mMarkers.get(0).time;
d("(%-4d ms) %s", duration, header);
for (Marker marker : mMarkers) {
long thisTime = marker.time;
d("(+%-4d) [%2d] %s", (thisTime - prevTime), marker.thread, marker.name);
prevTime = thisTime;
}
}
@Override
protected void finalize() throws Throwable {
// Catch requests that have been collected (and hence end-of-lifed)
// but had no debugging output printed for them.
if (!mFinished) {
finish("Request on the loose");
e("Marker log finalized without finish() - uncaught exit point for request");
}
}
/** Returns the time difference between the first and last events in this log. */
private long getTotalDuration() {
if (mMarkers.size() == 0) {
return 0;
}
long first = mMarkers.get(0).time;
long last = mMarkers.get(mMarkers.size() - 1).time;
return last - first;
}
}
}
@@ -1,266 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley.toolbox;
import android.os.SystemClock;
import com.b44t.messenger.volley.AuthFailureError;
import com.b44t.messenger.volley.Cache;
import com.b44t.messenger.volley.Cache.Entry;
import com.b44t.messenger.volley.Network;
import com.b44t.messenger.volley.NetworkError;
import com.b44t.messenger.volley.NetworkResponse;
import com.b44t.messenger.volley.NoConnectionError;
import com.b44t.messenger.volley.Request;
import com.b44t.messenger.volley.RetryPolicy;
import com.b44t.messenger.volley.ServerError;
import com.b44t.messenger.volley.TimeoutError;
import com.b44t.messenger.volley.VolleyError;
import com.b44t.messenger.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;
import java.net.SocketTimeoutException;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
/**
* A network performing Volley requests over an {@link HttpStack}.
*/
public class BasicNetwork implements Network {
protected static final boolean DEBUG = VolleyLog.DEBUG;
private static int SLOW_REQUEST_THRESHOLD_MS = 3000;
private static int DEFAULT_POOL_SIZE = 4096;
protected final HttpStack mHttpStack;
protected final ByteArrayPool mPool;
/**
* @param httpStack HTTP stack to be used
*/
public BasicNetwork(HttpStack httpStack) {
// If a pool isn't passed in, then build a small default pool that will give us a lot of
// benefit and not use too much memory.
this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
}
/**
* @param httpStack HTTP stack to be used
* @param pool a buffer pool that improves GC performance in copy operations
*/
public BasicNetwork(HttpStack httpStack, ByteArrayPool pool) {
mHttpStack = httpStack;
mPool = pool;
}
@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
Map<String, String> responseHeaders = Collections.emptyMap();
try {
// Gather headers.
Map<String, String> headers = new HashMap<String, String>();
addCacheHeaders(headers, request.getCacheEntry());
httpResponse = mHttpStack.performRequest(request, headers);
StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode();
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
// Handle cache validation.
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
Entry entry = request.getCacheEntry();
if (entry == null) {
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
// A HTTP 304 response does not have all header fields. We
// have to use the header fields from the cache entry plus
// the new ones from the response.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
entry.responseHeaders.putAll(responseHeaders);
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
entry.responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
// Some responses such as 204s do not have content. We must check.
if (httpResponse.getEntity() != null) {
responseContents = entityToBytes(httpResponse.getEntity());
} else {
// Add 0 byte response as a way of honestly representing a
// no-content request.
responseContents = new byte[0];
}
// if the request is slow, log it.
long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
logSlowRequests(requestLifetime, request, responseContents, statusLine);
if (statusCode < 200 || statusCode > 299) {
throw new IOException();
}
return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
SystemClock.elapsedRealtime() - requestStart);
} catch (SocketTimeoutException e) {
attemptRetryOnException("socket", request, new TimeoutError());
} catch (ConnectTimeoutException e) {
attemptRetryOnException("connection", request, new TimeoutError());
} catch (MalformedURLException e) {
throw new RuntimeException("Bad URL " + request.getUrl(), e);
} catch (IOException e) {
int statusCode = 0;
NetworkResponse networkResponse = null;
if (httpResponse != null) {
statusCode = httpResponse.getStatusLine().getStatusCode();
} else {
throw new NoConnectionError(e);
}
VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
if (responseContents != null) {
networkResponse = new NetworkResponse(statusCode, responseContents,
responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
statusCode == HttpStatus.SC_FORBIDDEN) {
attemptRetryOnException("auth",
request, new AuthFailureError(networkResponse));
} else {
// TODO: Only throw ServerError for 5xx status codes.
throw new ServerError(networkResponse);
}
} else {
throw new NetworkError(networkResponse);
}
}
}
}
/**
* Logs requests that took over SLOW_REQUEST_THRESHOLD_MS to complete.
*/
private void logSlowRequests(long requestLifetime, Request<?> request,
byte[] responseContents, StatusLine statusLine) {
if (DEBUG || requestLifetime > SLOW_REQUEST_THRESHOLD_MS) {
VolleyLog.d("HTTP response for request=<%s> [lifetime=%d], [size=%s], " +
"[rc=%d], [retryCount=%s]", request, requestLifetime,
responseContents != null ? responseContents.length : "null",
statusLine.getStatusCode(), request.getRetryPolicy().getCurrentRetryCount());
}
}
/**
* Attempts to prepare the request for a retry. If there are no more attempts remaining in the
* request's retry policy, a timeout exception is thrown.
* @param request The request to use.
*/
private static void attemptRetryOnException(String logPrefix, Request<?> request,
VolleyError exception) throws VolleyError {
RetryPolicy retryPolicy = request.getRetryPolicy();
int oldTimeout = request.getTimeoutMs();
try {
retryPolicy.retry(exception);
} catch (VolleyError e) {
request.addMarker(
String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));
throw e;
}
request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
}
private void addCacheHeaders(Map<String, String> headers, Cache.Entry entry) {
// If there's no cache entry, we're done.
if (entry == null) {
return;
}
if (entry.etag != null) {
headers.put("If-None-Match", entry.etag);
}
if (entry.lastModified > 0) {
Date refTime = new Date(entry.lastModified);
headers.put("If-Modified-Since", DateUtils.formatDate(refTime));
}
}
protected void logError(String what, String url, long start) {
long now = SystemClock.elapsedRealtime();
VolleyLog.v("HTTP ERROR(%s) %d ms to fetch %s", what, (now - start), url);
}
/** Reads the contents of HttpEntity into a byte[]. */
private byte[] entityToBytes(HttpEntity entity) throws IOException, ServerError {
PoolingByteArrayOutputStream bytes =
new PoolingByteArrayOutputStream(mPool, (int) entity.getContentLength());
byte[] buffer = null;
try {
InputStream in = entity.getContent();
if (in == null) {
throw new ServerError();
}
buffer = mPool.getBuf(1024);
int count;
while ((count = in.read(buffer)) != -1) {
bytes.write(buffer, 0, count);
}
return bytes.toByteArray();
} finally {
try {
// Close the InputStream and release the resources by "consuming the content".
entity.consumeContent();
} catch (IOException e) {
// This can happen if there was an exception above that left the entity in
// an invalid state.
VolleyLog.v("Error occured when calling consumingContent");
}
mPool.returnBuf(buffer);
bytes.close();
}
}
/**
* Converts Headers[] to Map<String, String>.
*/
protected static Map<String, String> convertHeaders(Header[] headers) {
Map<String, String> result = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
for (int i = 0; i < headers.length; i++) {
result.put(headers[i].getName(), headers[i].getValue());
}
return result;
}
}
@@ -1,135 +0,0 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley.toolbox;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
/**
* ByteArrayPool is a source and repository of <code>byte[]</code> objects. Its purpose is to
* supply those buffers to consumers who need to use them for a short period of time and then
* dispose of them. Simply creating and disposing such buffers in the conventional manner can
* considerable heap churn and garbage collection delays on Android, which lacks good management of
* short-lived heap objects. It may be advantageous to trade off some memory in the form of a
* permanently allocated pool of buffers in order to gain heap performance improvements; that is
* what this class does.
* <p>
* A good candidate user for this class is something like an I/O system that uses large temporary
* <code>byte[]</code> buffers to copy data around. In these use cases, often the consumer wants
* the buffer to be a certain minimum size to ensure good performance (e.g. when copying data chunks
* off of a stream), but doesn't mind if the buffer is larger than the minimum. Taking this into
* account and also to maximize the odds of being able to reuse a recycled buffer, this class is
* free to return buffers larger than the requested size. The caller needs to be able to gracefully
* deal with getting buffers any size over the minimum.
* <p>
* If there is not a suitably-sized buffer in its recycling pool when a buffer is requested, this
* class will allocate a new buffer and return it.
* <p>
* This class has no special ownership of buffers it creates; the caller is free to take a buffer
* it receives from this pool, use it permanently, and never return it to the pool; additionally,
* it is not harmful to return to this pool a buffer that was allocated elsewhere, provided there
* are no other lingering references to it.
* <p>
* This class ensures that the total size of the buffers in its recycling pool never exceeds a
* certain byte limit. When a buffer is returned that would cause the pool to exceed the limit,
* least-recently-used buffers are disposed.
*/
public class ByteArrayPool {
/** The buffer pool, arranged both by last use and by buffer size */
private List<byte[]> mBuffersByLastUse = new LinkedList<byte[]>();
private List<byte[]> mBuffersBySize = new ArrayList<byte[]>(64);
/** The total size of the buffers in the pool */
private int mCurrentSize = 0;
/**
* The maximum aggregate size of the buffers in the pool. Old buffers are discarded to stay
* under this limit.
*/
private final int mSizeLimit;
/** Compares buffers by size */
protected static final Comparator<byte[]> BUF_COMPARATOR = new Comparator<byte[]>() {
@Override
public int compare(byte[] lhs, byte[] rhs) {
return lhs.length - rhs.length;
}
};
/**
* @param sizeLimit the maximum size of the pool, in bytes
*/
public ByteArrayPool(int sizeLimit) {
mSizeLimit = sizeLimit;
}
/**
* Returns a buffer from the pool if one is available in the requested size, or allocates a new
* one if a pooled one is not available.
*
* @param len the minimum size, in bytes, of the requested buffer. The returned buffer may be
* larger.
* @return a byte[] buffer is always returned.
*/
public synchronized byte[] getBuf(int len) {
for (int i = 0; i < mBuffersBySize.size(); i++) {
byte[] buf = mBuffersBySize.get(i);
if (buf.length >= len) {
mCurrentSize -= buf.length;
mBuffersBySize.remove(i);
mBuffersByLastUse.remove(buf);
return buf;
}
}
return new byte[len];
}
/**
* Returns a buffer to the pool, throwing away old buffers if the pool would exceed its allotted
* size.
*
* @param buf the buffer to return to the pool.
*/
public synchronized void returnBuf(byte[] buf) {
if (buf == null || buf.length > mSizeLimit) {
return;
}
mBuffersByLastUse.add(buf);
int pos = Collections.binarySearch(mBuffersBySize, buf, BUF_COMPARATOR);
if (pos < 0) {
pos = -pos - 1;
}
mBuffersBySize.add(pos, buf);
mCurrentSize += buf.length;
trim();
}
/**
* Removes buffers from the pool until it is under its size limit.
*/
private synchronized void trim() {
while (mCurrentSize > mSizeLimit) {
byte[] buf = mBuffersByLastUse.remove(0);
mBuffersBySize.remove(buf);
mCurrentSize -= buf.length;
}
}
}
@@ -1,70 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley.toolbox;
import com.b44t.messenger.volley.Cache;
import com.b44t.messenger.volley.NetworkResponse;
import com.b44t.messenger.volley.Request;
import com.b44t.messenger.volley.Response;
import android.os.Handler;
import android.os.Looper;
/**
* A synthetic request used for clearing the cache.
*/
public class ClearCacheRequest extends Request<Object> {
private final Cache mCache;
private final Runnable mCallback;
/**
* Creates a synthetic request for clearing the cache.
* @param cache Cache to clear
* @param callback Callback to make on the main thread once the cache is clear,
* or null for none
*/
public ClearCacheRequest(Cache cache, Runnable callback) {
super(Method.GET, null, null);
mCache = cache;
mCallback = callback;
}
@Override
public boolean isCanceled() {
// This is a little bit of a hack, but hey, why not.
mCache.clear();
if (mCallback != null) {
Handler handler = new Handler(Looper.getMainLooper());
handler.postAtFrontOfQueue(mCallback);
}
return true;
}
@Override
public Priority getPriority() {
return Priority.IMMEDIATE;
}
@Override
protected Response<Object> parseNetworkResponse(NetworkResponse response) {
return null;
}
@Override
protected void deliverResponse(Object response) {
}
}
@@ -1,571 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley.toolbox;
import android.os.SystemClock;
import com.b44t.messenger.volley.Cache;
import com.b44t.messenger.volley.VolleyLog;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Cache implementation that caches files directly onto the hard disk in the specified
* directory. The default disk usage size is 5MB, but is configurable.
*/
public class DiskBasedCache implements Cache {
/** 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. */
private long mTotalSize = 0;
/** The root directory to use for the cache. */
private final File mRootDirectory;
/** The maximum size of the cache in bytes. */
private final int mMaxCacheSizeInBytes;
/** Default maximum disk usage in bytes. */
private static final int DEFAULT_DISK_USAGE_BYTES = 5 * 1024 * 1024;
/** 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 = 0x20150306;
/**
* Constructs an instance of the DiskBasedCache at the specified directory.
* @param rootDirectory The root directory of the cache.
* @param maxCacheSizeInBytes The maximum size of the cache in bytes.
*/
public DiskBasedCache(File rootDirectory, int maxCacheSizeInBytes) {
mRootDirectory = rootDirectory;
mMaxCacheSizeInBytes = maxCacheSizeInBytes;
}
/**
* 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) {
this(rootDirectory, DEFAULT_DISK_USAGE_BYTES);
}
/**
* Clears the cache. Deletes all cached files from disk.
*/
@Override
public synchronized void clear() {
File[] files = mRootDirectory.listFiles();
if (files != null) {
for (File file : files) {
file.delete();
}
}
mEntries.clear();
mTotalSize = 0;
VolleyLog.d("Cache cleared.");
}
/**
* Returns the cache entry with the specified key if it exists, null otherwise.
*/
@Override
public synchronized Entry get(String key) {
CacheHeader entry = mEntries.get(key);
// if the entry does not exist, return.
if (entry == null) {
return null;
}
File file = getFileForKey(key);
CountingInputStream cis = null;
try {
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);
} catch (IOException e) {
VolleyLog.d("%s: %s", file.getAbsolutePath(), e.toString());
remove(key);
return null;
} finally {
if (cis != null) {
try {
cis.close();
} catch (IOException ioe) {
return null;
}
}
}
}
/**
* Initializes the DiskBasedCache by scanning for all files currently in the
* specified root directory. Creates the root directory if necessary.
*/
@Override
public synchronized void initialize() {
if (!mRootDirectory.exists()) {
if (!mRootDirectory.mkdirs()) {
VolleyLog.e("Unable to create cache dir %s", mRootDirectory.getAbsolutePath());
}
return;
}
File[] files = mRootDirectory.listFiles();
if (files == null) {
return;
}
for (File file : files) {
BufferedInputStream fis = null;
try {
fis = new BufferedInputStream(new FileInputStream(file));
CacheHeader entry = CacheHeader.readHeader(fis);
entry.size = file.length();
putEntry(entry.key, entry);
} catch (IOException e) {
if (file != null) {
file.delete();
}
} finally {
try {
if (fis != null) {
fis.close();
}
} catch (IOException ignored) { }
}
}
}
/**
* Invalidates an entry in the cache.
* @param key Cache key
* @param fullExpire True to fully expire the entry, false to soft expire
*/
@Override
public synchronized void invalidate(String key, boolean fullExpire) {
Entry entry = get(key);
if (entry != null) {
entry.softTtl = 0;
if (fullExpire) {
entry.ttl = 0;
}
put(key, entry);
}
}
/**
* Puts the entry with the specified key into the cache.
*/
@Override
public synchronized void put(String key, Entry entry) {
pruneIfNeeded(entry.data.length);
File file = getFileForKey(key);
try {
BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(file));
CacheHeader e = new CacheHeader(key, entry);
boolean success = e.writeHeader(fos);
if (!success) {
fos.close();
VolleyLog.d("Failed to write header for %s", file.getAbsolutePath());
throw new IOException();
}
fos.write(entry.data);
fos.close();
putEntry(key, e);
return;
} catch (IOException e) {
}
boolean deleted = file.delete();
if (!deleted) {
VolleyLog.d("Could not clean up file %s", file.getAbsolutePath());
}
}
/**
* Removes the specified key from the cache if it exists.
*/
@Override
public synchronized void remove(String key) {
boolean deleted = getFileForKey(key).delete();
removeEntry(key);
if (!deleted) {
VolleyLog.d("Could not delete cache entry for key=%s, filename=%s",
key, getFilenameForKey(key));
}
}
/**
* 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.
*/
private String getFilenameForKey(String key) {
int firstHalfLength = key.length() / 2;
String localFilename = String.valueOf(key.substring(0, firstHalfLength).hashCode());
localFilename += String.valueOf(key.substring(firstHalfLength).hashCode());
return localFilename;
}
/**
* Returns a file object for the given cache key.
*/
public File getFileForKey(String key) {
return new File(mRootDirectory, getFilenameForKey(key));
}
/**
* 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) {
if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes) {
return;
}
if (VolleyLog.DEBUG) {
VolleyLog.v("Pruning old cache entries.");
}
long before = mTotalSize;
int prunedFiles = 0;
long startTime = SystemClock.elapsedRealtime();
Iterator<Map.Entry<String, CacheHeader>> iterator = mEntries.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, CacheHeader> entry = iterator.next();
CacheHeader e = entry.getValue();
boolean deleted = getFileForKey(e.key).delete();
if (deleted) {
mTotalSize -= e.size;
} else {
VolleyLog.d("Could not delete cache entry for key=%s, filename=%s",
e.key, getFilenameForKey(e.key));
}
iterator.remove();
prunedFiles++;
if ((mTotalSize + neededSpace) < mMaxCacheSizeInBytes * HYSTERESIS_FACTOR) {
break;
}
}
if (VolleyLog.DEBUG) {
VolleyLog.v("pruned %d files, %d bytes, %d ms",
prunedFiles, (mTotalSize - before), SystemClock.elapsedRealtime() - startTime);
}
}
/**
* Puts the entry with the specified key into the cache.
* @param key The key to identify the entry by.
* @param entry The entry to cache.
*/
private void putEntry(String key, CacheHeader entry) {
if (!mEntries.containsKey(key)) {
mTotalSize += entry.size;
} else {
CacheHeader oldEntry = mEntries.get(key);
mTotalSize += (entry.size - oldEntry.size);
}
mEntries.put(key, entry);
}
/**
* Removes the entry identified by 'key' from the cache.
*/
private void removeEntry(String key) {
CacheHeader entry = mEntries.get(key);
if (entry != null) {
mTotalSize -= entry.size;
mEntries.remove(key);
}
}
/**
* 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;
int pos = 0;
while (pos < length && ((count = in.read(bytes, pos, length - pos)) != -1)) {
pos += count;
}
if (pos != length) {
throw new IOException("Expected " + length + " bytes, read " + pos + " bytes");
}
return bytes;
}
/**
* Handles holding onto the cache headers for an entry.
*/
// Visible for testing.
static class CacheHeader {
/** 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. */
public String key;
/** ETag for cache coherence. */
public String etag;
/** 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;
/** Soft TTL for this record. */
public long softTtl;
/** Headers from the response resulting in this cache entry. */
public Map<String, String> responseHeaders;
private CacheHeader() { }
/**
* Instantiates a new CacheHeader object
* @param key The key that identifies the cache entry
* @param entry The cache entry.
*/
public CacheHeader(String key, Entry entry) {
this.key = key;
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;
}
/**
* Reads the header off of an InputStream and returns a CacheHeader object.
* @param is The InputStream to read from.
* @throws IOException
*/
public static CacheHeader readHeader(InputStream is) throws IOException {
CacheHeader entry = new CacheHeader();
int magic = readInt(is);
if (magic != CACHE_MAGIC) {
// don't bother deleting, it'll get pruned eventually
throw new IOException();
}
entry.key = readString(is);
entry.etag = readString(is);
if (entry.etag.equals("")) {
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;
}
/**
* Creates a cache entry for the specified data.
*/
public Entry toCacheEntry(byte[] data) {
Entry e = new Entry();
e.data = data;
e.etag = etag;
e.serverDate = serverDate;
e.lastModified = lastModified;
e.ttl = ttl;
e.softTtl = softTtl;
e.responseHeaders = responseHeaders;
return e;
}
/**
* Writes the contents of this CacheHeader to the specified OutputStream.
*/
public boolean writeHeader(OutputStream os) {
try {
writeInt(os, CACHE_MAGIC);
writeString(os, key);
writeString(os, etag == null ? "" : etag);
writeLong(os, serverDate);
writeLong(os, lastModified);
writeLong(os, ttl);
writeLong(os, softTtl);
writeStringStringMap(responseHeaders, os);
os.flush();
return true;
} catch (IOException e) {
VolleyLog.d("%s", e.toString());
return false;
}
}
}
private static class CountingInputStream extends FilterInputStream {
private int bytesRead = 0;
private CountingInputStream(InputStream in) {
super(in);
}
@Override
public int read() throws IOException {
int result = super.read();
if (result != -1) {
bytesRead++;
}
return result;
}
@Override
public int read(byte[] buffer, int offset, int count) throws IOException {
int result = super.read(buffer, offset, count);
if (result != -1) {
bytesRead += result;
}
return result;
}
}
/*
* Homebrewed simple serialization system used for reading and writing cache
* headers on disk. Once upon a time, this used the standard Java
* Object{Input,Output}Stream, but the default implementation relies heavily
* on reflection (even for standard types) and generates a ton of garbage.
*/
/**
* Simple wrapper around {@link InputStream#read()} that throws EOFException
* instead of returning -1.
*/
private static int read(InputStream is) throws IOException {
int b = is.read();
if (b == -1) {
throw new EOFException();
}
return b;
}
static void writeInt(OutputStream os, int n) throws IOException {
os.write((n >> 0) & 0xff);
os.write((n >> 8) & 0xff);
os.write((n >> 16) & 0xff);
os.write((n >> 24) & 0xff);
}
static int readInt(InputStream is) throws IOException {
int n = 0;
n |= (read(is) << 0);
n |= (read(is) << 8);
n |= (read(is) << 16);
n |= (read(is) << 24);
return n;
}
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));
}
static long readLong(InputStream is) throws IOException {
long n = 0;
n |= ((read(is) & 0xFFL) << 0);
n |= ((read(is) & 0xFFL) << 8);
n |= ((read(is) & 0xFFL) << 16);
n |= ((read(is) & 0xFFL) << 24);
n |= ((read(is) & 0xFFL) << 32);
n |= ((read(is) & 0xFFL) << 40);
n |= ((read(is) & 0xFFL) << 48);
n |= ((read(is) & 0xFFL) << 56);
return n;
}
static void writeString(OutputStream os, String s) throws IOException {
byte[] b = s.getBytes("UTF-8");
writeLong(os, b.length);
os.write(b, 0, b.length);
}
static String readString(InputStream is) throws IOException {
int n = (int) readLong(is);
byte[] b = streamToBytes(is, n);
return new String(b, "UTF-8");
}
static void writeStringStringMap(Map<String, String> map, OutputStream os) throws IOException {
if (map != null) {
writeInt(os, map.size());
for (Map.Entry<String, String> entry : map.entrySet()) {
writeString(os, entry.getKey());
writeString(os, entry.getValue());
}
} else {
writeInt(os, 0);
}
}
static Map<String, String> readStringStringMap(InputStream is) throws IOException {
int size = readInt(is);
Map<String, String> result = (size == 0)
? Collections.<String, String>emptyMap()
: new HashMap<String, String>(size);
for (int i = 0; i < size; i++) {
String key = readString(is).intern();
String value = readString(is).intern();
result.put(key, value);
}
return result;
}
}
@@ -1,194 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley.toolbox;
import com.b44t.messenger.volley.AuthFailureError;
import com.b44t.messenger.volley.Request;
import com.b44t.messenger.volley.Request.Method;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpOptions;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpTrace;
import org.apache.http.client.methods.HttpUriRequest;
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 java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* An HttpStack that performs request over an {@link HttpClient}.
*/
public class HttpClientStack implements HttpStack {
protected final HttpClient mClient;
private final static String HEADER_CONTENT_TYPE = "Content-Type";
public HttpClientStack(HttpClient client) {
mClient = client;
}
private static void addHeaders(HttpUriRequest httpRequest, Map<String, String> headers) {
for (String key : headers.keySet()) {
httpRequest.setHeader(key, headers.get(key));
}
}
@SuppressWarnings("unused")
private static List<NameValuePair> getPostParameterPairs(Map<String, String> postParams) {
List<NameValuePair> result = new ArrayList<NameValuePair>(postParams.size());
for (String key : postParams.keySet()) {
result.add(new BasicNameValuePair(key, postParams.get(key)));
}
return result;
}
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
addHeaders(httpRequest, additionalHeaders);
addHeaders(httpRequest, request.getHeaders());
onPrepareRequest(httpRequest);
HttpParams httpParams = httpRequest.getParams();
int timeoutMs = request.getTimeoutMs();
// TODO: Reevaluate this connection timeout based on more wide-scale
// data collection and possibly different for wifi vs. 3G.
HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
return mClient.execute(httpRequest);
}
/**
* Creates the appropriate subclass of HttpUriRequest for passed in request.
*/
@SuppressWarnings("deprecation")
/* protected */ static HttpUriRequest createHttpRequest(Request<?> request,
Map<String, String> additionalHeaders) throws AuthFailureError {
switch (request.getMethod()) {
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.
byte[] postBody = request.getPostBody();
if (postBody != null) {
HttpPost postRequest = new HttpPost(request.getUrl());
postRequest.addHeader(HEADER_CONTENT_TYPE, request.getPostBodyContentType());
HttpEntity entity;
entity = new ByteArrayEntity(postBody);
postRequest.setEntity(entity);
return postRequest;
} else {
return new HttpGet(request.getUrl());
}
}
case Method.GET:
return new HttpGet(request.getUrl());
case Method.DELETE:
return new HttpDelete(request.getUrl());
case Method.POST: {
HttpPost postRequest = new HttpPost(request.getUrl());
postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(postRequest, request);
return postRequest;
}
case Method.PUT: {
HttpPut putRequest = new HttpPut(request.getUrl());
putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(putRequest, request);
return putRequest;
}
case Method.HEAD:
return new HttpHead(request.getUrl());
case Method.OPTIONS:
return new HttpOptions(request.getUrl());
case Method.TRACE:
return new HttpTrace(request.getUrl());
case Method.PATCH: {
HttpPatch patchRequest = new HttpPatch(request.getUrl());
patchRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(patchRequest, request);
return patchRequest;
}
default:
throw new IllegalStateException("Unknown request method.");
}
}
private static void setEntityIfNonEmptyBody(HttpEntityEnclosingRequestBase httpRequest,
Request<?> request) throws AuthFailureError {
byte[] body = request.getBody();
if (body != null) {
HttpEntity entity = new ByteArrayEntity(body);
httpRequest.setEntity(entity);
}
}
/**
* Called before the request is executed using the underlying HttpClient.
*
* <p>Overwrite in subclasses to augment the request.</p>
*/
protected void onPrepareRequest(HttpUriRequest request) throws IOException {
// Nothing.
}
/**
* The HttpPatch class does not exist in the Android framework, so this has been defined here.
*/
public static final class HttpPatch extends HttpEntityEnclosingRequestBase {
public final static String METHOD_NAME = "PATCH";
public HttpPatch() {
super();
}
public HttpPatch(final URI uri) {
super();
setURI(uri);
}
/**
* @throws IllegalArgumentException if the uri is invalid.
*/
public HttpPatch(final String uri) {
super();
setURI(URI.create(uri));
}
@Override
public String getMethod() {
return METHOD_NAME;
}
}
}
@@ -1,168 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley.toolbox;
import com.b44t.messenger.volley.Cache;
import com.b44t.messenger.volley.NetworkResponse;
import org.apache.http.impl.cookie.DateParseException;
import org.apache.http.impl.cookie.DateUtils;
import org.apache.http.protocol.HTTP;
import java.util.Map;
/**
* Utility methods for parsing HTTP headers.
*/
public class HttpHeaderParser {
/**
* Extracts a {@link Cache.Entry} from a {@link NetworkResponse}.
*
* @param response The network response to parse headers from
* @return a cache entry for the given response, or null if the response is not cacheable.
*/
public static Cache.Entry parseCacheHeaders(NetworkResponse response) {
long now = System.currentTimeMillis();
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;
headerValue = headers.get("Date");
if (headerValue != null) {
serverDate = parseDateAsEpoch(headerValue);
}
headerValue = headers.get("Cache-Control");
if (headerValue != null) {
hasCacheControl = true;
String[] tokens = headerValue.split(",");
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")) {
mustRevalidate = true;
}
}
}
headerValue = headers.get("Expires");
if (headerValue != null) {
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 = finalExpire;
entry.serverDate = serverDate;
entry.lastModified = lastModified;
entry.responseHeaders = headers;
return entry;
}
/**
* Parse date in RFC1123 format, and return its value as epoch
*/
public static long parseDateAsEpoch(String dateStr) {
try {
// Parse date in RFC1123 format if this header contains one
return DateUtils.parseDate(dateStr).getTime();
} catch (DateParseException e) {
// Date in invalid format, fallback to 0
return 0;
}
}
/**
* 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, String defaultCharset) {
String contentType = headers.get(HTTP.CONTENT_TYPE);
if (contentType != null) {
String[] params = contentType.split(";");
for (int i = 1; i < params.length; i++) {
String[] pair = params[i].trim().split("=");
if (pair.length == 2) {
if (pair[0].equals("charset")) {
return pair[1];
}
}
}
}
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);
}
}
@@ -1,45 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley.toolbox;
import com.b44t.messenger.volley.AuthFailureError;
import com.b44t.messenger.volley.Request;
import org.apache.http.HttpResponse;
import java.io.IOException;
import java.util.Map;
/**
* An HTTP stack abstraction.
*/
public interface HttpStack {
/**
* Performs an HTTP request with the given parameters.
*
* <p>A GET request is sent if request.getPostBody() == null. A POST request is sent otherwise,
* and the Content-Type header is set to request.getPostBodyContentType().</p>
*
* @param request the request to perform
* @param additionalHeaders additional headers to be sent together with
* {@link Request#getHeaders()}
* @return the HTTP response
*/
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError;
}
@@ -1,262 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley.toolbox;
import com.b44t.messenger.volley.AuthFailureError;
import com.b44t.messenger.volley.Request;
import com.b44t.messenger.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 java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSocketFactory;
/**
* An {@link HttpStack} based on {@link HttpURLConnection}.
*/
public class HurlStack implements HttpStack {
private static final String HEADER_CONTENT_TYPE = "Content-Type";
/**
* An interface for transforming URLs before use.
*/
public interface UrlRewriter {
/**
* Returns a URL to use instead of the provided one, or null to indicate
* this URL should not be used at all.
*/
public String rewriteUrl(String originalUrl);
}
private final UrlRewriter mUrlRewriter;
private final SSLSocketFactory mSslSocketFactory;
public HurlStack() {
this(null);
}
/**
* @param urlRewriter Rewriter to use for request URLs
*/
public HurlStack(UrlRewriter urlRewriter) {
this(urlRewriter, null);
}
/**
* @param urlRewriter Rewriter to use for request URLs
* @param sslSocketFactory SSL factory to use for HTTPS connections
*/
public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
mUrlRewriter = urlRewriter;
mSslSocketFactory = sslSocketFactory;
}
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap<String, String> map = new HashMap<String, String>();
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
if (mUrlRewriter != null) {
String rewritten = mUrlRewriter.rewriteUrl(url);
if (rewritten == null) {
throw new IOException("URL blocked by rewriter: " + url);
}
url = rewritten;
}
URL parsedUrl = new URL(url);
HttpURLConnection connection = openConnection(parsedUrl, request);
for (String headerName : map.keySet()) {
connection.addRequestProperty(headerName, map.get(headerName));
}
setConnectionParametersForRequest(connection, request);
// Initialize HttpResponse with data from the HttpURLConnection.
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
int responseCode = connection.getResponseCode();
if (responseCode == -1) {
// -1 is returned by getResponseCode() if the response code could not be retrieved.
// Signal to the caller that something was wrong with the connection.
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
}
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
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));
response.addHeader(h);
}
}
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
* @return an HttpEntity populated with data from <code>connection</code>.
*/
private static HttpEntity entityFromConnection(HttpURLConnection connection) {
BasicHttpEntity entity = new BasicHttpEntity();
InputStream inputStream;
try {
inputStream = connection.getInputStream();
} catch (IOException ioe) {
inputStream = connection.getErrorStream();
}
entity.setContent(inputStream);
entity.setContentLength(connection.getContentLength());
entity.setContentEncoding(connection.getContentEncoding());
entity.setContentType(connection.getContentType());
return entity;
}
/**
* Create an {@link HttpURLConnection} for the specified {@code url}.
*/
protected HttpURLConnection createConnection(URL url) throws IOException {
return (HttpURLConnection) url.openConnection();
}
/**
* Opens an {@link HttpURLConnection} with parameters.
* @param url
* @return an open connection
* @throws IOException
*/
private HttpURLConnection openConnection(URL url, Request<?> request) throws IOException {
HttpURLConnection connection = createConnection(url);
int timeoutMs = request.getTimeoutMs();
connection.setConnectTimeout(timeoutMs);
connection.setReadTimeout(timeoutMs);
connection.setUseCaches(false);
connection.setDoInput(true);
// use caller-provided custom SslSocketFactory, if any, for HTTPS
if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) {
((HttpsURLConnection)connection).setSSLSocketFactory(mSslSocketFactory);
}
return connection;
}
@SuppressWarnings("deprecation")
/* package */ static void setConnectionParametersForRequest(HttpURLConnection connection,
Request<?> request) throws IOException, AuthFailureError {
switch (request.getMethod()) {
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.
byte[] postBody = request.getPostBody();
if (postBody != null) {
// Prepare output. There is no need to set Content-Length explicitly,
// since this is handled by HttpURLConnection using the size of the prepared
// output stream.
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.addRequestProperty(HEADER_CONTENT_TYPE,
request.getPostBodyContentType());
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(postBody);
out.close();
}
break;
case Method.GET:
// Not necessary to set the request method because connection defaults to GET but
// being explicit here.
connection.setRequestMethod("GET");
break;
case Method.DELETE:
connection.setRequestMethod("DELETE");
break;
case Method.POST:
connection.setRequestMethod("POST");
addBodyIfExists(connection, request);
break;
case Method.PUT:
connection.setRequestMethod("PUT");
addBodyIfExists(connection, request);
break;
case Method.HEAD:
connection.setRequestMethod("HEAD");
break;
case Method.OPTIONS:
connection.setRequestMethod("OPTIONS");
break;
case Method.TRACE:
connection.setRequestMethod("TRACE");
break;
case Method.PATCH:
connection.setRequestMethod("PATCH");
addBodyIfExists(connection, request);
break;
default:
throw new IllegalStateException("Unknown method type.");
}
}
private static void addBodyIfExists(HttpURLConnection connection, Request<?> request)
throws IOException, AuthFailureError {
byte[] body = request.getBody();
if (body != null) {
connection.setDoOutput(true);
connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(body);
out.close();
}
}
}
@@ -1,73 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley.toolbox;
import com.b44t.messenger.volley.NetworkResponse;
import com.b44t.messenger.volley.ParseError;
import com.b44t.messenger.volley.Response;
import com.b44t.messenger.volley.Response.ErrorListener;
import com.b44t.messenger.volley.Response.Listener;
import org.json.JSONArray;
import org.json.JSONException;
import java.io.UnsupportedEncodingException;
/**
* A request for retrieving a {@link JSONArray} response body at a given URL.
*/
public class JsonArrayRequest extends JsonRequest<JSONArray> {
/**
* Creates a new request.
* @param url URL to fetch the JSON from
* @param listener Listener to receive the JSON response
* @param errorListener Error listener, or null to ignore errors.
*/
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, PROTOCOL_CHARSET));
return Response.success(new JSONArray(jsonString),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JSONException je) {
return Response.error(new ParseError(je));
}
}
}
@@ -1,76 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley.toolbox;
import com.b44t.messenger.volley.NetworkResponse;
import com.b44t.messenger.volley.ParseError;
import com.b44t.messenger.volley.Response;
import com.b44t.messenger.volley.Response.ErrorListener;
import com.b44t.messenger.volley.Response.Listener;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.UnsupportedEncodingException;
/**
* A request for retrieving a {@link JSONObject} response body at a given URL, allowing for an
* optional {@link JSONObject} to be passed in as part of the request body.
*/
public class JsonObjectRequest extends JsonRequest<JSONObject> {
/**
* Creates a new request.
* @param method the HTTP method to use
* @param url URL to fetch the JSON from
* @param jsonRequest A {@link JSONObject} 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 JsonObjectRequest(int method, String url, JSONObject jsonRequest,
Listener<JSONObject> listener, ErrorListener errorListener) {
super(method, url, (jsonRequest == null) ? null : jsonRequest.toString(), listener,
errorListener);
}
/**
* 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, Listener<JSONObject> listener,
ErrorListener errorListener) {
this(jsonRequest == null ? Method.GET : Method.POST, url, jsonRequest,
listener, errorListener);
}
@Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
try {
String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
return Response.success(new JSONObject(jsonString),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JSONException je) {
return Response.error(new ParseError(je));
}
}
}
@@ -1,102 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley.toolbox;
import com.b44t.messenger.volley.NetworkResponse;
import com.b44t.messenger.volley.Request;
import com.b44t.messenger.volley.Response;
import com.b44t.messenger.volley.Response.ErrorListener;
import com.b44t.messenger.volley.Response.Listener;
import com.b44t.messenger.volley.VolleyLog;
import java.io.UnsupportedEncodingException;
/**
* A request for retrieving a T type response body at a given URL that also
* optionally sends along a JSON body in the request specified.
*
* @param <T> JSON type of response expected
*/
public abstract class JsonRequest<T> extends Request<T> {
/** 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 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, Listener<T> listener,
ErrorListener errorListener) {
this(Method.DEPRECATED_GET_OR_POST, url, requestBody, listener, errorListener);
}
public JsonRequest(int method, String url, String requestBody, Listener<T> listener,
ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
mRequestBody = requestBody;
}
@Override
protected void deliverResponse(T response) {
mListener.onResponse(response);
}
@Override
abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
/**
* @deprecated Use {@link #getBodyContentType()}.
*/
@Override
public String getPostBodyContentType() {
return getBodyContentType();
}
/**
* @deprecated Use {@link #getBody()}.
*/
@Override
public byte[] getPostBody() {
return getBody();
}
@Override
public String getBodyContentType() {
return PROTOCOL_CONTENT_TYPE;
}
@Override
public byte[] getBody() {
try {
return mRequestBody == null ? null : mRequestBody.getBytes(PROTOCOL_CHARSET);
} catch (UnsupportedEncodingException uee) {
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s",
mRequestBody, PROTOCOL_CHARSET);
return null;
}
}
}
@@ -1,49 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley.toolbox;
import com.b44t.messenger.volley.Cache;
/**
* A cache that doesn't.
*/
public class NoCache implements Cache {
@Override
public void clear() {
}
@Override
public Entry get(String key) {
return null;
}
@Override
public void put(String key, Entry entry) {
}
@Override
public void invalidate(String key, boolean fullExpire) {
}
@Override
public void remove(String key) {
}
@Override
public void initialize() {
}
}
@@ -1,93 +0,0 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley.toolbox;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
/**
* A variation of {@link java.io.ByteArrayOutputStream} that uses a pool of byte[] buffers instead
* of always allocating them fresh, saving on heap churn.
*/
public class PoolingByteArrayOutputStream extends ByteArrayOutputStream {
/**
* 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;
private final ByteArrayPool mPool;
/**
* Constructs a new PoolingByteArrayOutputStream with a default size. If more bytes are written
* to this instance, the underlying byte array will expand.
*/
public PoolingByteArrayOutputStream(ByteArrayPool pool) {
this(pool, DEFAULT_SIZE);
}
/**
* Constructs a new {@code ByteArrayOutputStream} with a default size of {@code size} bytes. If
* more than {@code size} bytes are written to this instance, the underlying byte array will
* expand.
*
* @param size initial size for the underlying byte array. The value will be pinned to a default
* minimum size.
*/
public PoolingByteArrayOutputStream(ByteArrayPool pool, int size) {
mPool = pool;
buf = mPool.getBuf(Math.max(size, DEFAULT_SIZE));
}
@Override
public void close() throws IOException {
mPool.returnBuf(buf);
buf = null;
super.close();
}
@Override
public void finalize() {
mPool.returnBuf(buf);
}
/**
* Ensures there is enough space in the buffer for the given number of additional bytes.
*/
private void expand(int i) {
/* Can the buffer handle @i more bytes, if not expand it */
if (count + i <= buf.length) {
return;
}
byte[] newbuf = mPool.getBuf((count + i) * 2);
System.arraycopy(buf, 0, newbuf, 0, count);
mPool.returnBuf(buf);
buf = newbuf;
}
@Override
public synchronized void write(byte[] buffer, int offset, int len) {
expand(len);
super.write(buffer, offset, len);
}
@Override
public synchronized void write(int oneByte) {
expand(1);
super.write(oneByte);
}
}
@@ -1,153 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley.toolbox;
import com.b44t.messenger.volley.Request;
import com.b44t.messenger.volley.Response;
import com.b44t.messenger.volley.VolleyError;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* A Future that represents a Volley request.
*
* Used by providing as your response and error listeners. For example:
* <pre>
* RequestFuture&lt;JSONObject&gt; future = RequestFuture.newFuture();
* MyRequest request = new MyRequest(URL, future, future);
*
* // If you want to be able to cancel the request:
* future.setRequest(requestQueue.add(request));
*
* // Otherwise:
* requestQueue.add(request);
*
* try {
* JSONObject response = future.get();
* // do something with response
* } catch (InterruptedException e) {
* // handle the error
* } catch (ExecutionException e) {
* // handle the error
* }
* </pre>
*
* @param <T> The type of parsed response this future expects.
*/
public class RequestFuture<T> implements Future<T>, Response.Listener<T>,
Response.ErrorListener {
private Request<?> mRequest;
private boolean mResultReceived = false;
private T mResult;
private VolleyError mException;
public static <E> RequestFuture<E> newFuture() {
return new RequestFuture<E>();
}
private RequestFuture() {}
public void setRequest(Request<?> request) {
mRequest = request;
}
@Override
public synchronized boolean cancel(boolean mayInterruptIfRunning) {
if (mRequest == null) {
return false;
}
if (!isDone()) {
mRequest.cancel();
return true;
} else {
return false;
}
}
@Override
public T get() throws InterruptedException, ExecutionException {
try {
return doGet(null);
} catch (TimeoutException e) {
throw new AssertionError(e);
}
}
@Override
public T get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return doGet(TimeUnit.MILLISECONDS.convert(timeout, unit));
}
private synchronized T doGet(Long timeoutMs)
throws InterruptedException, ExecutionException, TimeoutException {
if (mException != null) {
throw new ExecutionException(mException);
}
if (mResultReceived) {
return mResult;
}
if (timeoutMs == null) {
wait(0);
} else if (timeoutMs > 0) {
wait(timeoutMs);
}
if (mException != null) {
throw new ExecutionException(mException);
}
if (!mResultReceived) {
throw new TimeoutException();
}
return mResult;
}
@Override
public boolean isCancelled() {
if (mRequest == null) {
return false;
}
return mRequest.isCanceled();
}
@Override
public synchronized boolean isDone() {
return mResultReceived || mException != null || isCancelled();
}
@Override
public synchronized void onResponse(T response) {
mResultReceived = true;
mResult = response;
notifyAll();
}
@Override
public synchronized void onErrorResponse(VolleyError error) {
mException = error;
notifyAll();
}
}
@@ -1,73 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley.toolbox;
import com.b44t.messenger.volley.NetworkResponse;
import com.b44t.messenger.volley.Request;
import com.b44t.messenger.volley.Response;
import com.b44t.messenger.volley.Response.ErrorListener;
import com.b44t.messenger.volley.Response.Listener;
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 Listener<String> mListener;
/**
* Creates a new request with the given method.
*
* @param method the request {@link Method} to use
* @param url URL to fetch the string at
* @param listener Listener to receive the String response
* @param errorListener Error listener, or null to ignore errors
*/
public StringRequest(int method, String url, Listener<String> listener,
ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
}
/**
* Creates a new GET request.
*
* @param url URL to fetch the string at
* @param listener Listener to receive the String response
* @param errorListener Error listener, or null to ignore errors
*/
public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
this(Method.GET, url, listener, errorListener);
}
@Override
protected void deliverResponse(String response) {
mListener.onResponse(response);
}
@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
} catch (UnsupportedEncodingException e) {
parsed = new String(response.data);
}
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}
}
@@ -1,80 +0,0 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.b44t.messenger.volley.toolbox;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.http.AndroidHttpClient;
import android.os.Build;
import com.b44t.messenger.volley.Network;
import com.b44t.messenger.volley.RequestQueue;
import java.io.File;
public class Volley {
/** Default on-disk cache directory. */
private static final String DEFAULT_CACHE_DIR = "volley";
/**
* Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
*
* @param context A {@link Context} to use for creating the cache dir.
* @param stack An {@link HttpStack} to use for the network, or null for default.
* @return A started {@link RequestQueue} instance.
*/
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
Network network = new BasicNetwork(stack);
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
/**
* Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
*
* @param context A {@link Context} to use for creating the cache dir.
* @return A started {@link RequestQueue} instance.
*/
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, null);
}
}
@@ -43,7 +43,6 @@ import android.widget.PopupWindow;
import android.widget.ScrollView;
import com.b44t.messenger.AndroidUtilities;
import com.b44t.messenger.FileLog;
import com.b44t.messenger.R;
import com.b44t.ui.Components.LayoutHelper;
@@ -110,7 +109,7 @@ public class ActionBarPopupWindow extends PopupWindow {
scrollView.setVerticalScrollBarEnabled(false);
addView(scrollView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT));
} catch (Throwable e) {
FileLog.e("messenger", e);
}
@@ -321,7 +320,7 @@ public class ActionBarPopupWindow extends PopupWindow {
super.showAsDropDown(anchor, xoff, yoff);
registerListener(anchor);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -34,7 +34,6 @@ import android.view.View;
import android.view.ViewGroup;
import com.b44t.messenger.ApplicationLoader;
import com.b44t.messenger.FileLog;
public class BaseFragment {
@@ -60,7 +59,8 @@ public class BaseFragment {
RC13_AVATAR_IMAGE_CAPTURE = 13,
RC14_AVATAR_GET_CONTENT = 14,
RC21_CHAT_PICK_WO_COMPR = 21,
RC500_PHOTO_VIEW = 500;
RC500_PHOTO_VIEW = 500,
RC600_BATTERY_REQUEST_DONE = 600;
public BaseFragment() {
classGuid = ApplicationLoader.generateClassGuid();
@@ -94,7 +94,7 @@ public class BaseFragment {
try {
parent.removeView(fragmentView);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
fragmentView = null;
@@ -105,7 +105,7 @@ public class BaseFragment {
try {
parent.removeView(actionBar);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
actionBar = null;
@@ -122,7 +122,7 @@ public class BaseFragment {
try {
parent.removeView(fragmentView);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
if (parentLayout != null && parentLayout.getContext() != fragmentView.getContext()) {
@@ -135,7 +135,7 @@ public class BaseFragment {
try {
parent.removeView(actionBar);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
if (parentLayout != null && parentLayout.getContext() != actionBar.getContext()) {
@@ -203,7 +203,7 @@ public class BaseFragment {
visibleDialog = null;
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -267,7 +267,7 @@ public class BaseFragment {
visibleDialog = null;
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
if (actionBar != null) {
actionBar.onPause();
@@ -308,7 +308,7 @@ public class BaseFragment {
visibleDialog = null;
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
try {
visibleDialog = dialog;
@@ -323,7 +323,7 @@ public class BaseFragment {
visibleDialog.show();
return visibleDialog;
} catch (Exception e) {
FileLog.e("messenger", e);
}
return null;
}
@@ -59,7 +59,6 @@ import android.widget.TextView;
import com.b44t.messenger.AndroidUtilities;
import com.b44t.messenger.AnimatorListenerAdapterProxy;
import com.b44t.messenger.LocaleController;
import com.b44t.messenger.FileLog;
import com.b44t.messenger.R;
import com.b44t.ui.Components.LayoutHelper;
@@ -768,7 +767,7 @@ public class BottomSheet extends Dialog {
try {
BottomSheet.super.dismiss();
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
});
@@ -818,7 +817,7 @@ public class BottomSheet extends Dialog {
try {
dismissInternal();
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
});
@@ -44,7 +44,6 @@ import android.widget.FrameLayout;
import android.widget.ListView;
import com.b44t.messenger.AndroidUtilities;
import com.b44t.messenger.FileLog;
import com.b44t.messenger.R;
import com.b44t.messenger.AnimatorListenerAdapterProxy;
@@ -396,7 +395,7 @@ public class DrawerLayoutContainer extends FrameLayout {
child.layout(-child.getMeasuredWidth(), lp.topMargin, 0, lp.topMargin + child.getMeasuredHeight());
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
inLayout = false;
@@ -407,7 +406,7 @@ public class DrawerLayoutContainer extends FrameLayout {
if (!inLayout) {
/*StackTraceElement[] elements = Thread.currentThread().getStackTrace();
for (int a = 0; a < elements.length; a++) {
FileLog.d("messenger", "on " + elements[a]);
Log.d("DeltaChat", "on " + elements[a]);
}*/
super.requestLayout();
}
@@ -29,7 +29,6 @@ import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import com.b44t.messenger.FileLog;
import com.b44t.messenger.NotificationCenter;
import com.b44t.messenger.Utilities;
import com.b44t.messenger.support.widget.RecyclerView;
@@ -40,10 +40,8 @@ import com.b44t.messenger.MrContact;
import com.b44t.messenger.NotificationCenter;
import com.b44t.messenger.ApplicationLoader;
import com.b44t.messenger.FileLoader;
import com.b44t.messenger.FileLog;
import com.b44t.messenger.R;
import com.b44t.messenger.TLRPC;
import com.b44t.messenger.UserConfig;
import com.b44t.messenger.Utilities;
import com.b44t.ui.ActionBar.ActionBar;
import com.b44t.ui.ActionBar.BaseFragment;
@@ -264,7 +262,7 @@ public class AudioSelectActivity extends BaseFragment implements NotificationCen
id--;
}
} catch (Exception e) {
FileLog.e("messenger", e);
} finally {
if (cursor != null) {
cursor.close();
@@ -35,7 +35,6 @@ import android.view.MotionEvent;
import com.b44t.messenger.AndroidUtilities;
import com.b44t.messenger.ApplicationLoader;
import com.b44t.messenger.MessageObject;
import com.b44t.messenger.FileLog;
import com.b44t.ui.ActionBar.Theme;
public class ChatActionCell extends BaseCell {
@@ -117,13 +116,13 @@ public class ChatActionCell extends BaseCell {
}
textHeight = (int)Math.max(textHeight, Math.ceil(textLayout.getLineBottom(a)));
} catch (Exception e) {
FileLog.e("messenger", e);
return;
}
textWidth = (int)Math.max(textWidth, Math.ceil(lineWidth));
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
textX = (width - textWidth) / 2;
@@ -51,14 +51,12 @@ import com.b44t.messenger.ImageReceiver;
import com.b44t.messenger.LocaleController;
import com.b44t.messenger.MediaController;
import com.b44t.messenger.FileLoader;
import com.b44t.messenger.FileLog;
import com.b44t.messenger.MessageObject;
import com.b44t.messenger.MrChat;
import com.b44t.messenger.MrContact;
import com.b44t.messenger.MrMailbox;
import com.b44t.messenger.MrPoortext;
import com.b44t.messenger.R;
import com.b44t.messenger.UserObject;
import com.b44t.messenger.TLRPC;
import com.b44t.ui.Components.AvatarDrawable;
import com.b44t.ui.Components.LinkPath;
@@ -431,7 +429,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
}
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
invalidate();
return true;
@@ -445,7 +443,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
}
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
} else {
resetPressedLink(1);
@@ -487,14 +485,14 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
path.setCurrentLayout(captionLayout, start, 0);
captionLayout.getSelectionPath(start, buffer.getSpanEnd(pressedLink), path);
} catch (Exception e) {
FileLog.e("messenger", e);
}
invalidate();
return true;
}
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
} else if (pressedLinkType == 3) {
delegate.didPressedUrl(currentMessageObject, pressedLink, false);
@@ -912,7 +910,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
String newNameString = null;
if (drawName && isGroupChat && !currentMessageObject.isOutOwner()) {
if (newUser != null) {
newNameString = UserObject.getUserName(newUser);
newNameString = "ErrName"; // use MrContact.getName(), if really needed
} else if (newChat != null) {
newNameString = newChat.title;
}
@@ -1131,7 +1129,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
}
infoLayout = new StaticLayout(str2, infoPaint, infoWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
} catch (Exception e) {
FileLog.e("messenger", e);
}
if (drawPhotoImage) {
@@ -1206,7 +1204,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
}
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
invalidate();
break;
@@ -1569,7 +1567,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
}
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -1658,7 +1656,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
}
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -1846,7 +1844,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
try {
block.textLayout.draw(canvas);
} catch (Exception e) {
FileLog.e("messenger", e);
}
canvas.restore();
}
@@ -1939,7 +1937,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
try {
captionLayout.draw(canvas);
} catch (Exception e) {
FileLog.e("messenger", e);
}
canvas.restore();
}
@@ -1979,7 +1977,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
canvas.restore();
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
try {
@@ -1990,7 +1988,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
canvas.restore();
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -2217,7 +2215,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
nameWidth = 0;
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
if (currentNameString.length() == 0) {
currentNameString = null;
@@ -2251,7 +2249,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
forwardNameOffsetX[1] = forwardedNameLayout[1].getLineLeft(0);
namesOffset += dp(36);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -40,7 +40,6 @@ import android.view.View;
import com.b44t.messenger.AndroidUtilities;
import com.b44t.messenger.Emoji;
import com.b44t.messenger.FileLoader;
import com.b44t.messenger.FileLog;
import com.b44t.messenger.ImageLoader;
import com.b44t.messenger.ImageReceiver;
import com.b44t.messenger.LocaleController;
@@ -44,7 +44,6 @@ import com.b44t.messenger.LocaleController;
import com.b44t.messenger.MessageObject;
import com.b44t.messenger.MrChat;
import com.b44t.messenger.MrMailbox;
import com.b44t.messenger.FileLog;
import com.b44t.messenger.MrMsg;
import com.b44t.messenger.MrPoortext;
import com.b44t.messenger.TLRPC;
@@ -413,7 +412,7 @@ public class DialogCell extends BaseCell {
try {
nameLayout = new StaticLayout(nameStringFinal, currentNamePaint, nameWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
} catch (Exception e) {
FileLog.e("messenger", e);
}
int messageWidth = getMeasuredWidth() - AndroidUtilities.dp(AndroidUtilities.leftBaseline + 16);
@@ -467,7 +466,7 @@ public class DialogCell extends BaseCell {
try {
messageLayout = new StaticLayout(messageStringFinal, currentMessagePaint, messageWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
} catch (Exception e) {
FileLog.e("messenger", e);
}
double widthpx;
@@ -650,7 +649,7 @@ public class DialogCell extends BaseCell {
try {
messageLayout.draw(canvas);
} catch (Exception e) {
FileLog.e("messenger", e);
}
canvas.restore();
}
@@ -30,7 +30,6 @@ import android.widget.FrameLayout;
import android.widget.TextView;
import com.b44t.messenger.AndroidUtilities;
import com.b44t.messenger.FileLog;
import com.b44t.ui.Components.LayoutHelper;
public class DrawerActionCell extends FrameLayout {
@@ -61,7 +60,7 @@ public class DrawerActionCell extends FrameLayout {
textView.setText(text);
textView.setCompoundDrawablesWithIntrinsicBounds(resId, 0, 0, 0);
} catch (Throwable e) {
FileLog.e("messenger", e);
}
}
}
@@ -45,9 +45,7 @@ import com.b44t.messenger.AndroidUtilities;
import com.b44t.messenger.LocaleController;
import com.b44t.messenger.MrMailbox;
import com.b44t.messenger.ApplicationLoader;
import com.b44t.messenger.FileLog;
import com.b44t.messenger.R;
import com.b44t.messenger.TLRPC;
import com.b44t.ui.Components.LayoutHelper;
import com.b44t.ui.ActionBar.Theme;
@@ -99,7 +97,7 @@ public class DrawerProfileCell extends FrameLayout {
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(mrHeight), MeasureSpec.EXACTLY));
} catch (Exception e) {
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), AndroidUtilities.dp(mrHeight));
FileLog.e("messenger", e);
}
}
}
@@ -1,103 +0,0 @@
/*******************************************************************************
*
* Messenger Android Frontend
* (C) 2013-2016 Nikolai Kudashov
* (C) 2017 Björn Petersen
* Contact: r10s@b44t.com, http://b44t.com
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see http://www.gnu.org/licenses/ .
*
******************************************************************************/
package com.b44t.ui.Cells;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
import android.util.TypedValue;
import android.view.Gravity;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.b44t.messenger.AndroidUtilities;
import com.b44t.messenger.LocaleController;
import com.b44t.messenger.R;
import com.b44t.ui.Components.LayoutHelper;
public class TextColorCell extends FrameLayout {
private TextView textView;
private boolean needDivider;
private int currentColor;
private Drawable colorDrawable;
private static Paint paint;
public TextColorCell(Context context) {
super(context);
if (paint == null) {
paint = new Paint();
paint.setColor(0xffd9d9d9);
paint.setStrokeWidth(1);
}
colorDrawable = getResources().getDrawable(R.drawable.switch_to_on2);
textView = new TextView(context);
textView.setTextColor(0xff212121);
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
textView.setLines(1);
textView.setMaxLines(1);
textView.setSingleLine(true);
textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL);
addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 17, 0, 17, 0));
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(48) + (needDivider ? 1 : 0), MeasureSpec.EXACTLY));
}
public void setTextAndColor(String text, int color, boolean divider) {
textView.setText(text);
needDivider = divider;
currentColor = color;
colorDrawable.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY));
setWillNotDraw(!needDivider && currentColor == 0);
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
if (needDivider) {
canvas.drawLine(getPaddingLeft(), getHeight() - 1, getWidth() - getPaddingRight(), getHeight() - 1, paint);
}
if (currentColor != 0 && colorDrawable != null) {
int x;
int y = (getMeasuredHeight() - colorDrawable.getMinimumHeight()) / 2;
if (!LocaleController.isRTL) {
x = getMeasuredWidth() - colorDrawable.getIntrinsicWidth() - AndroidUtilities.dp(14.5f);
} else {
x = AndroidUtilities.dp(14.5f);
}
colorDrawable.setBounds(x, y, x + colorDrawable.getIntrinsicWidth(), y + colorDrawable.getIntrinsicHeight());
colorDrawable.draw(canvas);
}
}
}
@@ -37,22 +37,31 @@ import com.b44t.messenger.AndroidUtilities;
import com.b44t.messenger.LocaleController;
import com.b44t.ui.Components.LayoutHelper;
import static com.b44t.messenger.AndroidUtilities.dp;
public class TextSettingsCell extends FrameLayout {
private TextView textView;
private TextView valueTextView;
private ImageView valueImageView;
private static Paint paint;
private boolean valueIsColor;
private Paint dividerpaint;
private Paint circlepaint;
private Paint circleborderpaint;
private boolean needDivider;
public TextSettingsCell(Context context) {
super(context);
if (paint == null) {
paint = new Paint();
paint.setColor(0xffd9d9d9);
paint.setStrokeWidth(1);
}
dividerpaint = new Paint();
dividerpaint.setColor(0xffd9d9d9);
dividerpaint.setStrokeWidth(1);
circleborderpaint = new Paint();
circleborderpaint.setColor(0xff000000);
circleborderpaint.setStrokeWidth(1);
circlepaint = new Paint();
circlepaint.setAntiAlias(true);
textView = new TextView(context);
textView.setTextColor(0xff212121);
@@ -73,27 +82,24 @@ public class TextSettingsCell extends FrameLayout {
valueTextView.setEllipsize(TextUtils.TruncateAt.END);
valueTextView.setGravity((LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL);
addView(valueTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.TOP, 17, 0, 17, 0));
valueImageView = new ImageView(context);
valueImageView.setScaleType(ImageView.ScaleType.CENTER);
valueImageView.setVisibility(INVISIBLE);
addView(valueImageView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL, 17, 0, 17, 0));
}
private final int RADIUS = dp(8);
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), AndroidUtilities.dp(48) + (needDivider ? 1 : 0));
int availableWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - AndroidUtilities.dp(34);
int width = availableWidth / 2;
if (valueImageView.getVisibility() == VISIBLE) {
valueImageView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
}
if (valueTextView.getVisibility() == VISIBLE) {
valueTextView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
width = availableWidth - valueTextView.getMeasuredWidth() - AndroidUtilities.dp(8);
} else {
width = availableWidth;
if( valueIsColor ) {
width -= RADIUS * 3/*2 for the circle, 1 for space*/;
}
}
textView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
}
@@ -102,21 +108,16 @@ public class TextSettingsCell extends FrameLayout {
textView.setTextColor(color);
}
public void setTextValueColor(int color) {
valueTextView.setTextColor(color);
}
public void setText(String text, boolean divider) {
textView.setText(text);
valueTextView.setVisibility(INVISIBLE);
valueImageView.setVisibility(INVISIBLE);
needDivider = divider;
setWillNotDraw(!divider);
}
public void setTextAndValue(String text, String value, boolean divider) {
textView.setText(text);
valueImageView.setVisibility(INVISIBLE);
valueIsColor = false;
if (value != null) {
valueTextView.setText(value);
valueTextView.setVisibility(VISIBLE);
@@ -124,27 +125,31 @@ public class TextSettingsCell extends FrameLayout {
valueTextView.setVisibility(INVISIBLE);
}
needDivider = divider;
setWillNotDraw(!divider);
setWillNotDraw(!needDivider);
requestLayout();
}
public void setTextAndIcon(String text, int resId, boolean divider) {
public void setTextAndColor(String text, int color, boolean divider) {
textView.setText(text);
valueIsColor = true;
valueTextView.setVisibility(INVISIBLE);
if (resId != 0) {
valueImageView.setVisibility(VISIBLE);
valueImageView.setImageResource(resId);
} else {
valueImageView.setVisibility(INVISIBLE);
}
needDivider = divider;
setWillNotDraw(!divider);
circlepaint.setColor(color);
setWillNotDraw(false);
requestLayout();
}
@Override
protected void onDraw(Canvas canvas) {
if (needDivider) {
canvas.drawLine(getPaddingLeft(), getHeight() - 1, getWidth() - getPaddingRight(), getHeight() - 1, paint);
canvas.drawLine(getPaddingLeft(), getHeight() - 1, getWidth() - getPaddingRight(), getHeight() - 1, dividerpaint);
}
if( valueIsColor ) {
int x = getWidth()-getPaddingRight()-dp(17)-RADIUS;
int y = getHeight()/2;
canvas.drawCircle(x, y, RADIUS+dp(1), circleborderpaint);
canvas.drawCircle(x, y, RADIUS, circlepaint);
}
}
}
@@ -74,7 +74,6 @@ import com.b44t.messenger.support.widget.LinearLayoutManager;
import com.b44t.messenger.support.widget.RecyclerView;
import com.b44t.messenger.ApplicationLoader;
import com.b44t.messenger.TLRPC;
import com.b44t.messenger.FileLog;
import com.b44t.messenger.MessageObject;
import com.b44t.messenger.NotificationCenter;
import com.b44t.messenger.R;
@@ -1191,7 +1190,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
}
startActivityForResult(takePictureIntent, RC0_CHAT_IMAGE_CAPTURE);
} catch (Exception e) {
FileLog.e("messenger", e);
}
} else if (which == ChatAttachAlert.ATTACH_BUTTON_IDX_GALLERY ) {
if (Build.VERSION.SDK_INT >= 23 && getParentActivity().checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
@@ -1222,7 +1221,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
startActivityForResult(chooserIntent, RC1_CHAT_PICK);
} catch (Exception e) {
FileLog.e("messenger", e);
} */
}
@@ -1251,7 +1250,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
}
startActivityForResult(takeVideoIntent, RC2_CHAT_VIDEO_CAPTURE);
} catch (Exception e) {
FileLog.e("messenger", e);
}
} else if (which == ChatAttachAlert.ATTACH_BUTTON_IDX_FILE ) {
if (Build.VERSION.SDK_INT >= 23 && getParentActivity().checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
@@ -1274,7 +1273,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
photoPickerIntent.setType("*/*");
startActivityForResult(photoPickerIntent, RC21_CHAT_PICK_WO_COMPR);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
});
@@ -1366,9 +1365,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); // was: BottomSheet.Builder
//builder.setTitle(LocaleController.getString("Notifications", R.string.Notifications)); -- a title seems more confusing than helping -- the user has clicked "mute" before and there are several options all starting with "mute...", I think, this is very clear
CharSequence[] items = new CharSequence[]{
ApplicationLoader.applicationContext.getString(R.string.MuteFor1Hour),
ApplicationLoader.applicationContext.getString(R.string.MuteFor8Hours),
ApplicationLoader.applicationContext.getString(R.string.MuteFor2Days),
ProfileNotificationsActivity.muteForString( 60*60),
ProfileNotificationsActivity.muteForString( 8*60*60),
ProfileNotificationsActivity.muteForString(48*60*60),
LocaleController.getString("MuteAlways", R.string.MuteAlways)
};
builder.setItems(items, new DialogInterface.OnClickListener() {
@@ -1687,7 +1686,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
break;
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
arrayList.add(new MediaController.PhotoEntry(0, 0, 0, currentPicturePath, orientation, false));
@@ -1711,7 +1710,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
try {
videoPath = AndroidUtilities.getPath(uri);
} catch (Exception e) {
FileLog.e("messenger", e);
}
if (videoPath == null) {
showAttachmentError();
@@ -1731,7 +1730,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
m_mrChat.cleanDraft(); */
} else if (requestCode == RC2_CHAT_VIDEO_CAPTURE) {
String videoPath = null;
FileLog.d("messenger", "pic path " + currentPicturePath);
//Log.i("DeltaChat", "pic path " + currentPicturePath);
if (data != null && currentPicturePath != null) {
if (new File(currentPicturePath).exists()) {
data = null;
@@ -1740,9 +1739,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
if (data != null) {
Uri uri = data.getData();
if (uri != null) {
FileLog.d("messenger", "video record uri " + uri.toString());
//Log.i("DeltaChat", "video record uri " + uri.toString());
videoPath = AndroidUtilities.getPath(uri);
FileLog.d("messenger", "resolved path = " + videoPath);
//Log.i("DeltaChat", "resolved path = " + videoPath);
if (!(new File(videoPath).exists())) {
videoPath = currentPicturePath;
}
@@ -1787,7 +1786,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
uri = Uri.parse(secondExtraction);
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
String tempPath = AndroidUtilities.getPath(uri);
@@ -39,7 +39,6 @@ import android.os.Looper;
import android.view.View;
import com.b44t.messenger.AndroidUtilities;
import com.b44t.messenger.FileLog;
import java.io.File;
import java.util.concurrent.ScheduledThreadPoolExecutor;
@@ -142,7 +141,7 @@ public class AnimatedFileDrawable extends BitmapDrawable implements Animatable {
try {
backgroundBitmap = Bitmap.createBitmap(metaData[0], metaData[1], Bitmap.Config.ARGB_8888);
} catch (Throwable e) {
FileLog.e("messenger", e);
}
if (backgroundShader == null && backgroundBitmap != null && roundRadius != 0) {
backgroundShader = new BitmapShader(backgroundBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
@@ -152,7 +151,7 @@ public class AnimatedFileDrawable extends BitmapDrawable implements Animatable {
getVideoFrame(nativePtr, backgroundBitmap, metaData);
}
} catch (Throwable e) {
FileLog.e("messenger", e);
}
}
AndroidUtilities.runOnUIThread(uiRunnable);
@@ -35,7 +35,6 @@ import android.text.StaticLayout;
import android.text.TextPaint;
import com.b44t.messenger.AndroidUtilities;
import com.b44t.messenger.FileLog;
import com.b44t.messenger.R;
import com.b44t.messenger.TLRPC;
import com.b44t.messenger.ApplicationLoader;
@@ -183,7 +182,7 @@ public class AvatarDrawable extends Drawable {
textHeight = textLayout.getLineBottom(0);
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
} else {
textLayout = null;
@@ -39,7 +39,6 @@ import com.b44t.messenger.ImageLoader;
import com.b44t.messenger.MediaController;
import com.b44t.messenger.TLRPC;
import com.b44t.messenger.FileLoader;
import com.b44t.messenger.FileLog;
import com.b44t.messenger.UserConfig;
import com.b44t.ui.LaunchActivity;
import com.b44t.ui.PhotoAlbumPickerActivity;
@@ -85,7 +84,7 @@ public class AvatarUpdater implements PhotoCropActivity.PhotoEditActivityDelegat
}
parentFragment.startActivityForResult(takePictureIntent, BaseFragment.RC13_AVATAR_IMAGE_CAPTURE);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -114,7 +113,7 @@ public class AvatarUpdater implements PhotoCropActivity.PhotoEditActivityDelegat
photoPickerIntent.setType("image/*");
parentFragment.startActivityForResult(photoPickerIntent, BaseFragment.RC14_AVATAR_GET_CONTENT);
} catch (Exception e) {
FileLog.e("messenger", e);
} */
}
@@ -142,7 +141,7 @@ public class AvatarUpdater implements PhotoCropActivity.PhotoEditActivityDelegat
photoCropActivity.setDelegate(this);
activity.presentFragment(photoCropActivity);
} catch (Exception e) {
FileLog.e("messenger", e);
Bitmap bitmap = ImageLoader.loadBitmap(path, uri, 800, 800, true);
processBitmap(bitmap);
}
@@ -168,7 +167,7 @@ public class AvatarUpdater implements PhotoCropActivity.PhotoEditActivityDelegat
break;
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
final ArrayList<Object> arrayList = new ArrayList<>();
arrayList.add(new MediaController.PhotoEntry(0, 0, 0, currentPicturePath, orientation, false));
@@ -68,11 +68,9 @@ import com.b44t.messenger.MessageObject;
import com.b44t.messenger.MrContact;
import com.b44t.messenger.MrMailbox;
import com.b44t.messenger.SendMessagesHelper;
import com.b44t.messenger.FileLog;
import com.b44t.messenger.NotificationCenter;
import com.b44t.messenger.R;
import com.b44t.messenger.TLRPC;
import com.b44t.messenger.UserConfig;
import com.b44t.messenger.AnimatorListenerAdapterProxy;
import com.b44t.messenger.ApplicationLoader;
import com.b44t.ui.ActionBar.Theme;
@@ -82,7 +80,7 @@ import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
public class ChatActivityEnterView extends FrameLayout implements NotificationCenter.NotificationCenterDelegate, SizeNotifierFrameLayout.SizeNotifierFrameLayoutDelegate, StickersAlert.StickersAlertDelegate {
public class ChatActivityEnterView extends FrameLayout implements NotificationCenter.NotificationCenterDelegate, SizeNotifierFrameLayout.SizeNotifierFrameLayoutDelegate /*, StickersAlert.StickersAlertDelegate*/ {
public interface ChatActivityEnterViewDelegate {
void onMessageSend(CharSequence message);
@@ -181,7 +179,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} catch (Exception e) {
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), AndroidUtilities.dp(51));
FileLog.e("messenger", e);
}
}
@@ -219,7 +217,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
try {
return super.onTouchEvent(event);
} catch (Exception e) {
FileLog.e("messenger", e);
}
return false;
}
@@ -860,7 +858,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
mWakeLock.release();
mWakeLock = null;
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
if (sizeNotifierLayout != null) {
@@ -1158,7 +1156,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
mWakeLock.acquire();
}
} catch (Exception e) {
FileLog.e("messenger", e);
}
AndroidUtilities.lockOrientation(parentActivity);
@@ -1200,7 +1198,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
mWakeLock.release();
mWakeLock = null;
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
AndroidUtilities.unlockOrientation(parentActivity);
@@ -1274,7 +1272,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
messageEditText.setText(builder);
messageEditText.setSelection(start + text.length());
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
@@ -1291,7 +1289,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
try {
messageEditText.requestFocus();
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
}
@@ -1364,15 +1362,15 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
int j = i + localCharSequence.length();
messageEditText.setSelection(j, j);
} catch (Exception e) {
FileLog.e("messenger", e);
} finally {
innerTextChange = 0;
}
}
public void onStickerSelected(TLRPC.Document sticker) {
/*public void onStickerSelected(TLRPC.Document sticker) {
ChatActivityEnterView.this.onStickerSelected(sticker);
}
}*/
@Override
public void onStickersSettingsClick() {
@@ -1432,13 +1430,13 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
sizeNotifierLayout.addView(emojiView);
}
@Override
/*@Override
public void onStickerSelected(TLRPC.Document sticker) {
SendMessagesHelper.getInstance().sendSticker(sticker, dialog_id);
if (delegate != null) {
delegate.onMessageSend(null);
}
}
}*/
private void showPopup(int show, int contentType /*0=emojiView, 1=botKeyboardView*/ ) {
if (show == 1) {
@@ -49,7 +49,6 @@ import android.widget.TextView;
import com.b44t.messenger.AndroidUtilities;
import com.b44t.messenger.ApplicationLoader;
import com.b44t.messenger.FileLog;
import com.b44t.messenger.LocaleController;
import com.b44t.messenger.MediaController;
import com.b44t.messenger.MessageObject;
@@ -832,7 +831,7 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N
try {
animators.add(ViewAnimationUtils.createCircularReveal(containerView, finalRevealX, revealY, open ? 0 : finalRevealRadius, open ? finalRevealRadius : 0));
} catch (Exception e) {
FileLog.e("messenger", e);
}
animatorSet.setDuration(320);
} else {
@@ -866,7 +865,7 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N
try {
dismissInternal();
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
}
@@ -33,8 +33,6 @@ import android.graphics.RectF;
import android.graphics.Shader;
import android.view.View;
import com.b44t.messenger.FileLog;
public class ClippingImageView extends View {
private int clipBottom;
@@ -173,7 +171,7 @@ public class ClippingImageView extends View {
try {
canvas.drawBitmap(bmp, matrix, paint);
} catch (Exception e) {
FileLog.e("messenger", e);
}
}
canvas.restore();
@@ -36,7 +36,7 @@ public class ColorPickerView extends View {
private static final String STATE_PARENT = "parent";
private static final String STATE_ANGLE = "angle";
private static final String STATE_OLD_COLOR = "color";
private static final String STATE_SHOW_OLD_COLOR = "showColor";
//private static final String STATE_SHOW_OLD_COLOR = "showColor";
private static final int[] COLORS = new int[] { 0xFFFF0000, 0xFFFF00FF, 0xFF0000FF, 0xFF00FFFF, 0xFF00FF00, 0xFFFFFFFF, 0xFFFFFF00, 0xFFFF0000 };
@@ -56,7 +56,7 @@ public class ColorPickerView extends View {
private RectF mCenterRectangle = new RectF();
private boolean mUserIsMovingPointer = false;
private int mCenterOldColor;
private boolean mShowCenterOldColor;
private final boolean mShowCenterOldColor = false;
private int mCenterNewColor;
private float mTranslationOffset;
private float mSlopX;
@@ -145,7 +145,7 @@ public class ColorPickerView extends View {
mCenterNewColor = calculateColor(mAngle);
mCenterOldColor = calculateColor(mAngle);
mShowCenterOldColor = true;
//mShowCenterOldColor = true;
}
@Override
@@ -344,14 +344,14 @@ public class ColorPickerView extends View {
return mCenterOldColor;
}
public void setShowOldCenterColor(boolean show) {
/*public void setShowOldCenterColor(boolean show) {
mShowCenterOldColor = show;
invalidate();
}
public boolean getShowOldCenterColor() {
return mShowCenterOldColor;
}
}*/
@Override
protected Parcelable onSaveInstanceState() {
@@ -361,7 +361,7 @@ public class ColorPickerView extends View {
state.putParcelable(STATE_PARENT, superState);
state.putFloat(STATE_ANGLE, mAngle);
state.putInt(STATE_OLD_COLOR, mCenterOldColor);
state.putBoolean(STATE_SHOW_OLD_COLOR, mShowCenterOldColor);
//state.putBoolean(STATE_SHOW_OLD_COLOR, mShowCenterOldColor);
return state;
}
@@ -375,7 +375,7 @@ public class ColorPickerView extends View {
mAngle = savedState.getFloat(STATE_ANGLE);
setOldCenterColor(savedState.getInt(STATE_OLD_COLOR));
mShowCenterOldColor = savedState.getBoolean(STATE_SHOW_OLD_COLOR);
//mShowCenterOldColor = savedState.getBoolean(STATE_SHOW_OLD_COLOR);
int currentColor = calculateColor(mAngle);
mPointerColor.setColor(currentColor);
setNewCenterColor(currentColor);

Some files were not shown because too many files have changed in this diff Show More