diff --git a/TMessagesProj/build.gradle b/TMessagesProj/build.gradle index a4db15d0..2e2c6ce5 100644 --- a/TMessagesProj/build.gradle +++ b/TMessagesProj/build.gradle @@ -82,7 +82,7 @@ android { defaultConfig { minSdkVersion 8 targetSdkVersion 19 - versionCode 212 + versionCode 219 versionName "1.4.9" } } diff --git a/TMessagesProj/jni/Android.mk b/TMessagesProj/jni/Android.mk index 520e05f4..5bf5f279 100755 --- a/TMessagesProj/jni/Android.mk +++ b/TMessagesProj/jni/Android.mk @@ -154,6 +154,18 @@ LOCAL_SRC_FILES += \ ./opus/opusfile/opusfile.c \ ./opus/opusfile/stream.c +LOCAL_SRC_FILES += \ +./giflib/dgif_lib.c \ +./giflib/gifalloc.c + +LOCAL_SRC_FILES += \ +./aes/aes_core.c \ +./aes/aes_ige.c \ +./aes/aes_misc.c + +LOCAL_SRC_FILES += \ +./sqlite/sqlite3.c + LOCAL_C_INCLUDES := \ ./opus/include \ ./opus/silk \ @@ -163,16 +175,12 @@ LOCAL_C_INCLUDES := \ ./opus/opusfile LOCAL_SRC_FILES += \ -./aes_core.c \ -./aes_ige.c \ -./aes_misc.c \ ./jni.c \ -./sqlite3.c \ -./org_telegram_SQLite_SQLiteCursor.c \ -./org_telegram_SQLite_SQLiteDatabase.c \ -./org_telegram_SQLite_SQLitePreparedStatement.c \ -./org_telegram_SQLite.c \ -./audio.c +./sqlite_cursor.c \ +./sqlite_database.c \ +./sqlite_statement.c \ +./sqlite.c \ +./audio.c \ +./gif.c - include $(BUILD_SHARED_LIBRARY) \ No newline at end of file diff --git a/TMessagesProj/jni/aes.h b/TMessagesProj/jni/aes/aes.h similarity index 100% rename from TMessagesProj/jni/aes.h rename to TMessagesProj/jni/aes/aes.h diff --git a/TMessagesProj/jni/aes_core.c b/TMessagesProj/jni/aes/aes_core.c similarity index 100% rename from TMessagesProj/jni/aes_core.c rename to TMessagesProj/jni/aes/aes_core.c diff --git a/TMessagesProj/jni/aes_ige.c b/TMessagesProj/jni/aes/aes_ige.c similarity index 100% rename from TMessagesProj/jni/aes_ige.c rename to TMessagesProj/jni/aes/aes_ige.c diff --git a/TMessagesProj/jni/aes_locl.h b/TMessagesProj/jni/aes/aes_locl.h similarity index 100% rename from TMessagesProj/jni/aes_locl.h rename to TMessagesProj/jni/aes/aes_locl.h diff --git a/TMessagesProj/jni/aes_misc.c b/TMessagesProj/jni/aes/aes_misc.c similarity index 100% rename from TMessagesProj/jni/aes_misc.c rename to TMessagesProj/jni/aes/aes_misc.c diff --git a/TMessagesProj/jni/audio.c b/TMessagesProj/jni/audio.c index c66314f0..70b6e2fc 100644 --- a/TMessagesProj/jni/audio.c +++ b/TMessagesProj/jni/audio.c @@ -5,14 +5,7 @@ #include #include #include -#include "log.h" - -#ifndef max -#define max(x, y) ((x) > (y)) ? (x) : (y) -#endif -#ifndef min -#define min(x, y) ((x) < (y)) ? (x) : (y) -#endif +#include "utils.h" typedef struct { int version; @@ -540,9 +533,6 @@ int64_t _currentPcmOffset = 0; int _finished = 0; static const int playerBuffersCount = 3; static const int playerSampleRate = 48000; -int finished; -int pcmOffset; -int size; void cleanupPlayer() { if (_opusFile) { @@ -585,14 +575,14 @@ int initPlayer(const char *path) { return 1; } -void fillBuffer(uint8_t *buffer, int capacity) { +void fillBuffer(uint8_t *buffer, int capacity, int *args) { if (_opusFile) { - pcmOffset = max(0, op_pcm_tell(_opusFile)); + args[1] = max(0, op_pcm_tell(_opusFile)); if (_finished) { - finished = 1; - size = 0; - pcmOffset = 0; + args[0] = 0; + args[1] = 0; + args[2] = 1; return; } else { int writtenOutputBytes = 0; @@ -612,19 +602,19 @@ void fillBuffer(uint8_t *buffer, int capacity) { } } - size = writtenOutputBytes; + args[0] = writtenOutputBytes; - if (endOfFileReached || pcmOffset + size == _totalPcmDuration) { + if (endOfFileReached || args[1] + args[0] == _totalPcmDuration) { _finished = 1; - finished = 1; + args[2] = 1; } else { - finished = 0; + args[2] = 0; } } } else { memset(buffer, 0, capacity); - size = capacity; - pcmOffset = _totalPcmDuration; + args[0] = capacity; + args[1] = _totalPcmDuration; } } @@ -632,21 +622,11 @@ JNIEXPORT jlong Java_org_telegram_messenger_MediaController_getTotalPcmDuration( return _totalPcmDuration; } -JNIEXPORT int Java_org_telegram_messenger_MediaController_getFinished(JNIEnv *env, jclass class) { - return finished; -} - -JNIEXPORT int Java_org_telegram_messenger_MediaController_getSize(JNIEnv *env, jclass class) { - return size; -} - -JNIEXPORT jlong Java_org_telegram_messenger_MediaController_getPcmOffset(JNIEnv *env, jclass class) { - return pcmOffset; -} - -JNIEXPORT void Java_org_telegram_messenger_MediaController_readOpusFile(JNIEnv *env, jclass class, jobject buffer, jint capacity) { +JNIEXPORT void Java_org_telegram_messenger_MediaController_readOpusFile(JNIEnv *env, jclass class, jobject buffer, jint capacity, jintArray args) { + jint *argsArr = (*env)->GetIntArrayElements(env, args, 0); jbyte *bufferBytes = (*env)->GetDirectBufferAddress(env, buffer); - fillBuffer(bufferBytes, capacity); + fillBuffer(bufferBytes, capacity, argsArr); + (*env)->ReleaseIntArrayElements(env, args, argsArr, 0); } JNIEXPORT int Java_org_telegram_messenger_MediaController_seekOpusFile(JNIEnv *env, jclass class, jfloat position) { diff --git a/TMessagesProj/jni/build_ffmpeg_android.sh b/TMessagesProj/jni/build_ffmpeg_android.sh deleted file mode 100755 index 42180f25..00000000 --- a/TMessagesProj/jni/build_ffmpeg_android.sh +++ /dev/null @@ -1,128 +0,0 @@ -#!/bin/bash - -function build_one { - -echo "Cleaning..." -make clean - -echo "Configuring..." - -./configure --target-os=linux \ ---prefix=$PREFIX \ ---enable-cross-compile \ ---extra-libs="-lgcc" \ ---arch=$ARCH \ ---cc=$CC \ ---cross-prefix=$CROSS_PREFIX \ ---nm=$NM \ ---sysroot=$PLATFORM \ ---extra-cflags=" -O3 -fpic -DANDROID -DHAVE_SYS_UIO_H=1 -fasm -Wno-psabi -fno-short-enums -Dipv6mr_interface=ipv6mr_ifindex -fno-strict-aliasing -finline-limit=300 $OPTIMIZE_CFLAGS " \ ---disable-shared \ ---enable-static \ ---extra-ldflags="-Wl,-rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -nostdlib -lc -lm -ldl" \ -\ ---disable-everything \ ---disable-network \ ---enable-small \ ---enable-zlib \ ---disable-avfilter \ ---disable-avdevice \ ---disable-programs \ ---disable-doc \ ---disable-lsp \ ---disable-dwt \ ---disable-dct \ ---enable-stripping \ ---disable-postproc \ ---disable-fft \ ---disable-lzo \ ---disable-rdft \ ---disable-mdct \ ---disable-debug \ -\ ---enable-muxer='mp4' \ ---enable-protocol='file' \ ---enable-encoder='aac,mpeg4' \ ---enable-decoder='aac,amrnb,amrwb,flv,h263,h264' \ ---enable-demuxer='flv,mpegvideo,mov' \ ---enable-hwaccel='mpeg4_vaapi,mpeg4_vdpau' \ ---enable-swresample \ ---enable-swscale \ ---enable-asm \ -$ADDITIONAL_CONFIGURE_FLAG - -echo "continue?" -read -make -j8 install - -#$AR d libavcodec/libavcodec.a inverse.o - -#$LD -rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -soname libffmpeg.a -static -nostdlib -z noexecstack -Bsymbolic --whole-archive --no-undefined -o $PREFIX/libffmpeg.so libavcodec/libavcodec.a libavformat/libavformat.a libavutil/libavutil.a -lc -lm -lz -ldl --dynamic-linker=/system/bin/linker $GCCLIB - -} - -NDK=/Users/DrKLO/ndk9 - -#arm platform -PLATFORM=$NDK/platforms/android-8/arch-arm -PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/darwin-x86_64 -LD=$PREBUILT/bin/arm-linux-androideabi-ld -AR=$PREBUILT/bin/arm-linux-androideabi-ar -NM=$PREBUILT/bin/arm-linux-androideabi-nm -GCCLIB=$PREBUILT/lib/gcc/arm-linux-androideabi/4.8/libgcc.a -ARCH=arm -CC=$PREBUILT/bin/arm-linux-androideabi-gcc -CROSS_PREFIX=$PREBUILT/bin/arm-linux-androideabi- - -#arm v6 -CPU=armv6 -OPTIMIZE_CFLAGS="-marm -march=$CPU" -PREFIX=/Users/DrKLO/ndk9/platforms/android-9/arch-$ARCH/usr -ADDITIONAL_CONFIGURE_FLAG= -build_one - -#arm v7vfpv3 -#CPU=armv7-a -#OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfpv3-d16 -marm -march=$CPU " -#PREFIX=./android/$CPU -#ADDITIONAL_CONFIGURE_FLAG= -#build_one - -#arm v7vfp -#CPU=armv7-a -#OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU " -#PREFIX=./android/$CPU-vfp -#ADDITIONAL_CONFIGURE_FLAG= -#build_one - -#arm v7n -#CPU=armv7-a -#OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=neon -marm -march=$CPU -mtune=cortex-a8" -#PREFIX=./android/$CPU -#ADDITIONAL_CONFIGURE_FLAG=--enable-neon -#build_one - -#arm v6+vfp -#CPU=armv6 -#OPTIMIZE_CFLAGS="-DCMP_HAVE_VFP -mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU" -#PREFIX=./android/${CPU}_vfp -#ADDITIONAL_CONFIGURE_FLAG= -#build_one - -#x86 platform -PLATFORM=$NDK/platforms/android-9/arch-x86 -PREBUILT=$NDK/toolchains/x86-4.8/prebuilt/darwin-x86_64 -LD=$PREBUILT/bin/i686-linux-android-ld -AR=$PREBUILT/bin/i686-linux-android-ar -NM=$PREBUILT/bin/i686-linux-android-nm -GCCLIB=$PREBUILT/lib/gcc/i686-linux-android/4.8/libgcc.a -ARCH=x86 -CC=$PREBUILT/bin/i686-linux-android-gcc -CROSS_PREFIX=$PREBUILT/bin/i686-linux-android- - -CPU=i686 -OPTIMIZE_CFLAGS="-march=$CPU" -PREFIX=/Users/DrKLO/ndk9/platforms/android-9/arch-$ARCH/usr -ADDITIONAL_CONFIGURE_FLAG="--disable-mmx --disable-yasm" -build_one - diff --git a/TMessagesProj/jni/gif.c b/TMessagesProj/jni/gif.c new file mode 100644 index 00000000..b7881194 --- /dev/null +++ b/TMessagesProj/jni/gif.c @@ -0,0 +1,827 @@ +//tanks to https://github.com/koral--/android-gif-drawable +/* + MIT License + Copyright (c) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + // Copyright (c) 2011 Google Inc. All rights reserved. + // + // Redistribution and use in source and binary forms, with or without + // modification, are permitted provided that the following conditions are + // met: + // + // * Redistributions of source code must retain the above copyright + // notice, this list of conditions and the following disclaimer. + // * Redistributions in binary form must reproduce the above + // copyright notice, this list of conditions and the following disclaimer + // in the documentation and/or other materials provided with the + // distribution. + // * Neither the name of Google Inc. nor the names of its + // contributors may be used to endorse or promote products derived from + // this software without specific prior written permission. + // + // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + The GIFLIB distribution is Copyright (c) 1997 Eric S. Raymond + */ + +#include +#include +#include +#include +#include "gif.h" +#include "giflib/gif_lib.h" + +#define D_GIF_ERR_NO_FRAMES 1000 +#define D_GIF_ERR_INVALID_SCR_DIMS 1001 +#define D_GIF_ERR_INVALID_IMG_DIMS 1002 +#define D_GIF_ERR_IMG_NOT_CONFINED 1003 + +typedef struct { + uint8_t blue; + uint8_t green; + uint8_t red; + uint8_t alpha; +} argb; + +typedef struct { + unsigned int duration; + short transpIndex; + unsigned char disposalMethod; +} FrameInfo; + +typedef struct { + GifFileType *gifFilePtr; + unsigned long lastFrameReaminder; + unsigned long nextStartTime; + int currentIndex; + unsigned int lastDrawIndex; + FrameInfo *infos; + argb *backupPtr; + int startPos; + unsigned char *rasterBits; + char *comment; + unsigned short loopCount; + int currentLoop; + jfloat speedFactor; +} GifInfo; + +static ColorMapObject *defaultCmap = NULL; + +static ColorMapObject *genDefColorMap(void) { + ColorMapObject *cmap = GifMakeMapObject(256, NULL); + if (cmap != NULL) { + int iColor; + for (iColor = 0; iColor < 256; iColor++) { + cmap->Colors[iColor].Red = (GifByteType) iColor; + cmap->Colors[iColor].Green = (GifByteType) iColor; + cmap->Colors[iColor].Blue = (GifByteType) iColor; + } + } + return cmap; +} + +jint gifOnJNILoad(JavaVM *vm, void *reserved, JNIEnv *env) { + defaultCmap = genDefColorMap(); + if (defaultCmap == NULL) { + return -1; + } + return JNI_VERSION_1_4; +} + +void gifOnJNIUnload(JavaVM *vm, void *reserved) { + GifFreeMapObject(defaultCmap); +} + +static int fileReadFunc(GifFileType *gif, GifByteType *bytes, int size) { + FILE *file = (FILE *)gif->UserData; + return fread(bytes, 1, size, file); +} + +static int fileRewindFun(GifInfo *info) { + return fseek(info->gifFilePtr->UserData, info->startPos, SEEK_SET); +} + +static unsigned long getRealTime() { + struct timespec ts; + const clockid_t id = CLOCK_MONOTONIC; + if (id != (clockid_t) - 1 && clock_gettime(id, &ts) != -1) + return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; + return -1; +} + +static void cleanUp(GifInfo *info) { + if (info->backupPtr) { + free(info->backupPtr); + info->backupPtr = NULL; + } + if (info->infos) { + free(info->infos); + info->infos = NULL; + } + if (info->rasterBits) { + free(info->rasterBits); + info->rasterBits = NULL; + } + if (info->comment) { + free(info->comment); + info->comment = NULL; + } + + GifFileType *GifFile = info->gifFilePtr; + if (GifFile->SColorMap == defaultCmap) { + GifFile->SColorMap = NULL; + } + if (GifFile->SavedImages != NULL) { + SavedImage *sp; + for (sp = GifFile->SavedImages; sp < GifFile->SavedImages + GifFile->ImageCount; sp++) { + if (sp->ImageDesc.ColorMap != NULL) { + GifFreeMapObject(sp->ImageDesc.ColorMap); + sp->ImageDesc.ColorMap = NULL; + } + } + free(GifFile->SavedImages); + GifFile->SavedImages = NULL; + } + DGifCloseFile(GifFile); + free(info); +} + +static int getComment(GifByteType *Bytes, char **cmt) { + unsigned int len = (unsigned int) Bytes[0]; + unsigned int offset = *cmt != NULL ? strlen(*cmt) : 0; + char *ret = realloc(*cmt, (len + offset + 1) * sizeof(char)); + if (ret != NULL) { + memcpy(ret + offset, &Bytes[1], len); + ret[len + offset] = 0; + *cmt = ret; + return GIF_OK; + } + return GIF_ERROR; +} + +static void packARGB32(argb *pixel, GifByteType alpha, GifByteType red, GifByteType green, GifByteType blue) { + pixel->alpha = alpha; + pixel->red = red; + pixel->green = green; + pixel->blue = blue; +} + +static void getColorFromTable(int idx, argb *dst, const ColorMapObject *cmap) { + char colIdx = idx >= cmap->ColorCount ? 0 : idx; + GifColorType *col = &cmap->Colors[colIdx]; + packARGB32(dst, 0xFF, col->Red, col->Green, col->Blue); +} + +static void eraseColor(argb *bm, int w, int h, argb color) { + int i; + for (i = 0; i < w * h; i++) { + *(bm + i) = color; + } +} + +static inline bool setupBackupBmp(GifInfo *info, short transpIndex) { + GifFileType *fGIF = info->gifFilePtr; + info->backupPtr = calloc(fGIF->SWidth * fGIF->SHeight, sizeof(argb)); + if (!info->backupPtr) { + info->gifFilePtr->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return false; + } + argb paintingColor; + if (transpIndex == -1) { + getColorFromTable(fGIF->SBackGroundColor, &paintingColor, fGIF->SColorMap); + } else { + packARGB32(&paintingColor,0,0,0,0); + } + eraseColor(info->backupPtr, fGIF->SWidth, fGIF->SHeight, paintingColor); + return true; +} + +static int readExtensions(int ExtFunction, GifByteType *ExtData, GifInfo *info) { + if (ExtData == NULL) { + return GIF_OK; + } + if (ExtFunction == GRAPHICS_EXT_FUNC_CODE && ExtData[0] == 4) { + FrameInfo *fi = &info->infos[info->gifFilePtr->ImageCount]; + fi->transpIndex = -1; + char *b = (char *)ExtData + 1; + short delay = ((b[2] << 8) | b[1]); + fi->duration = delay > 1 ? delay * 10 : 100; + fi->disposalMethod = ((b[0] >> 2) & 7); + if (ExtData[1] & 1) + fi->transpIndex = (short) b[3]; + if (fi->disposalMethod == 3 && info->backupPtr == NULL) { + if (!setupBackupBmp(info,fi->transpIndex)) { + return GIF_ERROR; + } + } + } else if (ExtFunction == COMMENT_EXT_FUNC_CODE) { + if (getComment(ExtData, &info->comment) == GIF_ERROR) { + info->gifFilePtr->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } else if (ExtFunction == APPLICATION_EXT_FUNC_CODE && ExtData[0] == 11) { + if (strncmp("NETSCAPE2.0", &ExtData[1], 11) == 0 || strncmp("ANIMEXTS1.0", &ExtData[1], 11) == 0) { + if (DGifGetExtensionNext(info->gifFilePtr, &ExtData, &ExtFunction) == GIF_ERROR) { + return GIF_ERROR; + } + if (ExtFunction == APPLICATION_EXT_FUNC_CODE && ExtData[0] == 3 && ExtData[1] == 1) { + info->loopCount = (unsigned short) (ExtData[2] + (ExtData[3] << 8)); + } + } + } + return GIF_OK; +} + +static int DDGifSlurp(GifFileType *GifFile, GifInfo *info, bool shouldDecode) { + GifRecordType RecordType; + GifByteType *ExtData; + int codeSize; + int ExtFunction; + size_t ImageSize; + do { + if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) { + return GIF_ERROR; + } + switch (RecordType) { + case IMAGE_DESC_RECORD_TYPE: { + if (DGifGetImageDesc(GifFile, !shouldDecode) == GIF_ERROR) { + return GIF_ERROR; + } + int i = shouldDecode ? info->currentIndex : GifFile->ImageCount - 1; + SavedImage *sp = &GifFile->SavedImages[i]; + ImageSize = sp->ImageDesc.Width * sp->ImageDesc.Height; + + if (sp->ImageDesc.Width < 1 || sp->ImageDesc.Height < 1 || ImageSize > (SIZE_MAX / sizeof(GifPixelType))) { + GifFile->Error = D_GIF_ERR_INVALID_IMG_DIMS; + return GIF_ERROR; + } + if (sp->ImageDesc.Width > GifFile->SWidth || sp->ImageDesc.Height > GifFile->SHeight) { + GifFile->Error = D_GIF_ERR_IMG_NOT_CONFINED; + return GIF_ERROR; + } + if (shouldDecode) { + sp->RasterBits = info->rasterBits; + if (sp->ImageDesc.Interlace) { + int i, j; + int InterlacedOffset[] = { 0, 4, 2, 1 }; + int InterlacedJumps[] = { 8, 8, 4, 2 }; + for (i = 0; i < 4; i++) { + for (j = InterlacedOffset[i]; j < sp->ImageDesc.Height; j += InterlacedJumps[i]) { + if (DGifGetLine(GifFile, sp->RasterBits + j * sp->ImageDesc.Width, sp->ImageDesc.Width) == GIF_ERROR) { + return GIF_ERROR; + } + } + } + } else { + if (DGifGetLine(GifFile, sp->RasterBits, ImageSize) == GIF_ERROR) { + return GIF_ERROR; + } + } + if (info->currentIndex >= GifFile->ImageCount - 1) { + if (info->loopCount > 0) + info->currentLoop++; + if (fileRewindFun(info) != 0) { + info->gifFilePtr->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + } + return GIF_OK; + } else { + if (DGifGetCode(GifFile, &codeSize, &ExtData) == GIF_ERROR) { + return GIF_ERROR; + } + while (ExtData) { + if (DGifGetCodeNext(GifFile, &ExtData) == GIF_ERROR) { + return GIF_ERROR; + } + } + } + break; + } + + case EXTENSION_RECORD_TYPE: { + if (DGifGetExtension(GifFile, &ExtFunction, &ExtData) == GIF_ERROR) { + return GIF_ERROR; + } + + if (!shouldDecode) { + info->infos = realloc(info->infos, (GifFile->ImageCount + 1) * sizeof(FrameInfo)); + if (readExtensions(ExtFunction, ExtData, info) == GIF_ERROR) { + return GIF_ERROR; + } + } + while (ExtData) { + if (DGifGetExtensionNext(GifFile, &ExtData, &ExtFunction) == GIF_ERROR) { + return GIF_ERROR; + } + if (!shouldDecode) { + if (readExtensions(ExtFunction, ExtData, info) == GIF_ERROR) { + return GIF_ERROR; + } + } + } + break; + } + case TERMINATE_RECORD_TYPE: + default: + break; + } + } while (RecordType != TERMINATE_RECORD_TYPE); + + bool ok = true; + if (shouldDecode) { + ok = (fileRewindFun(info) == 0); + } + if (ok) { + return GIF_OK; + } else { + info->gifFilePtr->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } +} + +static void copyLine(argb *dst, const unsigned char *src, const ColorMapObject *cmap, int transparent, int width) { + for (; width > 0; width--, src++, dst++) { + if (*src != transparent) { + getColorFromTable(*src, dst, cmap); + } + } +} + +static argb *getAddr(argb *bm, int width, int left, int top) { + return bm + top * width + left; +} + +static void blitNormal(argb *bm, int width, int height, const SavedImage *frame, const ColorMapObject *cmap, int transparent) { + const unsigned char *src = (unsigned char *)frame->RasterBits; + argb *dst = getAddr(bm, width, frame->ImageDesc.Left, frame->ImageDesc.Top); + GifWord copyWidth = frame->ImageDesc.Width; + if (frame->ImageDesc.Left + copyWidth > width) { + copyWidth = width - frame->ImageDesc.Left; + } + + GifWord copyHeight = frame->ImageDesc.Height; + if (frame->ImageDesc.Top + copyHeight > height) { + copyHeight = height - frame->ImageDesc.Top; + } + + int srcPad, dstPad; + dstPad = width - copyWidth; + srcPad = frame->ImageDesc.Width - copyWidth; + for (; copyHeight > 0; copyHeight--) { + copyLine(dst, src, cmap, transparent, copyWidth); + src += frame->ImageDesc.Width; + dst += width; + } +} + +static void fillRect(argb *bm, int bmWidth, int bmHeight, GifWord left, GifWord top, GifWord width, GifWord height, argb col) { + uint32_t *dst = (uint32_t *)getAddr(bm, bmWidth, left, top); + GifWord copyWidth = width; + if (left + copyWidth > bmWidth) { + copyWidth = bmWidth - left; + } + + GifWord copyHeight = height; + if (top + copyHeight > bmHeight) { + copyHeight = bmHeight - top; + } + uint32_t *pColor = (uint32_t *)(&col); + for (; copyHeight > 0; copyHeight--) { + memset(dst, *pColor, copyWidth * sizeof(argb)); + dst += bmWidth; + } +} + +static void drawFrame(argb *bm, int bmWidth, int bmHeight, const SavedImage *frame, const ColorMapObject *cmap, short transpIndex) { + if (frame->ImageDesc.ColorMap != NULL) { + cmap = frame->ImageDesc.ColorMap; + if (cmap == NULL || cmap->ColorCount != (1 << cmap->BitsPerPixel)) { + cmap = defaultCmap; + } + } + + blitNormal(bm, bmWidth, bmHeight, frame, cmap, (int) transpIndex); +} + +static bool checkIfCover(const SavedImage *target, const SavedImage *covered) { + if (target->ImageDesc.Left <= covered->ImageDesc.Left + && covered->ImageDesc.Left + covered->ImageDesc.Width + <= target->ImageDesc.Left + target->ImageDesc.Width + && target->ImageDesc.Top <= covered->ImageDesc.Top + && covered->ImageDesc.Top + covered->ImageDesc.Height + <= target->ImageDesc.Top + target->ImageDesc.Height) { + return true; + } + return false; +} + +static inline void disposeFrameIfNeeded(argb *bm, GifInfo *info, unsigned int idx) { + argb *backup = info->backupPtr; + argb color; + packARGB32(&color, 0, 0, 0, 0); + GifFileType *fGif = info->gifFilePtr; + SavedImage *cur = &fGif->SavedImages[idx - 1]; + SavedImage *next = &fGif->SavedImages[idx]; + bool curTrans = info->infos[idx - 1].transpIndex != -1; + int curDisposal = info->infos[idx - 1].disposalMethod; + bool nextTrans = info->infos[idx].transpIndex != -1; + int nextDisposal = info->infos[idx].disposalMethod; + + argb *tmp; + if ((curDisposal == 2 || curDisposal == 3) && (nextTrans || !checkIfCover(next, cur))) { + switch (curDisposal) { + case 2: { + fillRect(bm, fGif->SWidth, fGif->SHeight, cur->ImageDesc.Left, cur->ImageDesc.Top, cur->ImageDesc.Width, cur->ImageDesc.Height, color); + } + break; + + case 3: { + tmp = bm; + bm = backup; + backup = tmp; + } + break; + } + } + + if (nextDisposal == 3) { + memcpy(backup, bm, fGif->SWidth * fGif->SHeight * sizeof(argb)); + } +} + +static jboolean reset(GifInfo *info) { + if (fileRewindFun(info) != 0) { + return JNI_FALSE; + } + info->nextStartTime = 0; + info->currentLoop = -1; + info->currentIndex = -1; + return JNI_TRUE; +} + +static void getBitmap(argb *bm, GifInfo *info) { + GifFileType *fGIF = info->gifFilePtr; + + argb paintingColor; + int i = info->currentIndex; + if (DDGifSlurp(fGIF, info, true) == GIF_ERROR) { + return; //TODO add leniency support + } + SavedImage *cur = &fGIF->SavedImages[i]; + + short transpIndex = info->infos[i].transpIndex; + if (i == 0) { + if (transpIndex == -1) { + getColorFromTable(fGIF->SBackGroundColor, &paintingColor, fGIF->SColorMap); + } else { + packARGB32(&paintingColor, 0, 0, 0, 0); + } + eraseColor(bm, fGIF->SWidth, fGIF->SHeight, paintingColor); + } else { + disposeFrameIfNeeded(bm, info, i); + } + drawFrame(bm, fGIF->SWidth, fGIF->SHeight, cur, fGIF->SColorMap, transpIndex); +} + +static void setMetaData(int width, int height, int ImageCount, int errorCode, JNIEnv *env, jintArray metaData) { + jint *ints = (*env)->GetIntArrayElements(env, metaData, 0); + *ints++ = width; + *ints++ = height; + *ints++ = ImageCount; + *ints = errorCode; + (*env)->ReleaseIntArrayElements(env, metaData, ints, 0); +} + +static jint open(GifFileType *GifFileIn, int Error, int startPos, JNIEnv *env, jintArray metaData) { + if (startPos < 0) { + Error = D_GIF_ERR_NOT_READABLE; + DGifCloseFile(GifFileIn); + } + if (Error != 0 || GifFileIn == NULL) { + setMetaData(0, 0, 0, Error, env, metaData); + return (jint) NULL; + } + int width = GifFileIn->SWidth, height = GifFileIn->SHeight; + unsigned int wxh = width * height; + if (wxh < 1 || wxh > INT_MAX) { + DGifCloseFile(GifFileIn); + setMetaData(width, height, 0, D_GIF_ERR_INVALID_SCR_DIMS, env, metaData); + return (jint) NULL; + } + GifInfo *info = malloc(sizeof(GifInfo)); + if (info == NULL) { + DGifCloseFile(GifFileIn); + setMetaData(width, height, 0, D_GIF_ERR_NOT_ENOUGH_MEM, env, metaData); + return (jint) NULL; + } + info->gifFilePtr = GifFileIn; + info->startPos = startPos; + info->currentIndex = -1; + info->nextStartTime = 0; + info->lastFrameReaminder = ULONG_MAX; + info->comment = NULL; + info->loopCount = 0; + info->currentLoop = -1; + info->speedFactor = 1.0; + info->rasterBits = calloc(GifFileIn->SHeight * GifFileIn->SWidth, sizeof(GifPixelType)); + info->infos = malloc(sizeof(FrameInfo)); + info->infos->duration = 0; + info->infos->disposalMethod = 0; + info->infos->transpIndex = -1; + info->backupPtr = NULL; + + if (info->rasterBits == NULL || info->infos == NULL) { + cleanUp(info); + setMetaData(width, height, 0, D_GIF_ERR_NOT_ENOUGH_MEM, env, metaData); + return (jint) NULL; + } + + if (DDGifSlurp(GifFileIn, info, false) == GIF_ERROR) { + Error = GifFileIn->Error; + } + if (GifFileIn->SColorMap == NULL || GifFileIn->SColorMap->ColorCount != (1 << GifFileIn->SColorMap->BitsPerPixel)) { + GifFreeMapObject(GifFileIn->SColorMap); + GifFileIn->SColorMap = defaultCmap; + } + int imgCount = GifFileIn->ImageCount; + if (imgCount < 1) { + Error = D_GIF_ERR_NO_FRAMES; + } + if (fileRewindFun(info) != 0) { + Error = D_GIF_ERR_READ_FAILED; + } + if (Error != 0) { + cleanUp(info); + } + setMetaData(width, height, imgCount, Error, env, metaData); + + return (jint)(Error == 0 ? info : NULL); +} + +JNIEXPORT jlong JNICALL Java_org_telegram_ui_Views_GifDrawable_getAllocationByteCount(JNIEnv *env, jclass class, jobject gifInfo) { + GifInfo *info = (GifInfo *)gifInfo; + if (info == NULL) { + return 0; + } + unsigned int pxCount = info->gifFilePtr->SWidth + info->gifFilePtr->SHeight; + jlong sum = pxCount * sizeof(char); + if (info->backupPtr != NULL) { + sum += pxCount * sizeof(argb); + } + return sum; +} + +JNIEXPORT jboolean JNICALL Java_org_telegram_ui_Views_GifDrawable_reset(JNIEnv *env, jclass class, jobject gifInfo) { + GifInfo *info = (GifInfo *)gifInfo; + if (info == NULL) { + return JNI_FALSE; + } + return reset(info); +} + +JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_setSpeedFactor(JNIEnv *env, jclass class, jobject gifInfo, jfloat factor) { + GifInfo *info = (GifInfo *)gifInfo; + if (info == NULL) { + return; + } + info->speedFactor = factor; +} + +JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_seekToTime(JNIEnv *env, jclass class, jobject gifInfo, jint desiredPos, jintArray jPixels) { + GifInfo *info = (GifInfo *)gifInfo; + if (info == NULL) { + return; + } + int imgCount = info->gifFilePtr->ImageCount; + if (imgCount <= 1) { + return; + } + + unsigned long sum = 0; + int i; + for (i = 0; i < imgCount; i++) { + unsigned long newSum = sum + info->infos[i].duration; + if (newSum >= desiredPos) { + break; + } + sum = newSum; + } + if (i < info->currentIndex) { + return; + } + + unsigned long lastFrameRemainder = desiredPos - sum; + if (i == imgCount - 1 && lastFrameRemainder > info->infos[i].duration) { + lastFrameRemainder = info->infos[i].duration; + } + info->lastFrameReaminder = lastFrameRemainder; + if (i > info->currentIndex) { + jint *pixels = (*env)->GetIntArrayElements(env, jPixels, 0); + while (info->currentIndex <= i) { + info->currentIndex++; + getBitmap((argb *)pixels, info); + } + (*env)->ReleaseIntArrayElements(env, jPixels, pixels, 0); + } + if (info->speedFactor == 1.0) { + info->nextStartTime = getRealTime() + lastFrameRemainder; + } else { + info->nextStartTime = getRealTime() + lastFrameRemainder * info->speedFactor; + } +} + +JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_seekToFrame(JNIEnv *env, jclass class, jobject gifInfo, jint desiredIdx, jintArray jPixels) { + GifInfo *info = (GifInfo *)gifInfo; + if (info == NULL) { + return; + } + if (desiredIdx <= info->currentIndex) { + return; + } + + int imgCount = info->gifFilePtr->ImageCount; + if (imgCount <= 1) { + return; + } + + if (desiredIdx >= imgCount) { + desiredIdx = imgCount - 1; + } + + info->lastFrameReaminder = 0; + jint *pixels = (*env)->GetIntArrayElements(env, jPixels, 0); + while (info->currentIndex < desiredIdx) { + info->currentIndex++; + getBitmap((argb *)pixels, info); + } + (*env)->ReleaseIntArrayElements(env, jPixels, pixels, 0); + if (info->speedFactor == 1.0) { + info->nextStartTime = getRealTime() + info->infos[info->currentIndex].duration; + } else { + info->nextStartTime = getRealTime() + info->infos[info->currentIndex].duration * info->speedFactor; + } + +} + +JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_renderFrame(JNIEnv *env, jclass class, jintArray jPixels, jobject gifInfo, jintArray metaData) { + + GifInfo *info = (GifInfo *)gifInfo; + if (info == NULL) { + return; + } + + bool needRedraw = false; + unsigned long rt = getRealTime(); + + if (rt >= info->nextStartTime && info->currentLoop < info->loopCount) { + if (++info->currentIndex >= info->gifFilePtr->ImageCount) { + info->currentIndex = 0; + } + needRedraw = true; + } + jint *rawMetaData = (*env)->GetIntArrayElements(env, metaData, 0); + + if (needRedraw) { + jint *pixels = (*env)->GetIntArrayElements(env, jPixels, 0); + getBitmap((argb *)pixels, info); + rawMetaData[3] = info->gifFilePtr->Error; + + (*env)->ReleaseIntArrayElements(env, jPixels, pixels, 0); + + int scaledDuration = info->infos[info->currentIndex].duration; + if (info->speedFactor != 1.0) { + scaledDuration /= info->speedFactor; + } + info->nextStartTime = rt + scaledDuration; + rawMetaData[4] = scaledDuration; + } else { + rawMetaData[4] = (int) (rt - info->nextStartTime); + } + (*env)->ReleaseIntArrayElements(env, metaData, rawMetaData, 0); +} + +JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_free(JNIEnv *env, jclass class, jobject gifInfo) { + if (gifInfo == NULL) { + return; + } + GifInfo *info = (GifInfo *)gifInfo; + FILE *file = info->gifFilePtr->UserData; + if (file) { + fclose(file); + } + info->gifFilePtr->UserData = NULL; + cleanUp(info); +} + +JNIEXPORT jstring JNICALL Java_org_telegram_ui_Views_GifDrawable_getComment(JNIEnv *env, jclass class, jobject gifInfo) { + if (gifInfo == NULL) { + return NULL; + } + GifInfo *info = (GifInfo *)gifInfo; + return (*env)->NewStringUTF(env, info->comment); +} + +JNIEXPORT jint JNICALL Java_org_telegram_ui_Views_GifDrawable_getLoopCount(JNIEnv *env, jclass class, jobject gifInfo) { + if (gifInfo == NULL) { + return 0; + } + return ((GifInfo *)gifInfo)->loopCount; +} + +JNIEXPORT jint JNICALL Java_org_telegram_ui_Views_GifDrawable_getDuration(JNIEnv *env, jclass class, jobject gifInfo) { + GifInfo *info = (GifInfo *)gifInfo; + if (info == NULL) { + return 0; + } + int i; + unsigned long sum = 0; + for (i = 0; i < info->gifFilePtr->ImageCount; i++) { + sum += info->infos[i].duration; + } + return sum; +} + +JNIEXPORT jint JNICALL Java_org_telegram_ui_Views_GifDrawable_getCurrentPosition(JNIEnv *env, jclass class, jobject gifInfo) { + GifInfo *info = (GifInfo *)gifInfo; + if (info == NULL) { + return 0; + } + int idx = info->currentIndex; + if (idx < 0 || info->gifFilePtr->ImageCount <= 1) { + return 0; + } + int i; + unsigned int sum = 0; + for (i = 0; i < idx; i++) { + sum += info->infos[i].duration; + } + unsigned long remainder = info->lastFrameReaminder == ULONG_MAX ? getRealTime() - info->nextStartTime : info->lastFrameReaminder; + return (int) (sum + remainder); +} + +JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_saveRemainder(JNIEnv *env, jclass class, jobject gifInfo) { + GifInfo *info = (GifInfo *)gifInfo; + if (info == NULL) { + return; + } + info->lastFrameReaminder = getRealTime() - info->nextStartTime; + if (info->lastFrameReaminder > 0) { + info->lastFrameReaminder = 0; + } +} + +JNIEXPORT void JNICALL Java_org_telegram_ui_Views_GifDrawable_restoreRemainder(JNIEnv *env, jclass class, jobject gifInfo) { + GifInfo *info = (GifInfo *)gifInfo; + if (info == NULL || info->lastFrameReaminder == ULONG_MAX) { + return; + } + info->nextStartTime = getRealTime() + info->lastFrameReaminder; + info->lastFrameReaminder = ULONG_MAX; +} + +JNIEXPORT jint JNICALL Java_org_telegram_ui_Views_GifDrawable_openFile(JNIEnv *env, jclass class, jintArray metaData, jstring jfname) { + if (jfname == NULL) { + setMetaData(0, 0, 0, D_GIF_ERR_OPEN_FAILED, env, metaData); + return (jint) NULL; + } + + const char *fname = (*env)->GetStringUTFChars(env, jfname, 0); + FILE *file = fopen(fname, "rb"); + (*env)->ReleaseStringUTFChars(env, jfname, fname); + if (file == NULL) { + setMetaData(0, 0, 0, D_GIF_ERR_OPEN_FAILED, env, metaData); + return (jint) NULL; + } + int Error = 0; + GifFileType *GifFileIn = DGifOpen(file, &fileReadFunc, &Error); + return open(GifFileIn, Error, ftell(file), env, metaData); +} diff --git a/TMessagesProj/jni/gif.h b/TMessagesProj/jni/gif.h new file mode 100644 index 00000000..96409be1 --- /dev/null +++ b/TMessagesProj/jni/gif.h @@ -0,0 +1,7 @@ +#ifndef gif_h +#define gif_h + +jint gifOnJNILoad(JavaVM *vm, void *reserved, JNIEnv *env); +void gifOnJNIUnload(JavaVM *vm, void *reserved); + +#endif \ No newline at end of file diff --git a/TMessagesProj/jni/giflib/config.h b/TMessagesProj/jni/giflib/config.h new file mode 100755 index 00000000..e68b0d9e --- /dev/null +++ b/TMessagesProj/jni/giflib/config.h @@ -0,0 +1,13 @@ + +// giflib config.h + +#ifndef GIF_CONFIG_H_DEFINED +#define GIF_CONFIG_H_DEFINED + +#include +#define HAVE_STDINT_H +#define HAVE_FCNTL_H + +typedef uint32_t UINT32; + +#endif diff --git a/TMessagesProj/jni/giflib/dgif_lib.c b/TMessagesProj/jni/giflib/dgif_lib.c new file mode 100755 index 00000000..92442408 --- /dev/null +++ b/TMessagesProj/jni/giflib/dgif_lib.c @@ -0,0 +1,1167 @@ +/****************************************************************************** + +dgif_lib.c - GIF decoding + +The functions here and in egif_lib.c are partitioned carefully so that +if you only require one of read and write capability, only one of these +two modules will be linked. Preserve this property! + +*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#endif /* _WIN32 */ + +#include "gif_lib.h" +#include "gif_lib_private.h" + +/* compose unsigned little endian value */ +#define UNSIGNED_LITTLE_ENDIAN(lo, hi) ((lo) | ((hi) << 8)) + +/* avoid extra function call in case we use fread (TVT) */ +#define READ(_gif,_buf,_len) \ + (((GifFilePrivateType*)_gif->Private)->Read ? \ + ((GifFilePrivateType*)_gif->Private)->Read(_gif,_buf,_len) : \ + fread(_buf,1,_len,((GifFilePrivateType*)_gif->Private)->File)) + +static int DGifGetWord(GifFileType *GifFile, GifWord *Word); +static int DGifSetupDecompress(GifFileType *GifFile); +static int DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line, + int LineLen); +static int DGifGetPrefixChar(GifPrefixType *Prefix, int Code, int ClearCode); +static int DGifDecompressInput(GifFileType *GifFile, int *Code); +static int DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf, + GifByteType *NextByte); + +/****************************************************************************** + Open a new GIF file for read, given by its name. + Returns dynamically allocated GifFileType pointer which serves as the GIF + info record. +******************************************************************************/ +GifFileType * +DGifOpenFileName(const char *FileName, int *Error) +{ + int FileHandle; + GifFileType *GifFile; + + if ((FileHandle = open(FileName, O_RDONLY)) == -1) { + if (Error != NULL) + *Error = D_GIF_ERR_OPEN_FAILED; + return NULL; + } + GifFile = DGifOpenFileHandle(FileHandle, Error); + return GifFile; +} + +/****************************************************************************** + Update a new GIF file, given its file handle. + Returns dynamically allocated GifFileType pointer which serves as the GIF + info record. +******************************************************************************/ +GifFileType * +DGifOpenFileHandle(int FileHandle, int *Error) +{ + char Buf[GIF_STAMP_LEN + 1]; + GifFileType *GifFile; + GifFilePrivateType *Private; + FILE *f; + + GifFile = (GifFileType *)malloc(sizeof(GifFileType)); + if (GifFile == NULL) { + if (Error != NULL) + *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + (void)close(FileHandle); + return NULL; + } + + /*@i1@*/memset(GifFile, '\0', sizeof(GifFileType)); + + /* Belt and suspenders, in case the null pointer isn't zero */ + GifFile->SavedImages = NULL; + GifFile->SColorMap = NULL; + + Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType)); + if (Private == NULL) { + if (Error != NULL) + *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + (void)close(FileHandle); + free((char *)GifFile); + return NULL; + } +#ifdef _WIN32 + _setmode(FileHandle, O_BINARY); /* Make sure it is in binary mode. */ +#endif /* _WIN32 */ + + f = fdopen(FileHandle, "rb"); /* Make it into a stream: */ + + /*@-mustfreeonly@*/ + GifFile->Private = (void *)Private; + Private->FileHandle = FileHandle; + Private->File = f; + Private->FileState = FILE_STATE_READ; + Private->Read = NULL; /* don't use alternate input method (TVT) */ + GifFile->UserData = NULL; /* TVT */ + /*@=mustfreeonly@*/ + + /* Let's see if this is a GIF file: */ + if (READ(GifFile, (unsigned char *)Buf, GIF_STAMP_LEN) != GIF_STAMP_LEN) { + if (Error != NULL) + *Error = D_GIF_ERR_READ_FAILED; + (void)fclose(f); + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + /* Check for GIF prefix at start of file */ + Buf[GIF_STAMP_LEN] = 0; + if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) { + if (Error != NULL) + *Error = D_GIF_ERR_NOT_GIF_FILE; + (void)fclose(f); + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + if (DGifGetScreenDesc(GifFile) == GIF_ERROR) { + (void)fclose(f); + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + GifFile->Error = 0; + + /* What version of GIF? */ + Private->gif89 = (Buf[GIF_VERSION_POS] == '9'); + + return GifFile; +} + +/****************************************************************************** + GifFileType constructor with user supplied input function (TVT) +******************************************************************************/ +GifFileType * +DGifOpen(void *userData, InputFunc readFunc, int *Error) +{ + char Buf[GIF_STAMP_LEN + 1]; + GifFileType *GifFile; + GifFilePrivateType *Private; + + GifFile = (GifFileType *)malloc(sizeof(GifFileType)); + if (GifFile == NULL) { + if (Error != NULL) + *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return NULL; + } + + memset(GifFile, '\0', sizeof(GifFileType)); + + /* Belt and suspenders, in case the null pointer isn't zero */ + GifFile->SavedImages = NULL; + GifFile->SColorMap = NULL; + + Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType)); + if (!Private) { + if (Error != NULL) + *Error = D_GIF_ERR_NOT_ENOUGH_MEM; + free((char *)GifFile); + return NULL; + } + + GifFile->Private = (void *)Private; + Private->FileHandle = 0; + Private->File = NULL; + Private->FileState = FILE_STATE_READ; + + Private->Read = readFunc; /* TVT */ + GifFile->UserData = userData; /* TVT */ + + /* Lets see if this is a GIF file: */ + if (READ(GifFile, (unsigned char *)Buf, GIF_STAMP_LEN) != GIF_STAMP_LEN) { + if (Error != NULL) + *Error = D_GIF_ERR_READ_FAILED; + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + /* Check for GIF prefix at start of file */ + Buf[GIF_STAMP_LEN] = '\0'; + if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) { + if (Error != NULL) + *Error = D_GIF_ERR_NOT_GIF_FILE; + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + if (DGifGetScreenDesc(GifFile) == GIF_ERROR) { + free((char *)Private); + free((char *)GifFile); + *Error = D_GIF_ERR_NO_SCRN_DSCR; + return NULL; + } + + GifFile->Error = 0; + + /* What version of GIF? */ + Private->gif89 = (Buf[GIF_VERSION_POS] == '9'); + + return GifFile; +} + +/****************************************************************************** + This routine should be called before any other DGif calls. Note that + this routine is called automatically from DGif file open routines. +******************************************************************************/ +int +DGifGetScreenDesc(GifFileType *GifFile) +{ + int BitsPerPixel; + bool SortFlag; + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + /* Put the screen descriptor into the file: */ + if (DGifGetWord(GifFile, &GifFile->SWidth) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->SHeight) == GIF_ERROR) + return GIF_ERROR; + + if (READ(GifFile, Buf, 3) != 3) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + GifFreeMapObject(GifFile->SColorMap); + GifFile->SColorMap = NULL; + return GIF_ERROR; + } + GifFile->SColorResolution = (((Buf[0] & 0x70) + 1) >> 4) + 1; + SortFlag = (Buf[0] & 0x08) != 0; + BitsPerPixel = (Buf[0] & 0x07) + 1; + GifFile->SBackGroundColor = Buf[1]; + GifFile->AspectByte = Buf[2]; + if (Buf[0] & 0x80) { /* Do we have global color map? */ + int i; + + GifFile->SColorMap = GifMakeMapObject(1 << BitsPerPixel, NULL); + if (GifFile->SColorMap == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + + /* Get the global color map: */ + GifFile->SColorMap->SortFlag = SortFlag; + for (i = 0; i < GifFile->SColorMap->ColorCount; i++) { + if (READ(GifFile, Buf, 3) != 3) { + GifFreeMapObject(GifFile->SColorMap); + GifFile->SColorMap = NULL; + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + GifFile->SColorMap->Colors[i].Red = Buf[0]; + GifFile->SColorMap->Colors[i].Green = Buf[1]; + GifFile->SColorMap->Colors[i].Blue = Buf[2]; + } + } else { + GifFile->SColorMap = NULL; + } + + return GIF_OK; +} + +/****************************************************************************** + This routine should be called before any attempt to read an image. +******************************************************************************/ +int +DGifGetRecordType(GifFileType *GifFile, GifRecordType* Type) +{ + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (READ(GifFile, &Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + + switch (Buf) { + case DESCRIPTOR_INTRODUCER: + *Type = IMAGE_DESC_RECORD_TYPE; + break; + case EXTENSION_INTRODUCER: + *Type = EXTENSION_RECORD_TYPE; + break; + case TERMINATOR_INTRODUCER: + *Type = TERMINATE_RECORD_TYPE; + break; + default: + *Type = UNDEFINED_RECORD_TYPE; + GifFile->Error = D_GIF_ERR_WRONG_RECORD; + return GIF_ERROR; + } + + return GIF_OK; +} + +/****************************************************************************** + This routine should be called before any attempt to read an image. + Note it is assumed the Image desc. header has been read. +******************************************************************************/ +int +DGifGetImageDesc(GifFileType *GifFile, bool changeImageCount) +{ + unsigned int BitsPerPixel; + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + SavedImage *sp; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (DGifGetWord(GifFile, &GifFile->Image.Left) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->Image.Top) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->Image.Width) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->Image.Height) == GIF_ERROR) + return GIF_ERROR; + if (READ(GifFile, Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + return GIF_ERROR; + } + BitsPerPixel = (Buf[0] & 0x07) + 1; + GifFile->Image.Interlace = (Buf[0] & 0x40) ? true : false; + + /* Setup the colormap */ + if (GifFile->Image.ColorMap) { + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + } + /* Does this image have local color map? */ + if (Buf[0] & 0x80) { + unsigned int i; + + GifFile->Image.ColorMap = GifMakeMapObject(1 << BitsPerPixel, NULL); + if (GifFile->Image.ColorMap == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + + /* Get the image local color map: */ + for (i = 0; i < GifFile->Image.ColorMap->ColorCount; i++) { + if (READ(GifFile, Buf, 3) != 3) { + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Error = D_GIF_ERR_READ_FAILED; + GifFile->Image.ColorMap = NULL; + return GIF_ERROR; + } + GifFile->Image.ColorMap->Colors[i].Red = Buf[0]; + GifFile->Image.ColorMap->Colors[i].Green = Buf[1]; + GifFile->Image.ColorMap->Colors[i].Blue = Buf[2]; + } + } + // if (changeImageCount) + { + if (GifFile->SavedImages) { + if ((GifFile->SavedImages = (SavedImage *)realloc(GifFile->SavedImages, + sizeof(SavedImage) * + (GifFile->ImageCount + 1))) == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } else { + if ((GifFile->SavedImages = + (SavedImage *) malloc(sizeof(SavedImage))) == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } + } + sp = &GifFile->SavedImages[GifFile->ImageCount]; + memcpy(&sp->ImageDesc, &GifFile->Image, sizeof(GifImageDesc)); + if (GifFile->Image.ColorMap != NULL) { + sp->ImageDesc.ColorMap = GifMakeMapObject( + GifFile->Image.ColorMap->ColorCount, + GifFile->Image.ColorMap->Colors); + if (sp->ImageDesc.ColorMap == NULL) { + GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } + sp->RasterBits = (unsigned char *)NULL; + sp->ExtensionBlockCount = 0; + sp->ExtensionBlocks = (ExtensionBlock *) NULL; + if (changeImageCount) + GifFile->ImageCount++; + + Private->PixelCount = (long)GifFile->Image.Width * + (long)GifFile->Image.Height; + + /* Reset decompress algorithm parameters. */ + (void)DGifSetupDecompress(GifFile); + + return GIF_OK; +} + +/****************************************************************************** + Get one full scanned line (Line) of length LineLen from GIF file. +******************************************************************************/ +int +DGifGetLine(GifFileType *GifFile, GifPixelType *Line, int LineLen) +{ + GifByteType *Dummy; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (!LineLen) + LineLen = GifFile->Image.Width; + + if ((Private->PixelCount -= LineLen) > 0xffff0000UL) { + GifFile->Error = D_GIF_ERR_DATA_TOO_BIG; + return GIF_ERROR; + } + + if (DGifDecompressLine(GifFile, Line, LineLen) == GIF_OK) { + if (Private->PixelCount == 0) { + /* We probably won't be called any more, so let's clean up + * everything before we return: need to flush out all the + * rest of image until an empty block (size 0) + * detected. We use GetCodeNext. + */ + do + if (DGifGetCodeNext(GifFile, &Dummy) == GIF_ERROR) + return GIF_ERROR; + while (Dummy != NULL) ; + } + return GIF_OK; + } else + return GIF_ERROR; +} + +/****************************************************************************** + Put one pixel (Pixel) into GIF file. +******************************************************************************/ +int +DGifGetPixel(GifFileType *GifFile, GifPixelType Pixel) +{ + GifByteType *Dummy; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + if (--Private->PixelCount > 0xffff0000UL) + { + GifFile->Error = D_GIF_ERR_DATA_TOO_BIG; + return GIF_ERROR; + } + + if (DGifDecompressLine(GifFile, &Pixel, 1) == GIF_OK) { + if (Private->PixelCount == 0) { + /* We probably won't be called any more, so let's clean up + * everything before we return: need to flush out all the + * rest of image until an empty block (size 0) + * detected. We use GetCodeNext. + */ + do + if (DGifGetCodeNext(GifFile, &Dummy) == GIF_ERROR) + return GIF_ERROR; + while (Dummy != NULL) ; + } + return GIF_OK; + } else + return GIF_ERROR; +} + +/****************************************************************************** + Get an extension block (see GIF manual) from GIF file. This routine only + returns the first data block, and DGifGetExtensionNext should be called + after this one until NULL extension is returned. + The Extension should NOT be freed by the user (not dynamically allocated). + Note it is assumed the Extension description header has been read. +******************************************************************************/ +int +DGifGetExtension(GifFileType *GifFile, int *ExtCode, GifByteType **Extension) +{ + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (READ(GifFile, &Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + *ExtCode = Buf; + + return DGifGetExtensionNext(GifFile, Extension, ExtCode); +} + +/****************************************************************************** + Get a following extension block (see GIF manual) from GIF file. This + routine should be called until NULL Extension is returned. + The Extension should NOT be freed by the user (not dynamically allocated). +******************************************************************************/ +int +DGifGetExtensionNext(GifFileType *GifFile, GifByteType ** Extension, int* ExtCode) +{ + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (READ(GifFile, &Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + if (Buf > 0) + { + if (*ExtCode==GRAPHICS_EXT_FUNC_CODE) + Buf=4; + *Extension = Private->Buf; /* Use private unused buffer. */ + (*Extension)[0] = Buf; /* Pascal strings notation (pos. 0 is len.). */ + /* coverity[tainted_data] */ + if (READ(GifFile, &((*Extension)[1]), Buf) != Buf) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + } else + *Extension = NULL; + + return GIF_OK; +} + +/****************************************************************************** + Extract a Graphics Control Block from raw extension data +******************************************************************************/ + +int DGifExtensionToGCB(const size_t GifExtensionLength, + const GifByteType *GifExtension, + GraphicsControlBlock *GCB) +{ + if (GifExtensionLength != 4) { + return GIF_ERROR; + } + + GCB->DisposalMode = (GifExtension[0] >> 2) & 0x07; + GCB->UserInputFlag = (GifExtension[0] & 0x02) != 0; + GCB->DelayTime = UNSIGNED_LITTLE_ENDIAN(GifExtension[1], GifExtension[2]); + if (GifExtension[0] & 0x01) + GCB->TransparentColor = (int)GifExtension[3]; + else + GCB->TransparentColor = NO_TRANSPARENT_COLOR; + + return GIF_OK; +} + +/****************************************************************************** + Extract the Graphics Control Block for a saved image, if it exists. +******************************************************************************/ + +int DGifSavedExtensionToGCB(GifFileType *GifFile, + int ImageIndex, GraphicsControlBlock *GCB) +{ + int i; + + if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1) + return GIF_ERROR; + + GCB->DisposalMode = DISPOSAL_UNSPECIFIED; + GCB->UserInputFlag = false; + GCB->DelayTime = 0; + GCB->TransparentColor = NO_TRANSPARENT_COLOR; + + for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount; i++) { + ExtensionBlock *ep = &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i]; + if (ep->Function == GRAPHICS_EXT_FUNC_CODE) + return DGifExtensionToGCB(ep->ByteCount, ep->Bytes, GCB); + } + + return GIF_ERROR; +} + +/****************************************************************************** + This routine should be called last, to close the GIF file. +******************************************************************************/ +int +DGifCloseFile(GifFileType *GifFile) +{ + GifFilePrivateType *Private; + + if (GifFile == NULL || GifFile->Private == NULL) + return GIF_ERROR; + + if (GifFile->Image.ColorMap) { + GifFreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + } + + if (GifFile->SColorMap) { + GifFreeMapObject(GifFile->SColorMap); + GifFile->SColorMap = NULL; + } + + if (GifFile->SavedImages) { + GifFreeSavedImages(GifFile); + GifFile->SavedImages = NULL; + } + + GifFreeExtensions(&GifFile->ExtensionBlockCount, &GifFile->ExtensionBlocks); + + Private = (GifFilePrivateType *) GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (Private->File && (fclose(Private->File) != 0)) { + GifFile->Error = D_GIF_ERR_CLOSE_FAILED; + return GIF_ERROR; + } + + free((char *)GifFile->Private); + + /* + * Without the #ifndef, we get spurious warnings because Coverity mistakenly + * thinks the GIF structure is freed on an error return. + */ +#ifndef __COVERITY__ + free(GifFile); +#endif /* __COVERITY__ */ + + return GIF_OK; +} + +/****************************************************************************** + Get 2 bytes (word) from the given file: +******************************************************************************/ +static int +DGifGetWord(GifFileType *GifFile, GifWord *Word) +{ + unsigned char c[2]; + + if (READ(GifFile, c, 2) != 2) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + + *Word = (GifWord)UNSIGNED_LITTLE_ENDIAN(c[0], c[1]); + return GIF_OK; +} + +/****************************************************************************** + Get the image code in compressed form. This routine can be called if the + information needed to be piped out as is. Obviously this is much faster + than decoding and encoding again. This routine should be followed by calls + to DGifGetCodeNext, until NULL block is returned. + The block should NOT be freed by the user (not dynamically allocated). +******************************************************************************/ +int +DGifGetCode(GifFileType *GifFile, int *CodeSize, GifByteType **CodeBlock) +{ + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + *CodeSize = Private->BitsPerPixel; + + return DGifGetCodeNext(GifFile, CodeBlock); +} + +/****************************************************************************** + Continue to get the image code in compressed form. This routine should be + called until NULL block is returned. + The block should NOT be freed by the user (not dynamically allocated). +******************************************************************************/ +int +DGifGetCodeNext(GifFileType *GifFile, GifByteType **CodeBlock) +{ + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + /* coverity[tainted_data_argument] */ + if (READ(GifFile, &Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + + /* coverity[lower_bounds] */ + if (Buf > 0) { + *CodeBlock = Private->Buf; /* Use private unused buffer. */ + (*CodeBlock)[0] = Buf; /* Pascal strings notation (pos. 0 is len.). */ + /* coverity[tainted_data] */ + if (READ(GifFile, &((*CodeBlock)[1]), Buf) != Buf) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + } else { + *CodeBlock = NULL; + Private->Buf[0] = 0; /* Make sure the buffer is empty! */ + Private->PixelCount = 0; /* And local info. indicate image read. */ + } + + return GIF_OK; +} + +/****************************************************************************** + Setup the LZ decompression for this image: +******************************************************************************/ +static int +DGifSetupDecompress(GifFileType *GifFile) +{ + int i, BitsPerPixel; + GifByteType CodeSize; + GifPrefixType *Prefix; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + READ(GifFile, &CodeSize, 1); /* Read Code size from file. */ + BitsPerPixel = CodeSize; + + Private->Buf[0] = 0; /* Input Buffer empty. */ + Private->BitsPerPixel = BitsPerPixel; + Private->ClearCode = (1 << BitsPerPixel); + Private->EOFCode = Private->ClearCode + 1; + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = BitsPerPixel + 1; /* Number of bits per code. */ + Private->MaxCode1 = 1 << Private->RunningBits; /* Max. code + 1. */ + Private->StackPtr = 0; /* No pixels on the pixel stack. */ + Private->LastCode = NO_SUCH_CODE; + Private->CrntShiftState = 0; /* No information in CrntShiftDWord. */ + Private->CrntShiftDWord = 0; + + Prefix = Private->Prefix; + for (i = 0; i <= LZ_MAX_CODE; i++) + Prefix[i] = NO_SUCH_CODE; + + return GIF_OK; +} + +/****************************************************************************** + The LZ decompression routine: + This version decompress the given GIF file into Line of length LineLen. + This routine can be called few times (one per scan line, for example), in + order the complete the whole image. +******************************************************************************/ +static int +DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line, int LineLen) +{ + int i = 0; + int j, CrntCode, EOFCode, ClearCode, CrntPrefix, LastCode, StackPtr; + GifByteType *Stack, *Suffix; + GifPrefixType *Prefix; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + StackPtr = Private->StackPtr; + Prefix = Private->Prefix; + Suffix = Private->Suffix; + Stack = Private->Stack; + EOFCode = Private->EOFCode; + ClearCode = Private->ClearCode; + LastCode = Private->LastCode; + + if (StackPtr > LZ_MAX_CODE) { + return GIF_ERROR; + } + + if (StackPtr != 0) { + /* Let pop the stack off before continueing to read the GIF file: */ + while (StackPtr != 0 && i < LineLen) + Line[i++] = Stack[--StackPtr]; + } + + while (i < LineLen) { /* Decode LineLen items. */ + if (DGifDecompressInput(GifFile, &CrntCode) == GIF_ERROR) + return GIF_ERROR; + + if (CrntCode == EOFCode) { + /* Note however that usually we will not be here as we will stop + * decoding as soon as we got all the pixel, or EOF code will + * not be read at all, and DGifGetLine/Pixel clean everything. */ + GifFile->Error = D_GIF_ERR_EOF_TOO_SOON; + return GIF_ERROR; + } else if (CrntCode == ClearCode) { + /* We need to start over again: */ + for (j = 0; j <= LZ_MAX_CODE; j++) + Prefix[j] = NO_SUCH_CODE; + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = Private->BitsPerPixel + 1; + Private->MaxCode1 = 1 << Private->RunningBits; + LastCode = Private->LastCode = NO_SUCH_CODE; + } else { + /* Its regular code - if in pixel range simply add it to output + * stream, otherwise trace to codes linked list until the prefix + * is in pixel range: */ + if (CrntCode < ClearCode) { + /* This is simple - its pixel scalar, so add it to output: */ + Line[i++] = CrntCode; + } else { + /* Its a code to needed to be traced: trace the linked list + * until the prefix is a pixel, while pushing the suffix + * pixels on our stack. If we done, pop the stack in reverse + * (thats what stack is good for!) order to output. */ + if (Prefix[CrntCode] == NO_SUCH_CODE) { + /* Only allowed if CrntCode is exactly the running code: + * In that case CrntCode = XXXCode, CrntCode or the + * prefix code is last code and the suffix char is + * exactly the prefix of last code! */ + if (CrntCode == Private->RunningCode - 2) { + CrntPrefix = LastCode; + Suffix[Private->RunningCode - 2] = + Stack[StackPtr++] = DGifGetPrefixChar(Prefix, + LastCode, + ClearCode); + } else { + GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + } else + CrntPrefix = CrntCode; + + /* Now (if image is O.K.) we should not get a NO_SUCH_CODE + * during the trace. As we might loop forever, in case of + * defective image, we use StackPtr as loop counter and stop + * before overflowing Stack[]. */ + while (StackPtr < LZ_MAX_CODE && + CrntPrefix > ClearCode && CrntPrefix <= LZ_MAX_CODE) { + Stack[StackPtr++] = Suffix[CrntPrefix]; + CrntPrefix = Prefix[CrntPrefix]; + } + if (StackPtr >= LZ_MAX_CODE || CrntPrefix > LZ_MAX_CODE) { + GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + /* Push the last character on stack: */ + Stack[StackPtr++] = CrntPrefix; + + /* Now lets pop all the stack into output: */ + while (StackPtr != 0 && i < LineLen) + Line[i++] = Stack[--StackPtr]; + } + if (LastCode != NO_SUCH_CODE && Prefix[Private->RunningCode - 2] == NO_SUCH_CODE) { + Prefix[Private->RunningCode - 2] = LastCode; + + if (CrntCode == Private->RunningCode - 2) { + /* Only allowed if CrntCode is exactly the running code: + * In that case CrntCode = XXXCode, CrntCode or the + * prefix code is last code and the suffix char is + * exactly the prefix of last code! */ + Suffix[Private->RunningCode - 2] = + DGifGetPrefixChar(Prefix, LastCode, ClearCode); + } else { + Suffix[Private->RunningCode - 2] = + DGifGetPrefixChar(Prefix, CrntCode, ClearCode); + } + } + LastCode = CrntCode; + } + } + + Private->LastCode = LastCode; + Private->StackPtr = StackPtr; + + return GIF_OK; +} + +/****************************************************************************** + Routine to trace the Prefixes linked list until we get a prefix which is + not code, but a pixel value (less than ClearCode). Returns that pixel value. + If image is defective, we might loop here forever, so we limit the loops to + the maximum possible if image O.k. - LZ_MAX_CODE times. +******************************************************************************/ +static int +DGifGetPrefixChar(GifPrefixType *Prefix, int Code, int ClearCode) +{ + int i = 0; + + while (Code > ClearCode && i++ <= LZ_MAX_CODE) { + if (Code > LZ_MAX_CODE) { + return NO_SUCH_CODE; + } + Code = Prefix[Code]; + } + return Code; +} + +/****************************************************************************** + Interface for accessing the LZ codes directly. Set Code to the real code + (12bits), or to -1 if EOF code is returned. +******************************************************************************/ +int +DGifGetLZCodes(GifFileType *GifFile, int *Code) +{ + GifByteType *CodeBlock; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + GifFile->Error = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (DGifDecompressInput(GifFile, Code) == GIF_ERROR) + return GIF_ERROR; + + if (*Code == Private->EOFCode) { + /* Skip rest of codes (hopefully only NULL terminating block): */ + do { + if (DGifGetCodeNext(GifFile, &CodeBlock) == GIF_ERROR) + return GIF_ERROR; + } while (CodeBlock != NULL) ; + + *Code = -1; + } else if (*Code == Private->ClearCode) { + /* We need to start over again: */ + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = Private->BitsPerPixel + 1; + Private->MaxCode1 = 1 << Private->RunningBits; + } + + return GIF_OK; +} + +/****************************************************************************** + The LZ decompression input routine: + This routine is responsable for the decompression of the bit stream from + 8 bits (bytes) packets, into the real codes. + Returns GIF_OK if read successfully. +******************************************************************************/ +static int +DGifDecompressInput(GifFileType *GifFile, int *Code) +{ + static const unsigned short CodeMasks[] = { + 0x0000, 0x0001, 0x0003, 0x0007, + 0x000f, 0x001f, 0x003f, 0x007f, + 0x00ff, 0x01ff, 0x03ff, 0x07ff, + 0x0fff + }; + + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + GifByteType NextByte; + + /* The image can't contain more than LZ_BITS per code. */ + if (Private->RunningBits > LZ_BITS) { + GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + + while (Private->CrntShiftState < Private->RunningBits) { + /* Needs to get more bytes from input stream for next code: */ + if (DGifBufferedInput(GifFile, Private->Buf, &NextByte) == GIF_ERROR) { + return GIF_ERROR; + } + Private->CrntShiftDWord |= + ((unsigned long)NextByte) << Private->CrntShiftState; + Private->CrntShiftState += 8; + } + *Code = Private->CrntShiftDWord & CodeMasks[Private->RunningBits]; + + Private->CrntShiftDWord >>= Private->RunningBits; + Private->CrntShiftState -= Private->RunningBits; + + /* If code cannot fit into RunningBits bits, must raise its size. Note + * however that codes above 4095 are used for special signaling. + * If we're using LZ_BITS bits already and we're at the max code, just + * keep using the table as it is, don't increment Private->RunningCode. + */ + if (Private->RunningCode < LZ_MAX_CODE + 2 && + ++Private->RunningCode > Private->MaxCode1 && + Private->RunningBits < LZ_BITS) { + Private->MaxCode1 <<= 1; + Private->RunningBits++; + } + return GIF_OK; +} + +/****************************************************************************** + This routines read one GIF data block at a time and buffers it internally + so that the decompression routine could access it. + The routine returns the next byte from its internal buffer (or read next + block in if buffer empty) and returns GIF_OK if succesful. +******************************************************************************/ +static int +DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf, GifByteType *NextByte) +{ + if (Buf[0] == 0) { + /* Needs to read the next buffer - this one is empty: */ + if (READ(GifFile, Buf, 1) != 1) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + /* There shouldn't be any empty data blocks here as the LZW spec + * says the LZW termination code should come first. Therefore we + * shouldn't be inside this routine at that point. + */ + if (Buf[0] == 0) { + GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + /* There shouldn't be any empty data blocks here as the LZW spec + * says the LZW termination code should come first. Therefore we + * shouldn't be inside this routine at that point. + */ + if (Buf[0] == 0) { + GifFile->Error = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + if (READ(GifFile, &Buf[1], Buf[0]) != Buf[0]) { + GifFile->Error = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + *NextByte = Buf[1]; + Buf[1] = 2; /* We use now the second place as last char read! */ + Buf[0]--; + } else { + *NextByte = Buf[Buf[1]++]; + Buf[0]--; + } + + return GIF_OK; +} + +/****************************************************************************** + This routine reads an entire GIF into core, hanging all its state info off + the GifFileType pointer. Call DGifOpenFileName() or DGifOpenFileHandle() + first to initialize I/O. Its inverse is EGifSpew(). + +int +DGifSlurp(GifFileType *GifFile) +{ + size_t ImageSize; + GifRecordType RecordType; + SavedImage *sp; + GifByteType *ExtData; + int ExtFunction; + + GifFile->ExtensionBlocks = NULL; + GifFile->ExtensionBlockCount = 0; + + do { + if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) + return (GIF_ERROR); + switch (RecordType) { + case IMAGE_DESC_RECORD_TYPE: + if (DGifGetImageDesc(GifFile) == GIF_ERROR) + return (GIF_ERROR); + + sp = &GifFile->SavedImages[GifFile->ImageCount - 1]; + if (GifFile->ExtensionBlocks) { + sp->ExtensionBlocks = GifFile->ExtensionBlocks; + sp->ExtensionBlockCount = GifFile->ExtensionBlockCount; + + GifFile->ExtensionBlocks = NULL; + GifFile->ExtensionBlockCount = 0; + } + // Allocate memory for the image + if (sp->ImageDesc.Width < 0 && sp->ImageDesc.Height < 0 && + sp->ImageDesc.Width > (INT_MAX / sp->ImageDesc.Height)) { + return GIF_ERROR; + } + ImageSize = sp->ImageDesc.Width * sp->ImageDesc.Height; + + if (ImageSize > (SIZE_MAX / sizeof(GifPixelType))) { + return GIF_ERROR; + } + sp->RasterBits = (unsigned char *)malloc(ImageSize * + sizeof(GifPixelType)); + + if (sp->RasterBits == NULL) { + return GIF_ERROR; + } + + if (sp->ImageDesc.Interlace) + { + int i, j; + // The way an interlaced image should be read - * offsets and jumps... + + int InterlacedOffset[] = { 0, 4, 2, 1 }; + int InterlacedJumps[] = { 8, 8, 4, 2 }; + // Need to perform 4 passes on the image + for (i = 0; i < 4; i++) + for (j = InterlacedOffset[i]; + j < sp->ImageDesc.Height; + j += InterlacedJumps[i]) { + if (DGifGetLine(GifFile, + sp->RasterBits+j*sp->ImageDesc.Width, + sp->ImageDesc.Width) == GIF_ERROR) + return GIF_ERROR; + } + } + else + { + if (DGifGetLine(GifFile,sp->RasterBits,ImageSize)==GIF_ERROR) + return (GIF_ERROR); + } + break; + + case EXTENSION_RECORD_TYPE: + if (DGifGetExtension(GifFile,&ExtFunction,&ExtData) == GIF_ERROR) + return (GIF_ERROR); + // Create an extension block with our data + if (GifAddExtensionBlock(&GifFile->ExtensionBlockCount, + &GifFile->ExtensionBlocks, + ExtFunction, ExtData[0], &ExtData[1]) + == GIF_ERROR) + return (GIF_ERROR); + while (ExtData != NULL) { + if (DGifGetExtensionNext(GifFile, &ExtData, &ExtFunction) == GIF_ERROR) + return (GIF_ERROR); + // Continue the extension block / + if (ExtData != NULL) + if (GifAddExtensionBlock(&GifFile->ExtensionBlockCount, + &GifFile->ExtensionBlocks, + CONTINUE_EXT_FUNC_CODE, + ExtData[0], &ExtData[1]) == GIF_ERROR) + return (GIF_ERROR); + } + break; + + case TERMINATE_RECORD_TYPE: + break; + + default: // Should be trapped by DGifGetRecordType + break; + } + } while (RecordType != TERMINATE_RECORD_TYPE); + + return (GIF_OK); +} + + end */ diff --git a/TMessagesProj/jni/giflib/gif_hash.c b/TMessagesProj/jni/giflib/gif_hash.c new file mode 100755 index 00000000..61a4d139 --- /dev/null +++ b/TMessagesProj/jni/giflib/gif_hash.c @@ -0,0 +1,132 @@ +/***************************************************************************** + +gif_hash.c -- module to support the following operations: + +1. InitHashTable - initialize hash table. +2. ClearHashTable - clear the hash table to an empty state. +2. InsertHashTable - insert one item into data structure. +3. ExistsHashTable - test if item exists in data structure. + +This module is used to hash the GIF codes during encoding. + +*****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "gif_lib.h" +#include "gif_hash.h" +#include "gif_lib_private.h" + +/* #define DEBUG_HIT_RATE Debug number of misses per hash Insert/Exists. */ + +#ifdef DEBUG_HIT_RATE +static long NumberOfTests = 0, + NumberOfMisses = 0; +#endif /* DEBUG_HIT_RATE */ + +static int KeyItem(uint32_t Item); + +/****************************************************************************** + Initialize HashTable - allocate the memory needed and clear it. * +******************************************************************************/ +GifHashTableType *_InitHashTable(void) +{ + GifHashTableType *HashTable; + + if ((HashTable = (GifHashTableType *) malloc(sizeof(GifHashTableType))) + == NULL) + return NULL; + + _ClearHashTable(HashTable); + + return HashTable; +} + +/****************************************************************************** + Routine to clear the HashTable to an empty state. * + This part is a little machine depended. Use the commented part otherwise. * +******************************************************************************/ +void _ClearHashTable(GifHashTableType *HashTable) +{ + memset(HashTable -> HTable, 0xFF, HT_SIZE * sizeof(uint32_t)); +} + +/****************************************************************************** + Routine to insert a new Item into the HashTable. The data is assumed to be * + new one. * +******************************************************************************/ +void _InsertHashTable(GifHashTableType *HashTable, uint32_t Key, int Code) +{ + int HKey = KeyItem(Key); + uint32_t *HTable = HashTable -> HTable; + +#ifdef DEBUG_HIT_RATE + NumberOfTests++; + NumberOfMisses++; +#endif /* DEBUG_HIT_RATE */ + + while (HT_GET_KEY(HTable[HKey]) != 0xFFFFFL) { +#ifdef DEBUG_HIT_RATE + NumberOfMisses++; +#endif /* DEBUG_HIT_RATE */ + HKey = (HKey + 1) & HT_KEY_MASK; + } + HTable[HKey] = HT_PUT_KEY(Key) | HT_PUT_CODE(Code); +} + +/****************************************************************************** + Routine to test if given Key exists in HashTable and if so returns its code * + Returns the Code if key was found, -1 if not. * +******************************************************************************/ +int _ExistsHashTable(GifHashTableType *HashTable, uint32_t Key) +{ + int HKey = KeyItem(Key); + uint32_t *HTable = HashTable -> HTable, HTKey; + +#ifdef DEBUG_HIT_RATE + NumberOfTests++; + NumberOfMisses++; +#endif /* DEBUG_HIT_RATE */ + + while ((HTKey = HT_GET_KEY(HTable[HKey])) != 0xFFFFFL) { +#ifdef DEBUG_HIT_RATE + NumberOfMisses++; +#endif /* DEBUG_HIT_RATE */ + if (Key == HTKey) return HT_GET_CODE(HTable[HKey]); + HKey = (HKey + 1) & HT_KEY_MASK; + } + + return -1; +} + +/****************************************************************************** + Routine to generate an HKey for the hashtable out of the given unique key. * + The given Key is assumed to be 20 bits as follows: lower 8 bits are the * + new postfix character, while the upper 12 bits are the prefix code. * + Because the average hit ratio is only 2 (2 hash references per entry), * + evaluating more complex keys (such as twin prime keys) does not worth it! * +******************************************************************************/ +static int KeyItem(uint32_t Item) +{ + return ((Item >> 12) ^ Item) & HT_KEY_MASK; +} + +#ifdef DEBUG_HIT_RATE +/****************************************************************************** + Debugging routine to print the hit ratio - number of times the hash table * + was tested per operation. This routine was used to test the KeyItem routine * +******************************************************************************/ +void HashTablePrintHitRatio(void) +{ + printf("Hash Table Hit Ratio is %ld/%ld = %ld%%.\n", + NumberOfMisses, NumberOfTests, + NumberOfMisses * 100 / NumberOfTests); +} +#endif /* DEBUG_HIT_RATE */ + +/* end */ diff --git a/TMessagesProj/jni/giflib/gif_hash.h b/TMessagesProj/jni/giflib/gif_hash.h new file mode 100755 index 00000000..ac20a43c --- /dev/null +++ b/TMessagesProj/jni/giflib/gif_hash.h @@ -0,0 +1,39 @@ +/****************************************************************************** + +gif_hash.h - magfic constants and declarations for GIF LZW + +******************************************************************************/ + +#ifndef _GIF_HASH_H_ +#define _GIF_HASH_H_ + +#include +#include + +#define HT_SIZE 8192 /* 12bits = 4096 or twice as big! */ +#define HT_KEY_MASK 0x1FFF /* 13bits keys */ +#define HT_KEY_NUM_BITS 13 /* 13bits keys */ +#define HT_MAX_KEY 8191 /* 13bits - 1, maximal code possible */ +#define HT_MAX_CODE 4095 /* Biggest code possible in 12 bits. */ + +/* The 32 bits of the long are divided into two parts for the key & code: */ +/* 1. The code is 12 bits as our compression algorithm is limited to 12bits */ +/* 2. The key is 12 bits Prefix code + 8 bit new char or 20 bits. */ +/* The key is the upper 20 bits. The code is the lower 12. */ +#define HT_GET_KEY(l) (l >> 12) +#define HT_GET_CODE(l) (l & 0x0FFF) +#define HT_PUT_KEY(l) (l << 12) +#define HT_PUT_CODE(l) (l & 0x0FFF) + +typedef struct GifHashTableType { + uint32_t HTable[HT_SIZE]; +} GifHashTableType; + +GifHashTableType *_InitHashTable(void); +void _ClearHashTable(GifHashTableType *HashTable); +void _InsertHashTable(GifHashTableType *HashTable, uint32_t Key, int Code); +int _ExistsHashTable(GifHashTableType *HashTable, uint32_t Key); + +#endif /* _GIF_HASH_H_ */ + +/* end */ diff --git a/TMessagesProj/jni/giflib/gif_lib.h b/TMessagesProj/jni/giflib/gif_lib.h new file mode 100755 index 00000000..b7aa9301 --- /dev/null +++ b/TMessagesProj/jni/giflib/gif_lib.h @@ -0,0 +1,307 @@ +/****************************************************************************** + +gif_lib.h - service library for decoding and encoding GIF images + +*****************************************************************************/ + +#ifndef _GIF_LIB_H_ +#define _GIF_LIB_H_ 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GIFLIB_MAJOR 5 +#define GIFLIB_MINOR 0 +#define GIFLIB_RELEASE 5 + +#define GIF_ERROR 0 +#define GIF_OK 1 + +#include +#include + +#define GIF_STAMP "GIFVER" /* First chars in file - GIF stamp. */ +#define GIF_STAMP_LEN sizeof(GIF_STAMP) - 1 +#define GIF_VERSION_POS 3 /* Version first character in stamp. */ +#define GIF87_STAMP "GIF87a" /* First chars in file - GIF stamp. */ +#define GIF89_STAMP "GIF89a" /* First chars in file - GIF stamp. */ + +typedef unsigned char GifPixelType; +typedef unsigned char *GifRowType; +typedef unsigned char GifByteType; +typedef unsigned int GifPrefixType; +typedef int GifWord; + +typedef struct GifColorType { + GifByteType Red, Green, Blue; +} GifColorType; + +typedef struct ColorMapObject { + int ColorCount; + int BitsPerPixel; + bool SortFlag; + GifColorType *Colors; /* on malloc(3) heap */ +} ColorMapObject; + +typedef struct GifImageDesc { + GifWord Left, Top, Width, Height; /* Current image dimensions. */ + bool Interlace; /* Sequential/Interlaced lines. */ + ColorMapObject *ColorMap; /* The local color map */ +} GifImageDesc; + +typedef struct ExtensionBlock { + int ByteCount; + GifByteType *Bytes; /* on malloc(3) heap */ + int Function; /* The block function code */ +#define CONTINUE_EXT_FUNC_CODE 0x00 /* continuation subblock */ +#define COMMENT_EXT_FUNC_CODE 0xfe /* comment */ +#define GRAPHICS_EXT_FUNC_CODE 0xf9 /* graphics control (GIF89) */ +#define PLAINTEXT_EXT_FUNC_CODE 0x01 /* plaintext */ +#define APPLICATION_EXT_FUNC_CODE 0xff /* application block */ +} ExtensionBlock; + +typedef struct SavedImage { + GifImageDesc ImageDesc; + GifByteType *RasterBits; /* on malloc(3) heap */ + int ExtensionBlockCount; /* Count of extensions before image */ + ExtensionBlock *ExtensionBlocks; /* Extensions before image */ +} SavedImage; + +typedef struct GifFileType { + GifWord SWidth, SHeight; /* Size of virtual canvas */ + GifWord SColorResolution; /* How many colors can we generate? */ + GifWord SBackGroundColor; /* Background color for virtual canvas */ + GifByteType AspectByte; /* Used to compute pixel aspect ratio */ + ColorMapObject *SColorMap; /* Global colormap, NULL if nonexistent. */ + int ImageCount; /* Number of current image (both APIs) */ + GifImageDesc Image; /* Current image (low-level API) */ + SavedImage *SavedImages; /* Image sequence (high-level API) */ + int ExtensionBlockCount; /* Count extensions past last image */ + ExtensionBlock *ExtensionBlocks; /* Extensions past last image */ + int Error; /* Last error condition reported */ + void *UserData; /* hook to attach user data (TVT) */ + void *Private; /* Don't mess with this! */ +} GifFileType; + +#define GIF_ASPECT_RATIO(n) ((n)+15.0/64.0) + +typedef enum { + UNDEFINED_RECORD_TYPE, + SCREEN_DESC_RECORD_TYPE, + IMAGE_DESC_RECORD_TYPE, /* Begin with ',' */ + EXTENSION_RECORD_TYPE, /* Begin with '!' */ + TERMINATE_RECORD_TYPE /* Begin with ';' */ +} GifRecordType; + +/* func type to read gif data from arbitrary sources (TVT) */ +typedef int (*InputFunc) (GifFileType *, GifByteType *, int); + +/* func type to write gif data to arbitrary targets. + * Returns count of bytes written. (MRB) + */ +typedef int (*OutputFunc) (GifFileType *, const GifByteType *, int); + +/****************************************************************************** + GIF89 structures +******************************************************************************/ + +typedef struct GraphicsControlBlock { + int DisposalMode; +#define DISPOSAL_UNSPECIFIED 0 /* No disposal specified. */ +#define DISPOSE_DO_NOT 1 /* Leave image in place */ +#define DISPOSE_BACKGROUND 2 /* Set area too background color */ +#define DISPOSE_PREVIOUS 3 /* Restore to previous content */ + bool UserInputFlag; /* User confirmation required before disposal */ + int DelayTime; /* pre-display delay in 0.01sec units */ + int TransparentColor; /* Palette index for transparency, -1 if none */ +#define NO_TRANSPARENT_COLOR -1 +} GraphicsControlBlock; + +/****************************************************************************** + GIF encoding routines +******************************************************************************/ + +/* Main entry points */ +GifFileType *EGifOpenFileName(const char *GifFileName, + const bool GifTestExistence, int *Error); +GifFileType *EGifOpenFileHandle(const int GifFileHandle, int *Error); +GifFileType *EGifOpen(void *userPtr, OutputFunc writeFunc, int *Error); +int EGifSpew(GifFileType * GifFile); +char *EGifGetGifVersion(GifFileType *GifFile); /* new in 5.x */ +int EGifCloseFile(GifFileType * GifFile); + +#define E_GIF_ERR_OPEN_FAILED 1 /* And EGif possible errors. */ +#define E_GIF_ERR_WRITE_FAILED 2 +#define E_GIF_ERR_HAS_SCRN_DSCR 3 +#define E_GIF_ERR_HAS_IMAG_DSCR 4 +#define E_GIF_ERR_NO_COLOR_MAP 5 +#define E_GIF_ERR_DATA_TOO_BIG 6 +#define E_GIF_ERR_NOT_ENOUGH_MEM 7 +#define E_GIF_ERR_DISK_IS_FULL 8 +#define E_GIF_ERR_CLOSE_FAILED 9 +#define E_GIF_ERR_NOT_WRITEABLE 10 + +/* These are legacy. You probably do not want to call them directly */ +int EGifPutScreenDesc(GifFileType *GifFile, + const int GifWidth, const int GifHeight, + const int GifColorRes, + const int GifBackGround, + const ColorMapObject *GifColorMap); +int EGifPutImageDesc(GifFileType *GifFile, + const int GifLeft, const int GifTop, + const int GifWidth, const int GifHeight, + const bool GifInterlace, + const ColorMapObject *GifColorMap); +void EGifSetGifVersion(GifFileType *GifFile, const bool gif89); +int EGifPutLine(GifFileType *GifFile, GifPixelType *GifLine, + int GifLineLen); +int EGifPutPixel(GifFileType *GifFile, const GifPixelType GifPixel); +int EGifPutComment(GifFileType *GifFile, const char *GifComment); +int EGifPutExtensionLeader(GifFileType *GifFile, const int GifExtCode); +int EGifPutExtensionBlock(GifFileType *GifFile, + const int GifExtLen, const void *GifExtension); +int EGifPutExtensionTrailer(GifFileType *GifFile); +int EGifPutExtension(GifFileType *GifFile, const int GifExtCode, + const int GifExtLen, + const void *GifExtension); +int EGifPutCode(GifFileType *GifFile, int GifCodeSize, + const GifByteType *GifCodeBlock); +int EGifPutCodeNext(GifFileType *GifFile, + const GifByteType *GifCodeBlock); + +/****************************************************************************** + GIF decoding routines +******************************************************************************/ + +/* Main entry points */ +GifFileType *DGifOpenFileName(const char *GifFileName, int *Error); +GifFileType *DGifOpenFileHandle(int GifFileHandle, int *Error); +int DGifSlurp(GifFileType * GifFile); +GifFileType *DGifOpen(void *userPtr, InputFunc readFunc, int *Error); /* new one (TVT) */ +int DGifCloseFile(GifFileType * GifFile); + +#define D_GIF_ERR_OPEN_FAILED 101 /* And DGif possible errors. */ +#define D_GIF_ERR_READ_FAILED 102 +#define D_GIF_ERR_NOT_GIF_FILE 103 +#define D_GIF_ERR_NO_SCRN_DSCR 104 +#define D_GIF_ERR_NO_IMAG_DSCR 105 +#define D_GIF_ERR_NO_COLOR_MAP 106 +#define D_GIF_ERR_WRONG_RECORD 107 +#define D_GIF_ERR_DATA_TOO_BIG 108 +#define D_GIF_ERR_NOT_ENOUGH_MEM 109 +#define D_GIF_ERR_CLOSE_FAILED 110 +#define D_GIF_ERR_NOT_READABLE 111 +#define D_GIF_ERR_IMAGE_DEFECT 112 +#define D_GIF_ERR_EOF_TOO_SOON 113 + +/* These are legacy. You probably do not want to call them directly */ +int DGifGetScreenDesc(GifFileType *GifFile); +int DGifGetRecordType(GifFileType *GifFile, GifRecordType *GifType); +int DGifGetImageDesc(GifFileType *GifFile, bool changeImageCount); +int DGifGetLine(GifFileType *GifFile, GifPixelType *GifLine, int GifLineLen); +int DGifGetPixel(GifFileType *GifFile, GifPixelType GifPixel); +int DGifGetComment(GifFileType *GifFile, char *GifComment); +int DGifGetExtension(GifFileType *GifFile, int *GifExtCode, + GifByteType **GifExtension); +int DGifGetExtensionNext(GifFileType *GifFile, GifByteType **GifExtension,int* ExtCode); +int DGifGetCode(GifFileType *GifFile, int *GifCodeSize, + GifByteType **GifCodeBlock); +int DGifGetCodeNext(GifFileType *GifFile, GifByteType **GifCodeBlock); +int DGifGetLZCodes(GifFileType *GifFile, int *GifCode); + + +/****************************************************************************** + Color table quantization (deprecated) +******************************************************************************/ +int GifQuantizeBuffer(unsigned int Width, unsigned int Height, + int *ColorMapSize, GifByteType * RedInput, + GifByteType * GreenInput, GifByteType * BlueInput, + GifByteType * OutputBuffer, + GifColorType * OutputColorMap); + +/****************************************************************************** + Error handling and reporting. +******************************************************************************/ +extern char *GifErrorString(int ErrorCode); /* new in 2012 - ESR */ + +/***************************************************************************** + Everything below this point is new after version 1.2, supporting `slurp + mode' for doing I/O in two big belts with all the image-bashing in core. +******************************************************************************/ + +/****************************************************************************** + Color map handling from gif_alloc.c +******************************************************************************/ + +extern ColorMapObject *GifMakeMapObject(int ColorCount, + const GifColorType *ColorMap); +extern void GifFreeMapObject(ColorMapObject *Object); +extern ColorMapObject *GifUnionColorMap(const ColorMapObject *ColorIn1, + const ColorMapObject *ColorIn2, + GifPixelType ColorTransIn2[]); +extern int GifBitSize(int n); + +/****************************************************************************** + Support for the in-core structures allocation (slurp mode). +******************************************************************************/ + +extern void GifApplyTranslation(SavedImage *Image, GifPixelType Translation[]); +extern int GifAddExtensionBlock(int *ExtensionBlock_Count, + ExtensionBlock **ExtensionBlocks, + int Function, + unsigned int Len, unsigned char ExtData[]); +extern void GifFreeExtensions(int *ExtensionBlock_Count, + ExtensionBlock **ExtensionBlocks); +extern SavedImage *GifMakeSavedImage(GifFileType *GifFile, + const SavedImage *CopyFrom); +extern void GifFreeSavedImages(GifFileType *GifFile); + +/****************************************************************************** + 5.x functions for GIF89 graphics control blocks +******************************************************************************/ + +int DGifExtensionToGCB(const size_t GifExtensionLength, + const GifByteType *GifExtension, + GraphicsControlBlock *GCB); +size_t EGifGCBToExtension(const GraphicsControlBlock *GCB, + GifByteType *GifExtension); + +int DGifSavedExtensionToGCB(GifFileType *GifFile, + int ImageIndex, + GraphicsControlBlock *GCB); +int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB, + GifFileType *GifFile, + int ImageIndex); + +/****************************************************************************** + The library's internal utility font +******************************************************************************/ + +#define GIF_FONT_WIDTH 8 +#define GIF_FONT_HEIGHT 8 +extern const unsigned char GifAsciiTable8x8[][GIF_FONT_WIDTH]; + +extern void GifDrawText8x8(SavedImage *Image, + const int x, const int y, + const char *legend, const int color); + +extern void GifDrawBox(SavedImage *Image, + const int x, const int y, + const int w, const int d, const int color); + +extern void GifDrawRectangle(SavedImage *Image, + const int x, const int y, + const int w, const int d, const int color); + +extern void GifDrawBoxedText8x8(SavedImage *Image, + const int x, const int y, + const char *legend, + const int border, const int bg, const int fg); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* _GIF_LIB_H */ + +/* end */ diff --git a/TMessagesProj/jni/giflib/gif_lib_private.h b/TMessagesProj/jni/giflib/gif_lib_private.h new file mode 100755 index 00000000..adaf5571 --- /dev/null +++ b/TMessagesProj/jni/giflib/gif_lib_private.h @@ -0,0 +1,59 @@ +/**************************************************************************** + +gif_lib_private.h - internal giflib routines and structures + +****************************************************************************/ + +#ifndef _GIF_LIB_PRIVATE_H +#define _GIF_LIB_PRIVATE_H + +#include "gif_lib.h" +#include "gif_hash.h" + +#define EXTENSION_INTRODUCER 0x21 +#define DESCRIPTOR_INTRODUCER 0x2c +#define TERMINATOR_INTRODUCER 0x3b + +#define LZ_MAX_CODE 4095 /* Biggest code possible in 12 bits. */ +#define LZ_BITS 12 + +#define FLUSH_OUTPUT 4096 /* Impossible code, to signal flush. */ +#define FIRST_CODE 4097 /* Impossible code, to signal first. */ +#define NO_SUCH_CODE 4098 /* Impossible code, to signal empty. */ + +#define FILE_STATE_WRITE 0x01 +#define FILE_STATE_SCREEN 0x02 +#define FILE_STATE_IMAGE 0x04 +#define FILE_STATE_READ 0x08 + +#define IS_READABLE(Private) (Private->FileState & FILE_STATE_READ) +#define IS_WRITEABLE(Private) (Private->FileState & FILE_STATE_WRITE) + +typedef struct GifFilePrivateType { + GifWord FileState, FileHandle, /* Where all this data goes to! */ + BitsPerPixel, /* Bits per pixel (Codes uses at least this + 1). */ + ClearCode, /* The CLEAR LZ code. */ + EOFCode, /* The EOF LZ code. */ + RunningCode, /* The next code algorithm can generate. */ + RunningBits, /* The number of bits required to represent RunningCode. */ + MaxCode1, /* 1 bigger than max. possible code, in RunningBits bits. */ + LastCode, /* The code before the current code. */ + CrntCode, /* Current algorithm code. */ + StackPtr, /* For character stack (see below). */ + CrntShiftState; /* Number of bits in CrntShiftDWord. */ + unsigned long CrntShiftDWord; /* For bytes decomposition into codes. */ + unsigned long PixelCount; /* Number of pixels in image. */ + FILE *File; /* File as stream. */ + InputFunc Read; /* function to read gif input (TVT) */ + OutputFunc Write; /* function to write gif output (MRB) */ + GifByteType Buf[256]; /* Compressed input is buffered here. */ + GifByteType Stack[LZ_MAX_CODE]; /* Decoded pixels are stacked here. */ + GifByteType Suffix[LZ_MAX_CODE + 1]; /* So we can trace the codes. */ + GifPrefixType Prefix[LZ_MAX_CODE + 1]; + GifHashTableType *HashTable; + bool gif89; +} GifFilePrivateType; + +#endif /* _GIF_LIB_PRIVATE_H */ + +/* end */ diff --git a/TMessagesProj/jni/giflib/gifalloc.c b/TMessagesProj/jni/giflib/gifalloc.c new file mode 100755 index 00000000..5726e768 --- /dev/null +++ b/TMessagesProj/jni/giflib/gifalloc.c @@ -0,0 +1,400 @@ +/***************************************************************************** + + GIF construction tools + +****************************************************************************/ + +#include +#include +#include + +#include "gif_lib.h" + +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) + +/****************************************************************************** + Miscellaneous utility functions +******************************************************************************/ + +/* return smallest bitfield size n will fit in */ +int +GifBitSize(int n) +{ + register int i; + + for (i = 1; i <= 8; i++) + if ((1 << i) >= n) + break; + return (i); +} + +/****************************************************************************** + Color map object functions +******************************************************************************/ + +/* + * Allocate a color map of given size; initialize with contents of + * ColorMap if that pointer is non-NULL. + */ +ColorMapObject * +GifMakeMapObject(int ColorCount, const GifColorType *ColorMap) +{ + ColorMapObject *Object; + + /*** FIXME: Our ColorCount has to be a power of two. Is it necessary to + * make the user know that or should we automatically round up instead? */ + if (ColorCount != (1 << GifBitSize(ColorCount))) { + return ((ColorMapObject *) NULL); + } + + Object = (ColorMapObject *)malloc(sizeof(ColorMapObject)); + if (Object == (ColorMapObject *) NULL) { + return ((ColorMapObject *) NULL); + } + + Object->Colors = (GifColorType *)calloc(ColorCount, sizeof(GifColorType)); + if (Object->Colors == (GifColorType *) NULL) { + free(Object); + return ((ColorMapObject *) NULL); + } + + Object->ColorCount = ColorCount; + Object->BitsPerPixel = GifBitSize(ColorCount); + + if (ColorMap != NULL) { + memcpy((char *)Object->Colors, + (char *)ColorMap, ColorCount * sizeof(GifColorType)); + } + + return (Object); +} + +/******************************************************************************* +Free a color map object +*******************************************************************************/ +void +GifFreeMapObject(ColorMapObject *Object) +{ + if (Object != NULL) { + (void)free(Object->Colors); + (void)free(Object); + } +} + +#ifdef DEBUG +void +DumpColorMap(ColorMapObject *Object, + FILE * fp) +{ + if (Object != NULL) { + int i, j, Len = Object->ColorCount; + + for (i = 0; i < Len; i += 4) { + for (j = 0; j < 4 && j < Len; j++) { + (void)fprintf(fp, "%3d: %02x %02x %02x ", i + j, + Object->Colors[i + j].Red, + Object->Colors[i + j].Green, + Object->Colors[i + j].Blue); + } + (void)fprintf(fp, "\n"); + } + } +} +#endif /* DEBUG */ + +/******************************************************************************* + Compute the union of two given color maps and return it. If result can't + fit into 256 colors, NULL is returned, the allocated union otherwise. + ColorIn1 is copied as is to ColorUnion, while colors from ColorIn2 are + copied iff they didn't exist before. ColorTransIn2 maps the old + ColorIn2 into the ColorUnion color map table./ +*******************************************************************************/ +ColorMapObject * +GifUnionColorMap(const ColorMapObject *ColorIn1, + const ColorMapObject *ColorIn2, + GifPixelType ColorTransIn2[]) +{ + int i, j, CrntSlot, RoundUpTo, NewGifBitSize; + ColorMapObject *ColorUnion; + + /* + * We don't worry about duplicates within either color map; if + * the caller wants to resolve those, he can perform unions + * with an empty color map. + */ + + /* Allocate table which will hold the result for sure. */ + ColorUnion = GifMakeMapObject(MAX(ColorIn1->ColorCount, + ColorIn2->ColorCount) * 2, NULL); + + if (ColorUnion == NULL) + return (NULL); + + /* + * Copy ColorIn1 to ColorUnion. + */ + for (i = 0; i < ColorIn1->ColorCount; i++) + ColorUnion->Colors[i] = ColorIn1->Colors[i]; + CrntSlot = ColorIn1->ColorCount; + + /* + * Potentially obnoxious hack: + * + * Back CrntSlot down past all contiguous {0, 0, 0} slots at the end + * of table 1. This is very useful if your display is limited to + * 16 colors. + */ + while (ColorIn1->Colors[CrntSlot - 1].Red == 0 + && ColorIn1->Colors[CrntSlot - 1].Green == 0 + && ColorIn1->Colors[CrntSlot - 1].Blue == 0) + CrntSlot--; + + /* Copy ColorIn2 to ColorUnion (use old colors if they exist): */ + for (i = 0; i < ColorIn2->ColorCount && CrntSlot <= 256; i++) { + /* Let's see if this color already exists: */ + for (j = 0; j < ColorIn1->ColorCount; j++) + if (memcmp (&ColorIn1->Colors[j], &ColorIn2->Colors[i], + sizeof(GifColorType)) == 0) + break; + + if (j < ColorIn1->ColorCount) + ColorTransIn2[i] = j; /* color exists in Color1 */ + else { + /* Color is new - copy it to a new slot: */ + ColorUnion->Colors[CrntSlot] = ColorIn2->Colors[i]; + ColorTransIn2[i] = CrntSlot++; + } + } + + if (CrntSlot > 256) { + GifFreeMapObject(ColorUnion); + return ((ColorMapObject *) NULL); + } + + NewGifBitSize = GifBitSize(CrntSlot); + RoundUpTo = (1 << NewGifBitSize); + + if (RoundUpTo != ColorUnion->ColorCount) { + register GifColorType *Map = ColorUnion->Colors; + + /* + * Zero out slots up to next power of 2. + * We know these slots exist because of the way ColorUnion's + * start dimension was computed. + */ + for (j = CrntSlot; j < RoundUpTo; j++) + Map[j].Red = Map[j].Green = Map[j].Blue = 0; + + /* perhaps we can shrink the map? */ + if (RoundUpTo < ColorUnion->ColorCount) + ColorUnion->Colors = (GifColorType *)realloc(Map, + sizeof(GifColorType) * RoundUpTo); + } + + ColorUnion->ColorCount = RoundUpTo; + ColorUnion->BitsPerPixel = NewGifBitSize; + + return (ColorUnion); +} + +/******************************************************************************* + Apply a given color translation to the raster bits of an image +*******************************************************************************/ +void +GifApplyTranslation(SavedImage *Image, GifPixelType Translation[]) +{ + register int i; + register int RasterSize = Image->ImageDesc.Height * Image->ImageDesc.Width; + + for (i = 0; i < RasterSize; i++) + Image->RasterBits[i] = Translation[Image->RasterBits[i]]; +} + +/****************************************************************************** + Extension record functions +******************************************************************************/ +int +GifAddExtensionBlock(int *ExtensionBlockCount, + ExtensionBlock **ExtensionBlocks, + int Function, + unsigned int Len, + unsigned char ExtData[]) +{ + ExtensionBlock *ep; + + if (*ExtensionBlocks == NULL) + *ExtensionBlocks=(ExtensionBlock *)malloc(sizeof(ExtensionBlock)); + else + *ExtensionBlocks = (ExtensionBlock *)realloc(*ExtensionBlocks, + sizeof(ExtensionBlock) * + (*ExtensionBlockCount + 1)); + + if (*ExtensionBlocks == NULL) + return (GIF_ERROR); + + ep = &(*ExtensionBlocks)[(*ExtensionBlockCount)++]; + + ep->Function = Function; + ep->ByteCount=Len; + ep->Bytes = (GifByteType *)malloc(ep->ByteCount); + if (ep->Bytes == NULL) + return (GIF_ERROR); + + if (ExtData != NULL) { + memcpy(ep->Bytes, ExtData, Len); + } + + return (GIF_OK); +} + +void +GifFreeExtensions(int *ExtensionBlockCount, + ExtensionBlock **ExtensionBlocks) +{ + ExtensionBlock *ep; + + if (*ExtensionBlocks == NULL) + return; + + for (ep = *ExtensionBlocks; + ep < (*ExtensionBlocks + *ExtensionBlockCount); + ep++) + (void)free((char *)ep->Bytes); + (void)free((char *)*ExtensionBlocks); + *ExtensionBlocks = NULL; + *ExtensionBlockCount = 0; +} + +/****************************************************************************** + Image block allocation functions +******************************************************************************/ + +/* Private Function: + * Frees the last image in the GifFile->SavedImages array + */ +void +FreeLastSavedImage(GifFileType *GifFile) +{ + SavedImage *sp; + + if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) + return; + + /* Remove one SavedImage from the GifFile */ + GifFile->ImageCount--; + sp = &GifFile->SavedImages[GifFile->ImageCount]; + + /* Deallocate its Colormap */ + if (sp->ImageDesc.ColorMap != NULL) { + GifFreeMapObject(sp->ImageDesc.ColorMap); + sp->ImageDesc.ColorMap = NULL; + } + + /* Deallocate the image data */ + if (sp->RasterBits != NULL) + free((char *)sp->RasterBits); + + /* Deallocate any extensions */ + GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks); + + /*** FIXME: We could realloc the GifFile->SavedImages structure but is + * there a point to it? Saves some memory but we'd have to do it every + * time. If this is used in GifFreeSavedImages then it would be inefficient + * (The whole array is going to be deallocated.) If we just use it when + * we want to free the last Image it's convenient to do it here. + */ +} + +/* + * Append an image block to the SavedImages array + */ +SavedImage * +GifMakeSavedImage(GifFileType *GifFile, const SavedImage *CopyFrom) +{ + if (GifFile->SavedImages == NULL) + GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage)); + else + GifFile->SavedImages = (SavedImage *)realloc(GifFile->SavedImages, + sizeof(SavedImage) * (GifFile->ImageCount + 1)); + + if (GifFile->SavedImages == NULL) + return ((SavedImage *)NULL); + else { + SavedImage *sp = &GifFile->SavedImages[GifFile->ImageCount++]; + memset((char *)sp, '\0', sizeof(SavedImage)); + + if (CopyFrom != NULL) { + memcpy((char *)sp, CopyFrom, sizeof(SavedImage)); + + /* + * Make our own allocated copies of the heap fields in the + * copied record. This guards against potential aliasing + * problems. + */ + + /* first, the local color map */ + if (sp->ImageDesc.ColorMap != NULL) { + sp->ImageDesc.ColorMap = GifMakeMapObject( + CopyFrom->ImageDesc.ColorMap->ColorCount, + CopyFrom->ImageDesc.ColorMap->Colors); + if (sp->ImageDesc.ColorMap == NULL) { + FreeLastSavedImage(GifFile); + return (SavedImage *)(NULL); + } + } + + /* next, the raster */ + sp->RasterBits = (unsigned char *)malloc(sizeof(GifPixelType) * + CopyFrom->ImageDesc.Height * + CopyFrom->ImageDesc.Width); + if (sp->RasterBits == NULL) { + FreeLastSavedImage(GifFile); + return (SavedImage *)(NULL); + } + memcpy(sp->RasterBits, CopyFrom->RasterBits, + sizeof(GifPixelType) * CopyFrom->ImageDesc.Height * + CopyFrom->ImageDesc.Width); + + /* finally, the extension blocks */ + if (sp->ExtensionBlocks != NULL) { + sp->ExtensionBlocks = (ExtensionBlock *)malloc( + sizeof(ExtensionBlock) * + CopyFrom->ExtensionBlockCount); + if (sp->ExtensionBlocks == NULL) { + FreeLastSavedImage(GifFile); + return (SavedImage *)(NULL); + } + memcpy(sp->ExtensionBlocks, CopyFrom->ExtensionBlocks, + sizeof(ExtensionBlock) * CopyFrom->ExtensionBlockCount); + } + } + + return (sp); + } +} + +void +GifFreeSavedImages(GifFileType *GifFile) +{ + SavedImage *sp; + + if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) { + return; + } + for (sp = GifFile->SavedImages; + sp < GifFile->SavedImages + GifFile->ImageCount; sp++) { + if (sp->ImageDesc.ColorMap != NULL) { + GifFreeMapObject(sp->ImageDesc.ColorMap); + sp->ImageDesc.ColorMap = NULL; + } + + if (sp->RasterBits != NULL) + free((char *)sp->RasterBits); + + GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks); + } + free((char *)GifFile->SavedImages); + GifFile->SavedImages = NULL; +} + +/* end */ diff --git a/TMessagesProj/jni/jni.c b/TMessagesProj/jni/jni.c index 452ac5b5..66a7976e 100644 --- a/TMessagesProj/jni/jni.c +++ b/TMessagesProj/jni/jni.c @@ -3,9 +3,32 @@ #include #include #include -#include -#include "aes.h" -#include "log.h" +#include +#include "aes/aes.h" +#include "utils.h" +#include "sqlite.h" +#include "gif.h" + +jint JNI_OnLoad(JavaVM *vm, void *reserved) { + JNIEnv *env = 0; + srand(time(NULL)); + + if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_4) != JNI_OK) { + return -1; + } + + if (sqliteOnJNILoad(vm, reserved, env) == -1) { + return -1; + } + + gifOnJNILoad(vm, reserved, env); + + return JNI_VERSION_1_4; +} + +void JNI_OnUnload(JavaVM *vm, void *reserved) { + gifOnJNIUnload(vm, reserved); +} JNIEXPORT jbyteArray Java_org_telegram_messenger_Utilities_aesIgeEncryption(JNIEnv *env, jclass class, jbyteArray _what, jbyteArray _key, jbyteArray _iv, jboolean encrypt, jboolean changeIv, jint l) { unsigned char *what = (unsigned char *)(*env)->GetByteArrayElements(env, _what, NULL); diff --git a/TMessagesProj/jni/org_telegram_SQLite.h b/TMessagesProj/jni/org_telegram_SQLite.h deleted file mode 100755 index 442aabaa..00000000 --- a/TMessagesProj/jni/org_telegram_SQLite.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef __SQLITE_H__ -#define __SQLITE_H__ - -#include -#include "sqlite3.h" - -void throw_sqlite3_exception(JNIEnv* env, sqlite3 *handle, int errcode); - -#endif diff --git a/TMessagesProj/jni/org_telegram_SQLite.c b/TMessagesProj/jni/sqlite.c similarity index 85% rename from TMessagesProj/jni/org_telegram_SQLite.c rename to TMessagesProj/jni/sqlite.c index 4929049a..124b3df2 100755 --- a/TMessagesProj/jni/org_telegram_SQLite.c +++ b/TMessagesProj/jni/sqlite.c @@ -1,5 +1,5 @@ -#include "sqlite3.h" -#include "org_telegram_SQLite.h" +#include "sqlite/sqlite3.h" +#include "sqlite.h" void throw_sqlite3_exception(JNIEnv *env, sqlite3 *handle, int errcode) { if (SQLITE_OK == errcode) { diff --git a/TMessagesProj/jni/sqlite.h b/TMessagesProj/jni/sqlite.h new file mode 100755 index 00000000..af19fcb8 --- /dev/null +++ b/TMessagesProj/jni/sqlite.h @@ -0,0 +1,10 @@ +#ifndef sqlite_h +#define sqlite_h + +#include +#include "sqlite/sqlite3.h" + +void throw_sqlite3_exception(JNIEnv* env, sqlite3 *handle, int errcode); +jint sqliteOnJNILoad(JavaVM *vm, void *reserved, JNIEnv *env); + +#endif diff --git a/TMessagesProj/jni/sqlite3.c b/TMessagesProj/jni/sqlite/sqlite3.c similarity index 100% rename from TMessagesProj/jni/sqlite3.c rename to TMessagesProj/jni/sqlite/sqlite3.c diff --git a/TMessagesProj/jni/sqlite3.h b/TMessagesProj/jni/sqlite/sqlite3.h similarity index 100% rename from TMessagesProj/jni/sqlite3.h rename to TMessagesProj/jni/sqlite/sqlite3.h diff --git a/TMessagesProj/jni/org_telegram_SQLite_SQLiteCursor.c b/TMessagesProj/jni/sqlite_cursor.c similarity index 98% rename from TMessagesProj/jni/org_telegram_SQLite_SQLiteCursor.c rename to TMessagesProj/jni/sqlite_cursor.c index 5f0fd98f..655876d9 100755 --- a/TMessagesProj/jni/org_telegram_SQLite_SQLiteCursor.c +++ b/TMessagesProj/jni/sqlite_cursor.c @@ -1,4 +1,4 @@ -#include "org_telegram_SQLite.h" +#include "sqlite.h" int Java_org_telegram_SQLite_SQLiteCursor_columnType(JNIEnv *env, jobject object, int statementHandle, int columnIndex) { sqlite3_stmt *handle = (sqlite3_stmt *)statementHandle; diff --git a/TMessagesProj/jni/org_telegram_SQLite_SQLiteDatabase.c b/TMessagesProj/jni/sqlite_database.c similarity index 97% rename from TMessagesProj/jni/org_telegram_SQLite_SQLiteDatabase.c rename to TMessagesProj/jni/sqlite_database.c index 9cbf2078..a249cff4 100755 --- a/TMessagesProj/jni/org_telegram_SQLite_SQLiteDatabase.c +++ b/TMessagesProj/jni/sqlite_database.c @@ -1,4 +1,4 @@ -#include "org_telegram_SQLite.h" +#include "sqlite.h" void Java_org_telegram_SQLite_SQLiteDatabase_closedb(JNIEnv *env, jobject object, int sqliteHandle) { sqlite3 *handle = (sqlite3 *)sqliteHandle; diff --git a/TMessagesProj/jni/org_telegram_SQLite_SQLitePreparedStatement.c b/TMessagesProj/jni/sqlite_statement.c similarity index 94% rename from TMessagesProj/jni/org_telegram_SQLite_SQLitePreparedStatement.c rename to TMessagesProj/jni/sqlite_statement.c index bd115f1a..f3d11003 100755 --- a/TMessagesProj/jni/org_telegram_SQLite_SQLitePreparedStatement.c +++ b/TMessagesProj/jni/sqlite_statement.c @@ -1,20 +1,10 @@ -#include -#include -#include "org_telegram_SQLite.h" +#include "sqlite.h" jfieldID queryArgsCountField; -jint JNI_OnLoad(JavaVM *vm, void *reserved) { - JNIEnv* env = 0; - srand(time(NULL)); - - if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) { - return -1; - } - +jint sqliteOnJNILoad(JavaVM *vm, void *reserved, JNIEnv *env) { jclass class = (*env)->FindClass(env, "org/telegram/SQLite/SQLitePreparedStatement"); queryArgsCountField = (*env)->GetFieldID(env, class, "queryArgsCount", "I"); - return JNI_VERSION_1_4; } diff --git a/TMessagesProj/jni/log.h b/TMessagesProj/jni/utils.h similarity index 77% rename from TMessagesProj/jni/log.h rename to TMessagesProj/jni/utils.h index 50d37155..49885b99 100644 --- a/TMessagesProj/jni/log.h +++ b/TMessagesProj/jni/utils.h @@ -9,4 +9,11 @@ #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) +#ifndef max +#define max(x, y) ((x) > (y)) ? (x) : (y) +#endif +#ifndef min +#define min(x, y) ((x) < (y)) ? (x) : (y) +#endif + #endif diff --git a/TMessagesProj/jni/video.c b/TMessagesProj/jni/video.c deleted file mode 100644 index b86bb218..00000000 --- a/TMessagesProj/jni/video.c +++ /dev/null @@ -1,18 +0,0 @@ -#include -#include "video.h" - -JNIEXPORT void Java_org_telegram_messenger_VideoTools_initialize(JNIEnv* env, jclass class) { - av_register_all(); -} - -JNIEXPORT void Java_org_telegram_messenger_VideoTools_convert(JNIEnv* env, jclass class, jstring in, jstring out, int bitr) { - char const *in_str = (*env)->GetStringUTFChars(env, in, 0); - char const *out_str = (*env)->GetStringUTFChars(env, out, 0); - convertFile(in_str, out_str, bitr); - if (in_str != 0) { - (*env)->ReleaseStringUTFChars(env, in, in_str); - } - if (out_str != 0) { - (*env)->ReleaseStringUTFChars(env, out, out_str); - } -} diff --git a/TMessagesProj/jni/video.h b/TMessagesProj/jni/video.h deleted file mode 100644 index cacae352..00000000 --- a/TMessagesProj/jni/video.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef video_h -#define video_h - -#include - -int prepare_for_video_conversion(const char *dst_filename, AVCodecContext *video_dec_ctx, AVCodecContext *audio_dec_ctx, AVFormatContext *fmt_ctx, AVStream *src_video_stream, AVStream *src_audio_stream, int bitr); -int write_video_frame(AVFrame *src_frame); -int write_audio_frame(AVFrame *src_frame, AVCodecContext *src_codec); -void post_video_conversion(); -void cleanup_out(); -void onError(); -void onProgress(); -void onDone(); - -void convertFile(const char *src_filename, const char *dst_filename, int bitr); - -#endif diff --git a/TMessagesProj/jni/video_audio_in.c b/TMessagesProj/jni/video_audio_in.c deleted file mode 100644 index 37a4ab37..00000000 --- a/TMessagesProj/jni/video_audio_in.c +++ /dev/null @@ -1,243 +0,0 @@ -#include "video.h" -#include -#include -#include -#include "log.h" - -AVPacket pkt; -int video_stream_idx = -1, audio_stream_idx = -1; -AVCodecContext *video_dec_ctx = NULL, *audio_dec_ctx = NULL; -AVFrame *frame = NULL; -AVStream *video_stream = NULL, *audio_stream = NULL; -AVFormatContext *fmt_ctx = NULL; -int64_t total_duration; -int64_t current_duration; -char *src = NULL; -char *dest = NULL; -int lastLog = 10; - -void cleanup_in() { - if (video_dec_ctx) { - avcodec_close(video_dec_ctx); - video_dec_ctx = NULL; - } - if (audio_dec_ctx) { - avcodec_close(audio_dec_ctx); - audio_dec_ctx = NULL; - } - if (fmt_ctx) { - avformat_close_input(&fmt_ctx); - fmt_ctx = NULL; - } - if (frame) { - av_frame_free(&frame); - frame = NULL; - } - if (src) { - free(src); - src = NULL; - } - if (dest) { - free(dest); - dest = NULL; - } - - total_duration = 0; - current_duration = 0; - video_stream_idx = -1; - audio_stream_idx = -1; - video_stream = NULL; - audio_stream = NULL; - lastLog = 10; -} - -void onError() { - cleanup_in(); - cleanup_out(); -} - -void onDone() { - LOGD("OK\n"); - cleanup_in(); - cleanup_out(); -} - -void onProgress() { - float progress = (float)current_duration / (float)total_duration * 100; - if (progress > lastLog) { - lastLog += 10; - LOGD("progress %.2f\n", progress); - } -} - -int open_codec_context(int *stream_idx, AVFormatContext *fmt_ctx, enum AVMediaType type) { - int ret; - AVStream *st; - AVCodecContext *dec_ctx = NULL; - AVCodec *dec = NULL; - AVDictionary *opts = NULL; - - ret = av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0); - if (ret < 0) { - LOGD("Could not find %s stream in input file\n", av_get_media_type_string(type)); - return ret; - } else { - *stream_idx = ret; - st = fmt_ctx->streams[*stream_idx]; - - dec_ctx = st->codec; - dec = avcodec_find_decoder(dec_ctx->codec_id); - if (!dec) { - LOGD("Failed to find %s codec\n", av_get_media_type_string(type)); - return ret; - } - - av_dict_set(&opts, "refcounted_frames", "1", 0); - if ((ret = avcodec_open2(dec_ctx, dec, &opts)) < 0) { - LOGD("Failed to open %s codec\n", av_get_media_type_string(type)); - return ret; - } - } - - return 0; -} - -int decode_packet(int *got_frame, int cached) { - int ret = 0; - int decoded = pkt.size; - - *got_frame = 0; - - if (pkt.stream_index == video_stream_idx) { - ret = avcodec_decode_video2(video_dec_ctx, frame, got_frame, &pkt); - if (ret < 0) { - LOGD("Error decoding video frame\n"); - return ret; - } - - if (*got_frame) { - ret = write_video_frame(frame); - if (ret < 0) { - return ret; - } - } - } else if (pkt.stream_index == audio_stream_idx) { - ret = avcodec_decode_audio4(audio_dec_ctx, frame, got_frame, &pkt); - - if (ret < 0) { - LOGD("Error decoding audio frame\n"); - return ret; - } - decoded = FFMIN(ret, pkt.size); - - if (*got_frame) { - ret = write_audio_frame(frame, audio_dec_ctx); - if (ret < 0) { - return -1; - } - } - frame->pts = AV_NOPTS_VALUE; - } - - if (*got_frame) { - av_frame_unref(frame); - } - - return decoded; -} - -void convertFile(const char *src_filename, const char *dst_filename, int bitr) { - int ret = 0; - int got_frame; - - src = malloc(strlen(src_filename) + 1); - memcpy(src, src_filename, strlen(src_filename)); - src[strlen(src_filename)] = '\0'; - dest = malloc(strlen(dst_filename) + 1); - memcpy(dest, dst_filename, strlen(dst_filename)); - dest[strlen(dst_filename)] = '\0'; - - if ((ret = avformat_open_input(&fmt_ctx, src, NULL, NULL)) < 0) { - LOGD("Could not open source file %s, %s\n", src, av_err2str(ret)); - onError(); - return; - } - - if (avformat_find_stream_info(fmt_ctx, NULL) < 0) { - LOGD("Could not find stream information\n"); - onError(); - return; - } - - if (open_codec_context(&video_stream_idx, fmt_ctx, AVMEDIA_TYPE_VIDEO) >= 0) { - video_stream = fmt_ctx->streams[video_stream_idx]; - video_dec_ctx = video_stream->codec; - } - - if (open_codec_context(&audio_stream_idx, fmt_ctx, AVMEDIA_TYPE_AUDIO) >= 0) { - audio_stream = fmt_ctx->streams[audio_stream_idx]; - audio_dec_ctx = audio_stream->codec; - } - - av_dump_format(fmt_ctx, 0, src, 0); - - if (!audio_stream && !video_stream) { - LOGD("Could not find audio or video stream in the input, aborting\n"); - onError(); - return; - } - - frame = av_frame_alloc(); - if (!frame) { - LOGD("Could not allocate frame\n"); - onError(); - return; - } - - av_init_packet(&pkt); - pkt.data = NULL; - pkt.size = 0; - - if (video_stream) { - LOGD("Demuxing video from file '%s'\n", src); - } - if (audio_stream) { - LOGD("Demuxing audio from file '%s'\n", src); - } - - ret = prepare_for_video_conversion(dest, video_dec_ctx, audio_dec_ctx, fmt_ctx, video_stream, audio_stream, bitr); - if (ret < 0) { - return; - } - if (video_stream) { - total_duration = video_stream->duration; - } - if (audio_stream) { - total_duration += audio_stream->duration; - } - - while (av_read_frame(fmt_ctx, &pkt) >= 0) { - AVPacket orig_pkt = pkt; - do { - ret = decode_packet(&got_frame, 0); - if (ret < 0) { - onError(); - return; - } - pkt.data += ret; - pkt.size -= ret; - current_duration += pkt.duration; - onProgress(); - } while (pkt.size > 0); - av_free_packet(&orig_pkt); - } - - pkt.data = NULL; - pkt.size = 0; - do { - decode_packet(&got_frame, 1); - } while (got_frame); - - post_video_conversion(); - onDone(); -} diff --git a/TMessagesProj/jni/video_audio_out.c b/TMessagesProj/jni/video_audio_out.c deleted file mode 100644 index 15d046bb..00000000 --- a/TMessagesProj/jni/video_audio_out.c +++ /dev/null @@ -1,366 +0,0 @@ -#include "video.h" -#include -#include -#include -#include -#include "log.h" - -AVFrame *out_frame = NULL; -struct SwsContext *sws_ctx = NULL; -AVStream *video_st = NULL, *audio_st = NULL; -AVFormatContext *oc = NULL; -AVOutputFormat *fmt = NULL; -AVPicture dst_picture; - -uint8_t **dst_samples_data = NULL; -SwrContext *swr_ctx = NULL; - -int current_n_out = 0; -int current_in_buff = 0; -uint8_t buff[4096 * 2]; - -int min(int val1, int val2) { - return val1 < val2 ? val1 : val2; -} - -int prepare_for_video_conversion(const char *dst_filename, AVCodecContext *video_dec_ctx, AVCodecContext *audio_dec_ctx, AVFormatContext *fmt_ctx, AVStream *src_video_stream, AVStream *src_audio_stream, int bitr) { - - if (!video_dec_ctx && !audio_dec_ctx) { - onError(); - return -1; - } - - avformat_alloc_output_context2(&oc, NULL, "mp4", dst_filename); - if (!oc) { - onError(); - return -1; - } - fmt = oc->oformat; - av_dict_copy(&oc->metadata, fmt_ctx->metadata, 0); - - int ret = 0; - if (!(fmt->flags & AVFMT_NOFILE)) { - ret = avio_open(&oc->pb, dst_filename, AVIO_FLAG_WRITE); - if (ret < 0) { - LOGD("Could not open '%s': %s\n", dst_filename, av_err2str(ret)); - onError(); - return -1; - } - } - - AVCodecContext *c; - - if (video_dec_ctx && src_video_stream && fmt_ctx) { - //calculate video resolution - int dst_width = video_dec_ctx->width, dst_height = video_dec_ctx->height; - if (video_dec_ctx->width > video_dec_ctx->height) { - if (video_dec_ctx->width > 480) { - float scale = video_dec_ctx->width / 480.0f; - dst_width = 480; - dst_height = ceilf(video_dec_ctx->height / scale); - } - } else { - if (video_dec_ctx->width > 480) { - float scale = video_dec_ctx->height / 480.0f; - dst_height = 480; - dst_width = ceilf(video_dec_ctx->width / scale); - } - } - if (video_dec_ctx->height != dst_height || video_dec_ctx->width != dst_width || video_dec_ctx->pix_fmt != AV_PIX_FMT_YUV420P) { - sws_ctx = sws_getContext(video_dec_ctx->width, video_dec_ctx->height, video_dec_ctx->pix_fmt, dst_width, dst_height, AV_PIX_FMT_YUV420P, SWS_BILINEAR, NULL, NULL, NULL); - if (!sws_ctx) { - LOGD("Could not initialize the conversion context\n"); - onError(); - return -1; - } - } - - //create video stream - AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_MPEG4); - if (!codec) { - LOGD("Could not find encoder for '%s'\n", avcodec_get_name(AV_CODEC_ID_MPEG4)); - onError(); - return -1; - } - - video_st = avformat_new_stream(oc, codec); - if (!video_st) { - LOGD("Could not allocate stream\n"); - onError(); - return -1; - } - video_st->id = oc->nb_streams - 1; - av_dict_copy(&video_st->metadata, src_video_stream->metadata, 0); - c = video_st->codec; - c->codec_id = AV_CODEC_ID_MPEG4; - c->bit_rate = bitr; - c->width = dst_width; - c->height = dst_height; - double fps = (double)src_video_stream->avg_frame_rate.num / (double)src_video_stream->avg_frame_rate.den; - c->time_base.den = 65535; - c->time_base.num = floor(65635 / fps); - c->gop_size = 12; - c->pix_fmt = AV_PIX_FMT_YUV420P; - - if (oc->oformat->flags & AVFMT_GLOBALHEADER) { - c->flags |= CODEC_FLAG_GLOBAL_HEADER; - } - - ret = avcodec_open2(c, codec, NULL); - if (ret < 0) { - LOGD("Could not open video codec: %s\n", av_err2str(ret)); - onError(); - return -1; - } - - out_frame = avcodec_alloc_frame(); - if (!out_frame) { - LOGD("Could not allocate video frame\n"); - onError(); - return -1; - } - - ret = avpicture_alloc(&dst_picture, c->pix_fmt, c->width, c->height); - if (ret < 0) { - LOGD("Could not allocate picture: %s\n", av_err2str(ret)); - onError(); - return -1; - } - - *((AVPicture *)out_frame) = dst_picture; - } - - //create audio stream - if (audio_dec_ctx && src_audio_stream) { - AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_AAC); - if (!codec) { - LOGD("Could not find encoder for '%s'\n", avcodec_get_name(AV_CODEC_ID_AAC)); - onError(); - return -1; - } - - audio_st = avformat_new_stream(oc, codec); - if (!audio_st) { - LOGD("Could not allocate stream\n"); - onError(); - return -1; - } - audio_st->id = oc->nb_streams - 1; - av_dict_copy(&audio_st->metadata, src_audio_stream->metadata, 0); - c = audio_st->codec; - c->sample_fmt = AV_SAMPLE_FMT_FLTP; - c->bit_rate = 40000; - c->sample_rate = min(audio_dec_ctx->sample_rate, 44100); - c->channels = 1; - - if (oc->oformat->flags & AVFMT_GLOBALHEADER) { - c->flags |= CODEC_FLAG_GLOBAL_HEADER; - } - - c = audio_st->codec; - c->strict_std_compliance = -2; - - swr_ctx = swr_alloc_set_opts(NULL, AV_CH_LAYOUT_MONO, c->sample_fmt, c->sample_rate, audio_dec_ctx->channel_layout, audio_dec_ctx->sample_fmt, audio_dec_ctx->sample_rate, 0, NULL); - if (!swr_ctx) { - LOGD("Could not allocate resampler context\n"); - onError(); - return -1; - } - - if ((ret = swr_init(swr_ctx)) < 0) { - LOGD("Failed to initialize the resampling context\n"); - onError(); - return -1; - } - - ret = avcodec_open2(c, codec, NULL); - if (ret < 0) { - LOGD("Could not open audio codec: %s\n", av_err2str(ret)); - onError(); - return -1; - } - - av_dump_format(oc, 0, dst_filename, 1); - - ret = avformat_write_header(oc, NULL); - if (ret < 0) { - LOGD("Error occurred when opening output file: %s\n", av_err2str(ret)); - onError(); - return -1; - } - - if (out_frame) { - out_frame->pts = 0; - } - } - return 0; -} - -void cleanup_out() { - if (video_st) { - avcodec_close(video_st->codec); - if (dst_picture.data) { - av_free(dst_picture.data[0]); - } - if (out_frame) { - av_free(out_frame); - out_frame = NULL; - } - video_st = NULL; - } - if (audio_st) { - avcodec_close(audio_st->codec); - if (dst_samples_data) { - av_free(dst_samples_data[0]); - dst_samples_data = NULL; - } - audio_st = NULL; - } - - if (fmt && !(fmt->flags & AVFMT_NOFILE)) { - avio_close(oc->pb); - fmt = NULL; - } - - if (oc) { - avformat_free_context(oc); - oc = NULL; - } - if (sws_ctx) { - sws_freeContext(sws_ctx); - sws_ctx = NULL; - } - if (swr_ctx) { - swr_free(&swr_ctx); - swr_ctx = NULL; - } - current_n_out = 0; - current_in_buff = 0; -} - -int write_video_frame(AVFrame *src_frame) { - int ret; - - if (sws_ctx) { - ret = sws_scale(sws_ctx, (const uint8_t * const *)src_frame->data, src_frame->linesize, 0, src_frame->height, out_frame->data, out_frame->linesize); - if (ret < 0) { - LOGD("scale error: %s\n", av_err2str(ret)); - onError(); - return -1; - } - } else { - for (int i = 0; i < 4; i++){ - out_frame->data[i] = src_frame->data[i]; - out_frame->linesize[i] = src_frame->linesize[i]; - } - } - - AVPacket pkt = { 0 }; - int got_packet; - av_init_packet(&pkt); - - ret = avcodec_encode_video2(video_st->codec, &pkt, out_frame, &got_packet); - if (ret < 0) { - LOGD("Error encoding video frame: %s\n", av_err2str(ret)); - onError(); - return -1; - } - - if (!ret && got_packet && pkt.size) { - pkt.stream_index = video_st->index; - ret = av_interleaved_write_frame(oc, &pkt); - } else { - ret = 0; - } - - if (ret != 0) { - LOGD("Error while writing video frame: %s\n", av_err2str(ret)); - onError(); - return -1; - } - int64_t val = av_rescale_q(1, video_st->codec->time_base, video_st->time_base); - out_frame->pts += val; - return 0; -} - -int check_write_packet(int flush) { - int got_packet, ret; - int writed = 0; - int dst_samples_size = av_samples_get_buffer_size(NULL, audio_st->codec->channels, audio_st->codec->frame_size, audio_st->codec->sample_fmt, 1); - while (current_n_out > audio_st->codec->frame_size || (flush && current_n_out)) { - AVFrame *frame = avcodec_alloc_frame(); - AVPacket pkt2 = { 0 }; - av_init_packet(&pkt2); - - frame->nb_samples = min(audio_st->codec->frame_size, current_n_out); - int nb_samples_size = min(dst_samples_size, current_in_buff); - ret = avcodec_fill_audio_frame(frame, audio_st->codec->channels, audio_st->codec->sample_fmt, buff + writed, nb_samples_size, 1); - - if (ret < 0) { - LOGD("Error fill frame: %s\n", av_err2str(ret)); - onError(); - return -1; - } - - ret = avcodec_encode_audio2(audio_st->codec, &pkt2, frame, &got_packet); - if (ret < 0) { - LOGD("Error encoding audio frame: %s\n", av_err2str(ret)); - onError(); - return -1; - } - - if (got_packet) { - pkt2.stream_index = audio_st->index; - ret = av_interleaved_write_frame(oc, &pkt2); - if (ret != 0) { - LOGD("Error while writing audio frame: %s\n", av_err2str(ret)); - onError(); - return -1; - } - } - writed += dst_samples_size; - current_n_out -= frame->nb_samples; - current_in_buff -= nb_samples_size; - avcodec_free_frame(&frame); - } - if (current_in_buff != 0 && writed != 0) { - memcpy(buff, buff + writed, current_in_buff); - } - return 0; -} - -int write_audio_frame(AVFrame *src_frame, AVCodecContext *src_codec) { - const int n_in = src_frame->nb_samples; - double ratio = (double)audio_st->codec->sample_rate / src_frame->sample_rate; - int n_out = n_in * ratio + 32; - int64_t delay = swr_get_delay(swr_ctx, audio_st->codec->sample_rate); - if (delay > 0) { - n_out += delay; - } - - if (!dst_samples_data) { - int ret = av_samples_alloc_array_and_samples(&dst_samples_data, NULL, audio_st->codec->channels, n_out, audio_st->codec->sample_fmt, 0); - if (ret < 0) { - LOGD("Could not allocate destination samples\n"); - onError(); - return -1; - } - } - - n_out = swr_convert(swr_ctx, dst_samples_data, n_out, (const uint8_t **)src_frame->extended_data, src_frame->nb_samples); - if (n_out <= 0) { - LOGD("Error while converting\n"); - onError(); - return -1; - } - int total_size = av_samples_get_buffer_size(NULL, audio_st->codec->channels, n_out, audio_st->codec->sample_fmt, 1); - memcpy(buff + current_in_buff, dst_samples_data[0], total_size); - current_n_out += n_out; - current_in_buff += total_size; - return check_write_packet(0); -} - -void post_video_conversion() { - check_write_packet(1); - av_write_trailer(oc); -} diff --git a/TMessagesProj/libs/armeabi-v7a/libtmessages.so b/TMessagesProj/libs/armeabi-v7a/libtmessages.so index d3c5a594..1f881519 100755 Binary files a/TMessagesProj/libs/armeabi-v7a/libtmessages.so and b/TMessagesProj/libs/armeabi-v7a/libtmessages.so differ diff --git a/TMessagesProj/libs/armeabi/libtmessages.so b/TMessagesProj/libs/armeabi/libtmessages.so index 8dd405a6..2a1df40f 100755 Binary files a/TMessagesProj/libs/armeabi/libtmessages.so and b/TMessagesProj/libs/armeabi/libtmessages.so differ diff --git a/TMessagesProj/libs/x86/libtmessages.so b/TMessagesProj/libs/x86/libtmessages.so index c5f0c5f6..214b9de5 100755 Binary files a/TMessagesProj/libs/x86/libtmessages.so and b/TMessagesProj/libs/x86/libtmessages.so differ diff --git a/TMessagesProj/src/main/java/org/telegram/PhoneFormat/CallingCodeInfo.java b/TMessagesProj/src/main/java/org/telegram/PhoneFormat/CallingCodeInfo.java index 87e6e008..666d508a 100644 --- a/TMessagesProj/src/main/java/org/telegram/PhoneFormat/CallingCodeInfo.java +++ b/TMessagesProj/src/main/java/org/telegram/PhoneFormat/CallingCodeInfo.java @@ -27,11 +27,11 @@ package org.telegram.PhoneFormat; import java.util.ArrayList; public class CallingCodeInfo { - public ArrayList countries; - public String callingCode; - public ArrayList trunkPrefixes; - public ArrayList intlPrefixes; - public ArrayList ruleSets; + public ArrayList countries = new ArrayList(); + public String callingCode = ""; + public ArrayList trunkPrefixes = new ArrayList(); + public ArrayList intlPrefixes = new ArrayList(); + public ArrayList ruleSets = new ArrayList(); //public ArrayList formatStrings; String matchingAccessCode(String str) { diff --git a/TMessagesProj/src/main/java/org/telegram/PhoneFormat/RuleSet.java b/TMessagesProj/src/main/java/org/telegram/PhoneFormat/RuleSet.java index e4668899..5b7ecacf 100644 --- a/TMessagesProj/src/main/java/org/telegram/PhoneFormat/RuleSet.java +++ b/TMessagesProj/src/main/java/org/telegram/PhoneFormat/RuleSet.java @@ -30,7 +30,7 @@ import java.util.regex.Pattern; public class RuleSet { public int matchLen; - public ArrayList rules; + public ArrayList rules = new ArrayList(); public boolean hasRuleWithIntlPrefix; public boolean hasRuleWithTrunkPrefix; public static Pattern pattern = Pattern.compile("[0-9]+"); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/AwakeService.java b/TMessagesProj/src/main/java/org/telegram/messenger/AwakeService.java index ea6e029c..b6843433 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/AwakeService.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/AwakeService.java @@ -37,7 +37,7 @@ public class AwakeService extends Service { public static void startService() { try { - if (MessagesController.isScreenOn && ApplicationLoader.lastPauseTime == 0) { + if (ApplicationLoader.isScreenOn && ApplicationLoader.lastPauseTime == 0) { return; } timeout = 10000; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java b/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java index 4810aaaf..17a9df38 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java @@ -649,7 +649,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. if (phone.startsWith("968")) { for (HashMap.Entry entry : datacenters.entrySet()) { Datacenter datacenter = entry.getValue(); - datacenter.overridePort = 14; + datacenter.overridePort = 8888; if (datacenter.connection != null) { datacenter.connection.suspendConnection(true); } @@ -2041,13 +2041,14 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. if (request.serverFailureCount < 1) { discardResponse = true; request.runningMinStartTime = request.runningStartTime + 1; - request.serverFailureCount++; } } else { discardResponse = true; - request.runningMinStartTime = request.runningStartTime + 1; + int delay = Math.min(1, request.serverFailureCount * 2); + request.runningMinStartTime = request.runningStartTime + delay; request.confirmed = false; } + request.serverFailureCount++; } else if (errorCode == 420) { if ((request.flags & RPCRequest.RPCRequestClassFailOnServerErrors) == 0) { double waitTime = 2.0; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java b/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java index 65b334bd..6efd387f 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java @@ -323,19 +323,9 @@ public class ContactsController { name = PhoneFormat.getInstance().format(phone); } - String[] args = name.split(" ", 2); - Contact contact = new Contact(); - if (args.length > 0) { - contact.first_name = args[0]; - } else { - contact.first_name = ""; - } - if (args.length > 1) { - contact.last_name = args[1]; - } else { - contact.last_name = ""; - } + contact.first_name = name; + contact.last_name = ""; contact.id = pCur.getInt(2); contactsMap.put(contact.id, contact); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/Datacenter.java b/TMessagesProj/src/main/java/org/telegram/messenger/Datacenter.java index ff979851..2bdce85d 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/Datacenter.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/Datacenter.java @@ -25,7 +25,7 @@ public class Datacenter { public ArrayList addresses = new ArrayList(); public HashMap ports = new HashMap(); public int[] defaultPorts = new int[] {-1, 80, -1, 443, -1, 443, -1, 80, -1, 443, -1}; - public int[] defaultPorts14 = new int[] {-1, 14, -1, 443, -1, 14, -1, 80, -1, 14, -1}; + public int[] defaultPorts8888 = new int[] {-1, 8888, -1, 443, -1, 8888, -1, 80, -1, 8888, -1}; public boolean authorized; public long authSessionId; public long authDownloadSessionId; @@ -136,8 +136,8 @@ public class Datacenter { int[] portsArray = defaultPorts; - if (overridePort == 14) { - portsArray = defaultPorts14; + if (overridePort == 8888) { + portsArray = defaultPorts8888; } if (currentPortNum >= defaultPorts.length) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java index 388d58f2..43697966 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java @@ -198,7 +198,7 @@ public class FileLoadOperation { } final boolean dontDelete = isLocalFile; if ((exist = cacheFileFinal.exists()) && !ignoreCache) { - Utilities.cacheOutQueue.postRunnable(new Runnable() { + FileLoader.cacheOutQueue.postRunnable(new Runnable() { @Override public void run() { try { @@ -403,7 +403,7 @@ public class FileLoadOperation { final boolean renamed = cacheFileTemp.renameTo(cacheFileFinal); if (needBitmapCreate) { - Utilities.cacheOutQueue.postRunnable(new Runnable() { + FileLoader.cacheOutQueue.postRunnable(new Runnable() { @Override public void run() { int delay = 20; @@ -520,7 +520,7 @@ public class FileLoadOperation { int readed = httpConnectionStream.read(data); if (readed > 0) { fileOutputStream.write(data, 0, readed); - Utilities.imageLoadQueue.postRunnable(new Runnable() { + FileLoader.fileLoaderQueue.postRunnable(new Runnable() { @Override public void run() { startDownloadHTTPRequest(); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java index 4a17defe..9e3671c7 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java @@ -38,6 +38,9 @@ import java.util.concurrent.ConcurrentHashMap; public class FileLoader { public LruCache memCache; + public static volatile DispatchQueue cacheOutQueue = new DispatchQueue("cacheOutQueue"); + public static volatile DispatchQueue fileLoaderQueue = new DispatchQueue("fileUploadQueue"); + private String ignoreRemoval = null; private ConcurrentHashMap imageLoading; private HashMap imageLoadingByKeys; @@ -50,9 +53,11 @@ public class FileLoader { private int currentUploadOperationsCount = 0; private Queue loadOperationQueue; private Queue audioLoadOperationQueue; + private Queue photoLoadOperationQueue; private ConcurrentHashMap loadOperationPaths; private int currentLoadOperationsCount = 0; private int currentAudioLoadOperationsCount = 0; + private int currentPhotoLoadOperationsCount = 0; public static long lastCacheOutTime = 0; public ConcurrentHashMap fileProgresses = new ConcurrentHashMap(); private long lastProgressUpdateTime = 0; @@ -108,21 +113,12 @@ public class FileLoader { try { Class cl = Class.forName("dalvik.system.VMRuntime"); Method getRt = cl.getMethod("getRuntime", new Class[0]); - runtime = getRt.invoke(null, new Object[0]); + Object obj = new Object[0]; + runtime = getRt.invoke(null, obj); trackAllocation = cl.getMethod("trackExternalAllocation", new Class[] {long.class}); trackFree = cl.getMethod("trackExternalFree", new Class[] {long.class}); success = true; - } catch (ClassNotFoundException e) { - FileLog.e("tmessages", e); - } catch (SecurityException e) { - FileLog.e("tmessages", e); - } catch (NoSuchMethodException e) { - FileLog.e("tmessages", e); - } catch (IllegalArgumentException e) { - FileLog.e("tmessages", e); - } catch (IllegalAccessException e) { - FileLog.e("tmessages", e); - } catch (InvocationTargetException e) { + } catch (Exception e) { FileLog.e("tmessages", e); } if (!success) { @@ -179,7 +175,7 @@ public class FileLoader { } } } - Utilities.imageLoadQueue.postRunnable(new Runnable() { + fileLoaderQueue.postRunnable(new Runnable() { @Override public void run() { synchronized (imageViewArray) { @@ -316,10 +312,11 @@ public class FileLoader { loadOperationPaths = new ConcurrentHashMap(); loadOperationQueue = new LinkedList(); audioLoadOperationQueue = new LinkedList(); + photoLoadOperationQueue = new LinkedList(); } public void cancelUploadFile(final String location, final boolean enc) { - Utilities.fileUploadQueue.postRunnable(new Runnable() { + fileLoaderQueue.postRunnable(new Runnable() { @Override public void run() { if (!enc) { @@ -344,7 +341,7 @@ public class FileLoader { } public void uploadFile(final String location, final byte[] key, final byte[] iv) { - Utilities.fileUploadQueue.postRunnable(new Runnable() { + fileLoaderQueue.postRunnable(new Runnable() { @Override public void run() { if (key != null) { @@ -365,7 +362,7 @@ public class FileLoader { operation.delegate = new FileUploadOperation.FileUploadOperationDelegate() { @Override public void didFinishUploadingFile(FileUploadOperation operation, final TLRPC.InputFile inputFile, final TLRPC.InputEncryptedFile inputEncryptedFile) { - Utilities.fileUploadQueue.postRunnable(new Runnable() { + fileLoaderQueue.postRunnable(new Runnable() { @Override public void run() { Utilities.stageQueue.postRunnable(new Runnable() { @@ -394,7 +391,7 @@ public class FileLoader { @Override public void didFailedUploadingFile(final FileUploadOperation operation) { - Utilities.fileUploadQueue.postRunnable(new Runnable() { + fileLoaderQueue.postRunnable(new Runnable() { @Override public void run() { Utilities.stageQueue.postRunnable(new Runnable() { @@ -454,7 +451,7 @@ public class FileLoader { if (video == null && photo == null && document == null && audio == null) { return; } - Utilities.fileUploadQueue.postRunnable(new Runnable() { + fileLoaderQueue.postRunnable(new Runnable() { @Override public void run() { String fileName = null; @@ -474,6 +471,8 @@ public class FileLoader { if (operation != null) { if (audio != null) { audioLoadOperationQueue.remove(operation); + } else if (photo != null) { + photoLoadOperationQueue.remove(operation); } else { loadOperationQueue.remove(operation); } @@ -488,7 +487,7 @@ public class FileLoader { } public void loadFile(final TLRPC.Video video, final TLRPC.PhotoSize photo, final TLRPC.Document document, final TLRPC.Audio audio) { - Utilities.fileUploadQueue.postRunnable(new Runnable() { + fileLoaderQueue.postRunnable(new Runnable() { @Override public void run() { String fileName = null; @@ -540,7 +539,7 @@ public class FileLoader { NotificationCenter.getInstance().postNotificationName(FileDidLoaded, arg1); } }); - Utilities.fileUploadQueue.postRunnable(new Runnable() { + fileLoaderQueue.postRunnable(new Runnable() { @Override public void run() { loadOperationPaths.remove(arg1); @@ -553,6 +552,15 @@ public class FileLoader { operation.start(); } } + } else if (photo != null) { + currentPhotoLoadOperationsCount--; + if (currentPhotoLoadOperationsCount < 2) { + FileLoadOperation operation = photoLoadOperationQueue.poll(); + if (operation != null) { + currentPhotoLoadOperationsCount++; + operation.start(); + } + } } else { currentLoadOperationsCount--; if (currentLoadOperationsCount < 2) { @@ -579,7 +587,7 @@ public class FileLoader { } }); } - Utilities.fileUploadQueue.postRunnable(new Runnable() { + fileLoaderQueue.postRunnable(new Runnable() { @Override public void run() { loadOperationPaths.remove(arg1); @@ -592,6 +600,15 @@ public class FileLoader { operation.start(); } } + } else if (photo != null) { + currentPhotoLoadOperationsCount--; + if (currentPhotoLoadOperationsCount < 2) { + FileLoadOperation operation = photoLoadOperationQueue.poll(); + if (operation != null) { + currentPhotoLoadOperationsCount++; + operation.start(); + } + } } else { currentLoadOperationsCount--; if (currentLoadOperationsCount < 2) { @@ -630,6 +647,13 @@ public class FileLoader { } else { audioLoadOperationQueue.add(operation); } + } else if (photo != null) { + if (currentPhotoLoadOperationsCount < 2) { + currentPhotoLoadOperationsCount++; + operation.start(); + } else { + photoLoadOperationQueue.add(operation); + } } else { if (currentLoadOperationsCount < 2) { currentLoadOperationsCount++; @@ -674,7 +698,7 @@ public class FileLoader { if (imageView == null) { return; } - Utilities.imageLoadQueue.postRunnable(new Runnable() { + fileLoaderQueue.postRunnable(new Runnable() { @Override public void run() { Integer TAG = getTag(imageView); @@ -776,14 +800,16 @@ public class FileLoader { if ((url == null && httpUrl == null) || imageView == null || (url != null && !(url instanceof TLRPC.TL_fileLocation) && !(url instanceof TLRPC.TL_fileEncryptedLocation))) { return; } - Utilities.imageLoadQueue.postRunnable(new Runnable() { + fileLoaderQueue.postRunnable(new Runnable() { @Override public void run() { String key; + String fileName = null; if (httpUrl != null) { key = Utilities.MD5(httpUrl); } else { key = url.volume_id + "_" + url.local_id; + fileName = key + ".jpg"; } if (filter != null) { key += "@" + filter; @@ -832,6 +858,7 @@ public class FileLoader { imageLoading.put(key, img); final String arg2 = key; + final String arg3 = fileName; FileLoadOperation loadOperation; if (httpUrl != null) { loadOperation = new FileLoadOperation(httpUrl); @@ -843,30 +870,43 @@ public class FileLoader { loadOperation.delegate = new FileLoadOperation.FileLoadOperationDelegate() { @Override public void didFinishLoadingFile(final FileLoadOperation operation) { - enqueueImageProcessingOperationWithImage(operation.image, filter, arg2, img); if (operation.totalBytesCount != 0) { - final String arg1 = operation.location.volume_id + "_" + operation.location.local_id + ".jpg"; - fileProgresses.remove(arg1); - Utilities.RunOnUIThread(new Runnable() { - @Override - public void run() { - NotificationCenter.getInstance().postNotificationName(FileLoadProgressChanged, arg1, 1.0f); - } - }); - Utilities.RunOnUIThread(new Runnable() { - @Override - public void run() { - NotificationCenter.getInstance().postNotificationName(FileDidLoaded, arg1); - } - }); + fileProgresses.remove(arg3); } + fileLoaderQueue.postRunnable(new Runnable() { + @Override + public void run() { + if (arg3 != null) { + loadOperationPaths.remove(arg3); + } + for (Object v : img.imageViewArray) { + imageLoadingByKeys.remove(getTag(v)); + } + checkOperationsAndClear(img.loadOperation); + imageLoading.remove(arg2); + } + }); + + Utilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + img.callAndClear(operation.image); + if (operation.image != null && memCache.get(arg2) == null) { + memCache.put(arg2, operation.image); + } + NotificationCenter.getInstance().postNotificationName(FileDidLoaded, arg3); + } + }); } @Override public void didFailedLoadingFile(final FileLoadOperation operation) { - Utilities.imageLoadQueue.postRunnable(new Runnable() { + fileLoaderQueue.postRunnable(new Runnable() { @Override public void run() { + if (arg3 != null) { + loadOperationPaths.remove(arg3); + } for (Object view : img.imageViewArray) { imageLoadingByKeys.remove(getTag(view)); imageLoading.remove(arg2); @@ -921,6 +961,9 @@ public class FileLoader { } else { operationsQueue.add(loadOperation); } + if (fileName != null) { + loadOperationPaths.put(fileName, loadOperation); + } } } }); @@ -936,85 +979,6 @@ public class FileLoader { } } - public void processImage(Bitmap image, Object imageView, String filter, boolean cancel) { - if (filter == null || imageView == null) { - return; - } - - - Integer TAG = getTag(imageView); - if (TAG == null) { - TAG = lastImageNum; - setTag(image, TAG); - lastImageNum++; - if (lastImageNum == Integer.MAX_VALUE) - lastImageNum = 0; - } - - boolean added = false; - boolean addToByKeys = true; - CacheImage alreadyLoadingImage = imageLoading.get(filter); - if (cancel) { - CacheImage ei = imageLoadingByKeys.get(TAG); - if (ei != null) { - if (ei != alreadyLoadingImage) { - ei.removeImageView(imageView); - if (ei.imageViewArray.size() == 0) { - checkOperationsAndClear(ei.loadOperation); - ei.cancelAndClear(); - imageLoading.remove(ei.key); - } - } else { - addToByKeys = false; - added = true; - } - } - } - - if (alreadyLoadingImage != null && addToByKeys) { - alreadyLoadingImage.addImageView(imageView); - imageLoadingByKeys.put(TAG, alreadyLoadingImage); - added = true; - } - - if (!added) { - CacheImage img = new CacheImage(); - img.key = filter; - img.addImageView(imageView); - imageLoadingByKeys.put(TAG, img); - imageLoading.put(filter, img); - - enqueueImageProcessingOperationWithImage(image, filter, filter, img); - } - } - - void enqueueImageProcessingOperationWithImage(final Bitmap image, final String filter, final String key, final CacheImage img) { - if (key == null) { - return; - } - - Utilities.imageLoadQueue.postRunnable(new Runnable() { - @Override - public void run() { - for (Object v : img.imageViewArray) { - imageLoadingByKeys.remove(getTag(v)); - } - checkOperationsAndClear(img.loadOperation); - imageLoading.remove(key); - } - }); - - Utilities.RunOnUIThread(new Runnable() { - @Override - public void run() { - img.callAndClear(image); - if (image != null && memCache.get(key) == null) { - memCache.put(key, image); - } - } - }); - } - public static Bitmap loadBitmap(String path, Uri uri, float maxWidth, float maxHeight) { BitmapFactory.Options bmOptions = new BitmapFactory.Options(); bmOptions.inJustDecodeBounds = true; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/GcmBroadcastReceiver.java b/TMessagesProj/src/main/java/org/telegram/messenger/GcmBroadcastReceiver.java index 61282fbd..0ac99fa3 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/GcmBroadcastReceiver.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/GcmBroadcastReceiver.java @@ -49,13 +49,13 @@ public class GcmBroadcastReceiver extends BroadcastReceiver { } } + AwakeService.startService(); + Utilities.RunOnUIThread(new Runnable() { @Override public void run() { ApplicationLoader.postInitApplication(); - AwakeService.startService(); - try { String key = intent.getStringExtra("loc_key"); if ("DC_UPDATE".equals(key)) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/LocaleController.java b/TMessagesProj/src/main/java/org/telegram/messenger/LocaleController.java index 80f3a294..2ba72963 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/LocaleController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/LocaleController.java @@ -12,9 +12,13 @@ import android.app.Activity; import android.content.SharedPreferences; import android.content.res.Configuration; import android.text.format.DateFormat; +import android.util.Xml; import org.telegram.ui.ApplicationLoader; +import org.xmlpull.v1.XmlPullParser; +import java.io.File; +import java.io.FileInputStream; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; @@ -26,6 +30,7 @@ import java.util.Locale; public class LocaleController { public static boolean isRTL = false; + private static boolean is24HourFormat = false; public static FastDateFormat formatterDay; public static FastDateFormat formatterWeek; public static FastDateFormat formatterMonth; @@ -37,6 +42,7 @@ public class LocaleController { private Locale currentLocale; private Locale systemDefaultLocale; private LocaleInfo currentLocaleInfo; + private LocaleInfo defaultLocalInfo; private HashMap localeValues = new HashMap(); private String languageOverride; private boolean changingConfiguration = false; @@ -45,11 +51,34 @@ public class LocaleController { public String name; public String nameEnglish; public String shortName; + public String pathToFile; + + public String getSaveString() { + return name + "|" + nameEnglish + "|" + shortName + "|" + pathToFile; + } + + public static LocaleInfo createWithString(String string) { + if (string == null || string.length() == 0) { + return null; + } + String[] args = string.split("\\|"); + if (args.length != 4) { + return null; + } + LocaleInfo localeInfo = new LocaleInfo(); + localeInfo.name = args[0]; + localeInfo.nameEnglish = args[1]; + localeInfo.shortName = args[2]; + localeInfo.pathToFile = args[3]; + return localeInfo; + } } public ArrayList sortedLanguages = new ArrayList(); public HashMap languagesDict = new HashMap(); + private ArrayList otherLanguages = new ArrayList(); + private static volatile LocaleController Instance = null; public static LocaleController getInstance() { LocaleController localInstance = Instance; @@ -65,48 +94,60 @@ public class LocaleController { } public LocaleController() { - LocaleController.LocaleInfo localeInfo = new LocaleController.LocaleInfo(); + LocaleInfo localeInfo = new LocaleInfo(); localeInfo.name = "English"; localeInfo.nameEnglish = "English"; localeInfo.shortName = "en"; + localeInfo.pathToFile = null; sortedLanguages.add(localeInfo); languagesDict.put(localeInfo.shortName, localeInfo); - localeInfo = new LocaleController.LocaleInfo(); + localeInfo = new LocaleInfo(); localeInfo.name = "Italiano"; localeInfo.nameEnglish = "Italian"; localeInfo.shortName = "it"; + localeInfo.pathToFile = null; sortedLanguages.add(localeInfo); languagesDict.put(localeInfo.shortName, localeInfo); - localeInfo = new LocaleController.LocaleInfo(); + localeInfo = new LocaleInfo(); localeInfo.name = "Español"; localeInfo.nameEnglish = "Spanish"; localeInfo.shortName = "es"; sortedLanguages.add(localeInfo); languagesDict.put(localeInfo.shortName, localeInfo); - localeInfo = new LocaleController.LocaleInfo(); + localeInfo = new LocaleInfo(); localeInfo.name = "Deutsch"; localeInfo.nameEnglish = "German"; localeInfo.shortName = "de"; + localeInfo.pathToFile = null; sortedLanguages.add(localeInfo); languagesDict.put(localeInfo.shortName, localeInfo); - localeInfo = new LocaleController.LocaleInfo(); + localeInfo = new LocaleInfo(); localeInfo.name = "Nederlands"; localeInfo.nameEnglish = "Dutch"; localeInfo.shortName = "nl"; + localeInfo.pathToFile = null; sortedLanguages.add(localeInfo); languagesDict.put(localeInfo.shortName, localeInfo); - localeInfo = new LocaleController.LocaleInfo(); + localeInfo = new LocaleInfo(); localeInfo.name = "العربية"; localeInfo.nameEnglish = "Arabic"; localeInfo.shortName = "ar"; + localeInfo.pathToFile = null; sortedLanguages.add(localeInfo); languagesDict.put(localeInfo.shortName, localeInfo); + loadOtherLanguages(); + + for (LocaleInfo locale : otherLanguages) { + sortedLanguages.add(locale); + languagesDict.put(locale.shortName, locale); + } + Collections.sort(sortedLanguages, new Comparator() { @Override public int compare(LocaleController.LocaleInfo o, LocaleController.LocaleInfo o2) { @@ -114,13 +155,15 @@ public class LocaleController { } }); - localeInfo = new LocaleController.LocaleInfo(); + defaultLocalInfo = localeInfo = new LocaleController.LocaleInfo(); localeInfo.name = "System default"; localeInfo.nameEnglish = "System default"; localeInfo.shortName = null; + localeInfo.pathToFile = null; sortedLanguages.add(0, localeInfo); systemDefaultLocale = Locale.getDefault(); + is24HourFormat = DateFormat.is24HourFormat(ApplicationLoader.applicationContext); LocaleInfo currentInfo = null; boolean override = false; @@ -146,7 +189,167 @@ public class LocaleController { } } + public boolean applyLanguageFile(File file) { + try { + HashMap stringMap = getLocaleFileStrings(file); + + String languageName = stringMap.get("LanguageName"); + String languageNameInEnglish = stringMap.get("LanguageNameInEnglish"); + String languageCode = stringMap.get("LanguageCode"); + + if (languageName != null && languageName.length() > 0 && + languageNameInEnglish != null && languageNameInEnglish.length() > 0 && + languageCode != null && languageCode.length() > 0) { + + if (languageName.contains("&") || languageName.contains("|")) { + return false; + } + if (languageNameInEnglish.contains("&") || languageNameInEnglish.contains("|")) { + return false; + } + if (languageCode.contains("&") || languageCode.contains("|")) { + return false; + } + + File finalFile = new File(ApplicationLoader.applicationContext.getFilesDir(), languageCode + ".xml"); + if (!Utilities.copyFile(file, finalFile)) { + return false; + } + + LocaleInfo localeInfo = languagesDict.get(languageCode); + if (localeInfo == null) { + localeInfo = new LocaleInfo(); + localeInfo.name = languageName; + localeInfo.nameEnglish = languageNameInEnglish; + localeInfo.shortName = languageCode; + + localeInfo.pathToFile = finalFile.getAbsolutePath(); + sortedLanguages.add(localeInfo); + languagesDict.put(localeInfo.shortName, localeInfo); + otherLanguages.add(localeInfo); + + Collections.sort(sortedLanguages, new Comparator() { + @Override + public int compare(LocaleController.LocaleInfo o, LocaleController.LocaleInfo o2) { + if (o.shortName == null) { + return -1; + } else if (o2.shortName == null) { + return 1; + } + return o.name.compareTo(o2.name); + } + }); + saveOtherLanguages(); + } + localeValues = stringMap; + applyLanguage(localeInfo, true, true); + return true; + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + return false; + } + + private void saveOtherLanguages() { + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("langconfig", Activity.MODE_PRIVATE); + SharedPreferences.Editor editor = preferences.edit(); + String locales = ""; + for (LocaleInfo localeInfo : otherLanguages) { + String loc = localeInfo.getSaveString(); + if (loc != null) { + if (locales.length() != 0) { + locales += "&"; + } + locales += loc; + } + } + editor.putString("locales", locales); + editor.commit(); + } + + public boolean deleteLanguage(LocaleInfo localeInfo) { + if (localeInfo.pathToFile == null) { + return false; + } + if (currentLocaleInfo == localeInfo) { + applyLanguage(defaultLocalInfo, true); + } + + otherLanguages.remove(localeInfo); + sortedLanguages.remove(localeInfo); + languagesDict.remove(localeInfo.shortName); + File file = new File(localeInfo.pathToFile); + file.delete(); + saveOtherLanguages(); + return true; + } + + private void loadOtherLanguages() { + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("langconfig", Activity.MODE_PRIVATE); + String locales = preferences.getString("locales", null); + if (locales == null || locales.length() == 0) { + return; + } + String[] localesArr = locales.split("&"); + for (String locale : localesArr) { + LocaleInfo localeInfo = LocaleInfo.createWithString(locale); + if (localeInfo != null) { + otherLanguages.add(localeInfo); + } + } + } + + private HashMap getLocaleFileStrings(File file) { + try { + HashMap stringMap = new HashMap(); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(new FileInputStream(file), "UTF-8"); + int eventType = parser.getEventType(); + String name = null; + String value = null; + String attrName = null; + while (eventType != XmlPullParser.END_DOCUMENT) { + if(eventType == XmlPullParser.START_TAG) { + name = parser.getName(); + int c = parser.getAttributeCount(); + if (c > 0) { + attrName = parser.getAttributeValue(0); + } + } else if(eventType == XmlPullParser.TEXT) { + if (attrName != null) { + value = parser.getText(); + if (value != null) { + value = value.trim(); + value = value.replace("\\n", "\n"); + value = value.replace("\\", ""); + } + } + } else if (eventType == XmlPullParser.END_TAG) { + value = null; + attrName = null; + name = null; + } + if (name != null && name.equals("string") && value != null && attrName != null && value.length() != 0 && attrName.length() != 0) { + stringMap.put(attrName, value); + name = null; + value = null; + attrName = null; + } + eventType = parser.next(); + } + return stringMap; + } catch (Exception e) { + FileLog.e("tmessages", e); + } + return null; + } + public void applyLanguage(LocaleInfo localeInfo, boolean override) { + applyLanguage(localeInfo, override, false); + } + + public void applyLanguage(LocaleInfo localeInfo, boolean override, boolean fromFile) { if (localeInfo == null) { return; } @@ -173,6 +376,11 @@ public class LocaleController { editor.commit(); } if (newLocale != null) { + if (localeInfo.pathToFile == null) { + localeValues.clear(); + } else if (!fromFile) { + localeValues = getLocaleFileStrings(new File(localeInfo.pathToFile)); + } currentLocale = newLocale; currentLocaleInfo = localeInfo; changingConfiguration = true; @@ -194,7 +402,7 @@ public class LocaleController { } public static String getCurrentLanguageName() { - return getString("LanguangeName", R.string.LanguangeName); + return getString("LanguageName", R.string.LanguageName); } public static String getString(String key, int res) { @@ -226,6 +434,7 @@ public class LocaleController { if (changingConfiguration) { return; } + is24HourFormat = DateFormat.is24HourFormat(ApplicationLoader.applicationContext); systemDefaultLocale = newConfig.locale; if (languageOverride != null) { LocaleInfo toSet = currentLocaleInfo; @@ -324,7 +533,7 @@ public class LocaleController { formatterWeek = FastDateFormat.getInstance("EEE", locale); if (lang != null) { - if (DateFormat.is24HourFormat(ApplicationLoader.applicationContext)) { + if (is24HourFormat) { formatterDay = FastDateFormat.getInstance("HH:mm", locale); } else { if (lang.toLowerCase().equals("ar")) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java index 1650af23..018ffefb 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java @@ -20,9 +20,12 @@ import android.media.audiofx.AutomaticGainControl; import android.net.Uri; import android.os.Environment; import android.os.Vibrator; +import android.view.View; import org.telegram.objects.MessageObject; import org.telegram.ui.ApplicationLoader; +import org.telegram.ui.Cells.ChatMediaCell; +import org.telegram.ui.Views.GifDrawable; import java.io.File; import java.io.FileInputStream; @@ -45,17 +48,16 @@ public class MediaController implements NotificationCenter.NotificationCenterDel private native int seekOpusFile(float position); private native int isOpusFile(String path); private native void closeOpusFile(); - private native void readOpusFile(ByteBuffer buffer, int capacity); - private native int getFinished(); - private native int getSize(); - private native long getPcmOffset(); + private native void readOpusFile(ByteBuffer buffer, int capacity, int[] args); private native long getTotalPcmDuration(); + public static int[] readArgs = new int[3]; public static interface FileDownloadProgressListener { public void onFailedDownload(String fileName); public void onSuccessDownload(String fileName); public void onProgressDownload(String fileName, float progress); + public void onProgressUpload(String fileName, float progress, boolean isEncrypted); public int getObserverTag(); } @@ -86,6 +88,10 @@ public class MediaController implements NotificationCenter.NotificationCenterDel private ArrayList deleteLaterArray = new ArrayList(); private int lastTag = 0; + private GifDrawable currentGifDrawable; + private MessageObject currentGifMessageObject; + private ChatMediaCell currentMediaCell; + private boolean isPaused = false; private MediaPlayer audioPlayer = null; private AudioTrack audioTrackPlayer = null; @@ -228,6 +234,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel NotificationCenter.getInstance().addObserver(this, FileLoader.FileDidFailedLoad); NotificationCenter.getInstance().addObserver(this, FileLoader.FileDidLoaded); NotificationCenter.getInstance().addObserver(this, FileLoader.FileLoadProgressChanged); + NotificationCenter.getInstance().addObserver(this, FileLoader.FileUploadProgressChanged); Timer progressTimer = new Timer(); progressTimer.schedule(new TimerTask() { @@ -275,6 +282,12 @@ public class MediaController implements NotificationCenter.NotificationCenterDel public void cleanup() { clenupPlayer(false); + if (currentGifDrawable != null) { + currentGifDrawable.recycle(); + currentGifDrawable = null; + } + currentMediaCell = null; + currentGifMessageObject = null; } public int generateObserverTag() { @@ -379,6 +392,22 @@ public class MediaController implements NotificationCenter.NotificationCenterDel } listenerInProgress = false; processLaterArrays(); + } else if (id == FileLoader.FileUploadProgressChanged) { + String location = (String)args[0]; + listenerInProgress = true; + String fileName = (String)args[0]; + ArrayList> arrayList = loadingFileObservers.get(fileName); + if (arrayList != null) { + Float progress = (Float)args[1]; + Boolean enc = (Boolean)args[2]; + for (WeakReference reference : arrayList) { + if (reference.get() != null) { + reference.get().onProgressUpload(fileName, progress, enc); + } + } + } + listenerInProgress = false; + processLaterArrays(); } } @@ -403,10 +432,10 @@ public class MediaController implements NotificationCenter.NotificationCenterDel } } if (buffer != null) { - readOpusFile(buffer.buffer, playerBufferSize); - buffer.size = getSize(); - buffer.pcmOffset = getPcmOffset(); - buffer.finished = getFinished(); + readOpusFile(buffer.buffer, playerBufferSize, readArgs); + buffer.size = readArgs[0]; + buffer.pcmOffset = readArgs[1]; + buffer.finished = readArgs[2]; if (buffer.finished == 1) { decodingFinished = true; } @@ -832,23 +861,27 @@ public class MediaController implements NotificationCenter.NotificationCenterDel fileBuffer.rewind(); if (android.os.Build.VERSION.SDK_INT >= 16) { - AutomaticGainControl agc = null; - try { - if (AutomaticGainControl.isAvailable()) { - agc = AutomaticGainControl.create(audioRecorder.getAudioSessionId()); - agc.setEnabled(true); - audioGainObj = agc; - } - } catch (Exception e) { + File f = new File("/vendor/lib/libaudioeffect_jni.so"); + File f2 = new File("/system/lib/libaudioeffect_jni.so"); + if (f.exists() || f2.exists()) { + AutomaticGainControl agc = null; try { - if (agc != null) { - agc.release(); - agc = null; + if (AutomaticGainControl.isAvailable()) { + agc = AutomaticGainControl.create(audioRecorder.getAudioSessionId()); + agc.setEnabled(true); + audioGainObj = agc; } - } catch (Exception e2) { - FileLog.e("tmessages", e2); + } catch (Exception e) { + try { + if (agc != null) { + agc.release(); + agc = null; + } + } catch (Exception e2) { + FileLog.e("tmessages", e2); + } + FileLog.e("tmessages", e); } - FileLog.e("tmessages", e); } } @@ -1077,4 +1110,73 @@ public class MediaController implements NotificationCenter.NotificationCenterDel }); } } + + public GifDrawable getGifDrawable(ChatMediaCell cell, boolean create) { + if (cell == null) { + return null; + } + + MessageObject messageObject = cell.getMessageObject(); + if (messageObject == null) { + return null; + } + + if (currentGifMessageObject != null && messageObject.messageOwner.id == currentGifMessageObject.messageOwner.id) { + currentMediaCell = cell; + currentGifDrawable.parentView = new WeakReference(cell); + return currentGifDrawable; + } + + if (create) { + if (currentMediaCell != null) { + if (currentGifDrawable != null) { + currentGifDrawable.stop(); + currentGifDrawable.recycle(); + } + currentMediaCell.clearGifImage(); + } + currentGifMessageObject = cell.getMessageObject(); + currentMediaCell = cell; + + File cacheFile = null; + if (currentGifMessageObject.messageOwner.attachPath != null && currentGifMessageObject.messageOwner.attachPath.length() != 0) { + File f = new File(currentGifMessageObject.messageOwner.attachPath); + if (f.length() > 0) { + cacheFile = f; + } + } else { + cacheFile = new File(Utilities.getCacheDir(), messageObject.getFileName()); + } + try { + currentGifDrawable = new GifDrawable(cacheFile); + currentGifDrawable.parentView = new WeakReference(cell); + return currentGifDrawable; + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + + return null; + } + + public void clearGifDrawable(ChatMediaCell cell) { + if (cell == null) { + return; + } + + MessageObject messageObject = cell.getMessageObject(); + if (messageObject == null) { + return; + } + + if (currentGifMessageObject != null && messageObject.messageOwner.id == currentGifMessageObject.messageOwner.id) { + if (currentGifDrawable != null) { + currentGifDrawable.stop(); + currentGifDrawable.recycle(); + currentGifDrawable = null; + } + currentMediaCell = null; + currentGifMessageObject = null; + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java index a71ef4b9..18f69341 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java @@ -26,7 +26,6 @@ import android.media.AudioManager; import android.media.SoundPool; import android.net.Uri; import android.os.Build; -import android.os.PowerManager; import android.os.Vibrator; import android.provider.Settings; import android.support.v4.app.NotificationCompat; @@ -100,7 +99,6 @@ public class MessagesController implements NotificationCenter.NotificationCenter public int fontSize = Utilities.dp(16); public long scheduleContactsReload = 0; - public static volatile boolean isScreenOn = true; public MessageObject currentPushMessage; private class UserActionUpdates extends TLRPC.Updates { @@ -201,14 +199,6 @@ public class MessagesController implements NotificationCenter.NotificationCenter } public MessagesController() { - try { - PowerManager pm = (PowerManager)ApplicationLoader.applicationContext.getSystemService(Context.POWER_SERVICE); - isScreenOn = pm.isScreenOn(); - FileLog.e("tmessages", "screen state = " + isScreenOn); - } catch (Exception e) { - FileLog.e("tmessages", e); - } - MessagesStorage storage = MessagesStorage.getInstance(); NotificationCenter.getInstance().addObserver(this, FileLoader.FileDidUpload); NotificationCenter.getInstance().addObserver(this, FileLoader.FileDidFailUpload); @@ -1895,6 +1885,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter delayedMessage.type = 2; delayedMessage.obj = newMsgObj; delayedMessage.documentLocation = document; + delayedMessage.location = document.thumb.location; performSendDelayedMessage(delayedMessage); } else if (type == 8) { reqSend.media = new TLRPC.TL_inputMediaUploadedAudio(); @@ -1976,9 +1967,15 @@ public class MessagesController implements NotificationCenter.NotificationCenter random.nextBytes(reqSend.media.iv); random.nextBytes(reqSend.media.key); reqSend.media.size = document.size; - reqSend.media.thumb = new byte[0]; - reqSend.media.thumb_h = 0; - reqSend.media.thumb_w = 0; + if (!(document.thumb instanceof TLRPC.TL_photoSizeEmpty)) { + reqSend.media.thumb = document.thumb.bytes; + reqSend.media.thumb_h = document.thumb.h; + reqSend.media.thumb_w = document.thumb.w; + } else { + reqSend.media.thumb = new byte[0]; + reqSend.media.thumb_h = 0; + reqSend.media.thumb_w = 0; + } reqSend.media.file_name = document.file_name; reqSend.media.mime_type = document.mime_type; @@ -2051,18 +2048,30 @@ public class MessagesController implements NotificationCenter.NotificationCenter if (size2.location != null && size.location != null && !(size instanceof TLRPC.TL_photoSizeEmpty) && !(size2 instanceof TLRPC.TL_photoSizeEmpty)) { String fileName = size2.location.volume_id + "_" + size2.location.local_id; String fileName2 = size.location.volume_id + "_" + size.location.local_id; - if (fileName.equals(fileName2)) { - return; + if (!fileName.equals(fileName2)) { + File cacheFile = new File(Utilities.getCacheDir(), fileName + ".jpg"); + File cacheFile2 = new File(Utilities.getCacheDir(), fileName2 + ".jpg"); + boolean result = cacheFile.renameTo(cacheFile2); + FileLoader.getInstance().replaceImageInCache(fileName, fileName2); + size2.location = size.location; } - File cacheFile = new File(Utilities.getCacheDir(), fileName + ".jpg"); - File cacheFile2 = new File(Utilities.getCacheDir(), fileName2 + ".jpg"); - boolean result = cacheFile.renameTo(cacheFile2); - FileLoader.getInstance().replaceImageInCache(fileName, fileName2); - size2.location = size.location; - sentMessage.message = newMsg.message; - sentMessage.attachPath = newMsg.attachPath; } + sentMessage.message = newMsg.message; + sentMessage.attachPath = newMsg.attachPath; } else if (sentMessage.media instanceof TLRPC.TL_messageMediaDocument && sentMessage.media.document != null && newMsg.media instanceof TLRPC.TL_messageMediaDocument && newMsg.media.document != null) { + TLRPC.PhotoSize size2 = newMsg.media.document.thumb; + TLRPC.PhotoSize size = sentMessage.media.document.thumb; + if (size2.location != null && size.location != null && !(size instanceof TLRPC.TL_photoSizeEmpty) && !(size2 instanceof TLRPC.TL_photoSizeEmpty)) { + String fileName = size2.location.volume_id + "_" + size2.location.local_id; + String fileName2 = size.location.volume_id + "_" + size.location.local_id; + if (!fileName.equals(fileName2)) { + File cacheFile = new File(Utilities.getCacheDir(), fileName + ".jpg"); + File cacheFile2 = new File(Utilities.getCacheDir(), fileName2 + ".jpg"); + boolean result = cacheFile.renameTo(cacheFile2); + FileLoader.getInstance().replaceImageInCache(fileName, fileName2); + size2.location = size.location; + } + } sentMessage.message = newMsg.message; sentMessage.attachPath = newMsg.attachPath; } else if (sentMessage.media instanceof TLRPC.TL_messageMediaAudio && sentMessage.media.audio != null && newMsg.media instanceof TLRPC.TL_messageMediaAudio && newMsg.media.audio != null) { @@ -2071,14 +2080,13 @@ public class MessagesController implements NotificationCenter.NotificationCenter String fileName = newMsg.media.audio.dc_id + "_" + newMsg.media.audio.id + ".m4a"; String fileName2 = sentMessage.media.audio.dc_id + "_" + sentMessage.media.audio.id + ".m4a"; - if (fileName.equals(fileName2)) { - return; + if (!fileName.equals(fileName2)) { + File cacheFile = new File(Utilities.getCacheDir(), fileName); + File cacheFile2 = new File(Utilities.getCacheDir(), fileName2); + cacheFile.renameTo(cacheFile2); + newMsg.media.audio.dc_id = sentMessage.media.audio.dc_id; + newMsg.media.audio.id = sentMessage.media.audio.id; } - File cacheFile = new File(Utilities.getCacheDir(), fileName); - File cacheFile2 = new File(Utilities.getCacheDir(), fileName2); - cacheFile.renameTo(cacheFile2); - newMsg.media.audio.dc_id = sentMessage.media.audio.dc_id; - newMsg.media.audio.id = sentMessage.media.audio.id; } } else if (file != null) { if (newMsg.media instanceof TLRPC.TL_messageMediaPhoto && newMsg.media.photo != null) { @@ -2154,12 +2162,11 @@ public class MessagesController implements NotificationCenter.NotificationCenter String fileName = audio.dc_id + "_" + audio.id + ".m4a"; String fileName2 = newMsg.media.audio.dc_id + "_" + newMsg.media.audio.id + ".m4a"; - if (fileName.equals(fileName2)) { - return; + if (!fileName.equals(fileName2)) { + File cacheFile = new File(Utilities.getCacheDir(), fileName); + File cacheFile2 = new File(Utilities.getCacheDir(), fileName2); + cacheFile.renameTo(cacheFile2); } - File cacheFile = new File(Utilities.getCacheDir(), fileName); - File cacheFile2 = new File(Utilities.getCacheDir(), fileName2); - cacheFile.renameTo(cacheFile2); ArrayList arr = new ArrayList(); arr.add(newMsg); @@ -2169,7 +2176,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter } private void performSendEncryptedRequest(final TLRPC.DecryptedMessage req, final MessageObject newMsgObj, final TLRPC.EncryptedChat chat, final TLRPC.InputEncryptedFile encryptedFile) { - if (req == null) { + if (req == null || chat.auth_key == null || chat instanceof TLRPC.TL_encryptedChatRequested || chat instanceof TLRPC.TL_encryptedChatWaiting) { return; } //TLRPC.decryptedMessageLayer messageLayer = new TLRPC.decryptedMessageLayer(); @@ -2429,12 +2436,18 @@ public class MessagesController implements NotificationCenter.NotificationCenter FileLoader.getInstance().uploadFile(location, message.sendEncryptedRequest.media.key, message.sendEncryptedRequest.media.iv); } } else if (message.type == 2) { - String location = message.documentLocation.path; - putToDelayedMessages(location, message); - if (message.sendRequest != null) { + if (message.sendRequest != null && message.sendRequest.media.thumb == null && message.location != null) { + String location = Utilities.getCacheDir() + "/" + message.location.volume_id + "_" + message.location.local_id + ".jpg"; + putToDelayedMessages(location, message); FileLoader.getInstance().uploadFile(location, null, null); } else { - FileLoader.getInstance().uploadFile(location, message.sendEncryptedRequest.media.key, message.sendEncryptedRequest.media.iv); + String location = message.documentLocation.path; + putToDelayedMessages(location, message); + if (message.sendRequest != null) { + FileLoader.getInstance().uploadFile(location, null, null); + } else { + FileLoader.getInstance().uploadFile(location, message.sendEncryptedRequest.media.key, message.sendEncryptedRequest.media.iv); + } } } else if (message.type == 3) { String location = message.audioLocation.path; @@ -2545,8 +2558,13 @@ public class MessagesController implements NotificationCenter.NotificationCenter performSendMessageRequest(message.sendRequest, message.obj); } } else if (message.type == 2) { - message.sendRequest.media.file = file; - performSendMessageRequest(message.sendRequest, message.obj); + if (message.sendRequest.media.thumb == null && message.location != null) { + message.sendRequest.media.thumb = file; + performSendDelayedMessage(message); + } else { + message.sendRequest.media.file = file; + performSendMessageRequest(message.sendRequest, message.obj); + } } else if (message.type == 3) { message.sendRequest.media.file = file; performSendMessageRequest(message.sendRequest, message.obj); @@ -3345,7 +3363,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter }); } else { gettingDifference = false; - loadCurrentState(); + getDifference(); FileLog.e("tmessages", "get difference error, don't know what to do :("); } } @@ -3396,7 +3414,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter } else { dialog_id = obj.messageOwner.to_id.user_id; } - if (dialog_id != openned_dialog_id || ApplicationLoader.lastPauseTime != 0 || !isScreenOn) { + if (dialog_id != openned_dialog_id || ApplicationLoader.lastPauseTime != 0 || !ApplicationLoader.isScreenOn) { showInAppNotification(obj); } } @@ -3455,7 +3473,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter } else { dialog_id = obj.messageOwner.to_id.user_id; } - if (dialog_id != openned_dialog_id || ApplicationLoader.lastPauseTime != 0 || !isScreenOn) { + if (dialog_id != openned_dialog_id || ApplicationLoader.lastPauseTime != 0 || !ApplicationLoader.isScreenOn) { showInAppNotification(obj); } } @@ -4212,7 +4230,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter return; } - if (ApplicationLoader.lastPauseTime == 0 && isScreenOn) { + if (ApplicationLoader.lastPauseTime == 0 && ApplicationLoader.isScreenOn) { boolean inAppSounds = preferences.getBoolean("EnableInAppSounds", true); boolean inAppVibrate = preferences.getBoolean("EnableInAppVibrate", true); boolean inAppPreview = preferences.getBoolean("EnableInAppPreview", true); @@ -4692,7 +4710,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter newMessage.media.geo.lat = decryptedMessage.media.lat; newMessage.media.geo._long = decryptedMessage.media._long; } else if (decryptedMessage.media instanceof TLRPC.TL_decryptedMessageMediaPhoto) { - if (decryptedMessage.media.key.length != 32 || decryptedMessage.media.iv.length != 32) { + if (decryptedMessage.media.key == null || decryptedMessage.media.key.length != 32 || decryptedMessage.media.iv == null || decryptedMessage.media.iv.length != 32) { return null; } newMessage.media = new TLRPC.TL_messageMediaPhoto(); @@ -4725,7 +4743,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter big.location.local_id = message.file.key_fingerprint; newMessage.media.photo.sizes.add(big); } else if (decryptedMessage.media instanceof TLRPC.TL_decryptedMessageMediaVideo) { - if (decryptedMessage.media.key.length != 32 || decryptedMessage.media.iv.length != 32) { + if (decryptedMessage.media.key == null || decryptedMessage.media.key.length != 32 || decryptedMessage.media.iv == null || decryptedMessage.media.iv.length != 32) { return null; } newMessage.media = new TLRPC.TL_messageMediaVideo(); @@ -4754,7 +4772,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter newMessage.media.video.key = decryptedMessage.media.key; newMessage.media.video.iv = decryptedMessage.media.iv; } else if (decryptedMessage.media instanceof TLRPC.TL_decryptedMessageMediaDocument) { - if (decryptedMessage.media.key.length != 32 || decryptedMessage.media.iv.length != 32) { + if (decryptedMessage.media.key == null || decryptedMessage.media.key.length != 32 || decryptedMessage.media.iv == null || decryptedMessage.media.iv.length != 32) { return null; } newMessage.media = new TLRPC.TL_messageMediaDocument(); @@ -4781,7 +4799,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter } newMessage.media.document.dc_id = message.file.dc_id; } else if (decryptedMessage.media instanceof TLRPC.TL_decryptedMessageMediaAudio) { - if (decryptedMessage.media.key.length != 32 || decryptedMessage.media.iv.length != 32) { + if (decryptedMessage.media.key == null || decryptedMessage.media.key.length != 32 || decryptedMessage.media.iv == null || decryptedMessage.media.iv.length != 32) { return null; } newMessage.media = new TLRPC.TL_messageMediaAudio(); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java index 446e37ae..91ebc6b6 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java @@ -2825,4 +2825,109 @@ public class MessagesStorage { } }); } + + public TLRPC.User getUser(final int user_id) { + TLRPC.User user = null; + try { + SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, status FROM users WHERE uid = %d", user_id)); + if (cursor.next()) { + byte[] userData = cursor.byteArrayValue(0); + if (userData != null) { + SerializedData data = new SerializedData(userData); + user = (TLRPC.User) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + if (user != null) { + if (user.status != null) { + user.status.expires = cursor.intValue(1); + } + } + } + } + cursor.dispose(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + return user; + } + + public ArrayList getUsers(final ArrayList uids, final boolean[] error) { + ArrayList users = new ArrayList(); + try { + String uidsStr = ""; + + for (Integer uid : uids) { + if (uidsStr.length() != 0) { + uidsStr += ","; + } + uidsStr += uid; + } + + SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, status FROM users WHERE uid IN (%s)", uidsStr)); + while (cursor.next()) { + byte[] userData = cursor.byteArrayValue(0); + if (userData != null) { + SerializedData data = new SerializedData(userData); + TLRPC.User user = (TLRPC.User) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + if (user != null) { + if (user.status != null) { + user.status.expires = cursor.intValue(1); + } + users.add(user); + } else { + error[0] = true; + break; + } + } else { + error[0] = true; + break; + } + } + cursor.dispose(); + } catch (Exception e) { + error[0] = true; + FileLog.e("tmessages", e); + } + return users; + } + + public TLRPC.Chat getChat(final int chat_id) { + TLRPC.Chat chat = null; + try { + SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT data FROM chats WHERE uid = %d", chat_id)); + if (cursor.next()) { + byte[] chatData = cursor.byteArrayValue(0); + if (chatData != null) { + SerializedData data = new SerializedData(chatData); + chat = (TLRPC.Chat) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + } + } + cursor.dispose(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + return chat; + } + + public TLRPC.EncryptedChat getEncryptedChat(final int chat_id) { + TLRPC.EncryptedChat chat = null; + try { + SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, user, g, authkey, ttl FROM enc_chats WHERE uid = %d", chat_id)); + if (cursor.next()) { + byte[] chatData = cursor.byteArrayValue(0); + if (chatData != null) { + SerializedData data = new SerializedData(chatData); + chat = (TLRPC.EncryptedChat) TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + if (chat != null) { + chat.user_id = cursor.intValue(1); + chat.a_or_b = cursor.byteArrayValue(2); + chat.auth_key = cursor.byteArrayValue(3); + chat.ttl = cursor.intValue(4); + } + } + } + cursor.dispose(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + return chat; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/NativeLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/NativeLoader.java index 872d3e3d..7816e3f0 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NativeLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NativeLoader.java @@ -10,7 +10,6 @@ package org.telegram.messenger; import android.content.Context; import android.os.Build; -import android.util.Log; import java.io.File; import java.io.FileOutputStream; @@ -22,9 +21,9 @@ import java.util.zip.ZipFile; public class NativeLoader { private static final long sizes[] = new long[] { - 782992, //armeabi - 766628, //armeabi-v7a - 1352692, //x86 + 795280, //armeabi + 778916, //armeabi-v7a + 1377300, //x86 0, //mips }; @@ -35,7 +34,7 @@ public class NativeLoader { return; } - if (Build.VERSION.SDK_INT >= 9) { + if (Build.VERSION.SDK_INT > 10) { try { String folder = null; long libSize = 0; @@ -48,6 +47,7 @@ public class NativeLoader { } else if (Build.CPU_ABI.equalsIgnoreCase("armeabi")) { folder = "armeabi"; libSize = sizes[0]; + libSize2 = sizes[1]; } else if (Build.CPU_ABI.equalsIgnoreCase("x86")) { folder = "x86"; libSize = sizes[2]; @@ -57,13 +57,13 @@ public class NativeLoader { } else { System.loadLibrary("tmessages"); nativeLoaded = true; - Log.e("tmessages", "Unsupported arch: " + Build.CPU_ABI); + FileLog.e("tmessages", "Unsupported arch: " + Build.CPU_ABI); return; } File destFile = new File(context.getApplicationInfo().nativeLibraryDir + "/libtmessages.so"); if (destFile.exists() && (destFile.length() == libSize || libSize2 != 0 && destFile.length() == libSize2)) { - Log.d("tmessages", "Load normal lib"); + FileLog.d("tmessages", "Load normal lib"); try { System.loadLibrary("tmessages"); nativeLoaded = true; @@ -77,7 +77,7 @@ public class NativeLoader { if (destLocalFile.exists()) { if (destLocalFile.length() == libSize) { try { - Log.d("tmessages", "Load local lib"); + FileLog.d("tmessages", "Load local lib"); System.load(destLocalFile.getAbsolutePath()); nativeLoaded = true; return; @@ -89,7 +89,7 @@ public class NativeLoader { } } - Log.e("tmessages", "Library not found, arch = " + folder); + FileLog.e("tmessages", "Library not found, arch = " + folder); ZipFile zipFile = null; InputStream stream = null; @@ -114,25 +114,25 @@ public class NativeLoader { nativeLoaded = true; return; } catch (Exception e) { - e.printStackTrace(); + FileLog.e("tmessages", e); } finally { if (stream != null) { try { stream.close(); } catch (Exception e) { - e.printStackTrace(); + FileLog.e("tmessages", e); } } if (zipFile != null) { try { zipFile.close(); } catch (Exception e) { - e.printStackTrace(); + FileLog.e("tmessages", e); } } } } catch (Exception e) { - e.printStackTrace(); + FileLog.e("tmessages", e); } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ScreenReceiver.java b/TMessagesProj/src/main/java/org/telegram/messenger/ScreenReceiver.java index 8f79680f..2abdd5d0 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ScreenReceiver.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ScreenReceiver.java @@ -12,15 +12,17 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import org.telegram.ui.ApplicationLoader; + public class ScreenReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { FileLog.e("tmessages", "screen off"); - MessagesController.isScreenOn = false; + ApplicationLoader.isScreenOn = false; } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { FileLog.e("tmessages", "screen on"); - MessagesController.isScreenOn = true; + ApplicationLoader.isScreenOn = true; } } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java b/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java index f8ca9bc5..5f725929 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java @@ -76,9 +76,6 @@ public class Utilities { public static volatile DispatchQueue stageQueue = new DispatchQueue("stageQueue"); public static volatile DispatchQueue globalQueue = new DispatchQueue("globalQueue"); - public static volatile DispatchQueue cacheOutQueue = new DispatchQueue("cacheOutQueue"); - public static volatile DispatchQueue imageLoadQueue = new DispatchQueue("imageLoadQueue"); - public static volatile DispatchQueue fileUploadQueue = new DispatchQueue("fileUploadQueue"); public static int[] arrColors = {0xffee4928, 0xff41a903, 0xffe09602, 0xff0f94ed, 0xff8f3bf7, 0xfffc4380, 0xff00a1c4, 0xffeb7002}; public static int[] arrUsersAvatars = { diff --git a/TMessagesProj/src/main/java/org/telegram/objects/MessageObject.java b/TMessagesProj/src/main/java/org/telegram/objects/MessageObject.java index 31ac47df..c4f37d0c 100644 --- a/TMessagesProj/src/main/java/org/telegram/objects/MessageObject.java +++ b/TMessagesProj/src/main/java/org/telegram/objects/MessageObject.java @@ -35,6 +35,7 @@ public class MessageObject { public TLRPC.Message messageOwner; public CharSequence messageText; public int type; + public int contentType; public ArrayList photoThumbs; public Bitmap imagePreview; public PhotoObject previewPhoto; @@ -244,6 +245,11 @@ public class MessageObject { } else if (message.media instanceof TLRPC.TL_messageMediaUnsupported) { messageText = LocaleController.getString("UnsuppotedMedia", R.string.UnsuppotedMedia); } else if (message.media instanceof TLRPC.TL_messageMediaDocument) { + if (!(message.media.document.thumb instanceof TLRPC.TL_photoSizeEmpty)) { + photoThumbs = new ArrayList(); + PhotoObject obj = new PhotoObject(message.media.document.thumb); + photoThumbs.add(obj); + } messageText = LocaleController.getString("AttachDocument", R.string.AttachDocument); } else if (message.media instanceof TLRPC.TL_messageMediaAudio) { messageText = LocaleController.getString("AttachAudio", R.string.AttachAudio); @@ -255,68 +261,53 @@ public class MessageObject { if (message instanceof TLRPC.TL_message || (message instanceof TLRPC.TL_messageForwarded && (message.media == null || !(message.media instanceof TLRPC.TL_messageMediaEmpty)))) { if (message.media == null || message.media instanceof TLRPC.TL_messageMediaEmpty) { - if (message.from_id == UserConfig.clientUserId) { - type = 0; - } else { - type = 1; - } + contentType = type = 0; } else if (message.media != null && message.media instanceof TLRPC.TL_messageMediaPhoto) { - if (message.from_id == UserConfig.clientUserId) { - type = 2; - } else { - type = 3; - } + contentType = type = 1; } else if (message.media != null && message.media instanceof TLRPC.TL_messageMediaGeo) { if (message.from_id == UserConfig.clientUserId) { - type = 4; + contentType = type = 4; } else { - type = 5; + contentType = type = 5; } } else if (message.media != null && message.media instanceof TLRPC.TL_messageMediaVideo) { if (message.from_id == UserConfig.clientUserId) { - type = 6; + contentType = type = 6; } else { - type = 7; + contentType = type = 7; } } else if (message.media != null && message.media instanceof TLRPC.TL_messageMediaContact) { if (message.from_id == UserConfig.clientUserId) { - type = 12; + contentType = type = 12; } else { - type = 13; + contentType = type = 13; } } else if (message.media != null && message.media instanceof TLRPC.TL_messageMediaUnsupported) { - if (message.from_id == UserConfig.clientUserId) { - type = 0; - } else { - type = 1; - } + contentType = type = 0; } else if (message.media != null && message.media instanceof TLRPC.TL_messageMediaDocument) { - if (message.from_id == UserConfig.clientUserId) { - type = 16; + if (message.media.document.thumb != null && !(message.media.document.thumb instanceof TLRPC.TL_photoSizeEmpty) && message.media.document.mime_type != null && message.media.document.mime_type.equals("image/gif")) { + contentType = 1; + type = 8; } else { - type = 17; + if (message.from_id == UserConfig.clientUserId) { + contentType = type = 8; + } else { + contentType = type = 9; + } } } else if (message.media != null && message.media instanceof TLRPC.TL_messageMediaAudio) { - if (message.from_id == UserConfig.clientUserId) { - type = 18; - } else { - type = 19; - } + contentType = type = 2; } } else if (message instanceof TLRPC.TL_messageService) { if (message.action instanceof TLRPC.TL_messageActionLoginUnknownLocation) { - type = 1; + contentType = type = 0; } else if (message.action instanceof TLRPC.TL_messageActionChatEditPhoto || message.action instanceof TLRPC.TL_messageActionUserUpdatedPhoto) { - type = 11; + contentType = type = 11; } else { - type = 10; + contentType = type = 10; } } else if (message instanceof TLRPC.TL_messageForwarded) { - if (message.from_id == UserConfig.clientUserId) { - type = 8; - } else { - type = 9; - } + contentType = type = 0; } Calendar rightNow = new GregorianCalendar(); diff --git a/TMessagesProj/src/main/java/org/telegram/objects/PhotoObject.java b/TMessagesProj/src/main/java/org/telegram/objects/PhotoObject.java index 54731382..440cd256 100644 --- a/TMessagesProj/src/main/java/org/telegram/objects/PhotoObject.java +++ b/TMessagesProj/src/main/java/org/telegram/objects/PhotoObject.java @@ -60,6 +60,9 @@ public class PhotoObject { int closestHeight = 9999; TLRPC.PhotoSize closestObject = null; for (TLRPC.PhotoSize obj : sizes) { + if (obj == null) { + continue; + } int diffW = Math.abs(obj.w - width); int diffH = Math.abs(obj.h - height); if (closestObject == null || closestObject instanceof TLRPC.TL_photoCachedSize || closestWidth > diffW || closestHeight > diffH) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ApplicationLoader.java b/TMessagesProj/src/main/java/org/telegram/ui/ApplicationLoader.java index 4d947129..efc7560e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ApplicationLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ApplicationLoader.java @@ -21,6 +21,7 @@ import android.content.res.Configuration; import android.graphics.Bitmap; import android.os.AsyncTask; import android.os.Handler; +import android.os.PowerManager; import android.view.ViewConfiguration; import com.google.android.gms.common.ConnectionResult; @@ -56,6 +57,7 @@ public class ApplicationLoader extends Application { public static volatile Context applicationContext = null; public static volatile Handler applicationHandler = null; private static volatile boolean applicationInited = false; + public static volatile boolean isScreenOn = false; public static ArrayList fragmentsStack = new ArrayList(); @@ -63,10 +65,17 @@ public class ApplicationLoader extends Application { if (applicationInited) { return; } + applicationInited = true; NativeLoader.initNativeLibs(applicationContext); + try { + LocaleController.getInstance(); + } catch (Exception e) { + e.printStackTrace(); + } + try { final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_SCREEN_OFF); @@ -76,6 +85,14 @@ public class ApplicationLoader extends Application { e.printStackTrace(); } + try { + PowerManager pm = (PowerManager)ApplicationLoader.applicationContext.getSystemService(Context.POWER_SERVICE); + isScreenOn = pm.isScreenOn(); + FileLog.e("tmessages", "screen state = " + isScreenOn); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + UserConfig.loadConfig(); if (UserConfig.currentUser != null) { boolean changed = false; @@ -119,12 +136,6 @@ public class ApplicationLoader extends Application { super.onCreate(); lastPauseTime = System.currentTimeMillis(); applicationContext = getApplicationContext(); - NativeLoader.initNativeLibs(this); - try { - LocaleController.getInstance(); - } catch (Exception e) { - e.printStackTrace(); - } applicationHandler = new Handler(applicationContext.getMainLooper()); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatAudioCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatAudioCell.java index b6063301..386ef617 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatAudioCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatAudioCell.java @@ -60,8 +60,8 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega private TLRPC.FileLocation currentPhoto; private String currentNameString; - public ChatAudioCell(Context context, boolean isChat) { - super(context, isChat); + public ChatAudioCell(Context context) { + super(context, false); TAG = MediaController.getInstance().generateObserverTag(); avatarImage = new ImageReceiver(); @@ -94,6 +94,16 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega } } + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (avatarImage != null) { + avatarImage.clearImage(); + currentPhoto = null; + } + MediaController.getInstance().removeLoadingFileObserver(this); + } + @Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); @@ -221,7 +231,7 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega } progressView.setProgress(0); } else { - MediaController.getInstance().addLoadingFileObserver(currentMessageObject.getFileName(), this); + MediaController.getInstance().addLoadingFileObserver(fileName, this); if (!FileLoader.getInstance().isLoadingFile(fileName)) { buttonState = 2; progressView.setProgress(0); @@ -254,6 +264,11 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega invalidate(); } + @Override + public void onProgressUpload(String fileName, float progress, boolean isEncrypted) { + + } + @Override public int getObserverTag() { return TAG; @@ -272,7 +287,7 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); setMeasuredDimension(width, Utilities.dp(68)); - if (chat) { + if (isChat) { backgroundWidth = Math.min(width - Utilities.dp(102), Utilities.dp(300)); } else { backgroundWidth = Math.min(width - Utilities.dp(50), Utilities.dp(300)); @@ -289,7 +304,7 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega buttonX = layoutWidth - backgroundWidth + Utilities.dp(67); timeX = layoutWidth - backgroundWidth + Utilities.dp(71); } else { - if (chat) { + if (isChat) { avatarImage.imageX = Utilities.dp(69); seekBarX = Utilities.dp(158); buttonX = Utilities.dp(128); @@ -346,10 +361,10 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega if (messageObject.messageOwner.out) { seekBar.type = 0; - progressView.type = 0; + progressView.setProgressColors(0xffb4e396, 0xff6ac453); } else { seekBar.type = 1; - progressView.type = 1; + progressView.setProgressColors(0xffd9e2eb, 0xff86c5f8); } super.setMessageObject(messageObject); @@ -396,10 +411,4 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega timeLayout.draw(canvas); canvas.restore(); } - - @Override - protected void finalize() throws Throwable { - MediaController.getInstance().removeLoadingFileObserver(this); - super.finalize(); - } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatBaseCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatBaseCell.java index c5b4e94c..9ce7e17f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatBaseCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatBaseCell.java @@ -17,9 +17,11 @@ import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; +import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.SoundEffectConstants; import android.view.View; +import android.view.ViewConfiguration; import org.telegram.messenger.LocaleController; import org.telegram.messenger.TLRPC; @@ -28,6 +30,7 @@ import org.telegram.messenger.R; import org.telegram.messenger.Utilities; import org.telegram.objects.MessageObject; import org.telegram.ui.Views.ImageReceiver; +import org.telegram.ui.Views.OnSwipeTouchListener; import java.lang.ref.WeakReference; @@ -35,25 +38,41 @@ public class ChatBaseCell extends BaseCell { public static interface ChatBaseCellDelegate { public abstract void didPressedUserAvatar(ChatBaseCell cell, TLRPC.User user); + public abstract void didPressedCanceSendButton(ChatBaseCell cell); + public abstract void didLongPressed(ChatBaseCell cell); + public abstract boolean canPerformActions(); + public boolean onSwipeLeft(); + public boolean onSwipeRight(); } - protected boolean chat; + public boolean isChat = false; protected boolean isPressed = false; protected boolean forwardName = false; + protected boolean media = false; private boolean isCheckPressed = true; private boolean wasLayout = false; + protected boolean isAvatarVisible = false; protected MessageObject currentMessageObject; private static Drawable backgroundDrawableIn; private static Drawable backgroundDrawableInSelected; private static Drawable backgroundDrawableOut; private static Drawable backgroundDrawableOutSelected; + private static Drawable backgroundMediaDrawableIn; + private static Drawable backgroundMediaDrawableInSelected; + private static Drawable backgroundMediaDrawableOut; + private static Drawable backgroundMediaDrawableOutSelected; private static Drawable checkDrawable; private static Drawable halfCheckDrawable; private static Drawable clockDrawable; + private static Drawable checkMediaDrawable; + private static Drawable halfCheckMediaDrawable; + private static Drawable clockMediaDrawable; private static Drawable errorDrawable; + protected static Drawable mediaBackgroundDrawable; private static TextPaint timePaintIn; private static TextPaint timePaintOut; + private static TextPaint timeMediaPaint; private static TextPaint namePaint; private static TextPaint forwardNamePaint; @@ -95,14 +114,69 @@ public class ChatBaseCell extends BaseCell { protected int namesOffset = 0; - public ChatBaseCell(Context context, boolean isChat) { + private boolean checkingForLongPress = false; + private int pressCount = 0; + private CheckForLongPress pendingCheckForLongPress = null; + private CheckForTap pendingCheckForTap = null; + private OnSwipeTouchListener onSwipeTouchListener; + + private final class CheckForTap implements Runnable { + public void run() { + if (pendingCheckForLongPress == null) { + pendingCheckForLongPress = new CheckForLongPress(); + } + pendingCheckForLongPress.currentPressCount = ++pressCount; + postDelayed(pendingCheckForLongPress, ViewConfiguration.getLongPressTimeout() - ViewConfiguration.getTapTimeout()); + } + } + + class CheckForLongPress implements Runnable { + public int currentPressCount; + + public void run() { + if (checkingForLongPress && getParent() != null && currentPressCount == pressCount) { + if (delegate != null) { + checkingForLongPress = false; + MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0); + onTouchEvent(event); + event.recycle(); + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + delegate.didLongPressed(ChatBaseCell.this); + } + } + } + } + + public ChatBaseCell(Context context, boolean isMedia) { super(context); init(); - chat = isChat; - if (chat) { - avatarImage = new ImageReceiver(); - avatarImage.parentView = new WeakReference(this); - } + media = isMedia; + avatarImage = new ImageReceiver(); + avatarImage.parentView = new WeakReference(this); + onSwipeTouchListener = new OnSwipeTouchListener() { + public void onSwipeRight() { + if (delegate != null && delegate.onSwipeRight()) { + MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0); + onTouchEvent(event); + event.recycle(); + } + } + + public void onSwipeLeft() { + if (delegate != null && delegate.onSwipeLeft()) { + MotionEvent event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0); + onTouchEvent(event); + event.recycle(); + } + } + }; + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + avatarImage.clearImage(); + currentPhoto = null; } private void init() { @@ -111,10 +185,18 @@ public class ChatBaseCell extends BaseCell { backgroundDrawableInSelected = getResources().getDrawable(R.drawable.msg_in_selected); backgroundDrawableOut = getResources().getDrawable(R.drawable.msg_out); backgroundDrawableOutSelected = getResources().getDrawable(R.drawable.msg_out_selected); + backgroundMediaDrawableIn = getResources().getDrawable(R.drawable.msg_in_photo); + backgroundMediaDrawableInSelected = getResources().getDrawable(R.drawable.msg_in_photo_selected); + backgroundMediaDrawableOut = getResources().getDrawable(R.drawable.msg_out_photo); + backgroundMediaDrawableOutSelected = getResources().getDrawable(R.drawable.msg_out_photo_selected); checkDrawable = getResources().getDrawable(R.drawable.msg_check); halfCheckDrawable = getResources().getDrawable(R.drawable.msg_halfcheck); clockDrawable = getResources().getDrawable(R.drawable.msg_clock); + checkMediaDrawable = getResources().getDrawable(R.drawable.msg_check_w); + halfCheckMediaDrawable = getResources().getDrawable(R.drawable.msg_halfcheck_w); + clockMediaDrawable = getResources().getDrawable(R.drawable.msg_clock_photo); errorDrawable = getResources().getDrawable(R.drawable.msg_warning); + mediaBackgroundDrawable = getResources().getDrawable(R.drawable.phototime); timePaintIn = new TextPaint(TextPaint.ANTI_ALIAS_FLAG); timePaintIn.setTextSize(Utilities.dp(12)); @@ -124,6 +206,10 @@ public class ChatBaseCell extends BaseCell { timePaintOut.setTextSize(Utilities.dp(12)); timePaintOut.setColor(0xff70b15c); + timeMediaPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG); + timeMediaPaint.setTextSize(Utilities.dp(12)); + timeMediaPaint.setColor(0xffffffff); + namePaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG); namePaint.setTextSize(Utilities.dp(15)); @@ -151,7 +237,7 @@ public class ChatBaseCell extends BaseCell { TLRPC.User newUser = MessagesController.getInstance().users.get(currentMessageObject.messageOwner.from_id); TLRPC.FileLocation newPhoto = null; - if (avatarImage != null && newUser != null && newUser.photo != null) { + if (isAvatarVisible && newUser != null && newUser.photo != null) { newPhoto = newUser.photo.photo_small; } @@ -160,7 +246,7 @@ public class ChatBaseCell extends BaseCell { } String newNameString = null; - if (drawName && chat && newUser != null && !currentMessageObject.messageOwner.out) { + if (drawName && isChat && newUser != null && !currentMessageObject.messageOwner.out) { newNameString = Utilities.formatName(newUser.first_name, newUser.last_name); } @@ -180,6 +266,7 @@ public class ChatBaseCell extends BaseCell { currentMessageObject = messageObject; isPressed = false; isCheckPressed = true; + isAvatarVisible = false; wasLayout = false; if (currentMessageObject.messageOwner.id < 0 && currentMessageObject.messageOwner.send_state != MessagesController.MESSAGE_SEND_STATE_SEND_ERROR && currentMessageObject.messageOwner.send_state != MessagesController.MESSAGE_SEND_STATE_SENT) { @@ -189,7 +276,8 @@ public class ChatBaseCell extends BaseCell { } currentUser = MessagesController.getInstance().users.get(messageObject.messageOwner.from_id); - if (avatarImage != null) { + if (isChat && !messageObject.messageOwner.out) { + isAvatarVisible = true; if (currentUser != null) { if (currentUser.photo != null) { currentPhoto = currentUser.photo.photo_small; @@ -200,10 +288,14 @@ public class ChatBaseCell extends BaseCell { } } - if (currentMessageObject.messageOwner.out) { - currentTimePaint = timePaintOut; + if (!media) { + if (currentMessageObject.messageOwner.out) { + currentTimePaint = timePaintOut; + } else { + currentTimePaint = timePaintIn; + } } else { - currentTimePaint = timePaintIn; + currentTimePaint = timeMediaPaint; } currentTimeString = LocaleController.formatterDay.format((long) (currentMessageObject.messageOwner.date) * 1000); @@ -211,7 +303,7 @@ public class ChatBaseCell extends BaseCell { namesOffset = 0; - if (drawName && chat && currentUser != null && !currentMessageObject.messageOwner.out) { + if (drawName && isChat && currentUser != null && !currentMessageObject.messageOwner.out) { currentNameString = Utilities.formatName(currentUser.first_name, currentUser.last_name); nameWidth = getMaxNameWidth(); @@ -269,47 +361,82 @@ public class ChatBaseCell extends BaseCell { return backgroundWidth - Utilities.dp(8); } + protected void startCheckLongPress() { + if (checkingForLongPress) { + return; + } + checkingForLongPress = true; + if (pendingCheckForTap == null) { + pendingCheckForTap = new CheckForTap(); + } + postDelayed(pendingCheckForTap, ViewConfiguration.getTapTimeout()); + } + + protected void cancelCheckLongPress() { + checkingForLongPress = false; + if (pendingCheckForLongPress != null) { + removeCallbacks(pendingCheckForLongPress); + } + if (pendingCheckForTap != null) { + removeCallbacks(pendingCheckForTap); + } + } + + protected void checkSwipes(MotionEvent event) { + onSwipeTouchListener.onTouch(this, event); + } + @Override public boolean onTouchEvent(MotionEvent event) { boolean result = false; float x = event.getX(); float y = event.getY(); if (event.getAction() == MotionEvent.ACTION_DOWN) { - if (avatarImage != null && x >= avatarImage.imageX && x <= avatarImage.imageX + avatarImage.imageW && y >= avatarImage.imageY && y <= avatarImage.imageY + avatarImage.imageH) { - avatarPressed = true; - result = true; - } else if (drawForwardedName && forwardedNameLayout != null) { - if (x >= forwardNameX && x <= forwardNameX + forwardedNameWidth && y >= forwardNameY && y <= forwardNameY + Utilities.dp(32)) { - forwardNamePressed = true; + if (delegate == null || delegate.canPerformActions()) { + if (isAvatarVisible && x >= avatarImage.imageX && x <= avatarImage.imageX + avatarImage.imageW && y >= avatarImage.imageY && y <= avatarImage.imageY + avatarImage.imageH) { + avatarPressed = true; result = true; + } else if (drawForwardedName && forwardedNameLayout != null) { + if (x >= forwardNameX && x <= forwardNameX + forwardedNameWidth && y >= forwardNameY && y <= forwardNameY + Utilities.dp(32)) { + forwardNamePressed = true; + result = true; + } + } + if (result) { + startCheckLongPress(); } } - } else if (avatarPressed) { - if (event.getAction() == MotionEvent.ACTION_UP) { - avatarPressed = false; - playSoundEffect(SoundEffectConstants.CLICK); - if (delegate != null) { - delegate.didPressedUserAvatar(this, currentUser); - } - } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { - avatarPressed = false; - } else if (event.getAction() == MotionEvent.ACTION_MOVE) { - if (avatarImage != null && !(x >= avatarImage.imageX && x <= avatarImage.imageX + avatarImage.imageW && y >= avatarImage.imageY && y <= avatarImage.imageY + avatarImage.imageH)) { + } else { + if (event.getAction() != MotionEvent.ACTION_MOVE) { + cancelCheckLongPress(); + } + if (avatarPressed) { + if (event.getAction() == MotionEvent.ACTION_UP) { avatarPressed = false; + playSoundEffect(SoundEffectConstants.CLICK); + if (delegate != null) { + delegate.didPressedUserAvatar(this, currentUser); + } + } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { + avatarPressed = false; + } else if (event.getAction() == MotionEvent.ACTION_MOVE) { + if (isAvatarVisible && !(x >= avatarImage.imageX && x <= avatarImage.imageX + avatarImage.imageW && y >= avatarImage.imageY && y <= avatarImage.imageY + avatarImage.imageH)) { + avatarPressed = false; + } } - } - } else if (forwardNamePressed) { - if (event.getAction() == MotionEvent.ACTION_UP) { - forwardNamePressed = false; - playSoundEffect(SoundEffectConstants.CLICK); - if (delegate != null) { - delegate.didPressedUserAvatar(this, currentForwardUser); - } - } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { - forwardNamePressed = false; - } else if (event.getAction() == MotionEvent.ACTION_MOVE) { - if (!(x >= forwardNameX && x <= forwardNameX + forwardedNameWidth && y >= forwardNameY && y <= forwardNameY + Utilities.dp(32))) { + } else if (forwardNamePressed) { + if (event.getAction() == MotionEvent.ACTION_UP) { forwardNamePressed = false; + playSoundEffect(SoundEffectConstants.CLICK); + if (delegate != null) { + delegate.didPressedUserAvatar(this, currentForwardUser); + } + } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { + forwardNamePressed = false; + } else if (event.getAction() == MotionEvent.ACTION_MOVE) { + if (!(x >= forwardNameX && x <= forwardNameX + forwardedNameWidth && y >= forwardNameY && y <= forwardNameY + Utilities.dp(32))) { + forwardNamePressed = false; + } } } } @@ -329,13 +456,21 @@ public class ChatBaseCell extends BaseCell { layoutHeight = getMeasuredHeight(); timeLayout = new StaticLayout(currentTimeString, currentTimePaint, timeWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); - if (!currentMessageObject.messageOwner.out) { - timeX = backgroundWidth - Utilities.dp(9) - timeWidth + (chat ? Utilities.dp(52) : 0); + if (!media) { + if (!currentMessageObject.messageOwner.out) { + timeX = backgroundWidth - Utilities.dp(9) - timeWidth + (isChat ? Utilities.dp(52) : 0); + } else { + timeX = layoutWidth - timeWidth - Utilities.dpf(38.5f); + } } else { - timeX = layoutWidth - timeWidth - Utilities.dpf(38.5f); + if (!currentMessageObject.messageOwner.out) { + timeX = backgroundWidth - Utilities.dp(4) - timeWidth + (isChat ? Utilities.dp(52) : 0); + } else { + timeX = layoutWidth - timeWidth - Utilities.dpf(42.0f); + } } - if (avatarImage != null) { + if (isAvatarVisible) { avatarImage.imageX = Utilities.dp(6); avatarImage.imageY = layoutHeight - Utilities.dp(45); avatarImage.imageW = Utilities.dp(42); @@ -346,6 +481,11 @@ public class ChatBaseCell extends BaseCell { } } + protected void onAfterBackgroundDraw(Canvas canvas) { + + } + + @Override protected void onDraw(Canvas canvas) { if (currentMessageObject == null) { @@ -353,36 +493,54 @@ public class ChatBaseCell extends BaseCell { } if (!wasLayout) { - requestFocus(); + requestLayout(); return; } - if (avatarImage != null) { + if (isAvatarVisible) { avatarImage.draw(canvas, Utilities.dp(6), layoutHeight - Utilities.dp(45), Utilities.dp(42), Utilities.dp(42)); } Drawable currentBackgroundDrawable = null; if (currentMessageObject.messageOwner.out) { if (isPressed() && isCheckPressed || !isCheckPressed && isPressed) { - currentBackgroundDrawable = backgroundDrawableOutSelected; + if (!media) { + currentBackgroundDrawable = backgroundDrawableOutSelected; + } else { + currentBackgroundDrawable = backgroundMediaDrawableOutSelected; + } } else { - currentBackgroundDrawable = backgroundDrawableOut; + if (!media) { + currentBackgroundDrawable = backgroundDrawableOut; + } else { + currentBackgroundDrawable = backgroundMediaDrawableOut; + } } - setDrawableBounds(currentBackgroundDrawable, layoutWidth - backgroundWidth, Utilities.dp(1), backgroundWidth, layoutHeight - Utilities.dp(2)); + setDrawableBounds(currentBackgroundDrawable, layoutWidth - backgroundWidth - (!media ? 0 : Utilities.dp(9)), Utilities.dp(1), backgroundWidth, layoutHeight - Utilities.dp(2)); } else { if (isPressed() && isCheckPressed || !isCheckPressed && isPressed) { - currentBackgroundDrawable = backgroundDrawableInSelected; + if (!media) { + currentBackgroundDrawable = backgroundDrawableInSelected; + } else { + currentBackgroundDrawable = backgroundMediaDrawableInSelected; + } } else { - currentBackgroundDrawable = backgroundDrawableIn; + if (!media) { + currentBackgroundDrawable = backgroundDrawableIn; + } else { + currentBackgroundDrawable = backgroundMediaDrawableIn; + } } - if (chat) { - setDrawableBounds(currentBackgroundDrawable, Utilities.dp(52), Utilities.dp(1), backgroundWidth, layoutHeight - Utilities.dp(2)); + if (isChat) { + setDrawableBounds(currentBackgroundDrawable, Utilities.dp(52 + (!media ? 0 : 9)), Utilities.dp(1), backgroundWidth, layoutHeight - Utilities.dp(2)); } else { - setDrawableBounds(currentBackgroundDrawable, 0, Utilities.dp(1), backgroundWidth, layoutHeight - Utilities.dp(2)); + setDrawableBounds(currentBackgroundDrawable, (!media ? 0 : Utilities.dp(9)), Utilities.dp(1), backgroundWidth, layoutHeight - Utilities.dp(2)); } } currentBackgroundDrawable.draw(canvas); + onAfterBackgroundDraw(canvas); + if (drawName && nameLayout != null) { canvas.save(); canvas.translate(currentBackgroundDrawable.getBounds().left + Utilities.dp(19) - nameOffsetX, Utilities.dp(10)); @@ -407,10 +565,20 @@ public class ChatBaseCell extends BaseCell { canvas.restore(); } - canvas.save(); - canvas.translate(timeX, layoutHeight - Utilities.dpf(6.5f) - timeLayout.getHeight()); - timeLayout.draw(canvas); - canvas.restore(); + if (media) { + setDrawableBounds(mediaBackgroundDrawable, timeX - Utilities.dp(3), layoutHeight - Utilities.dpf(27.5f), timeWidth + Utilities.dp(6 + (currentMessageObject.messageOwner.out ? 20 : 0)), Utilities.dpf(16.5f)); + mediaBackgroundDrawable.draw(canvas); + + canvas.save(); + canvas.translate(timeX, layoutHeight - Utilities.dpf(12.0f) - timeLayout.getHeight()); + timeLayout.draw(canvas); + canvas.restore(); + } else { + canvas.save(); + canvas.translate(timeX, layoutHeight - Utilities.dpf(6.5f) - timeLayout.getHeight()); + timeLayout.draw(canvas); + canvas.restore(); + } if (currentMessageObject.messageOwner.out) { boolean drawCheck1 = false; @@ -441,24 +609,48 @@ public class ChatBaseCell extends BaseCell { } if (drawClock) { - setDrawableBounds(clockDrawable, layoutWidth - Utilities.dpf(18.5f) - clockDrawable.getIntrinsicWidth(), layoutHeight - Utilities.dpf(8.5f) - clockDrawable.getIntrinsicHeight()); - clockDrawable.draw(canvas); + if (!media) { + setDrawableBounds(clockDrawable, layoutWidth - Utilities.dpf(18.5f) - clockDrawable.getIntrinsicWidth(), layoutHeight - Utilities.dpf(8.5f) - clockDrawable.getIntrinsicHeight()); + clockDrawable.draw(canvas); + } else { + setDrawableBounds(clockMediaDrawable, layoutWidth - Utilities.dpf(22.0f) - clockMediaDrawable.getIntrinsicWidth(), layoutHeight - Utilities.dpf(13.0f) - clockMediaDrawable.getIntrinsicHeight()); + clockMediaDrawable.draw(canvas); + } } if (drawCheck2) { - if (drawCheck1) { - setDrawableBounds(checkDrawable, layoutWidth - Utilities.dpf(22.5f) - checkDrawable.getIntrinsicWidth(), layoutHeight - Utilities.dpf(8.5f) - checkDrawable.getIntrinsicHeight()); + if (!media) { + if (drawCheck1) { + setDrawableBounds(checkDrawable, layoutWidth - Utilities.dpf(22.5f) - checkDrawable.getIntrinsicWidth(), layoutHeight - Utilities.dpf(8.5f) - checkDrawable.getIntrinsicHeight()); + } else { + setDrawableBounds(checkDrawable, layoutWidth - Utilities.dpf(18.5f) - checkDrawable.getIntrinsicWidth(), layoutHeight - Utilities.dpf(8.5f) - checkDrawable.getIntrinsicHeight()); + } + checkDrawable.draw(canvas); } else { - setDrawableBounds(checkDrawable, layoutWidth - Utilities.dpf(18.5f) - checkDrawable.getIntrinsicWidth(), layoutHeight - Utilities.dpf(8.5f) - checkDrawable.getIntrinsicHeight()); + if (drawCheck1) { + setDrawableBounds(checkMediaDrawable, layoutWidth - Utilities.dpf(26.0f) - checkMediaDrawable.getIntrinsicWidth(), layoutHeight - Utilities.dpf(13.0f) - checkMediaDrawable.getIntrinsicHeight()); + } else { + setDrawableBounds(checkMediaDrawable, layoutWidth - Utilities.dpf(22.0f) - checkMediaDrawable.getIntrinsicWidth(), layoutHeight - Utilities.dpf(13.0f) - checkMediaDrawable.getIntrinsicHeight()); + } + checkMediaDrawable.draw(canvas); } - checkDrawable.draw(canvas); } if (drawCheck1) { - setDrawableBounds(halfCheckDrawable, layoutWidth - Utilities.dp(18) - halfCheckDrawable.getIntrinsicWidth(), layoutHeight - Utilities.dpf(8.5f) - halfCheckDrawable.getIntrinsicHeight()); - halfCheckDrawable.draw(canvas); + if (!media) { + setDrawableBounds(halfCheckDrawable, layoutWidth - Utilities.dp(18) - halfCheckDrawable.getIntrinsicWidth(), layoutHeight - Utilities.dpf(8.5f) - halfCheckDrawable.getIntrinsicHeight()); + halfCheckDrawable.draw(canvas); + } else { + setDrawableBounds(halfCheckMediaDrawable, layoutWidth - Utilities.dpf(20.5f) - halfCheckMediaDrawable.getIntrinsicWidth(), layoutHeight - Utilities.dpf(13.0f) - halfCheckMediaDrawable.getIntrinsicHeight()); + halfCheckMediaDrawable.draw(canvas); + } } if (drawError) { - setDrawableBounds(errorDrawable, layoutWidth - Utilities.dp(18) - errorDrawable.getIntrinsicWidth(), layoutHeight - Utilities.dpf(6.5f) - errorDrawable.getIntrinsicHeight()); - errorDrawable.draw(canvas); + if (!media) { + setDrawableBounds(errorDrawable, layoutWidth - Utilities.dp(18) - errorDrawable.getIntrinsicWidth(), layoutHeight - Utilities.dpf(6.5f) - errorDrawable.getIntrinsicHeight()); + errorDrawable.draw(canvas); + } else { + setDrawableBounds(errorDrawable, layoutWidth - Utilities.dpf(20.5f) - errorDrawable.getIntrinsicWidth(), layoutHeight - Utilities.dpf(12.5f) - errorDrawable.getIntrinsicHeight()); + errorDrawable.draw(canvas); + } } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMediaCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMediaCell.java new file mode 100644 index 00000000..2d9aa536 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMediaCell.java @@ -0,0 +1,537 @@ +/* + * This is the source code of Telegram for Android v. 1.4.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2014. + */ + +package org.telegram.ui.Cells; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.view.MotionEvent; +import android.view.SoundEffectConstants; +import android.view.View; + +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.MediaController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.R; +import org.telegram.messenger.Utilities; +import org.telegram.objects.MessageObject; +import org.telegram.objects.PhotoObject; +import org.telegram.ui.Views.GifDrawable; +import org.telegram.ui.Views.ImageReceiver; +import org.telegram.ui.Views.ProgressView; + +import java.io.File; +import java.lang.ref.WeakReference; +import java.util.Locale; + +public class ChatMediaCell extends ChatBaseCell implements MediaController.FileDownloadProgressListener { + + public static interface ChatMediaCellDelegate { + public abstract void didPressedImage(ChatBaseCell cell); + } + + private static Drawable placeholderInDrawable; + private static Drawable placeholderOutDrawable; + private static Drawable[][] buttonStatesDrawables = new Drawable[3][2]; + private static TextPaint infoPaint; + + private GifDrawable gifDrawable = null; + + private int photoWidth; + private int photoHeight; + private PhotoObject currentPhotoObject; + private String currentPhotoFilter; + private ImageReceiver photoImage; + private ProgressView progressView; + public boolean downloadPhotos = true; + private boolean progressVisible = false; + + private int TAG; + + private int buttonState = 0; + private int buttonPressed = 0; + private boolean imagePressed = false; + private int buttonX; + private int buttonY; + + private StaticLayout infoLayout; + protected int infoWidth; + private String currentInfoString; + + public ChatMediaCellDelegate mediaDelegate = null; + + public ChatMediaCell(Context context) { + super(context, true); + + if (placeholderInDrawable == null) { + placeholderInDrawable = getResources().getDrawable(R.drawable.photo_placeholder_in); + placeholderOutDrawable = getResources().getDrawable(R.drawable.photo_placeholder_out); + buttonStatesDrawables[0][0] = getResources().getDrawable(R.drawable.photoload); + buttonStatesDrawables[0][1] = getResources().getDrawable(R.drawable.photoload_pressed); + buttonStatesDrawables[1][0] = getResources().getDrawable(R.drawable.photocancel); + buttonStatesDrawables[1][1] = getResources().getDrawable(R.drawable.photocancel_pressed); + buttonStatesDrawables[2][0] = getResources().getDrawable(R.drawable.photogif); + buttonStatesDrawables[2][1] = getResources().getDrawable(R.drawable.photogif_pressed); + + infoPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + infoPaint.setColor(0xffffffff); + infoPaint.setTextSize(Utilities.dp(12)); + } + + TAG = MediaController.getInstance().generateObserverTag(); + + photoImage = new ImageReceiver(); + photoImage.parentView = new WeakReference(this); + progressView = new ProgressView(); + progressView.setProgressColors(0x802a2a2a, 0xffffffff); + } + + public void clearGifImage() { + if (currentMessageObject != null && currentMessageObject.type == 8) { + gifDrawable = null; + buttonState = 2; + invalidate(); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (photoImage != null) { + photoImage.clearImage(); + currentPhotoObject = null; + } + if (gifDrawable != null) { + MediaController.getInstance().clearGifDrawable(this); + gifDrawable = null; + } + MediaController.getInstance().removeLoadingFileObserver(this); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + float x = event.getX(); + float y = event.getY(); + + boolean result = false; + int side = Utilities.dp(44); + checkSwipes(event); + if (event.getAction() == MotionEvent.ACTION_DOWN) { + if (delegate == null || delegate.canPerformActions()) { + if (buttonState != -1 && x >= buttonX && x <= buttonX + side && y >= buttonY && y <= buttonY + side) { + buttonPressed = 1; + invalidate(); + result = true; + } else if (x >= photoImage.imageX && x <= photoImage.imageX + photoImage.imageW && y >= photoImage.imageY && y <= photoImage.imageY + photoImage.imageH) { + imagePressed = true; + result = true; + } + if (result) { + startCheckLongPress(); + } + } + } else { + if (event.getAction() != MotionEvent.ACTION_MOVE) { + cancelCheckLongPress(); + } + if (buttonPressed == 1) { + if (event.getAction() == MotionEvent.ACTION_UP) { + buttonPressed = 0; + playSoundEffect(SoundEffectConstants.CLICK); + didPressedButton(); + invalidate(); + } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { + buttonPressed = 0; + invalidate(); + } else if (event.getAction() == MotionEvent.ACTION_MOVE) { + if (!(x >= buttonX && x <= buttonX + side && y >= buttonY && y <= buttonY + side)) { + buttonPressed = 0; + invalidate(); + } + } + } else if (imagePressed) { + if (event.getAction() == MotionEvent.ACTION_UP) { + imagePressed = false; + playSoundEffect(SoundEffectConstants.CLICK); + didPressedImage(); + invalidate(); + } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { + imagePressed = false; + invalidate(); + } else if (event.getAction() == MotionEvent.ACTION_MOVE) { + if (!(x >= photoImage.imageX && x <= photoImage.imageX + photoImage.imageW && y >= photoImage.imageY && y <= photoImage.imageY + photoImage.imageH)) { + imagePressed = false; + invalidate(); + } + } + } + } + if (!result) { + result = super.onTouchEvent(event); + } + + return result; + } + + private void didPressedImage() { + if (currentMessageObject.type == 1) { + if (buttonState == -1) { + if (currentMessageObject.type == 1) { + if (mediaDelegate != null) { + mediaDelegate.didPressedImage(this); + } + } + } else if (buttonState == 0) { + didPressedButton(); + } + } else if (currentMessageObject.type == 8) { + if (buttonState == -1) { + buttonState = 2; + if (gifDrawable != null) { + gifDrawable.pause(); + } + invalidate(); + } else if (buttonState == 2 || buttonState == 0) { + didPressedButton(); + } + } + } + + private void didPressedButton() { + if (buttonState == 0) { + if (currentMessageObject.type == 1) { + if (currentMessageObject.imagePreview != null) { + photoImage.setImage(currentPhotoObject.photoOwner.location, currentPhotoFilter, new BitmapDrawable(currentMessageObject.imagePreview), currentPhotoObject.photoOwner.size); + } else { + photoImage.setImage(currentPhotoObject.photoOwner.location, currentPhotoFilter, currentMessageObject.messageOwner.out ? placeholderOutDrawable : placeholderInDrawable, currentPhotoObject.photoOwner.size); + } + } else if (currentMessageObject.type == 8) { + FileLoader.getInstance().loadFile(null, null, currentMessageObject.messageOwner.media.document, null); + } + progressVisible = true; + buttonState = 1; + invalidate(); + } else if (buttonState == 1) { + if (currentMessageObject.messageOwner.out && currentMessageObject.messageOwner.send_state == MessagesController.MESSAGE_SEND_STATE_SENDING) { + if (delegate != null) { + delegate.didPressedCanceSendButton(this); + } + } else { + if (currentMessageObject.type == 1) { + FileLoader.getInstance().cancelLoadingForImageView(photoImage); + } else if (currentMessageObject.type == 8) { + FileLoader.getInstance().cancelLoadFile(null, null, currentMessageObject.messageOwner.media.document, null); + } + progressVisible = false; + buttonState = 0; + invalidate(); + } + } else if (buttonState == 2) { + if (gifDrawable == null) { + gifDrawable = MediaController.getInstance().getGifDrawable(this, true); + } + if (gifDrawable != null) { + gifDrawable.start(); + gifDrawable.invalidateSelf(); + buttonState = -1; + invalidate(); + } + } + } + + @Override + protected boolean isUserDataChanged() { + return currentPhotoObject == null || super.isUserDataChanged(); + } + + @Override + public void setMessageObject(MessageObject messageObject) { + if (currentMessageObject != messageObject || isUserDataChanged()) { + super.setMessageObject(messageObject); + + progressVisible = false; + buttonState = -1; + gifDrawable = null; + + if (messageObject.type == 8) { + gifDrawable = MediaController.getInstance().getGifDrawable(this, false); + + String str = Utilities.formatFileSize(messageObject.messageOwner.media.document.size); + if (currentInfoString == null || !currentInfoString.equals(str)) { + currentInfoString = str; + infoWidth = (int) Math.ceil(infoPaint.measureText(currentInfoString)); + infoLayout = new StaticLayout(currentInfoString, infoPaint, infoWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + } + } else { + currentInfoString = null; + infoLayout = null; + } + + photoWidth = (int) (Math.min(Utilities.displaySize.x, Utilities.displaySize.y) * 0.7f); + photoHeight = photoWidth + Utilities.dp(100); + if (messageObject.type == 6 || messageObject.type == 7) { + photoWidth = (int) (Math.min(Utilities.displaySize.x, Utilities.displaySize.y) / 2.5f); + photoHeight = photoWidth + 100; + } + if (photoWidth > 800) { + photoWidth = 800; + } + if (photoHeight > 800) { + photoHeight = 800; + } + + currentPhotoObject = PhotoObject.getClosestImageWithSize(messageObject.photoThumbs, photoWidth, photoHeight); + if (currentPhotoObject != null) { + float scale = (float) currentPhotoObject.photoOwner.w / (float) photoWidth; + + int w = (int) (currentPhotoObject.photoOwner.w / scale); + int h = (int) (currentPhotoObject.photoOwner.h / scale); + if (h > photoHeight) { + float scale2 = h; + h = photoHeight; + scale2 /= h; + w = (int) (w / scale2); + } else if (h < Utilities.dp(120)) { + h = Utilities.dp(120); + float hScale = (float) currentPhotoObject.photoOwner.h / h; + if (currentPhotoObject.photoOwner.w / hScale < photoWidth) { + w = (int) (currentPhotoObject.photoOwner.w / hScale); + } + } + + photoWidth = w; + photoHeight = h; + backgroundWidth = w + Utilities.dp(12); + currentPhotoFilter = String.format(Locale.US, "%d_%d", (int) (w / Utilities.density), (int) (h / Utilities.density)); + + if (currentPhotoObject.image != null) { + photoImage.setImageBitmap(currentPhotoObject.image); + } else { + boolean photoExist = true; + String fileName = MessageObject.getAttachFileName(currentPhotoObject.photoOwner); + if (messageObject.type == 1) { + File cacheFile = new File(Utilities.getCacheDir(), fileName); + if (!cacheFile.exists()) { + photoExist = false; + } else { + MediaController.getInstance().removeLoadingFileObserver(this); + } + } + if (photoExist || downloadPhotos) { + if (messageObject.imagePreview != null) { + photoImage.setImage(currentPhotoObject.photoOwner.location, currentPhotoFilter, new BitmapDrawable(messageObject.imagePreview), currentPhotoObject.photoOwner.size); + } else { + photoImage.setImage(currentPhotoObject.photoOwner.location, currentPhotoFilter, messageObject.messageOwner.out ? placeholderOutDrawable : placeholderInDrawable, currentPhotoObject.photoOwner.size); + } + } else { + if (messageObject.imagePreview != null) { + photoImage.setImageBitmap(messageObject.imagePreview); + } else { + photoImage.setImageBitmap(messageObject.messageOwner.out ? placeholderOutDrawable : placeholderInDrawable); + } + } + } + } else { + photoImage.setImageBitmap(messageObject.messageOwner.out ? placeholderOutDrawable : placeholderInDrawable); + } + + invalidate(); + /*if ((type == 6 || type == 7) && videoTimeText != null) { + int duration = message.messageOwner.media.video.duration; + int minutes = duration / 60; + int seconds = duration - minutes * 60; + videoTimeText.setText(String.format("%d:%02d", minutes, seconds)); + }*/ + } + updateButtonState(); + } + + public void updateButtonState() { + String fileName = null; + File cacheFile = null; + if (currentMessageObject.type == 1) { + if (currentPhotoObject == null) { + return; + } + fileName = MessageObject.getAttachFileName(currentPhotoObject.photoOwner); + cacheFile = new File(Utilities.getCacheDir(), fileName); + } else if (currentMessageObject.type == 8) { + if (currentMessageObject.messageOwner.attachPath != null && currentMessageObject.messageOwner.attachPath.length() != 0) { + File f = new File(currentMessageObject.messageOwner.attachPath); + if (f.exists()) { + fileName = currentMessageObject.messageOwner.attachPath; + cacheFile = f; + } + } else { + fileName = currentMessageObject.getFileName(); + cacheFile = new File(Utilities.getCacheDir(), fileName); + } + } + if (fileName == null) { + return; + } + if (currentMessageObject.messageOwner.out && currentMessageObject.messageOwner.send_state == MessagesController.MESSAGE_SEND_STATE_SENDING) { + if (currentMessageObject.messageOwner.attachPath != null) { + MediaController.getInstance().addLoadingFileObserver(currentMessageObject.messageOwner.attachPath, this); + progressVisible = true; + buttonState = 1; + Float progress = FileLoader.getInstance().fileProgresses.get(currentMessageObject.messageOwner.attachPath); + if (progress != null) { + progressView.setProgress(progress); + } else { + progressView.setProgress(0); + } + } + } else { + if (currentMessageObject.messageOwner.attachPath != null) { + MediaController.getInstance().removeLoadingFileObserver(this); + } + if (cacheFile.exists() && cacheFile.length() == 0) { + cacheFile.delete(); + } + if (!cacheFile.exists()) { + MediaController.getInstance().addLoadingFileObserver(fileName, this); + if (!FileLoader.getInstance().isLoadingFile(fileName)) { + if (currentMessageObject.type != 1 || !downloadPhotos) { + buttonState = 0; + progressVisible = false; + } else { + buttonState = -1; + progressVisible = true; + } + progressView.setProgress(0); + } else { + if (currentMessageObject.type != 1 || !downloadPhotos) { + buttonState = 1; + } else { + buttonState = -1; + } + progressVisible = true; + Float progress = FileLoader.getInstance().fileProgresses.get(fileName); + if (progress != null) { + progressView.setProgress(progress); + } else { + progressView.setProgress(0); + } + } + } else { + MediaController.getInstance().removeLoadingFileObserver(this); + progressVisible = false; + if (currentMessageObject.type == 8 && (gifDrawable == null || gifDrawable != null && !gifDrawable.isRunning())) { + buttonState = 2; + } else { + buttonState = -1; + } + invalidate(); + } + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), photoHeight + Utilities.dp(14)); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + + if (currentMessageObject.messageOwner.out) { + photoImage.imageX = layoutWidth - backgroundWidth - Utilities.dp(3); + } else { + if (isChat) { + photoImage.imageX = Utilities.dp(67); + } else { + photoImage.imageX = Utilities.dp(15); + } + } + photoImage.imageY = Utilities.dp(7); + photoImage.imageW = photoWidth; + photoImage.imageH = photoHeight; + + progressView.width = timeX - photoImage.imageX - Utilities.dpf(23.0f); + progressView.height = Utilities.dp(3); + progressView.progressHeight = Utilities.dp(3); + + int size = Utilities.dp(44); + buttonX = (int)(photoImage.imageX + (photoWidth - size) / 2.0f); + buttonY = (int)(photoImage.imageY + (photoHeight - size) / 2.0f); + } + + @Override + protected void onAfterBackgroundDraw(Canvas canvas) { + if (gifDrawable != null) { + canvas.save(); + gifDrawable.setBounds(photoImage.imageX, photoImage.imageY, photoImage.imageX + photoWidth, photoImage.imageY + photoHeight); + gifDrawable.draw(canvas); + canvas.restore(); + } else { + photoImage.draw(canvas, photoImage.imageX, photoImage.imageY, photoWidth, photoHeight); + } + + if (progressVisible) { + setDrawableBounds(mediaBackgroundDrawable, photoImage.imageX + Utilities.dp(4), layoutHeight - Utilities.dpf(27.5f), progressView.width + Utilities.dp(12), Utilities.dpf(16.5f)); + mediaBackgroundDrawable.draw(canvas); + + canvas.save(); + canvas.translate(photoImage.imageX + Utilities.dp(10), layoutHeight - Utilities.dpf(21.0f)); + progressView.draw(canvas); + canvas.restore(); + } + + if (buttonState >= 0 && buttonState < 3) { + Drawable currentButtonDrawable = buttonStatesDrawables[buttonState][buttonPressed]; + setDrawableBounds(currentButtonDrawable, buttonX, buttonY); + currentButtonDrawable.draw(canvas); + } + + if (infoLayout != null && (buttonState == 1 || buttonState == 0)) { + setDrawableBounds(mediaBackgroundDrawable, photoImage.imageX + Utilities.dp(4), photoImage.imageY + Utilities.dp(4), infoWidth + Utilities.dp(8), Utilities.dpf(16.5f)); + mediaBackgroundDrawable.draw(canvas); + + canvas.save(); + canvas.translate(photoImage.imageX + Utilities.dp(8), photoImage.imageY + Utilities.dpf(5.5f)); + infoLayout.draw(canvas); + canvas.restore(); + } + } + + @Override + public void onFailedDownload(String fileName) { + updateButtonState(); + } + + @Override + public void onSuccessDownload(String fileName) { + updateButtonState(); + } + + @Override + public void onProgressDownload(String fileName, float progress) { + progressVisible = true; + progressView.setProgress(progress); + invalidate(); + } + + @Override + public void onProgressUpload(String fileName, float progress, boolean isEncrypted) { + progressView.setProgress(progress); + invalidate(); + } + + @Override + public int getObserverTag() { + return TAG; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java index 0ed5388a..3055cddc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java @@ -28,8 +28,8 @@ public class ChatMessageCell extends ChatBaseCell { private int firstVisibleBlockNum = 0; private int totalVisibleBlocksCount = 0; - public ChatMessageCell(Context context, boolean isChat) { - super(context, isChat); + public ChatMessageCell(Context context) { + super(context, false); drawForwardedName = true; } @@ -131,7 +131,7 @@ public class ChatMessageCell extends ChatBaseCell { } pressedLink = null; int maxWidth; - if (chat) { + if (isChat && !messageObject.messageOwner.out) { maxWidth = Utilities.displaySize.x - Utilities.dp(122); drawName = true; } else { @@ -180,7 +180,7 @@ public class ChatMessageCell extends ChatBaseCell { textX = layoutWidth - backgroundWidth + Utilities.dp(10); textY = Utilities.dp(10) + namesOffset; } else { - textX = Utilities.dp(19) + (chat ? Utilities.dp(52) : 0); + textX = Utilities.dp(19) + (isChat ? Utilities.dp(52) : 0); textY = Utilities.dp(10) + namesOffset; } } @@ -196,7 +196,7 @@ public class ChatMessageCell extends ChatBaseCell { textX = layoutWidth - backgroundWidth + Utilities.dp(10); textY = Utilities.dp(10) + namesOffset; } else { - textX = Utilities.dp(19) + (chat ? Utilities.dp(52) : 0); + textX = Utilities.dp(19) + (isChat ? Utilities.dp(52) : 0); textY = Utilities.dp(10) + namesOffset; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatOrUserCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatOrUserCell.java index 93e70064..5dd1ca49 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatOrUserCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatOrUserCell.java @@ -114,6 +114,15 @@ public class ChatOrUserCell extends BaseCell { update(0); } + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (avatarImage != null) { + avatarImage.clearImage(); + lastAvatar = null; + } + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), Utilities.dp(64)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java index b89617a4..df5e8bc8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java @@ -152,6 +152,14 @@ public class DialogCell extends BaseCell { update(0); } + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (avatarImage != null) { + avatarImage.clearImage(); + } + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), Utilities.dp(70)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java index 2a4eabbd..442cc4b3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java @@ -46,6 +46,7 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; +import android.view.SubMenu; import android.view.Surface; import android.view.View; import android.view.ViewGroup; @@ -88,6 +89,7 @@ import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; import org.telegram.ui.Cells.ChatAudioCell; import org.telegram.ui.Cells.ChatBaseCell; +import org.telegram.ui.Cells.ChatMediaCell; import org.telegram.ui.Cells.ChatMessageCell; import org.telegram.ui.Views.BackupImageView; import org.telegram.ui.Views.BaseFragment; @@ -104,6 +106,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Locale; +import java.util.concurrent.Semaphore; public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLayout.SizeNotifierRelativeLayoutDelegate, NotificationCenter.NotificationCenterDelegate, MessagesActivity.MessagesActivityDelegate, DocumentSelectActivity.DocumentSelectActivityDelegate { private LayoutListView chatListView; @@ -195,6 +198,18 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa private CharSequence lastPrintString; + private final static int copy = 1; + private final static int forward = 2; + private final static int delete = 3; + private final static int chat_enc_timer = 4; + private final static int chat_menu_attach = 5; + private final static int attach_photo = 6; + private final static int attach_gallery = 7; + private final static int attach_video = 8; + private final static int attach_document = 9; + private final static int attach_location = 10; + private final static int chat_menu_avatar = 11; + ActionMode mActionMode = null; private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() { @Override @@ -202,11 +217,14 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa menu.clear(); MenuInflater inflater = actionMode.getMenuInflater(); if (currentEncryptedChat == null) { - inflater.inflate(R.menu.messages_full_menu, menu); + menu.add(Menu.NONE, copy, Menu.NONE, LocaleController.getString("Copy", R.string.Copy)).setIcon(R.drawable.ic_ab_fwd_copy); + menu.add(Menu.NONE, forward, Menu.NONE, LocaleController.getString("Forward", R.string.Forward)).setIcon(R.drawable.ic_ab_fwd_forward); + menu.add(Menu.NONE, delete, Menu.NONE, LocaleController.getString("Delete", R.string.Delete)).setIcon(R.drawable.ic_ab_fwd_delete); } else { - inflater.inflate(R.menu.messages_encrypted_menu, menu); + menu.add(Menu.NONE, copy, Menu.NONE, LocaleController.getString("Copy", R.string.Copy)).setIcon(R.drawable.ic_ab_fwd_copy); + menu.add(Menu.NONE, delete, Menu.NONE, LocaleController.getString("Delete", R.string.Delete)).setIcon(R.drawable.ic_ab_fwd_delete); } - menu.findItem(R.id.copy).setVisible(selectedMessagesCanCopyIds.size() != 0); + menu.findItem(copy).setVisible(selectedMessagesCanCopyIds.size() != 0); return true; } @@ -218,7 +236,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa @Override public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) { switch (menuItem.getItemId()) { - case R.id.copy: { + case copy: { String str = ""; ArrayList ids = new ArrayList(selectedMessagesCanCopyIds.keySet()); if (currentEncryptedChat == null) { @@ -245,7 +263,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } break; } - case R.id.delete: { + case delete: { ArrayList ids = new ArrayList(selectedMessagesIds.keySet()); ArrayList random_ids = null; if (currentEncryptedChat != null) { @@ -260,7 +278,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa MessagesController.getInstance().deleteMessages(ids, random_ids, currentEncryptedChat); break; } - case R.id.forward: { + case forward: { MessagesActivity fragment = new MessagesActivity(); fragment.selectAlertString = R.string.ForwardMessagesTo; fragment.selectAlertStringDesc = "ForwardMessagesTo"; @@ -288,31 +306,99 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa @Override public boolean onFragmentCreate() { super.onFragmentCreate(); - int chatId = getArguments().getInt("chat_id", 0); - int userId = getArguments().getInt("user_id", 0); - int encId = getArguments().getInt("enc_id", 0); + final int chatId = getArguments().getInt("chat_id", 0); + final int userId = getArguments().getInt("user_id", 0); + final int encId = getArguments().getInt("enc_id", 0); if (chatId != 0) { currentChat = MessagesController.getInstance().chats.get(chatId); if (currentChat == null) { - return false; + final Semaphore semaphore = new Semaphore(0); + MessagesStorage.getInstance().storageQueue.postRunnable(new Runnable() { + @Override + public void run() { + currentChat = MessagesStorage.getInstance().getChat(chatId); + semaphore.release(); + } + }); + try { + semaphore.acquire(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + if (currentChat != null) { + MessagesController.getInstance().chats.put(currentChat.id, currentChat); + } else { + return false; + } } MessagesController.getInstance().loadChatInfo(currentChat.id); dialog_id = -chatId; } else if (userId != 0) { currentUser = MessagesController.getInstance().users.get(userId); if (currentUser == null) { - return false; + final Semaphore semaphore = new Semaphore(0); + MessagesStorage.getInstance().storageQueue.postRunnable(new Runnable() { + @Override + public void run() { + currentUser = MessagesStorage.getInstance().getUser(userId); + semaphore.release(); + } + }); + try { + semaphore.acquire(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + if (currentUser != null) { + MessagesController.getInstance().users.putIfAbsent(currentUser.id, currentUser); + } else { + return false; + } } dialog_id = userId; } else if (encId != 0) { currentEncryptedChat = MessagesController.getInstance().encryptedChats.get(encId); if (currentEncryptedChat == null) { - return false; + final Semaphore semaphore = new Semaphore(0); + MessagesStorage.getInstance().storageQueue.postRunnable(new Runnable() { + @Override + public void run() { + currentEncryptedChat = MessagesStorage.getInstance().getEncryptedChat(encId); + semaphore.release(); + } + }); + try { + semaphore.acquire(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + if (currentEncryptedChat != null) { + MessagesController.getInstance().encryptedChats.putIfAbsent(currentEncryptedChat.id, currentEncryptedChat); + } else { + return false; + } } currentUser = MessagesController.getInstance().users.get(currentEncryptedChat.user_id); if (currentUser == null) { - return false; + final Semaphore semaphore = new Semaphore(0); + MessagesStorage.getInstance().storageQueue.postRunnable(new Runnable() { + @Override + public void run() { + currentUser = MessagesStorage.getInstance().getUser(currentEncryptedChat.user_id); + semaphore.release(); + } + }); + try { + semaphore.acquire(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + if (currentUser != null) { + MessagesController.getInstance().users.putIfAbsent(currentUser.id, currentUser); + } else { + return false; + } } dialog_id = ((long)encId) << 32; maxMessageId = Integer.MIN_VALUE; @@ -812,49 +898,21 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa chatListView.setOnTouchListener(new OnSwipeTouchListener() { public void onSwipeRight() { - try { - if (visibleDialog != null) { - visibleDialog.dismiss(); - visibleDialog = null; - } - } catch (Exception e) { - FileLog.e("tmessages", e); - } - finishFragment(true); + ChatActivity.this.onSwipeRight(); } public void onSwipeLeft() { - if (swipeOpening) { - return; - } - try { - if (visibleDialog != null) { - visibleDialog.dismiss(); - visibleDialog = null; - } - } catch (Exception e) { - FileLog.e("tmessages", e); - } - if (avatarImageView != null) { - swipeOpening = true; - avatarImageView.performClick(); - } + ChatActivity.this.onSwipeLeft(); } }); emptyView.setOnTouchListener(new OnSwipeTouchListener() { public void onSwipeRight() { - finishFragment(true); + ChatActivity.this.onSwipeRight(); } public void onSwipeLeft() { - if (swipeOpening) { - return; - } - if (avatarImageView != null) { - swipeOpening = true; - avatarImageView.performClick(); - } + ChatActivity.this.onSwipeLeft(); } }); if (currentChat != null && (currentChat instanceof TLRPC.TL_chatForbidden || currentChat.left)) { @@ -871,6 +929,38 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa return fragmentView; } + private boolean onSwipeLeft() { + if (swipeOpening) { + return false; + } + try { + if (visibleDialog != null) { + visibleDialog.dismiss(); + visibleDialog = null; + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + if (avatarImageView != null) { + swipeOpening = true; + avatarImageView.performClick(); + } + return true; + } + + private boolean onSwipeRight() { + try { + if (visibleDialog != null) { + visibleDialog.dismiss(); + visibleDialog = null; + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + finishFragment(true); + return true; + } + private void checkSendButton() { String message = messsageEditText.getText().toString().trim(); message = message.replaceAll("\n\n+", "\n\n"); @@ -1156,9 +1246,29 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa return 1; } else { if (!(messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaEmpty)) { - if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVideo || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaPhoto || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument) { - File f = new File(Utilities.getCacheDir(), messageObject.getFileName()); - if (f.exists()) { + if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVideo || + messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaPhoto || + messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument) { + boolean canSave = false; + if (messageObject.messageOwner.attachPath != null && messageObject.messageOwner.attachPath.length() != 0) { + File f = new File(messageObject.messageOwner.attachPath); + if (f.exists()) { + canSave = true; + } + } + if (!canSave) { + File f = new File(Utilities.getCacheDir(), messageObject.getFileName()); + if (f.exists()) { + canSave = true; + } + } + if (canSave) { + if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument) { + String mime = messageObject.messageOwner.media.document.mime_type; + if (mime != null && mime.equals("text/xml")) { + return 5; + } + } return 4; } } @@ -1180,12 +1290,32 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa return 1; } else { if (!(messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaEmpty)) { - /*if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVideo || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaPhoto) { - File f = new File(Utilities.getCacheDir(), messageObject.getFileName()); - if (f.exists()) { - return 4; + if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVideo || + messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaPhoto || + messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument) { + boolean canSave = false; + if (messageObject.messageOwner.attachPath != null && messageObject.messageOwner.attachPath.length() != 0) { + File f = new File(messageObject.messageOwner.attachPath); + if (f.exists()) { + canSave = true; + } } - }*/ + if (!canSave) { + File f = new File(Utilities.getCacheDir(), messageObject.getFileName()); + if (f.exists()) { + canSave = true; + } + } + if (canSave) { + if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument) { + String mime = messageObject.messageOwner.media.document.mime_type; + if (mime != null && mime.equals("text/xml")) { + return 5; + } + } + //return 4; + } + } return 2; } else { return 3; @@ -1197,17 +1327,17 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa private void addToSelectedMessages(MessageObject messageObject) { if (selectedMessagesIds.containsKey(messageObject.messageOwner.id)) { selectedMessagesIds.remove(messageObject.messageOwner.id); - if (messageObject.type == 0 || messageObject.type == 1 || messageObject.type == 8 || messageObject.type == 9) { + if (messageObject.type == 0) { selectedMessagesCanCopyIds.remove(messageObject.messageOwner.id); } } else { selectedMessagesIds.put(messageObject.messageOwner.id, messageObject); - if (messageObject.type == 0 || messageObject.type == 1 || messageObject.type == 8 || messageObject.type == 9) { + if (messageObject.type == 0) { selectedMessagesCanCopyIds.put(messageObject.messageOwner.id, messageObject); } } if (mActionMode != null && mActionMode.getMenu() != null) { - mActionMode.getMenu().findItem(R.id.copy).setVisible(selectedMessagesCanCopyIds.size() != 0); + mActionMode.getMenu().findItem(copy).setVisible(selectedMessagesCanCopyIds.size() != 0); } } @@ -1400,8 +1530,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); + public void onActivityResultFragment(int requestCode, int resultCode, Intent data) { if (resultCode == Activity.RESULT_OK) { if (requestCode == 0) { Utilities.addMediaToGallery(currentPicturePath); @@ -1450,6 +1579,18 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } } + @Override + public void saveSelfArgs(Bundle args) { + if (currentPicturePath != null) { + args.putString("path", currentPicturePath); + } + } + + @Override + public void restoreSelfArgs(Bundle args) { + currentPicturePath = args.getString("path"); + } + public boolean processSendingText(String text) { text = text.replaceAll("\n\n+", "\n\n"); text = text.replaceAll(" +", " "); @@ -1493,11 +1634,9 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa String ext = ""; int idx = documentFilePath.lastIndexOf("."); if (idx != -1) { - ext = documentFilePath.substring(idx); + ext = documentFilePath.substring(idx + 1); } TLRPC.TL_document document = new TLRPC.TL_document(); - document.thumb = new TLRPC.TL_photoSizeEmpty(); - document.thumb.type = "s"; document.id = 0; document.user_id = UserConfig.clientUserId; document.date = ConnectionsManager.getInstance().getCurrentTime(); @@ -1516,6 +1655,21 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } else { document.mime_type = "application/octet-stream"; } + if (document.mime_type.equals("image/gif")) { + try { + Bitmap bitmap = FileLoader.loadBitmap(f.getAbsolutePath(), null, 90, 90); + if (bitmap != null) { + document.thumb = FileLoader.scaleAndSaveImage(bitmap, 90, 90, 55, currentEncryptedChat != null); + document.thumb.type = "s"; + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + if (document.thumb == null) { + document.thumb = new TLRPC.TL_photoSizeEmpty(); + document.thumb.type = "s"; + } MessagesController.getInstance().sendMessage(document, dialog_id); } @@ -1627,7 +1781,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa dateMsg.message = LocaleController.formatDateChat(obj.messageOwner.date); dateMsg.id = 0; MessageObject dateObj = new MessageObject(dateMsg, null); - dateObj.type = 10; + dateObj.contentType = dateObj.type = 10; if (forwardLoad) { messages.add(0, dateObj); } else { @@ -1650,7 +1804,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa dateMsg.message = ""; dateMsg.id = 0; MessageObject dateObj = new MessageObject(dateMsg, null); - dateObj.type = 15; + dateObj.contentType = dateObj.type = 15; boolean dateAdded = true; if (a != messArr.size() - 1) { MessageObject next = messArr.get(a + 1); @@ -1905,7 +2059,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa dateMsg.message = LocaleController.formatDateChat(obj.messageOwner.date); dateMsg.id = 0; MessageObject dateObj = new MessageObject(dateMsg, null); - dateObj.type = 10; + dateObj.contentType = dateObj.type = 10; messages.add(0, dateObj); } if (!obj.messageOwner.out && obj.messageOwner.unread) { @@ -2607,16 +2761,32 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { menu.clear(); + + SupportMenuItem timeItem = null; + if (currentEncryptedChat != null) { - inflater.inflate(R.menu.chat_enc_menu, menu); - } else { - inflater.inflate(R.menu.chat_menu, menu); + timeItem = (SupportMenuItem)menu.add(Menu.NONE, chat_enc_timer, Menu.NONE, null); + timeItem.setShowAsAction(SupportMenuItem.SHOW_AS_ACTION_ALWAYS); + timeItem.setActionView(R.layout.chat_header_enc_layout); } - SupportMenuItem timeItem = (SupportMenuItem)menu.findItem(R.id.chat_enc_timer); + + SubMenu subMenu = menu.addSubMenu(Menu.NONE, chat_menu_attach, Menu.NONE, LocaleController.getString("Attach", R.string.Attach)).setIcon(R.drawable.ic_ab_attach); + SupportMenuItem menuItem = (SupportMenuItem)subMenu.getItem(); + menuItem.setShowAsAction(SupportMenuItem.SHOW_AS_ACTION_ALWAYS); + + subMenu.add(Menu.NONE, attach_photo, Menu.NONE, LocaleController.getString("ChatTakePhoto", R.string.ChatTakePhoto)).setIcon(R.drawable.ic_attach_photo); + subMenu.add(Menu.NONE, attach_gallery, Menu.NONE, LocaleController.getString("ChatGallery", R.string.ChatGallery)).setIcon(R.drawable.ic_attach_gallery); + subMenu.add(Menu.NONE, attach_video, Menu.NONE, LocaleController.getString("ChatVideo", R.string.ChatVideo)).setIcon(R.drawable.ic_attach_video); + subMenu.add(Menu.NONE, attach_document, Menu.NONE, LocaleController.getString("ChatDocument", R.string.ChatDocument)).setIcon(R.drawable.ic_ab_doc); + subMenu.add(Menu.NONE, attach_location, Menu.NONE, LocaleController.getString("ChatLocation", R.string.ChatLocation)).setIcon(R.drawable.ic_attach_location); + + SupportMenuItem avatarItem = (SupportMenuItem)menu.add(Menu.NONE, chat_menu_avatar, Menu.NONE, null); + avatarItem.setShowAsAction(SupportMenuItem.SHOW_AS_ACTION_ALWAYS); + avatarItem.setActionView(R.layout.chat_header_layout); + if (currentEncryptedChat != null && !(currentEncryptedChat instanceof TLRPC.TL_encryptedChat) || currentChat != null && (currentChat instanceof TLRPC.TL_chatForbidden || currentChat.left)) { - SupportMenuItem item = (SupportMenuItem)menu.findItem(R.id.chat_menu_attach); - if (item != null) { - item.setVisible(false); + if (menuItem != null) { + menuItem.setVisible(false); } if (timeItem != null) { @@ -2672,7 +2842,6 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa timerButton.setTime(currentEncryptedChat.ttl); } - SupportMenuItem avatarItem = (SupportMenuItem)menu.findItem(R.id.chat_menu_avatar); View avatarLayout = avatarItem.getActionView(); avatarImageView = (BackupImageView)avatarLayout.findViewById(R.id.chat_avatar_image); @@ -2784,6 +2953,8 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } else if (type == 4) { items = new CharSequence[] {LocaleController.getString(selectedObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument ? "SaveToDownloads" : "SaveToGallery", selectedObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument ? R.string.SaveToDownloads : R.string.SaveToGallery), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)}; + } else if (type == 5) { + items = new CharSequence[] {LocaleController.getString("ApplyLocalizationFile", R.string.ApplyLocalizationFile), LocaleController.getString("SaveToDownloads", R.string.SaveToDownloads), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)}; } } else { if (type == 0) { @@ -2797,6 +2968,8 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } else if (type == 4) { items = new CharSequence[] {LocaleController.getString(selectedObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument ? "SaveToDownloads" : "SaveToGallery", selectedObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument ? R.string.SaveToDownloads : R.string.SaveToGallery), LocaleController.getString("Delete", R.string.Delete)}; + } else if (type == 5) { + items = new CharSequence[] {LocaleController.getString("ApplyLocalizationFile", R.string.ApplyLocalizationFile), LocaleController.getString("Delete", R.string.Delete)}; } } @@ -2843,9 +3016,9 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa String fileName = selectedObject.getFileName(); if (selectedObject.type == 6 || selectedObject.type == 7) { MediaController.saveFile(fileName, parentActivity, 1, null); - } else if (selectedObject.type == 2 || selectedObject.type == 3) { + } else if (selectedObject.type == 1) { MediaController.saveFile(fileName, parentActivity, 0, null); - } else if (selectedObject.type == 16 || selectedObject.type == 17) { + } else if (selectedObject.type == 8 || selectedObject.type == 9) { MediaController.saveFile(fileName, parentActivity, 2, selectedObject.messageOwner.media.document.file_name); } } else if (i == 1) { @@ -2860,11 +3033,66 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa processSelectedOption(1); } } + } else if (type == 5) { + if (currentEncryptedChat == null) { + if (i == 1) { + String fileName = selectedObject.getFileName(); + if (selectedObject.type == 6 || selectedObject.type == 7) { + MediaController.saveFile(fileName, parentActivity, 1, null); + } else if (selectedObject.type == 1) { + MediaController.saveFile(fileName, parentActivity, 0, null); + } else if (selectedObject.type == 8 || selectedObject.type == 9) { + MediaController.saveFile(fileName, parentActivity, 2, selectedObject.messageOwner.media.document.file_name); + } + } else if (i == 2) { + processSelectedOption(2); + } else if (i == 3) { + processSelectedOption(1); + } + } else { + if (i == 1) { + processSelectedOption(1); + } + } + if (i == 0) { + File locFile = null; + if (selectedObject.messageOwner.attachPath != null && selectedObject.messageOwner.attachPath.length() != 0) { + File f = new File(selectedObject.messageOwner.attachPath); + if (f.exists()) { + locFile = f; + } + } + if (locFile == null) { + File f = new File(Utilities.getCacheDir(), selectedObject.getFileName()); + if (f.exists()) { + locFile = f; + } + } + if (locFile != null) { + if (LocaleController.getInstance().applyLanguageFile(locFile)) { + ((LaunchActivity)parentActivity).presentFragment(new LanguageSelectActivity(), "settings_lang", false); + } else if (parentActivity != null) { + AlertDialog.Builder builder = new AlertDialog.Builder(parentActivity); + builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); + builder.setMessage(LocaleController.getString("IncorrectLocalization", R.string.IncorrectLocalization)); + builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null); + visibleDialog = builder.show(); + visibleDialog.setCanceledOnTouchOutside(true); + + visibleDialog.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + visibleDialog = null; + } + }); + } + } + } } } }); - builder.setTitle(R.string.Message); + builder.setTitle(LocaleController.getString("Message", R.string.Message)); visibleDialog = builder.show(); visibleDialog.setCanceledOnTouchOutside(true); @@ -2886,17 +3114,15 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa private void processSelectedOption(int option) { if (option == 0) { if (selectedObject != null && selectedObject.messageOwner.id < 0) { - if (selectedObject.type == 0 || selectedObject.type == 1) { + if (selectedObject.type == 0) { if (selectedObject.messageOwner instanceof TLRPC.TL_messageForwarded) { MessagesController.getInstance().sendMessage(selectedObject, dialog_id); } else { MessagesController.getInstance().sendMessage(selectedObject.messageOwner.message, dialog_id); } - } else if (selectedObject.type == 8 || selectedObject.type == 9) { - MessagesController.getInstance().sendMessage(selectedObject, dialog_id); } else if (selectedObject.type == 4 || selectedObject.type == 5) { MessagesController.getInstance().sendMessage(selectedObject.messageOwner.media.geo.lat, selectedObject.messageOwner.media.geo._long, dialog_id); - } else if (selectedObject.type == 2 || selectedObject.type == 3) { + } else if (selectedObject.type == 1) { if (selectedObject.messageOwner instanceof TLRPC.TL_messageForwarded) { MessagesController.getInstance().sendMessage(selectedObject, dialog_id); } else { @@ -2914,11 +3140,11 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } else if (selectedObject.type == 12 || selectedObject.type == 13) { TLRPC.User user = MessagesController.getInstance().users.get(selectedObject.messageOwner.media.user_id); MessagesController.getInstance().sendMessage(user, dialog_id); - } else if (selectedObject.type == 16 || selectedObject.type == 17) { + } else if (selectedObject.type == 8 || selectedObject.type == 9) { TLRPC.TL_document document = (TLRPC.TL_document)selectedObject.messageOwner.media.document; document.path = selectedObject.messageOwner.attachPath; MessagesController.getInstance().sendMessage(document, dialog_id); - } else if (selectedObject.type == 18 || selectedObject.type == 19) { + } else if (selectedObject.type == 2) { TLRPC.TL_audio audio = (TLRPC.TL_audio)selectedObject.messageOwner.media.audio; audio.path = selectedObject.messageOwner.attachPath; MessagesController.getInstance().sendMessage(audio, dialog_id); @@ -2984,8 +3210,6 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa public void didSelectFile(DocumentSelectActivity activity, String path, String name, String ext, long size) { activity.finishFragment(); TLRPC.TL_document document = new TLRPC.TL_document(); - document.thumb = new TLRPC.TL_photoSizeEmpty(); - document.thumb.type = "s"; document.id = 0; document.user_id = UserConfig.clientUserId; document.date = ConnectionsManager.getInstance().getCurrentTime(); @@ -3004,6 +3228,21 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } else { document.mime_type = "application/octet-stream"; } + if (document.mime_type.equals("image/gif")) { + try { + Bitmap bitmap = FileLoader.loadBitmap(path, null, 90, 90); + if (bitmap != null) { + document.thumb = FileLoader.scaleAndSaveImage(bitmap, 90, 90, 80, currentEncryptedChat != null); + document.thumb.type = "s"; + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + if (document.thumb == null) { + document.thumb = new TLRPC.TL_photoSizeEmpty(); + document.thumb.type = "s"; + } MessagesController.getInstance().sendMessage(document, dialog_id); } @@ -3096,7 +3335,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa case android.R.id.home: finishFragment(); break; - case R.id.attach_photo: { + case attach_photo: { try { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); File image = Utilities.generatePicturePath(); @@ -3104,23 +3343,23 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(image)); currentPicturePath = image.getAbsolutePath(); } - startActivityForResult(takePictureIntent, 0); + parentActivity.startActivityForResult(takePictureIntent, 0); } catch (Exception e) { FileLog.e("tmessages", e); } break; } - case R.id.attach_gallery: { + case attach_gallery: { try { Intent photoPickerIntent = new Intent(Intent.ACTION_PICK); photoPickerIntent.setType("image/*"); - startActivityForResult(photoPickerIntent, 1); + parentActivity.startActivityForResult(photoPickerIntent, 1); } catch (Exception e) { FileLog.e("tmessages", e); } break; } - case R.id.attach_video: { + case attach_video: { try { Intent pickIntent = new Intent(); pickIntent.setType("video/*"); @@ -3138,13 +3377,13 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa Intent chooserIntent = Intent.createChooser(pickIntent, ""); chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { takeVideoIntent }); - startActivityForResult(chooserIntent, 2); + parentActivity.startActivityForResult(chooserIntent, 2); } catch (Exception e) { FileLog.e("tmessages", e); } break; } - case R.id.attach_location: { + case attach_location: { if (!isGoogleMapsInstalled()) { return true; } @@ -3152,7 +3391,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa ((LaunchActivity)parentActivity).presentFragment(fragment, "location", false); break; } - case R.id.attach_document: { + case attach_document: { DocumentSelectActivity fragment = new DocumentSelectActivity(); fragment.delegate = this; ((LaunchActivity)parentActivity).presentFragment(fragment, "document", false); @@ -3242,9 +3481,9 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa private void updateRowBackground(ChatListRowHolderEx holder, boolean disableSelection, boolean selected) { int messageType = holder.message.type; if (!disableSelection) { - if (messageType == 2 || messageType == 4 || messageType == 6) { + if (messageType == 4 || messageType == 6) { holder.chatBubbleView.setBackgroundResource(R.drawable.chat_outgoing_photo_states); - } else if (messageType == 3 || messageType == 5 || messageType == 7) { + } else if (messageType == 5 || messageType == 7) { holder.chatBubbleView.setBackgroundResource(R.drawable.chat_incoming_photo_states); } else if (messageType == 12) { holder.chatBubbleView.setBackgroundResource(R.drawable.chat_outgoing_text_states); @@ -3252,21 +3491,21 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } else if (messageType == 13) { holder.chatBubbleView.setBackgroundResource(R.drawable.chat_incoming_text_states); holder.chatBubbleView.setPadding(Utilities.dp(15), Utilities.dp(6), Utilities.dp(9), 0); - } else if (messageType == 16) { + } else if (messageType == 8) { holder.chatBubbleView.setBackgroundResource(R.drawable.chat_outgoing_text_states); holder.chatBubbleView.setPadding(Utilities.dp(9), Utilities.dp(9), Utilities.dp(18), 0); - } else if (messageType == 17) { + } else if (messageType == 9) { holder.chatBubbleView.setBackgroundResource(R.drawable.chat_incoming_text_states); holder.chatBubbleView.setPadding(Utilities.dp(18), Utilities.dp(9), Utilities.dp(9), 0); } } else { - if (messageType == 2 || messageType == 4 || messageType == 6) { + if (messageType == 4 || messageType == 6) { if (selected) { holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out_photo_selected); } else { holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out_photo); } - } else if (messageType == 3 || messageType == 5 || messageType == 7) { + } else if (messageType == 5 || messageType == 7) { if (selected) { holder.chatBubbleView.setBackgroundResource(R.drawable.msg_in_photo_selected); } else { @@ -3286,14 +3525,14 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa holder.chatBubbleView.setBackgroundResource(R.drawable.msg_in); } holder.chatBubbleView.setPadding(Utilities.dp(15), Utilities.dp(6), Utilities.dp(9), 0); - } else if (messageType == 16) { + } else if (messageType == 8) { if (selected) { holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out_selected); } else { holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out); } holder.chatBubbleView.setPadding(Utilities.dp(9), Utilities.dp(9), Utilities.dp(18), 0); - } else if (messageType == 17) { + } else if (messageType == 9) { if (selected) { holder.chatBubbleView.setBackgroundResource(R.drawable.msg_in_selected); } else { @@ -3373,17 +3612,11 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } } MessageObject message = messages.get(messages.size() - i - offset); - int type = message.type; + int type = message.contentType; if (view == null) { LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); if (type == 0) { - view = new ChatMessageCell(mContext, false); - } else if (type == 1) { - view = new ChatMessageCell(mContext, currentChat != null); - } else if (type == 8) { - view = new ChatMessageCell(mContext, false); - } else if (type == 9) { - view = new ChatMessageCell(mContext, currentChat != null); + view = new ChatMessageCell(mContext); } else if (type == 4) { view = li.inflate(R.layout.chat_outgoing_location_layout, viewGroup, false); } else if (type == 5) { @@ -3392,14 +3625,9 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } else { view = li.inflate(R.layout.chat_incoming_location_layout, viewGroup, false); } - } else if (type == 2) { - view = li.inflate(R.layout.chat_outgoing_photo_layout, viewGroup, false); - } else if (type == 3) { - if (currentChat != null) { - view = li.inflate(R.layout.chat_group_incoming_photo_layout, viewGroup, false); - } else { - view = li.inflate(R.layout.chat_incoming_photo_layout, viewGroup, false); - } + } else if (type == 1) { + view = new ChatMediaCell(mContext); + ((ChatMediaCell)view).downloadPhotos = downloadPhotos; } else if (type == 6) { view = li.inflate(R.layout.chat_outgoing_video_layout, viewGroup, false); } else if (type == 7) { @@ -3422,36 +3650,19 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } } else if (type == 15) { view = li.inflate(R.layout.chat_unread_layout, viewGroup, false); - } else if (type == 16) { + } else if (type == 8) { view = li.inflate(R.layout.chat_outgoing_document_layout, viewGroup, false); - } else if (type == 17) { + } else if (type == 9) { if (currentChat != null) { view = li.inflate(R.layout.chat_group_incoming_document_layout, viewGroup, false); } else { view = li.inflate(R.layout.chat_incoming_document_layout, viewGroup, false); } - } else if (type == 18) { - view = new ChatAudioCell(mContext, false); - } else if (type == 19) { - view = new ChatAudioCell(mContext, currentChat != null); + } else if (type == 2) { + view = new ChatAudioCell(mContext); } } - if (view instanceof ChatBaseCell) { - ((ChatBaseCell)view).delegate = new ChatBaseCell.ChatBaseCellDelegate() { - @Override - public void didPressedUserAvatar(ChatBaseCell cell, TLRPC.User user) { - if (user != null && user.id != UserConfig.clientUserId) { - UserProfileActivity fragment = new UserProfileActivity(); - Bundle args = new Bundle(); - args.putInt("user_id", user.id); - fragment.setArguments(args); - ((LaunchActivity)parentActivity).presentFragment(fragment, "user_" + user.id, false); - } - } - }; - } - boolean selected = false; boolean disableSelection = false; if (mActionMode != null) { @@ -3467,15 +3678,69 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } if (view instanceof ChatBaseCell) { + ((ChatBaseCell)view).delegate = new ChatBaseCell.ChatBaseCellDelegate() { + @Override + public void didPressedUserAvatar(ChatBaseCell cell, TLRPC.User user) { + if (user != null && user.id != UserConfig.clientUserId) { + UserProfileActivity fragment = new UserProfileActivity(); + Bundle args = new Bundle(); + args.putInt("user_id", user.id); + fragment.setArguments(args); + ((LaunchActivity)parentActivity).presentFragment(fragment, "user_" + user.id, false); + } + } + + @Override + public void didPressedCanceSendButton(ChatBaseCell cell) { + MessageObject message = cell.getMessageObject(); + if (message.messageOwner.send_state != 0) { + MessagesController.getInstance().cancelSendingMessage(message); + } + } + + @Override + public void didLongPressed(ChatBaseCell cell) { + createMenu(cell, false); + } + + @Override + public boolean canPerformActions() { + return mActionMode == null; + } + + @Override + public boolean onSwipeLeft() { + return ChatActivity.this.onSwipeLeft(); + } + + @Override + public boolean onSwipeRight() { + return ChatActivity.this.onSwipeRight(); + } + }; + if (view instanceof ChatMediaCell) { + ((ChatMediaCell)view).mediaDelegate = new ChatMediaCell.ChatMediaCellDelegate() { + @Override + public void didPressedImage(ChatBaseCell cell) { + NotificationCenter.getInstance().addToMemCache(51, cell.getMessageObject()); + Intent intent = new Intent(parentActivity, GalleryImageViewer.class); + startActivity(intent); + } + }; + } + + ((ChatBaseCell)view).isChat = currentChat != null; ((ChatBaseCell)view).setMessageObject(message); ((ChatBaseCell)view).setCheckPressed(!disableSelection, disableSelection && selected); if (view instanceof ChatAudioCell && downloadAudios) { ((ChatAudioCell)view).downloadAudioIfNeed(); + } else if (view instanceof ChatMediaCell) { + ((ChatMediaCell)view).downloadPhotos = downloadPhotos; } } else { ChatListRowHolderEx holder = (ChatListRowHolderEx)view.getTag(); if (holder == null) { - holder = new ChatListRowHolderEx(view, type); + holder = new ChatListRowHolderEx(view, message.type); view.setTag(holder); } holder.message = message; @@ -3499,12 +3764,12 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa return 14; } MessageObject message = messages.get(messages.size() - i - offset); - return message.type; + return message.contentType; } @Override public int getViewTypeCount() { - return 20; + return 16; } @Override @@ -3540,7 +3805,6 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa public ImageView addContactButton; public View addContactView; public View chatBubbleView; - public View photoProgressView; public ProgressBar actionProgress; public View actionView; @@ -3569,12 +3833,12 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa avatarImageView.setImage(photo, "50_50", placeHolderId); } - if (type != 12 && type != 13 && nameTextView != null && fromUser != null && type != 16 && type != 17) { + if (type != 12 && type != 13 && nameTextView != null && fromUser != null && type != 8 && type != 9) { nameTextView.setText(Utilities.formatName(fromUser.first_name, fromUser.last_name)); nameTextView.setTextColor(Utilities.getColorForId(message.messageOwner.from_id)); } - if (type == 2 || type == 3 || type == 6 || type == 7) { + if (type == 6 || type == 7) { int width = (int)(Math.min(displaySize.x, displaySize.y) * 0.7f); int height = width + Utilities.dp(100); if (type == 6 || type == 7) { @@ -3589,11 +3853,6 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } PhotoObject photo = PhotoObject.getClosestImageWithSize(message.photoThumbs, width, height); - if (type == 3) { - if (photoProgressView != null) { - photoProgressView.setVisibility(View.GONE); - } - } if (photo != null) { float scale = (float)photo.photoOwner.w / (float)width; @@ -3626,17 +3885,6 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa if (photo.image != null) { photoImage.setImageBitmap(photo.image); } else { - if (type == 2 || type == 3) { - String fileName = MessageObject.getAttachFileName(photo.photoOwner); - File cacheFile = new File(Utilities.getCacheDir(), fileName); - if (!cacheFile.exists()) { - photoFileName = fileName; - photoFile = cacheFile; - } else { - photoFileName = null; - photoFile = null; - } - } if (photoFileName == null) { if (message.imagePreview != null) { photoImage.setImage(photo.photoOwner.location, String.format(Locale.US, "%d_%d", (int)(w / Utilities.density), (int)(h / Utilities.density)), message.imagePreview); @@ -3737,7 +3985,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } else { messageTextView.setText(LocaleController.formatString("FewNewMessages", R.string.FewNewMessages, unread_to_load)); } - } else if (type == 16 || type == 17) { + } else if (type == 8 || type == 9) { TLRPC.Document document = message.messageOwner.media.document; if (document instanceof TLRPC.TL_document || document instanceof TLRPC.TL_documentEncrypted) { nameTextView.setText(message.messageOwner.media.document.file_name); @@ -3764,7 +4012,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } else if (document.thumb instanceof TLRPC.TL_photoCachedSize) { } else { - if (type == 16) { + if (type == 8) { contactAvatar.setImageResource(R.drawable.doc_green); } else { contactAvatar.setImageResource(R.drawable.doc_blue); @@ -3773,7 +4021,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } else { nameTextView.setText("Error"); phoneTextView.setText("Error"); - if (type == 16) { + if (type == 8) { contactAvatar.setImageResource(R.drawable.doc_green); } else { contactAvatar.setImageResource(R.drawable.doc_blue); @@ -3791,19 +4039,16 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa if (halfCheckImage != null) { if (message.messageOwner.send_state == MessagesController.MESSAGE_SEND_STATE_SENDING) { checkImage.setVisibility(View.INVISIBLE); - if (type == 2 || type == 6 || type == 4) { + if (type == 6 || type == 4) { halfCheckImage.setImageResource(R.drawable.msg_clock_photo); } else { halfCheckImage.setImageResource(R.drawable.msg_clock); } halfCheckImage.setVisibility(View.VISIBLE); - if (actionView != null || photoProgressView != null) { + if (actionView != null) { if (actionView != null) { actionView.setVisibility(View.VISIBLE); } - if (photoProgressView != null) { - photoProgressView.setVisibility(View.VISIBLE); - } Float progress = FileLoader.getInstance().fileProgresses.get(message.messageOwner.attachPath); if (progress != null) { actionProgress.setProgress((int)(progress * 100)); @@ -3825,9 +4070,6 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa if (actionView != null) { actionView.setVisibility(View.GONE); } - if (photoProgressView != null) { - photoProgressView.setVisibility(View.GONE); - } if (actionAttachButton != null) { actionAttachButton.setVisibility(View.GONE); } @@ -3835,7 +4077,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa if (!message.messageOwner.unread) { halfCheckImage.setVisibility(View.VISIBLE); checkImage.setVisibility(View.VISIBLE); - if (type == 2 || type == 6 || type == 4) { + if (type == 6 || type == 4) { halfCheckImage.setImageResource(R.drawable.msg_halfcheck_w); } else { halfCheckImage.setImageResource(R.drawable.msg_halfcheck); @@ -3843,7 +4085,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } else { halfCheckImage.setVisibility(View.VISIBLE); checkImage.setVisibility(View.INVISIBLE); - if (type == 2 || type == 6 || type == 4) { + if (type == 6 || type == 4) { halfCheckImage.setImageResource(R.drawable.msg_check_w); } else { halfCheckImage.setImageResource(R.drawable.msg_check); @@ -3852,16 +4094,13 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa if (actionView != null) { actionView.setVisibility(View.GONE); } - if (photoProgressView != null) { - photoProgressView.setVisibility(View.GONE); - } if (actionAttachButton != null) { actionAttachButton.setVisibility(View.VISIBLE); } } } } - if (message.type == 2 || message.type == 3 || message.type == 6 || message.type == 7 || message.type == 16 || message.type == 17) { + if (message.type == 6 || message.type == 7 || message.type == 8 || message.type == 9) { Integer tag = (Integer)actionProgress.getTag(); String file = progressByTag.get(tag); if (file != null) { @@ -3885,37 +4124,31 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa actionAttachButton.setVisibility(View.VISIBLE); if (message.type == 6 || message.type == 7) { actionAttachButton.setText(LocaleController.getString("ViewVideo", R.string.ViewVideo)); - } else if (message.type == 16 || message.type == 17) { + } else if (message.type == 8 || message.type == 9) { actionAttachButton.setText(LocaleController.getString("Open", R.string.Open)); } } if (actionView != null) { actionView.setVisibility(View.GONE); } - if (photoProgressView != null) { - photoProgressView.setVisibility(View.GONE); - } } else { load = true; } } if (load && message.messageOwner.attachPath != null && message.messageOwner.attachPath.length() != 0 || !load && (message.messageOwner.attachPath == null || message.messageOwner.attachPath.length() == 0)) { File cacheFile = null; - if (((message.type == 2 || message.type == 3) && photoFileName == null) || (cacheFile = new File(Utilities.getCacheDir(), fileName)).exists()) { + if ((cacheFile = new File(Utilities.getCacheDir(), fileName)).exists()) { if (actionAttachButton != null) { actionAttachButton.setVisibility(View.VISIBLE); if (message.type == 6 || message.type == 7) { actionAttachButton.setText(LocaleController.getString("ViewVideo", R.string.ViewVideo)); - } else if (message.type == 16 || message.type == 17) { + } else if (message.type == 8 || message.type == 9) { actionAttachButton.setText(LocaleController.getString("Open", R.string.Open)); } } if (actionView != null) { actionView.setVisibility(View.GONE); } - if (photoProgressView != null) { - photoProgressView.setVisibility(View.GONE); - } load = false; } else { load = true; @@ -3932,47 +4165,32 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa progressByTag.put((Integer)actionProgress.getTag(), fileName); addToLoadingFile(fileName, actionProgress); if (actionView != null) { - if ((message.type == 2 || message.type == 3) && downloadPhotos) { - actionView.setVisibility(View.GONE); - } else { - actionView.setVisibility(View.VISIBLE); - if (photoFileName != null) { - actionCancelButton.setImageResource(R.drawable.photo_download_cancel_states); - } + actionView.setVisibility(View.VISIBLE); + if (photoFileName != null) { + actionCancelButton.setImageResource(R.drawable.photo_download_cancel_states); } } - if (photoProgressView != null) { - photoProgressView.setVisibility(View.VISIBLE); - } if (actionAttachButton != null) { actionAttachButton.setVisibility(View.GONE); } } else { if (actionView != null) { - if ((message.type == 2 || message.type == 3) && !downloadPhotos) { - actionView.setVisibility(View.VISIBLE); - actionCancelButton.setImageResource(R.drawable.photo_download_states); - } else { - actionView.setVisibility(View.GONE); - } - } - if (photoProgressView != null) { - photoProgressView.setVisibility(View.GONE); + actionView.setVisibility(View.GONE); } if (actionAttachButton != null) { actionAttachButton.setVisibility(View.VISIBLE); if (message.type == 6 || message.type == 7) { actionAttachButton.setText(String.format("%s %.1f MB", LocaleController.getString("DOWNLOAD", R.string.DOWNLOAD), message.messageOwner.media.video.size / 1024.0f / 1024.0f)); - } else if (message.type == 16 || message.type == 17) { + } else if (message.type == 8 || message.type == 9) { actionAttachButton.setText(LocaleController.getString("DOWNLOAD", R.string.DOWNLOAD)); } } } } } - if (message.type == 16 || message.type == 17) { + if (message.type == 8 || message.type == 9) { int width; - if (currentChat != null && type != 16) { + if (currentChat != null && type != 8) { if (actionView.getVisibility() == View.VISIBLE) { width = displaySize.x - Utilities.dp(290); } else { @@ -4011,7 +4229,6 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa addContactButton = (ImageView)view.findViewById(R.id.add_contact_button); addContactView = view.findViewById(R.id.add_contact_view); chatBubbleView = view.findViewById(R.id.chat_bubble_layout); - photoProgressView = view.findViewById(R.id.photo_progress); if (messageTextView != null) { messageTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, MessagesController.getInstance().fontSize); } @@ -4069,7 +4286,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa contactView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - if (message.type == 16 || message.type == 17) { + if (message.type == 8 || message.type == 9) { processOnClick(view); } else if (message.type == 12 || message.type == 13) { if (mActionMode != null) { @@ -4150,15 +4367,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa contactAvatar.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - if (message.type == 18 || message.type == 19) { - if (message.messageOwner.media.audio.user_id != UserConfig.clientUserId && message.messageOwner.media.audio.user_id != 0) { - UserProfileActivity fragment = new UserProfileActivity(); - Bundle args = new Bundle(); - args.putInt("user_id", message.messageOwner.media.audio.user_id); - fragment.setArguments(args); - ((LaunchActivity)parentActivity).presentFragment(fragment, "user_" + message.messageOwner.media.audio.user_id, false); - } - } + } }); } @@ -4203,33 +4412,17 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa if (file != null) { progressBarMap.remove(file); } - } else if (message.type == 6 || message.type == 7 || message.type == 16 || message.type == 17) { + } else if (message.type == 6 || message.type == 7 || message.type == 8 || message.type == 9) { String file = progressByTag.get(tag); if (file != null) { loadingFile.remove(file); if (message.type == 6 || message.type == 7) { FileLoader.getInstance().cancelLoadFile(message.messageOwner.media.video, null, null, null); - } else if (message.type == 16 || message.type == 17) { + } else if (message.type == 8 || message.type == 9) { FileLoader.getInstance().cancelLoadFile(null, null, message.messageOwner.media.document, null); } updateVisibleRows(); } - } else if (message.type == 2 || message.type == 3) { - if (photoFile != null && !photoFile.exists() && photoObjectToSet != null) { - if (loadingFile.containsKey(photoFileName)) { - loadingFile.remove(photoFileName); - FileLoader.getInstance().cancelLoadingForImageView(photoImage); - updateVisibleRows(); - } else { - addToLoadingFile(photoFileName, actionProgress); - if (message.imagePreview != null) { - photoImage.setImage(photoObjectToSet.photoOwner.location, photoFilter, message.imagePreview, photoObjectToSet.photoOwner.size); - } else { - photoImage.setImage(photoObjectToSet.photoOwner.location, photoFilter, message.messageOwner.out ? R.drawable.photo_placeholder_out : R.drawable.photo_placeholder_in, photoObjectToSet.photoOwner.size); - } - updateVisibleRows(); - } - } } } } @@ -4256,7 +4449,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa private void alertUserOpenError() { AlertDialog.Builder builder = new AlertDialog.Builder(parentActivity); - builder.setTitle(R.string.AppName); + builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); builder.setPositiveButton(R.string.OK, null); if (message.type == 6 || message.type == 7) { builder.setMessage(R.string.NoPlayerInstalled); @@ -4287,25 +4480,11 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa NotificationCenter.getInstance().addToMemCache(0, message); LocationActivity fragment = new LocationActivity(); ((LaunchActivity)parentActivity).presentFragment(fragment, "location_view", false); - } else if (message.type == 2 || message.type == 3) { - if (photoFile == null || photoObjectToSet == null || photoFile != null && photoFile.exists()) { - NotificationCenter.getInstance().addToMemCache(51, message); - Intent intent = new Intent(parentActivity, GalleryImageViewer.class); - startActivity(intent); - } else { - addToLoadingFile(photoFileName, actionProgress); - if (message.imagePreview != null) { - photoImage.setImage(photoObjectToSet.photoOwner.location, photoFilter, message.imagePreview, photoObjectToSet.photoOwner.size); - } else { - photoImage.setImage(photoObjectToSet.photoOwner.location, photoFilter, message.messageOwner.out ? R.drawable.photo_placeholder_out : R.drawable.photo_placeholder_in, photoObjectToSet.photoOwner.size); - } - updateVisibleRows(); - } } else if (message.type == 11) { NotificationCenter.getInstance().addToMemCache(51, message); Intent intent = new Intent(parentActivity, GalleryImageViewer.class); startActivity(intent); - } else if (message.type == 6 || message.type == 7 || message.type == 16 || message.type == 17) { + } else if (message.type == 6 || message.type == 7 || message.type == 8 || message.type == 9) { File f = null; String fileName = message.getFileName(); if (message.messageOwner.attachPath != null && message.messageOwner.attachPath.length() != 0) { @@ -4320,7 +4499,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa Intent intent = new Intent(Intent.ACTION_VIEW); if (message.type == 6 || message.type == 7) { intent.setDataAndType(Uri.fromFile(f), "video/mp4"); - } else if (message.type == 16 || message.type == 17) { + } else if (message.type == 8 || message.type == 9) { MimeTypeMap myMime = MimeTypeMap.getSingleton(); int idx = fileName.lastIndexOf("."); if (idx != -1) { @@ -4355,7 +4534,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa addToLoadingFile(fileName, actionProgress); if (message.type == 6 || message.type == 7) { FileLoader.getInstance().loadFile(message.messageOwner.media.video, null, null, null); - } else if (message.type == 16 || message.type == 17) { + } else if (message.type == 8 || message.type == 9) { FileLoader.getInstance().loadFile(null, null, message.messageOwner.media.document, null); } updateVisibleRows(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatProfileActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatProfileActivity.java index 0d845f2b..2d55257e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatProfileActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatProfileActivity.java @@ -22,6 +22,7 @@ import android.os.Bundle; import android.provider.Settings; import android.support.v4.internal.view.SupportMenuItem; import android.support.v7.app.ActionBar; +import android.support.v7.widget.SearchView; import android.text.Html; import android.view.LayoutInflater; import android.view.Menu; @@ -163,6 +164,9 @@ public class ChatProfileActivity extends BaseFragment implements NotificationCen editor.commit(); listView.invalidateViews(); } else if (i == 3) { + if (parentActivity == null) { + return; + } try { Intent tmpIntent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER); tmpIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION); @@ -187,7 +191,7 @@ public class ChatProfileActivity extends BaseFragment implements NotificationCen } tmpIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, currentSound); - startActivityForResult(tmpIntent, 15); + parentActivity.startActivityForResult(tmpIntent, 3); } catch (Exception e) { FileLog.e("tmessages", e); } @@ -247,11 +251,10 @@ public class ChatProfileActivity extends BaseFragment implements NotificationCen } @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); + public void onActivityResultFragment(int requestCode, int resultCode, Intent data) { avatarUpdater.onActivityResult(requestCode, resultCode, data); if (resultCode == Activity.RESULT_OK) { - if (requestCode == 15) { + if (requestCode == 3) { Uri ringtone = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI); String name = null; if (ringtone != null && parentActivity != null) { @@ -481,16 +484,21 @@ public class ChatProfileActivity extends BaseFragment implements NotificationCen @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.group_profile_menu, menu); - SupportMenuItem doneItem = (SupportMenuItem)menu.findItem(R.id.block_user); - TextView doneTextView = (TextView)doneItem.getActionView().findViewById(R.id.done_button); - doneTextView.setText(LocaleController.getString("AddMember", R.string.AddMember)); - doneTextView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - openAddMenu(); - } - }); + SupportMenuItem item = (SupportMenuItem)menu.add(Menu.NONE, 0, Menu.NONE, LocaleController.getString("AddMember", R.string.AddMember)); + item.setShowAsAction(SupportMenuItem.SHOW_AS_ACTION_ALWAYS); + LayoutInflater li = (LayoutInflater)ApplicationLoader.applicationContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + item.setActionView(R.layout.group_profile_add_member_layout); + + TextView textView = (TextView)item.getActionView().findViewById(R.id.done_button); + if (textView != null) { + textView.setText(LocaleController.getString("AddMember", R.string.AddMember)); + textView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + openAddMenu(); + } + }); + } } private class ListAdapter extends BaseAdapter { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java index 61d29c26..a5fe2b81 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java @@ -306,7 +306,7 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter private void didSelectResult(final TLRPC.User user, boolean useAlert) { if (useAlert && selectAlertString != 0) { AlertDialog.Builder builder = new AlertDialog.Builder(parentActivity); - builder.setTitle(R.string.AppName); + builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); builder.setMessage(LocaleController.formatString(selectAlertStringDesc, selectAlertString, Utilities.formatName(user.first_name, user.last_name))); builder.setPositiveButton(R.string.OK, new DialogInterface.OnClickListener() { @Override @@ -400,9 +400,9 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); - inflater.inflate(R.menu.contacts_menu, menu); - searchItem = (SupportMenuItem)menu.findItem(R.id.messages_list_menu_search); - searchView = (SearchView)searchItem.getActionView(); + searchItem = (SupportMenuItem)menu.add(Menu.NONE, 0, Menu.NONE, LocaleController.getString("Search", R.string.Search)).setIcon(R.drawable.ic_ab_search); + searchItem.setShowAsAction(SupportMenuItem.SHOW_AS_ACTION_ALWAYS|SupportMenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW); + searchItem.setActionView(searchView = new SearchView(parentActivity)); TextView textView = (TextView) searchView.findViewById(R.id.search_src_text); if (textView != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/CountrySelectActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/CountrySelectActivity.java index 1c914a0d..23f58c30 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/CountrySelectActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/CountrySelectActivity.java @@ -19,7 +19,6 @@ import android.support.v7.app.ActionBarActivity; import android.support.v7.widget.SearchView; import android.view.LayoutInflater; import android.view.Menu; -import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -191,11 +190,9 @@ public class CountrySelectActivity extends ActionBarActivity { @Override public boolean onCreateOptionsMenu(Menu menu) { - MenuInflater inflater = getMenuInflater(); - - inflater.inflate(R.menu.contacts_menu, menu); - searchItem = (SupportMenuItem)menu.findItem(R.id.messages_list_menu_search); - searchView = (SearchView)searchItem.getActionView(); + searchItem = (SupportMenuItem)menu.add(Menu.NONE, 0, Menu.NONE, LocaleController.getString("Search", R.string.Search)).setIcon(R.drawable.ic_ab_search); + searchItem.setShowAsAction(SupportMenuItem.SHOW_AS_ACTION_ALWAYS|SupportMenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW); + searchItem.setActionView(searchView = new SearchView(this)); TextView textView = (TextView) searchView.findViewById(R.id.search_src_text); if (textView != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DocumentSelectActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/DocumentSelectActivity.java index 9fde8ddc..9d7cab97 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DocumentSelectActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DocumentSelectActivity.java @@ -345,7 +345,7 @@ public class DocumentSelectActivity extends BaseFragment { private void showErrorBox(String error){ new AlertDialog.Builder(parentActivity) - .setTitle(R.string.AppName) + .setTitle(LocaleController.getString("AppName", R.string.AppName)) .setMessage(error) .setPositiveButton(R.string.OK, null) .show(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GalleryImageViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/GalleryImageViewer.java index 6548dc71..1caf0d20 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GalleryImageViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GalleryImageViewer.java @@ -18,7 +18,6 @@ import android.support.v4.view.ViewPager; import android.support.v7.app.ActionBar; import android.view.Display; import android.view.Menu; -import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -82,6 +81,9 @@ public class GalleryImageViewer extends AbstractGalleryActivity implements Notif public static int needShowAllMedia = 2000; + private final static int gallery_menu_save = 1; + private final static int gallery_menu_showall = 2; + @SuppressWarnings("unchecked") @Override public void onCreate(Bundle savedInstanceState) { @@ -640,11 +642,9 @@ public class GalleryImageViewer extends AbstractGalleryActivity implements Notif @Override public boolean onCreateOptionsMenu(Menu menu) { - MenuInflater inflater = getMenuInflater(); - if (withoutBottom) { - inflater.inflate(R.menu.gallery_save_only_menu, menu); - } else { - inflater.inflate(R.menu.gallery_menu, menu); + menu.add(Menu.NONE, gallery_menu_save, Menu.NONE, LocaleController.getString("SaveToGallery", R.string.SaveToGallery)); + if (!withoutBottom) { + menu.add(Menu.NONE, gallery_menu_showall, Menu.NONE, LocaleController.getString("ShowAllMedia", R.string.ShowAllMedia)); } return super.onCreateOptionsMenu(menu); } @@ -721,7 +721,7 @@ public class GalleryImageViewer extends AbstractGalleryActivity implements Notif finish(); System.gc(); break; - case R.id.gallery_menu_save: + case gallery_menu_save: if (currentFileName == null) { return; } @@ -737,7 +737,7 @@ public class GalleryImageViewer extends AbstractGalleryActivity implements Notif // startActivityForResult(intent, 10); // break; // } - case R.id.gallery_menu_showall: { + case gallery_menu_showall: { if (fromAll) { finish(); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java index 5776b0a0..c550918f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java @@ -421,8 +421,10 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.group_create_menu, menu); - SupportMenuItem doneItem = (SupportMenuItem)menu.findItem(R.id.done_menu_item); + SupportMenuItem doneItem = (SupportMenuItem)menu.add(Menu.NONE, 0, Menu.NONE, null); + doneItem.setShowAsAction(SupportMenuItem.SHOW_AS_ACTION_ALWAYS); + doneItem.setActionView(R.layout.group_create_done_layout); + TextView doneTextView = (TextView)doneItem.getActionView().findViewById(R.id.done_button); doneTextView.setText(LocaleController.getString("Next", R.string.Next)); doneTextView.setOnClickListener(new View.OnClickListener() { @@ -431,11 +433,12 @@ public class GroupCreateActivity extends BaseFragment implements NotificationCen if (!selectedContacts.isEmpty()) { ArrayList result = new ArrayList(); result.addAll(selectedContacts.keySet()); - NotificationCenter.getInstance().addToMemCache(2, result); - } else { - return; + Bundle args = new Bundle(); + args.putIntegerArrayList("result", result); + GroupCreateFinalActivity fragment = new GroupCreateFinalActivity(); + fragment.setArguments(args); + ((LaunchActivity)parentActivity).presentFragment(fragment, "group_craate_final", false); } - ((LaunchActivity)parentActivity).presentFragment(new GroupCreateFinalActivity(), "group_craate_final", false); } }); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java index 79438d06..86a986c2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java @@ -28,6 +28,7 @@ import android.widget.TextView; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessagesStorage; import org.telegram.messenger.TLRPC; import org.telegram.messenger.FileLog; import org.telegram.messenger.MessagesController; @@ -42,10 +43,11 @@ import org.telegram.ui.Views.PinnedHeaderListView; import org.telegram.ui.Views.SectionedBaseAdapter; import java.util.ArrayList; +import java.util.concurrent.Semaphore; public class GroupCreateFinalActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate, AvatarUpdater.AvatarUpdaterDelegate { private PinnedHeaderListView listView; - private TextView nameTextView; + private EditText nameTextView; private TLRPC.FileLocation avatar; private TLRPC.InputFile uploadedAvatar; private ArrayList selectedContacts; @@ -54,6 +56,7 @@ public class GroupCreateFinalActivity extends BaseFragment implements Notificati private boolean donePressed; private AvatarUpdater avatarUpdater = new AvatarUpdater(); private ProgressDialog progressDialog = null; + private String nameToSet = null; @SuppressWarnings("unchecked") @Override @@ -64,7 +67,40 @@ public class GroupCreateFinalActivity extends BaseFragment implements Notificati NotificationCenter.getInstance().addObserver(this, MessagesController.chatDidFailCreate); avatarUpdater.parentFragment = this; avatarUpdater.delegate = this; - selectedContacts = (ArrayList)NotificationCenter.getInstance().getFromMemCache(2); + selectedContacts = getArguments().getIntegerArrayList("result"); + final ArrayList usersToLoad = new ArrayList(); + for (Integer uid : selectedContacts) { + if (MessagesController.getInstance().users.get(uid) == null) { + usersToLoad.add(uid); + } + } + if (!usersToLoad.isEmpty()) { + final Semaphore semaphore = new Semaphore(0); + final ArrayList users = new ArrayList(); + final boolean[] error = new boolean[1]; + MessagesStorage.getInstance().storageQueue.postRunnable(new Runnable() { + @Override + public void run() { + users.addAll(MessagesStorage.getInstance().getUsers(usersToLoad, error)); + semaphore.release(); + } + }); + try { + semaphore.acquire(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + if (error[0]) { + return false; + } + if (!users.isEmpty()) { + for (TLRPC.User user : users) { + MessagesController.getInstance().users.putIfAbsent(user.id, user); + } + } else { + return false; + } + } return true; } @@ -125,6 +161,10 @@ public class GroupCreateFinalActivity extends BaseFragment implements Notificati nameTextView = (EditText)fragmentView.findViewById(R.id.bubble_input_text); nameTextView.setHint(LocaleController.getString("EnterGroupNamePlaceholder", R.string.EnterGroupNamePlaceholder)); + if (nameToSet != null) { + nameTextView.setText(nameToSet); + nameToSet = null; + } listView = (PinnedHeaderListView)fragmentView.findViewById(R.id.listView); listView.setAdapter(new ListAdapter(parentActivity)); } else { @@ -200,15 +240,44 @@ public class GroupCreateFinalActivity extends BaseFragment implements Notificati } @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); + public void onActivityResultFragment(int requestCode, int resultCode, Intent data) { avatarUpdater.onActivityResult(requestCode, resultCode, data); } + @Override + public void saveSelfArgs(Bundle args) { + if (avatarUpdater != null && avatarUpdater.currentPicturePath != null) { + args.putString("path", avatarUpdater.currentPicturePath); + } + if (nameTextView != null) { + String text = nameTextView.getText().toString(); + if (text != null && text.length() != 0) { + args.putString("nameTextView", text); + } + } + } + + @Override + public void restoreSelfArgs(Bundle args) { + if (avatarUpdater != null) { + avatarUpdater.currentPicturePath = args.getString("path"); + } + String text = args.getString("nameTextView"); + if (text != null) { + if (nameTextView != null) { + nameTextView.setText(text); + } else { + nameToSet = text; + } + } + } + @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.group_create_menu, menu); - SupportMenuItem doneItem = (SupportMenuItem)menu.findItem(R.id.done_menu_item); + SupportMenuItem doneItem = (SupportMenuItem)menu.add(Menu.NONE, 0, Menu.NONE, null); + doneItem.setShowAsAction(SupportMenuItem.SHOW_AS_ACTION_ALWAYS); + doneItem.setActionView(R.layout.group_create_done_layout); + TextView doneTextView = (TextView)doneItem.getActionView().findViewById(R.id.done_button); doneTextView.setText(LocaleController.getString("Done", R.string.Done)); doneTextView.setOnClickListener(new View.OnClickListener() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LanguageSelectActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LanguageSelectActivity.java index 76e83756..fa6cf2dc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LanguageSelectActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LanguageSelectActivity.java @@ -8,7 +8,9 @@ package org.telegram.ui; +import android.app.AlertDialog; import android.content.Context; +import android.content.DialogInterface; import android.os.Bundle; import android.support.v4.internal.view.SupportMenuItem; import android.support.v4.view.MenuItemCompat; @@ -114,10 +116,62 @@ public class LanguageSelectActivity extends BaseFragment { } } } + if (searchItem != null && searchItem.isActionViewExpanded()) { + searchItem.collapseActionView(); + } finishFragment(); } }); + listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { + @Override + public boolean onItemLongClick(AdapterView adapterView, View view, int i, long l) { + if (parentActivity == null) { + return false; + } + LocaleController.LocaleInfo localeInfo = null; + if (searching && searchWas) { + if (i >= 0 && i < searchResult.size()) { + localeInfo = searchResult.get(i); + } + } else { + if (i >= 0 && i < LocaleController.getInstance().sortedLanguages.size()) { + localeInfo = LocaleController.getInstance().sortedLanguages.get(i); + } + } + if (localeInfo == null || localeInfo.pathToFile == null) { + return false; + } + final LocaleController.LocaleInfo finalLocaleInfo = localeInfo; + AlertDialog.Builder builder = new AlertDialog.Builder(parentActivity); + builder.setMessage(LocaleController.getString("DeleteLocalization", R.string.DeleteLocalization)); + builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); + builder.setPositiveButton(LocaleController.getString("Delete", R.string.Delete), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + if (LocaleController.getInstance().deleteLanguage(finalLocaleInfo)) { + if (searchResult != null) { + searchResult.remove(finalLocaleInfo); + } + if (listAdapter != null) { + listAdapter.notifyDataSetChanged(); + } + if (searchListViewAdapter != null) { + searchListViewAdapter.notifyDataSetChanged(); + } + applySelfActionBar(); + if (searchItem != null && searchItem.isActionViewExpanded()) { + searchItem.collapseActionView(); + } + } + } + }); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + builder.show().setCanceledOnTouchOutside(true); + return true; + } + }); + listView.setOnTouchListener(new OnSwipeTouchListener() { public void onSwipeRight() { finishFragment(true); @@ -184,10 +238,8 @@ public class LanguageSelectActivity extends BaseFragment { int itemId = item.getItemId(); switch (itemId) { case android.R.id.home: - if (searchItem != null) { - if (searchItem.isActionViewExpanded()) { - searchItem.collapseActionView(); - } + if (searchItem != null && searchItem.isActionViewExpanded()) { + searchItem.collapseActionView(); } finishFragment(); break; @@ -197,9 +249,9 @@ public class LanguageSelectActivity extends BaseFragment { @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.contacts_menu, menu); - searchItem = (SupportMenuItem)menu.findItem(R.id.messages_list_menu_search); - searchView = (SearchView)searchItem.getActionView(); + searchItem = (SupportMenuItem)menu.add(Menu.NONE, 0, Menu.NONE, LocaleController.getString("Search", R.string.Search)).setIcon(R.drawable.ic_ab_search); + searchItem.setShowAsAction(SupportMenuItem.SHOW_AS_ACTION_ALWAYS|SupportMenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW); + searchItem.setActionView(searchView = new SearchView(parentActivity)); TextView textView = (TextView) searchView.findViewById(R.id.search_src_text); if (textView != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java index 0b003f29..24fea83d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java @@ -65,8 +65,8 @@ public class LaunchActivity extends ActionBarActivity implements NotificationCen private String videoPath = null; private String sendingText = null; private String documentPath = null; - private Uri[] imagesPathArray = null; - private String[] documentsPathArray = null; + private ArrayList imagesPathArray = null; + private ArrayList documentsPathArray = null; private ArrayList contactsToSend = null; private int currentConnectionState; private View statusView; @@ -140,6 +140,46 @@ public class LaunchActivity extends ActionBarActivity implements NotificationCen MessagesActivity fragment = new MessagesActivity(); fragment.onFragmentCreate(); ApplicationLoader.fragmentsStack.add(fragment); + + try { + if (savedInstanceState != null) { + String fragmentName = savedInstanceState.getString("fragment"); + if (fragmentName != null) { + Bundle args = savedInstanceState.getBundle("args"); + if (fragmentName.equals("chat")) { + if (args != null) { + ChatActivity chat = new ChatActivity(); + chat.setArguments(args); + if (chat.onFragmentCreate()) { + ApplicationLoader.fragmentsStack.add(chat); + chat.restoreSelfArgs(savedInstanceState); + } + } + } else if (fragmentName.equals("settings")) { + SettingsActivity settings = new SettingsActivity(); + settings.onFragmentCreate(); + settings.restoreSelfArgs(savedInstanceState); + ApplicationLoader.fragmentsStack.add(settings); + } else if (fragmentName.equals("group")) { + if (args != null) { + GroupCreateFinalActivity group = new GroupCreateFinalActivity(); + group.setArguments(args); + if (group.onFragmentCreate()) { + group.restoreSelfArgs(savedInstanceState); + ApplicationLoader.fragmentsStack.add(group); + } + } + } else if (fragmentName.equals("wallpapers")) { + SettingsWallpapersActivity settings = new SettingsWallpapersActivity(); + settings.onFragmentCreate(); + settings.restoreSelfArgs(savedInstanceState); + ApplicationLoader.fragmentsStack.add(settings); + } + } + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } } handleIntent(getIntent(), false, savedInstanceState != null); @@ -289,7 +329,18 @@ public class LaunchActivity extends ActionBarActivity implements NotificationCen parcelable = Uri.parse(parcelable.toString()); } if (parcelable != null && type != null && type.startsWith("image/")) { - photoPath = (Uri)parcelable; + if (type.equals("image/gif")) { + try { + documentPath = Utilities.getPath((Uri)parcelable); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + if (documentPath == null) { + photoPath = (Uri) parcelable; + } + } else { + photoPath = (Uri) parcelable; + } } else { path = Utilities.getPath((Uri)parcelable); if (path != null) { @@ -316,31 +367,42 @@ public class LaunchActivity extends ActionBarActivity implements NotificationCen String type = intent.getType(); if (uris != null) { if (type != null && type.startsWith("image/")) { - Uri[] uris2 = new Uri[uris.size()]; - for (int i = 0; i < uris2.length; i++) { - Parcelable parcelable = uris.get(i); + for (Parcelable parcelable : uris) { if (!(parcelable instanceof Uri)) { parcelable = Uri.parse(parcelable.toString()); } - uris2[i] = (Uri)parcelable; + if (type.equals("image/gif")) { + if (documentsPathArray == null) { + documentsPathArray = new ArrayList(); + } + try { + documentsPathArray.add(Utilities.getPath((Uri) parcelable)); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } else { + if (imagesPathArray == null) { + imagesPathArray = new ArrayList(); + } + imagesPathArray.add((Uri) parcelable); + } } - imagesPathArray = uris2; } else { - String[] uris2 = new String[uris.size()]; - for (int i = 0; i < uris2.length; i++) { - Parcelable parcelable = uris.get(i); + for (Parcelable parcelable : uris) { if (!(parcelable instanceof Uri)) { parcelable = Uri.parse(parcelable.toString()); } - String path = Utilities.getPath((Uri)parcelable); + String path = Utilities.getPath((Uri) parcelable); if (path != null) { if (path.startsWith("file:")) { path = path.replace("file://", ""); } - uris2[i] = path; + if (documentsPathArray == null) { + documentsPathArray = new ArrayList(); + } + documentsPathArray.add(path); } } - documentsPathArray = uris2; } } else { error = true; @@ -497,21 +559,27 @@ public class LaunchActivity extends ActionBarActivity implements NotificationCen } if (photoPath != null) { fragment.processSendingPhoto(null, photoPath); - } else if (videoPath != null) { + } + if (videoPath != null) { fragment.processSendingVideo(videoPath); - } else if (sendingText != null) { + } + if (sendingText != null) { fragment.processSendingText(sendingText); - } else if (documentPath != null) { + } + if (documentPath != null) { fragment.processSendingDocument(documentPath); - } else if (imagesPathArray != null) { + } + if (imagesPathArray != null) { for (Uri path : imagesPathArray) { fragment.processSendingPhoto(null, path); } - } else if (documentsPathArray != null) { + } + if (documentsPathArray != null) { for (String path : documentsPathArray) { fragment.processSendingDocument(path); } - } else if (contactsToSend != null && !contactsToSend.isEmpty()) { + } + if (contactsToSend != null && !contactsToSend.isEmpty()) { for (TLRPC.User user : contactsToSend) { MessagesController.getInstance().sendMessage(user, dialog_id); } @@ -536,6 +604,15 @@ public class LaunchActivity extends ActionBarActivity implements NotificationCen } } + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (ApplicationLoader.fragmentsStack.size() != 0) { + BaseFragment fragment = ApplicationLoader.fragmentsStack.get(ApplicationLoader.fragmentsStack.size() - 1); + fragment.onActivityResultFragment(requestCode, resultCode, data); + } + } + @Override protected void onPause() { super.onPause(); @@ -932,6 +1009,22 @@ public class LaunchActivity extends ActionBarActivity implements NotificationCen protected void onSaveInstanceState(Bundle outState) { try { super.onSaveInstanceState(outState); + if (!ApplicationLoader.fragmentsStack.isEmpty()) { + BaseFragment lastFragment = ApplicationLoader.fragmentsStack.get(ApplicationLoader.fragmentsStack.size() - 1); + Bundle args = lastFragment.getArguments(); + if (lastFragment instanceof ChatActivity && args != null) { + outState.putBundle("args", args); + outState.putString("fragment", "chat"); + } else if (lastFragment instanceof SettingsActivity) { + outState.putString("fragment", "settings"); + } else if (lastFragment instanceof GroupCreateFinalActivity && args != null) { + outState.putBundle("args", args); + outState.putString("fragment", "group"); + } else if (lastFragment instanceof SettingsWallpapersActivity) { + outState.putString("fragment", "wallpapers"); + } + lastFragment.saveSelfArgs(outState); + } } catch (Exception e) { FileLog.e("tmessages", e); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java index ede48dd2..37dc07ec 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java @@ -10,6 +10,7 @@ package org.telegram.ui; import android.location.Location; import android.os.Bundle; +import android.support.v4.internal.view.SupportMenuItem; import android.support.v7.app.ActionBar; import android.view.LayoutInflater; import android.view.Menu; @@ -53,6 +54,11 @@ public class LocationActivity extends BaseFragment implements NotificationCenter private boolean userLocationMoved = false; private boolean firstWas = false; + private final static int map_to_my_location = 1; + private final static int map_list_menu_map = 2; + private final static int map_list_menu_satellite = 3; + private final static int map_list_menu_hybrid = 4; + public SupportMapFragment mapFragment = new SupportMapFragment() { @Override public void onActivityCreated(Bundle savedInstanceState) { @@ -261,29 +267,34 @@ public class LocationActivity extends BaseFragment implements NotificationCenter @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.location_menu, menu); + SupportMenuItem item = (SupportMenuItem)menu.add(Menu.NONE, map_to_my_location, Menu.NONE, LocaleController.getString("MyLocation", R.string.MyLocation)).setIcon(R.drawable.ic_ab_location); + item.setShowAsAction(SupportMenuItem.SHOW_AS_ACTION_ALWAYS); + + menu.add(Menu.NONE, map_list_menu_map, Menu.NONE, LocaleController.getString("Map", R.string.Map)); + menu.add(Menu.NONE, map_list_menu_satellite, Menu.NONE, LocaleController.getString("Satellite", R.string.Satellite)); + menu.add(Menu.NONE, map_list_menu_hybrid, Menu.NONE, LocaleController.getString("Hybrid", R.string.Hybrid)); } @Override public boolean onOptionsItemSelected(MenuItem item) { int itemId = item.getItemId(); switch (itemId) { - case R.id.map_list_menu_map: + case map_list_menu_map: if (googleMap != null) { googleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL); } break; - case R.id.map_list_menu_satellite: + case map_list_menu_satellite: if (googleMap != null) { googleMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE); } break; - case R.id.map_list_menu_hybrid: + case map_list_menu_hybrid: if (googleMap != null) { googleMap.setMapType(GoogleMap.MAP_TYPE_HYBRID); } break; - case R.id.map_to_my_location: + case map_to_my_location: if (myLocation != null) { LatLng latLng = new LatLng(myLocation.getLatitude(), myLocation.getLongitude()); if (googleMap != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java index 390ca5db..bfad4766 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java @@ -19,7 +19,6 @@ import android.support.v4.internal.view.SupportMenuItem; import android.support.v7.app.ActionBarActivity; import android.view.Display; import android.view.Menu; -import android.view.MenuInflater; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.ImageView; @@ -113,9 +112,10 @@ public class LoginActivity extends ActionBarActivity implements SlideView.SlideV @Override public boolean onCreateOptionsMenu(Menu menu) { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.group_create_menu, menu); - SupportMenuItem doneItem = (SupportMenuItem)menu.findItem(R.id.done_menu_item); + SupportMenuItem doneItem = (SupportMenuItem)menu.add(Menu.NONE, 0, Menu.NONE, null); + doneItem.setShowAsAction(SupportMenuItem.SHOW_AS_ACTION_ALWAYS); + doneItem.setActionView(R.layout.group_create_done_layout); + TextView doneTextView = (TextView)doneItem.getActionView().findViewById(R.id.done_button); doneTextView.setText(LocaleController.getString("Done", R.string.Done)); doneTextView.setOnClickListener(new View.OnClickListener() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/MessagesActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/MessagesActivity.java index d7079ca0..77a364c1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/MessagesActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/MessagesActivity.java @@ -75,6 +75,12 @@ public class MessagesActivity extends BaseFragment implements NotificationCenter public MessagesActivityDelegate delegate; + private final static int messages_list_menu_new_messages = 1; + private final static int messages_list_menu_new_chat = 2; + private final static int messages_list_menu_new_secret_chat = 3; + private final static int messages_list_menu_contacts = 4; + private final static int messages_list_menu_settings = 5; + public static interface MessagesActivityDelegate { public abstract void didSelectDialog(MessagesActivity fragment, long dialog_id); } @@ -434,7 +440,7 @@ public class MessagesActivity extends BaseFragment implements NotificationCenter private void didSelectResult(final long dialog_id, boolean useAlert) { if (useAlert && selectAlertString != 0) { AlertDialog.Builder builder = new AlertDialog.Builder(parentActivity); - builder.setTitle(R.string.AppName); + builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); int lower_part = (int)dialog_id; if (lower_part != 0) { if (lower_part > 0) { @@ -535,13 +541,25 @@ public class MessagesActivity extends BaseFragment implements NotificationCenter @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - if (onlySelect) { - inflater.inflate(R.menu.messages_list_select_menu, menu); - } else { - inflater.inflate(R.menu.messages_list_menu, menu); + searchItem = (SupportMenuItem)menu.add(Menu.NONE, 0, Menu.NONE, LocaleController.getString("Search", R.string.Search)).setIcon(R.drawable.ic_ab_search); + searchItem.setShowAsAction(SupportMenuItem.SHOW_AS_ACTION_ALWAYS|SupportMenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW); + searchItem.setActionView(searchView = new SearchView(parentActivity)); + if (!onlySelect) { + SupportMenuItem item = (SupportMenuItem)menu.add(Menu.NONE, messages_list_menu_new_messages, Menu.NONE, LocaleController.getString("NewMessages", R.string.NewMessages)).setIcon(R.drawable.ic_ab_compose); + item.setShowAsAction(SupportMenuItem.SHOW_AS_ACTION_ALWAYS); + + item = (SupportMenuItem)menu.add(Menu.NONE, messages_list_menu_new_chat, Menu.NONE, LocaleController.getString("NewGroup", R.string.NewGroup)); + item.setShowAsAction(SupportMenuItem.SHOW_AS_ACTION_NEVER); + + item = (SupportMenuItem)menu.add(Menu.NONE, messages_list_menu_new_secret_chat, Menu.NONE, LocaleController.getString("NewSecretChat", R.string.NewSecretChat)); + item.setShowAsAction(SupportMenuItem.SHOW_AS_ACTION_NEVER); + + item = (SupportMenuItem)menu.add(Menu.NONE, messages_list_menu_contacts, Menu.NONE, LocaleController.getString("Contacts", R.string.Contacts)); + item.setShowAsAction(SupportMenuItem.SHOW_AS_ACTION_NEVER); + + item = (SupportMenuItem)menu.add(Menu.NONE, messages_list_menu_settings, Menu.NONE, LocaleController.getString("Settings", R.string.Settings)); + item.setShowAsAction(SupportMenuItem.SHOW_AS_ACTION_NEVER); } - searchItem = (SupportMenuItem)menu.findItem(R.id.messages_list_menu_search); - searchView = (SearchView) searchItem.getActionView(); TextView textView = (TextView) searchView.findViewById(R.id.search_src_text); if (textView != null) { @@ -634,15 +652,15 @@ public class MessagesActivity extends BaseFragment implements NotificationCenter } switch (itemId) { - case R.id.messages_list_menu_settings: { + case messages_list_menu_settings: { ((LaunchActivity)inflaterActivity).presentFragment(new SettingsActivity(), "settings", false); break; } - case R.id.messages_list_menu_contacts: { + case messages_list_menu_contacts: { ((LaunchActivity)inflaterActivity).presentFragment(new ContactsActivity(), "contacts", false); break; } - case R.id.messages_list_menu_new_messages: { + case messages_list_menu_new_messages: { BaseFragment fragment = new ContactsActivity(); Bundle bundle = new Bundle(); bundle.putBoolean("onlyUsers", true); @@ -653,7 +671,7 @@ public class MessagesActivity extends BaseFragment implements NotificationCenter ((LaunchActivity)inflaterActivity).presentFragment(fragment, "contacts_chat", false); break; } - case R.id.messages_list_menu_new_secret_chat: { + case messages_list_menu_new_secret_chat: { BaseFragment fragment = new ContactsActivity(); Bundle bundle = new Bundle(); bundle.putBoolean("onlyUsers", true); @@ -665,7 +683,7 @@ public class MessagesActivity extends BaseFragment implements NotificationCenter ((LaunchActivity)inflaterActivity).presentFragment(fragment, "contacts_chat", false); break; } - case R.id.messages_list_menu_new_chat: { + case messages_list_menu_new_chat: { ((LaunchActivity)inflaterActivity).presentFragment(new GroupCreateActivity(), "group_create", false); break; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java index 53e3be4f..2a30b930 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java @@ -20,6 +20,8 @@ import android.net.Uri; import android.os.Bundle; import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBarActivity; +import android.text.Html; +import android.text.method.LinkMovementMethod; import android.util.Base64; import android.view.LayoutInflater; import android.view.MenuItem; @@ -88,6 +90,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter private int audioDownloadSection; private int audioDownloadChatRow; private int audioDownloadPrivateRow; + private int telegramFaqRow; private int languageRow; @Override @@ -178,6 +181,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter clearLogsRow = rowCount++; switchBackendButtonRow = rowCount++; } + telegramFaqRow = rowCount++; askQuestionRow = rowCount++; logoutRow = rowCount++; @@ -241,91 +245,25 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter } else if (i == backgroundRow) { ((LaunchActivity)parentActivity).presentFragment(new SettingsWallpapersActivity(), "settings_wallpapers", false); } else if (i == askQuestionRow) { - final SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); - int uid = preferences.getInt("support_id", 0); - TLRPC.User supportUser = null; - if (uid != 0) { - supportUser = MessagesController.getInstance().users.get(uid); - if (supportUser == null) { - String userString = preferences.getString("support_user", null); - if (userString != null) { - try { - byte[] datacentersBytes = Base64.decode(userString, Base64.DEFAULT); - if (datacentersBytes != null) { - SerializedData data = new SerializedData(datacentersBytes); - supportUser = (TLRPC.User)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); - - } - } catch (Exception e) { - FileLog.e("tmessages", e); - supportUser = null; - } - } - } + if (parentActivity == null) { + return; } - if (supportUser == null) { - if (parentActivity == null) { - return; - } - final ProgressDialog progressDialog = new ProgressDialog(parentActivity); - progressDialog.setMessage(parentActivity.getString(R.string.Loading)); - progressDialog.setCanceledOnTouchOutside(false); - progressDialog.setCancelable(false); - progressDialog.show(); - TLRPC.TL_help_getSupport req = new TLRPC.TL_help_getSupport(); - ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() { - @Override - public void run(TLObject response, TLRPC.TL_error error) { - if (error == null) { + final TextView message = new TextView(parentActivity); + message.setText(Html.fromHtml(LocaleController.getString("AskAQuestionInfo", R.string.AskAQuestionInfo))); + message.setTextSize(18); + message.setPadding(Utilities.dp(8), Utilities.dp(5), Utilities.dp(8), Utilities.dp(6)); + message.setMovementMethod(LinkMovementMethod.getInstance()); - final TLRPC.TL_help_support res = (TLRPC.TL_help_support)response; - Utilities.RunOnUIThread(new Runnable() { - @Override - public void run() { - if (parentActivity == null) { - return; - } - SharedPreferences.Editor editor = preferences.edit(); - editor.putInt("support_id", res.user.id); - SerializedData data = new SerializedData(); - res.user.serializeToStream(data); - editor.putString("support_user", Base64.encodeToString(data.toByteArray(), Base64.DEFAULT)); - editor.commit(); - try { - progressDialog.dismiss(); - } catch (Exception e) { - FileLog.e("tmessages", e); - } - MessagesController.getInstance().users.put(res.user.id, res.user); - ChatActivity fragment = new ChatActivity(); - Bundle bundle = new Bundle(); - bundle.putInt("user_id", res.user.id); - fragment.setArguments(bundle); - ((LaunchActivity)parentActivity).presentFragment(fragment, "chat" + Math.random(), false); - } - }); - } else { - Utilities.RunOnUIThread(new Runnable() { - @Override - public void run() { - try { - progressDialog.dismiss(); - } catch (Exception e) { - FileLog.e("tmessages", e); - } - } - }); - } - } - }, null, true, RPCRequest.RPCRequestClassGeneric); - } else { - MessagesController.getInstance().users.putIfAbsent(supportUser.id, supportUser); - ChatActivity fragment = new ChatActivity(); - Bundle bundle = new Bundle(); - bundle.putInt("user_id", supportUser.id); - fragment.setArguments(bundle); - ((LaunchActivity)parentActivity).presentFragment(fragment, "chat" + Math.random(), false); - } + AlertDialog.Builder builder = new AlertDialog.Builder(parentActivity); + builder.setView(message); + builder.setPositiveButton(LocaleController.getString("AskButton", R.string.AskButton), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + performAskAQuestion(); + } + }); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + builder.show().setCanceledOnTouchOutside(true); } else if (i == sendLogsRow) { sendLogs(); } else if (i == clearLogsRow) { @@ -409,7 +347,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter listView.invalidateViews(); } } else if (i == languageRow) { - ((LaunchActivity)parentActivity).presentFragment(new LanguageSelectActivity(), "settings_wallpapers", false); + ((LaunchActivity)parentActivity).presentFragment(new LanguageSelectActivity(), "settings_lang", false); } else if (i == switchBackendButtonRow) { AlertDialog.Builder builder = new AlertDialog.Builder(parentActivity); builder.setMessage(LocaleController.getString("AreYouSure", R.string.AreYouSure)); @@ -422,11 +360,14 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); builder.show().setCanceledOnTouchOutside(true); + } else if (i == telegramFaqRow) { + try { + Intent pickIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(LocaleController.getString("TelegramFaqUrl", R.string.TelegramFaqUrl))); + parentActivity.startActivity(pickIntent); + } catch (Exception e) { + FileLog.e("tmessages", e); + } } -// else if (i == 6) { -// UserConfig.saveIncomingPhotos = !UserConfig.saveIncomingPhotos; -// listView.invalidateViews(); -// } } }); @@ -444,12 +385,113 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter return fragmentView; } + public void performAskAQuestion() { + final SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); + int uid = preferences.getInt("support_id", 0); + TLRPC.User supportUser = null; + if (uid != 0) { + supportUser = MessagesController.getInstance().users.get(uid); + if (supportUser == null) { + String userString = preferences.getString("support_user", null); + if (userString != null) { + try { + byte[] datacentersBytes = Base64.decode(userString, Base64.DEFAULT); + if (datacentersBytes != null) { + SerializedData data = new SerializedData(datacentersBytes); + supportUser = (TLRPC.User)TLClassStore.Instance().TLdeserialize(data, data.readInt32()); + + } + } catch (Exception e) { + FileLog.e("tmessages", e); + supportUser = null; + } + } + } + } + if (supportUser == null) { + if (parentActivity == null) { + return; + } + final ProgressDialog progressDialog = new ProgressDialog(parentActivity); + progressDialog.setMessage(parentActivity.getString(R.string.Loading)); + progressDialog.setCanceledOnTouchOutside(false); + progressDialog.setCancelable(false); + progressDialog.show(); + TLRPC.TL_help_getSupport req = new TLRPC.TL_help_getSupport(); + ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() { + @Override + public void run(TLObject response, TLRPC.TL_error error) { + if (error == null) { + + final TLRPC.TL_help_support res = (TLRPC.TL_help_support)response; + Utilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + if (parentActivity == null) { + return; + } + SharedPreferences.Editor editor = preferences.edit(); + editor.putInt("support_id", res.user.id); + SerializedData data = new SerializedData(); + res.user.serializeToStream(data); + editor.putString("support_user", Base64.encodeToString(data.toByteArray(), Base64.DEFAULT)); + editor.commit(); + try { + progressDialog.dismiss(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + MessagesController.getInstance().users.put(res.user.id, res.user); + ChatActivity fragment = new ChatActivity(); + Bundle bundle = new Bundle(); + bundle.putInt("user_id", res.user.id); + fragment.setArguments(bundle); + ((LaunchActivity)parentActivity).presentFragment(fragment, "chat" + Math.random(), false); + } + }); + } else { + Utilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + try { + progressDialog.dismiss(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + }); + } + } + }, null, true, RPCRequest.RPCRequestClassGeneric); + } else { + MessagesController.getInstance().users.putIfAbsent(supportUser.id, supportUser); + ChatActivity fragment = new ChatActivity(); + Bundle bundle = new Bundle(); + bundle.putInt("user_id", supportUser.id); + fragment.setArguments(bundle); + ((LaunchActivity)parentActivity).presentFragment(fragment, "chat" + Math.random(), false); + } + } + @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); + public void onActivityResultFragment(int requestCode, int resultCode, Intent data) { avatarUpdater.onActivityResult(requestCode, resultCode, data); } + @Override + public void saveSelfArgs(Bundle args) { + if (avatarUpdater != null && avatarUpdater.currentPicturePath != null) { + args.putString("path", avatarUpdater.currentPicturePath); + } + } + + @Override + public void restoreSelfArgs(Bundle args) { + if (avatarUpdater != null) { + avatarUpdater.currentPicturePath = args.getString("path"); + } + } + @Override public void didReceivedNotification(int id, Object... args) { if (id == MessagesController.updateInterfaces) { @@ -557,7 +599,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter return i == textSizeRow || i == enableAnimationsRow || i == blockedRow || i == notificationRow || i == backgroundRow || i == askQuestionRow || i == sendLogsRow || i == sendByEnterRow || i == terminateSessionsRow || i == photoDownloadPrivateRow || i == photoDownloadChatRow || i == clearLogsRow || i == audioDownloadChatRow || i == audioDownloadPrivateRow || i == languageRow || - i == switchBackendButtonRow; + i == switchBackendButtonRow || i == telegramFaqRow; } @Override @@ -766,6 +808,9 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter } else if (i == switchBackendButtonRow) { textView.setText("Switch Backend"); divider.setVisibility(View.VISIBLE); + } else if (i == telegramFaqRow) { + textView.setText(LocaleController.getString("TelegramFAQ", R.string.TelegramFaq)); + divider.setVisibility(View.VISIBLE); } } else if (type == 3) { if (view == null) { @@ -901,7 +946,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter return 5; } else if (i == enableAnimationsRow || i == sendByEnterRow || i == photoDownloadChatRow || i == photoDownloadPrivateRow || i == audioDownloadChatRow || i == audioDownloadPrivateRow) { return 3; - } else if (i == numberRow || i == notificationRow || i == blockedRow || i == backgroundRow || i == askQuestionRow || i == sendLogsRow || i == terminateSessionsRow || i == clearLogsRow || i == switchBackendButtonRow) { + } else if (i == numberRow || i == notificationRow || i == blockedRow || i == backgroundRow || i == askQuestionRow || i == sendLogsRow || i == terminateSessionsRow || i == clearLogsRow || i == switchBackendButtonRow || i == telegramFaqRow) { return 2; } else if (i == logoutRow) { return 4; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SettingsBlockedUsers.java b/TMessagesProj/src/main/java/org/telegram/ui/SettingsBlockedUsers.java index 237ef028..8573684f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SettingsBlockedUsers.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SettingsBlockedUsers.java @@ -12,6 +12,7 @@ import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; +import android.support.v4.internal.view.SupportMenuItem; import android.support.v7.app.ActionBar; import android.view.LayoutInflater; import android.view.Menu; @@ -37,7 +38,6 @@ import org.telegram.messenger.Utilities; import org.telegram.ui.Cells.ChatOrUserCell; import org.telegram.ui.Views.BaseFragment; import org.telegram.ui.Views.OnSwipeTouchListener; -import org.w3c.dom.Text; import java.util.ArrayList; import java.util.HashMap; @@ -52,6 +52,8 @@ public class SettingsBlockedUsers extends BaseFragment implements NotificationCe private HashMap blockedContactsDict = new HashMap(); private int selectedUserId; + private final static int block_user = 1; + @Override public boolean onFragmentCreate() { super.onFragmentCreate(); @@ -293,7 +295,8 @@ public class SettingsBlockedUsers extends BaseFragment implements NotificationCe } public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.settings_block_users_bar_menu, menu); + SupportMenuItem item = (SupportMenuItem)menu.add(Menu.NONE, block_user, Menu.NONE, null).setIcon(R.drawable.plus); + item.setShowAsAction(SupportMenuItem.SHOW_AS_ACTION_ALWAYS); } @Override @@ -303,7 +306,7 @@ public class SettingsBlockedUsers extends BaseFragment implements NotificationCe case android.R.id.home: finishFragment(); break; - case R.id.block_user: + case block_user: ContactsActivity fragment = new ContactsActivity(); fragment.animationType = 1; Bundle bundle = new Bundle(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SettingsNotificationsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/SettingsNotificationsActivity.java index b7210195..d6075051 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SettingsNotificationsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SettingsNotificationsActivity.java @@ -103,6 +103,9 @@ public class SettingsNotificationsActivity extends BaseFragment { editor.commit(); listView.invalidateViews(); } else if (i == 4 || i == 9) { + if (parentActivity == null) { + return; + } try { SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); Intent tmpIntent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER); @@ -137,7 +140,7 @@ public class SettingsNotificationsActivity extends BaseFragment { } } tmpIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, currentSound); - startActivityForResult(tmpIntent, i); + parentActivity.startActivityForResult(tmpIntent, i); } catch (Exception e) { FileLog.e("tmessages", e); } @@ -228,8 +231,7 @@ public class SettingsNotificationsActivity extends BaseFragment { } @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); + public void onActivityResultFragment(int requestCode, int resultCode, Intent data) { if (resultCode == Activity.RESULT_OK) { Uri ringtone = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI); String name = null; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SettingsWallpapersActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/SettingsWallpapersActivity.java index f294315e..4cb4165d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SettingsWallpapersActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SettingsWallpapersActivity.java @@ -15,7 +15,6 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; -import android.database.Cursor; import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; @@ -80,6 +79,8 @@ public class SettingsWallpapersActivity extends BaseFragment implements Notifica selectedBackground = preferences.getInt("selectedBackground", 1000001); selectedColor = preferences.getInt("selectedColor", 0); MessagesStorage.getInstance().getWallpapers(); + File toFile = new File(ApplicationLoader.applicationContext.getFilesDir(), "wallpaper-temp.jpg"); + toFile.delete(); return true; } @@ -113,6 +114,9 @@ public class SettingsWallpapersActivity extends BaseFragment implements Notifica builder.setItems(items, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { + if (parentActivity == null) { + return; + } if (i == 0) { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); File image = Utilities.generatePicturePath(); @@ -120,11 +124,11 @@ public class SettingsWallpapersActivity extends BaseFragment implements Notifica takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(image)); currentPicturePath = image.getAbsolutePath(); } - startActivityForResult(takePictureIntent, 0); + parentActivity.startActivityForResult(takePictureIntent, 10); } else if (i == 1) { Intent photoPickerIntent = new Intent(Intent.ACTION_PICK); photoPickerIntent.setType("image/*"); - startActivityForResult(photoPickerIntent, 1); + parentActivity.startActivityForResult(photoPickerIntent, 11); } } }); @@ -168,7 +172,13 @@ public class SettingsWallpapersActivity extends BaseFragment implements Notifica FileLog.e("tmessages", e); } } else { - done = true; + if (selectedBackground == -1) { + File fromFile = new File(ApplicationLoader.applicationContext.getFilesDir(), "wallpaper-temp.jpg"); + File toFile = new File(ApplicationLoader.applicationContext.getFilesDir(), "wallpaper.jpg"); + done = fromFile.renameTo(toFile); + } else { + done = true; + } } if (done) { @@ -194,14 +204,13 @@ public class SettingsWallpapersActivity extends BaseFragment implements Notifica } @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); + public void onActivityResultFragment(int requestCode, int resultCode, Intent data) { if (resultCode == Activity.RESULT_OK) { - if (requestCode == 0) { + if (requestCode == 10) { Utilities.addMediaToGallery(currentPicturePath); try { Bitmap bitmap = FileLoader.loadBitmap(currentPicturePath, null, Utilities.dp(320), Utilities.dp(480)); - File toFile = new File(ApplicationLoader.applicationContext.getFilesDir(), "wallpaper.jpg"); + File toFile = new File(ApplicationLoader.applicationContext.getFilesDir(), "wallpaper-temp.jpg"); FileOutputStream stream = new FileOutputStream(toFile); bitmap.compress(Bitmap.CompressFormat.JPEG, 87, stream); selectedBackground = -1; @@ -211,22 +220,13 @@ public class SettingsWallpapersActivity extends BaseFragment implements Notifica FileLog.e("tmessages", e); } currentPicturePath = null; - } else if (requestCode == 1) { - Uri imageUri = data.getData(); - Cursor cursor = parentActivity.getContentResolver().query(imageUri, new String[]{android.provider.MediaStore.Images.ImageColumns.DATA}, null, null, null); - if (cursor == null) { + } else if (requestCode == 11) { + if (data == null || data.getData() == null) { return; } - try { - String imageFilePath = null; - if (cursor.moveToFirst()) { - imageFilePath = cursor.getString(0); - } - cursor.close(); - - Bitmap bitmap = FileLoader.loadBitmap(imageFilePath, null, Utilities.dp(320), Utilities.dp(480)); - File toFile = new File(ApplicationLoader.applicationContext.getFilesDir(), "wallpaper.jpg"); + Bitmap bitmap = FileLoader.loadBitmap(null, data.getData(), Utilities.dp(320), Utilities.dp(480)); + File toFile = new File(ApplicationLoader.applicationContext.getFilesDir(), "wallpaper-temp.jpg"); FileOutputStream stream = new FileOutputStream(toFile); bitmap.compress(Bitmap.CompressFormat.JPEG, 87, stream); selectedBackground = -1; @@ -239,6 +239,18 @@ public class SettingsWallpapersActivity extends BaseFragment implements Notifica } } + @Override + public void saveSelfArgs(Bundle args) { + if (currentPicturePath != null) { + args.putString("path", currentPicturePath); + } + } + + @Override + public void restoreSelfArgs(Bundle args) { + currentPicturePath = args.getString("path"); + } + private void processSelectedBackground() { TLRPC.WallPaper wallPaper = wallpappersByIds.get(selectedBackground); if (selectedBackground != -1 && selectedBackground != 1000001 && wallPaper != null && wallPaper instanceof TLRPC.TL_wallPaper) { @@ -277,7 +289,10 @@ public class SettingsWallpapersActivity extends BaseFragment implements Notifica backgroundImage.setBackgroundColor(0); selectedColor = 0; } else if (selectedBackground == -1) { - File toFile = new File(ApplicationLoader.applicationContext.getFilesDir(), "wallpaper.jpg"); + File toFile = new File(ApplicationLoader.applicationContext.getFilesDir(), "wallpaper-temp.jpg"); + if (!toFile.exists()) { + toFile = new File(ApplicationLoader.applicationContext.getFilesDir(), "wallpaper.jpg"); + } if (toFile.exists()) { backgroundImage.setImageURI(Uri.fromFile(toFile)); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/UserProfileActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/UserProfileActivity.java index 41993d46..00045c87 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/UserProfileActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/UserProfileActivity.java @@ -66,6 +66,12 @@ public class UserProfileActivity extends BaseFragment implements NotificationCen private long dialog_id; private TLRPC.EncryptedChat currentEncryptedChat; + private final static int add_contact = 1; + private final static int block_contact = 2; + private final static int share_contact = 3; + private final static int edit_contact = 4; + private final static int delete_contact = 5; + @Override public boolean onFragmentCreate() { super.onFragmentCreate(); @@ -144,6 +150,9 @@ public class UserProfileActivity extends BaseFragment implements NotificationCen } else if (i == 5 && dialog_id == 0 || dialog_id != 0 && (i == 7 && currentEncryptedChat instanceof TLRPC.TL_encryptedChat || i == 5 && !(currentEncryptedChat instanceof TLRPC.TL_encryptedChat))) { + if (parentActivity == null) { + return; + } try { Intent tmpIntent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER); tmpIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION); @@ -168,7 +177,7 @@ public class UserProfileActivity extends BaseFragment implements NotificationCen } tmpIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, currentSound); - startActivityForResult(tmpIntent, 0); + parentActivity.startActivityForResult(tmpIntent, 12); } catch (Exception e) { FileLog.e("tmessages", e); } @@ -256,8 +265,7 @@ public class UserProfileActivity extends BaseFragment implements NotificationCen } @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); + public void onActivityResultFragment(int requestCode, int resultCode, Intent data) { if (resultCode == Activity.RESULT_OK) { if (data == null) { return; @@ -279,7 +287,7 @@ public class UserProfileActivity extends BaseFragment implements NotificationCen SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); SharedPreferences.Editor editor = preferences.edit(); - if (requestCode == 0) { + if (requestCode == 12) { if (name != null && ringtone != null) { editor.putString("sound_" + user_id, name); editor.putString("sound_path_" + user_id, ringtone.toString()); @@ -424,7 +432,7 @@ public class UserProfileActivity extends BaseFragment implements NotificationCen case android.R.id.home: finishFragment(); break; - case R.id.block_contact: { + case block_contact: { TLRPC.User user = MessagesController.getInstance().users.get(user_id); if (user == null) { break; @@ -442,7 +450,7 @@ public class UserProfileActivity extends BaseFragment implements NotificationCen }, null, true, RPCRequest.RPCRequestClassGeneric); break; } - case R.id.add_contact: { + case add_contact: { TLRPC.User user = MessagesController.getInstance().users.get(user_id); ContactAddActivity fragment = new ContactAddActivity(); Bundle args = new Bundle(); @@ -451,7 +459,7 @@ public class UserProfileActivity extends BaseFragment implements NotificationCen ((LaunchActivity)parentActivity).presentFragment(fragment, "add_contact_" + user.id, false); break; } - case R.id.share_contact: { + case share_contact: { MessagesActivity fragment = new MessagesActivity(); Bundle args = new Bundle(); args.putBoolean("onlySelect", true); @@ -461,7 +469,7 @@ public class UserProfileActivity extends BaseFragment implements NotificationCen ((LaunchActivity)parentActivity).presentFragment(fragment, "chat_select", false); break; } - case R.id.edit_contact: { + case edit_contact: { ContactAddActivity fragment = new ContactAddActivity(); Bundle args = new Bundle(); args.putInt("user_id", user_id); @@ -469,7 +477,7 @@ public class UserProfileActivity extends BaseFragment implements NotificationCen ((LaunchActivity)parentActivity).presentFragment(fragment, "add_contact_" + user_id, false); break; } - case R.id.delete_contact: { + case delete_contact: { final TLRPC.User user = MessagesController.getInstance().users.get(user_id); if (user == null) { break; @@ -501,12 +509,16 @@ public class UserProfileActivity extends BaseFragment implements NotificationCen return; } if (user.phone != null && user.phone.length() != 0) { - inflater.inflate(R.menu.user_profile_menu, menu); + menu.add(Menu.NONE, add_contact, Menu.NONE, LocaleController.getString("AddContact", R.string.AddContact)); + menu.add(Menu.NONE, block_contact, Menu.NONE, LocaleController.getString("BlockContact", R.string.BlockContact)); } else { - inflater.inflate(R.menu.user_profile_block_menu, menu); + menu.add(Menu.NONE, block_contact, Menu.NONE, LocaleController.getString("BlockContact", R.string.BlockContact)); } } else { - inflater.inflate(R.menu.user_profile_contact_menu, menu); + menu.add(Menu.NONE, share_contact, Menu.NONE, LocaleController.getString("ShareContact", R.string.ShareContact)); + menu.add(Menu.NONE, block_contact, Menu.NONE, LocaleController.getString("BlockContact", R.string.BlockContact)); + menu.add(Menu.NONE, edit_contact, Menu.NONE, LocaleController.getString("EditContact", R.string.EditContact)); + menu.add(Menu.NONE, delete_contact, Menu.NONE, LocaleController.getString("DeleteContact", R.string.DeleteContact)); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Views/AvatarUpdater.java b/TMessagesProj/src/main/java/org/telegram/ui/Views/AvatarUpdater.java index 304606d6..e3bdab82 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Views/AvatarUpdater.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Views/AvatarUpdater.java @@ -58,7 +58,7 @@ public class AvatarUpdater implements NotificationCenter.NotificationCenterDeleg takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(image)); currentPicturePath = image.getAbsolutePath(); } - parentFragment.startActivityForResult(takePictureIntent, 0); + parentFragment.parentActivity.startActivityForResult(takePictureIntent, 13); } catch (Exception e) { FileLog.e("tmessages", e); } @@ -68,7 +68,7 @@ public class AvatarUpdater implements NotificationCenter.NotificationCenterDeleg try { Intent photoPickerIntent = new Intent(Intent.ACTION_PICK); photoPickerIntent.setType("image/*"); - parentFragment.startActivityForResult(photoPickerIntent, 1); + parentFragment.parentActivity.startActivityForResult(photoPickerIntent, 14); } catch (Exception e) { FileLog.e("tmessages", e); } @@ -102,12 +102,12 @@ public class AvatarUpdater implements NotificationCenter.NotificationCenterDeleg public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == Activity.RESULT_OK) { - if (requestCode == 0) { + if (requestCode == 13) { Utilities.addMediaToGallery(currentPicturePath); startCrop(currentPicturePath, null); currentPicturePath = null; - } else if (requestCode == 1) { + } else if (requestCode == 14) { if (data == null || data.getData() == null) { return; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Views/BackupImageView.java b/TMessagesProj/src/main/java/org/telegram/ui/Views/BackupImageView.java index da182549..980efc9f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Views/BackupImageView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Views/BackupImageView.java @@ -176,6 +176,12 @@ public class BackupImageView extends ImageView { } } + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + recycleBitmap(null); + } + @Override protected void onDraw(Canvas canvas) { try { @@ -188,12 +194,6 @@ public class BackupImageView extends ImageView { } } - @Override - protected void finalize() throws Throwable { - recycleBitmap(null); - super.finalize(); - } - public void setImageResourceMy(int resId) { if (ignoreLayout) { makeRequest = false; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Views/BaseFragment.java b/TMessagesProj/src/main/java/org/telegram/ui/Views/BaseFragment.java index 557552db..4ab44f30 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Views/BaseFragment.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Views/BaseFragment.java @@ -8,6 +8,7 @@ package org.telegram.ui.Views; +import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v7.app.ActionBarActivity; @@ -28,6 +29,7 @@ public class BaseFragment extends Fragment { public int classGuid = 0; public boolean firstStart = true; public boolean animationInProgress = false; + private long currentAnimationDuration = 0; private boolean removeParentOnDestroy = false; private boolean removeParentOnAnimationEnd = true; @@ -108,6 +110,16 @@ public class BaseFragment extends Fragment { public void onAnimationStart() { animationInProgress = true; + if (fragmentView != null) { + fragmentView.postDelayed(new Runnable() { + @Override + public void run() { + if (animationInProgress) { + onAnimationEnd(); + } + } + }, currentAnimationDuration); + } } public void onAnimationEnd() { @@ -137,6 +149,7 @@ public class BaseFragment extends Fragment { public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) { if (nextAnim != 0) { Animation anim = AnimationUtils.loadAnimation(getActivity(), nextAnim); + currentAnimationDuration = anim.getDuration(); anim.setAnimationListener(new Animation.AnimationListener() { @@ -149,7 +162,9 @@ public class BaseFragment extends Fragment { } public void onAnimationEnd(Animation animation) { - BaseFragment.this.onAnimationEnd(); + if (animationInProgress) { + BaseFragment.this.onAnimationEnd(); + } } }); @@ -166,4 +181,16 @@ public class BaseFragment extends Fragment { public void applySelfActionBar() { } + + public void onActivityResultFragment(int requestCode, int resultCode, Intent data) { + + } + + public void saveSelfArgs(Bundle args) { + + } + + public void restoreSelfArgs(Bundle args) { + + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Views/GifDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Views/GifDrawable.java new file mode 100644 index 00000000..cf65c148 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Views/GifDrawable.java @@ -0,0 +1,407 @@ +/* +https://github.com/koral--/android-gif-drawable/ +MIT License +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + */ + +package org.telegram.ui.Views; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.Animatable; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Looper; +import android.view.View; +import android.widget.MediaController; + +import java.io.File; +import java.lang.ref.WeakReference; +import java.util.Locale; + +public class GifDrawable extends Drawable implements Animatable, MediaController.MediaPlayerControl { + + private static native void renderFrame(int[] pixels, int gifFileInPtr, int[] metaData); + private static native int openFile(int[] metaData, String filePath); + private static native void free(int gifFileInPtr); + private static native boolean reset(int gifFileInPtr); + private static native void setSpeedFactor(int gifFileInPtr, float factor); + private static native String getComment(int gifFileInPtr); + private static native int getLoopCount(int gifFileInPtr); + private static native int getDuration(int gifFileInPtr); + private static native int getCurrentPosition(int gifFileInPtr); + private static native int seekToTime(int gifFileInPtr, int pos, int[] pixels); + private static native int seekToFrame(int gifFileInPtr, int frameNr, int[] pixels); + private static native int saveRemainder(int gifFileInPtr); + private static native int restoreRemainder(int gifFileInPtr); + private static native long getAllocationByteCount(int gifFileInPtr); + + private static final Handler UI_HANDLER = new Handler(Looper.getMainLooper()); + + private volatile int mGifInfoPtr; + private volatile boolean mIsRunning = true; + + private final int[] mMetaData = new int[5];//[w, h, imageCount, errorCode, post invalidation time] + private final long mInputSourceLength; + + private float mSx = 1f; + private float mSy = 1f; + private boolean mApplyTransformation; + private final Rect mDstRect = new Rect(); + + public WeakReference parentView = null; + + protected final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG); + protected final int[] mColors; + + private final Runnable mResetTask = new Runnable() { + @Override + public void run() { + reset(mGifInfoPtr); + } + }; + + private final Runnable mStartTask = new Runnable() { + @Override + public void run() { + restoreRemainder(mGifInfoPtr); + if (parentView != null && parentView.get() != null) { + parentView.get().invalidate(); + } + mMetaData[4] = 0; + } + }; + + private final Runnable mSaveRemainderTask = new Runnable() { + @Override + public void run() { + saveRemainder(mGifInfoPtr); + } + }; + + private final Runnable mInvalidateTask = new Runnable() { + @Override + public void run() { + if (parentView != null && parentView.get() != null) { + parentView.get().invalidate(); + } + } + }; + + private static void runOnUiThread(Runnable task) { + if (Looper.myLooper() == UI_HANDLER.getLooper()) { + task.run(); + } else { + UI_HANDLER.post(task); + } + } + + public GifDrawable(String filePath) throws Exception { + mInputSourceLength = new File(filePath).length(); + mGifInfoPtr = openFile(mMetaData, filePath); + mColors = new int[mMetaData[0] * mMetaData[1]]; + } + + public GifDrawable(File file) throws Exception { + mInputSourceLength = file.length(); + mGifInfoPtr = openFile(mMetaData, file.getPath()); + mColors = new int[mMetaData[0] * mMetaData[1]]; + } + + public void recycle() { + mIsRunning = false; + int tmpPtr = mGifInfoPtr; + mGifInfoPtr = 0; + free(tmpPtr); + } + + @Override + protected void finalize() throws Throwable { + try { + recycle(); + } finally { + super.finalize(); + } + } + + @Override + public int getIntrinsicHeight() { + return mMetaData[1]; + } + + @Override + public int getIntrinsicWidth() { + return mMetaData[0]; + } + + @Override + public void setAlpha(int alpha) { + mPaint.setAlpha(alpha); + } + + @Override + public void setColorFilter(ColorFilter cf) { + mPaint.setColorFilter(cf); + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSPARENT; + } + + @Override + public void start() { + if (mIsRunning) { + return; + } + mIsRunning = true; + runOnUiThread(mStartTask); + } + + public void reset() { + runOnUiThread(mResetTask); + } + + @Override + public void stop() { + mIsRunning = false; + runOnUiThread(mSaveRemainderTask); + } + + @Override + public boolean isRunning() { + return mIsRunning; + } + + public String getComment() { + return getComment(mGifInfoPtr); + } + + public int getLoopCount() { + return getLoopCount(mGifInfoPtr); + } + + @Override + public String toString() { + return String.format(Locale.US, "Size: %dx%d, %d frames, error: %d", mMetaData[0], mMetaData[1], mMetaData[2], mMetaData[3]); + } + + public int getNumberOfFrames() { + return mMetaData[2]; + } + + public int getError() { + return mMetaData[3]; + } + + public void setSpeed(float factor) { + if (factor <= 0f) { + throw new IllegalArgumentException("Speed factor is not positive"); + } + setSpeedFactor(mGifInfoPtr, factor); + } + + @Override + public void pause() { + stop(); + } + + @Override + public int getDuration() { + return getDuration(mGifInfoPtr); + } + + @Override + public int getCurrentPosition() { + return getCurrentPosition(mGifInfoPtr); + } + + @Override + public void seekTo(final int position) { + if (position < 0) { + throw new IllegalArgumentException("Position is not positive"); + } + runOnUiThread(new Runnable() { + @Override + public void run() { + seekToTime(mGifInfoPtr, position, mColors); + if (parentView != null && parentView.get() != null) { + parentView.get().invalidate(); + } + } + }); + } + + public void seekToFrame(final int frameIndex) { + if (frameIndex < 0) { + throw new IllegalArgumentException("frameIndex is not positive"); + } + runOnUiThread(new Runnable() { + @Override + public void run() { + seekToFrame(mGifInfoPtr, frameIndex, mColors); + if (parentView != null && parentView.get() != null) { + parentView.get().invalidate(); + } + } + }); + } + + @Override + public boolean isPlaying() { + return mIsRunning; + } + + @Override + public int getBufferPercentage() { + return 100; + } + + @Override + public boolean canPause() { + return true; + } + + @Override + public boolean canSeekBackward() { + return false; + } + + @Override + public boolean canSeekForward() { + return getNumberOfFrames() > 1; + } + + @Override + public int getAudioSessionId() { + return 0; + } + + public int getFrameByteCount() { + return mMetaData[0] * mMetaData[1] * 4; + } + + public long getAllocationByteCount() { + return getAllocationByteCount(mGifInfoPtr) + mColors.length * 4L; + } + + public long getInputSourceByteCount() { + return mInputSourceLength; + } + + public void getPixels(int[] pixels) { + if (pixels.length < mColors.length) { + throw new ArrayIndexOutOfBoundsException("Pixels array is too small. Required length: " + mColors.length); + } + System.arraycopy(mColors, 0, pixels, 0, mColors.length); + } + + public int getPixel(int x, int y) { + if (x < 0) { + throw new IllegalArgumentException("x must be >= 0"); + } + if (y < 0) { + throw new IllegalArgumentException("y must be >= 0"); + } + if (x >= mMetaData[0]) { + throw new IllegalArgumentException("x must be < GIF width"); + } + if (y >= mMetaData[1]) { + throw new IllegalArgumentException("y must be < GIF height"); + } + return mColors[mMetaData[1] * y + x]; + } + + public Bitmap getBitmap() { + seekToFrame(mGifInfoPtr, 0, mColors); + return Bitmap.createBitmap(mColors, 0, mMetaData[0], mMetaData[0], mMetaData[1], Bitmap.Config.ARGB_8888); + } + + @Override + protected void onBoundsChange(Rect bounds) { + super.onBoundsChange(bounds); + mApplyTransformation = true; + } + + @Override + public void draw(Canvas canvas) { + if (mApplyTransformation) { + mDstRect.set(getBounds()); + mSx = (float) mDstRect.width() / mMetaData[0]; + mSy = (float) mDstRect.height() / mMetaData[1]; + mApplyTransformation = false; + } + if (mPaint.getShader() == null) { + if (mIsRunning) { + renderFrame(mColors, mGifInfoPtr, mMetaData); + } else { + mMetaData[4] = -1; + } + canvas.translate(mDstRect.left, mDstRect.top); + canvas.scale(mSx, mSy); + canvas.drawBitmap(mColors, 0, mMetaData[0], 0f, 0f, mMetaData[0], mMetaData[1], true, mPaint); + if (mMetaData[4] >= 0 && mMetaData[2] > 1) { + UI_HANDLER.postDelayed(mInvalidateTask, mMetaData[4]); + } + } else { + canvas.drawRect(mDstRect, mPaint); + } + } + + public final Paint getPaint() { + return mPaint; + } + + @Override + public int getAlpha() { + return mPaint.getAlpha(); + } + + @Override + public void setFilterBitmap(boolean filter) { + mPaint.setFilterBitmap(filter); + if (parentView != null && parentView.get() != null) { + parentView.get().invalidate(); + } + } + + @Override + public void setDither(boolean dither) { + mPaint.setDither(dither); + if (parentView != null && parentView.get() != null) { + parentView.get().invalidate(); + } + } + + @Override + public int getMinimumHeight() { + return mMetaData[1]; + } + + @Override + public int getMinimumWidth() { + return mMetaData[0]; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Views/ImageReceiver.java b/TMessagesProj/src/main/java/org/telegram/ui/Views/ImageReceiver.java index 999d2d86..fccfb5f0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Views/ImageReceiver.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Views/ImageReceiver.java @@ -111,6 +111,34 @@ public class ImageReceiver { } } + public void setImageBitmap(Bitmap bitmap) { + currentPath = null; + last_path = null; + last_httpUrl = null; + last_filter = null; + last_placeholder = null; + last_size = 0; + FileLoader.getInstance().cancelLoadingForImageView(this); + if (bitmap != null) { + recycleBitmap(null); + currentImage = new BitmapDrawable(null, bitmap); + } + } + + public void setImageBitmap(Drawable bitmap) { + currentPath = null; + last_path = null; + last_httpUrl = null; + last_filter = null; + last_placeholder = null; + last_size = 0; + FileLoader.getInstance().cancelLoadingForImageView(this); + if (bitmap != null) { + recycleBitmap(null); + currentImage = bitmap; + } + } + public void clearImage() { recycleBitmap(null); } @@ -137,6 +165,7 @@ public class ImageReceiver { } else { currentImage = null; } + currentPath = null; } } } @@ -160,10 +189,4 @@ public class ImageReceiver { FileLog.e("tmessages", e); } } - - @Override - protected void finalize() throws Throwable { - recycleBitmap(null); - super.finalize(); - } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Views/ProgressView.java b/TMessagesProj/src/main/java/org/telegram/ui/Views/ProgressView.java index da0e6218..cc6e9501 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Views/ProgressView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Views/ProgressView.java @@ -14,50 +14,35 @@ import android.graphics.Paint; import org.telegram.messenger.Utilities; public class ProgressView { - private static Paint innerPaint1; - private static Paint outerPaint1; - private static Paint innerPaint2; - private static Paint outerPaint2; + private Paint innerPaint; + private Paint outerPaint; - public int type; - public int thumbX = 0; + public float currentProgress = 0; public int width; public int height; + public float progressHeight = Utilities.dpf(2.0f); public ProgressView() { - if (innerPaint1 == null) { - innerPaint1 = new Paint(); - outerPaint1 = new Paint(); - innerPaint2 = new Paint(); - outerPaint2 = new Paint(); + innerPaint = new Paint(); + outerPaint = new Paint(); + } - innerPaint1.setColor(0xffb4e396); - outerPaint1.setColor(0xff6ac453); - innerPaint2.setColor(0xffd9e2eb); - outerPaint2.setColor(0xff86c5f8); - } + public void setProgressColors(int innerColor, int outerColor) { + innerPaint.setColor(innerColor); + outerPaint.setColor(outerColor); } public void setProgress(float progress) { - thumbX = (int)Math.ceil(width * progress); - if (thumbX < 0) { - thumbX = 0; - } else if (thumbX > width) { - thumbX = width; + currentProgress = progress; + if (currentProgress < 0) { + currentProgress = 0; + } else if (currentProgress > 1) { + currentProgress = 1; } } public void draw(Canvas canvas) { - Paint inner = null; - Paint outer = null; - if (type == 0) { - inner = innerPaint1; - outer = outerPaint1; - } else if (type == 1) { - inner = innerPaint2; - outer = outerPaint2; - } - canvas.drawRect(0, height / 2 - Utilities.dp(1), width, height / 2 + Utilities.dp(1), inner); - canvas.drawRect(0, height / 2 - Utilities.dp(1), thumbX, height / 2 + Utilities.dp(1), outer); + canvas.drawRect(0, height / 2 - progressHeight / 2.0f, width, height / 2 + progressHeight / 2.0f, innerPaint); + canvas.drawRect(0, height / 2 - progressHeight / 2.0f, width * currentProgress, height / 2 + progressHeight / 2.0f, outerPaint); } } diff --git a/TMessagesProj/src/main/res/layout/chat_group_incoming_photo_layout.xml b/TMessagesProj/src/main/res/layout/chat_group_incoming_photo_layout.xml deleted file mode 100644 index 28199c0c..00000000 --- a/TMessagesProj/src/main/res/layout/chat_group_incoming_photo_layout.xml +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/layout/chat_incoming_photo_layout.xml b/TMessagesProj/src/main/res/layout/chat_incoming_photo_layout.xml deleted file mode 100644 index ab8b885e..00000000 --- a/TMessagesProj/src/main/res/layout/chat_incoming_photo_layout.xml +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/layout/chat_layout.xml b/TMessagesProj/src/main/res/layout/chat_layout.xml index 22385b23..9d7f7137 100644 --- a/TMessagesProj/src/main/res/layout/chat_layout.xml +++ b/TMessagesProj/src/main/res/layout/chat_layout.xml @@ -151,7 +151,7 @@ android:layout_marginTop="2dp" android:maxLines="4" android:minHeight="48dp" - android:textSize="18sp" + android:textSize="18dp" android:textColorHint="#909090" android:ems="10" android:imeOptions="flagNoExtractUi" diff --git a/TMessagesProj/src/main/res/layout/chat_outgoing_photo_layout.xml b/TMessagesProj/src/main/res/layout/chat_outgoing_photo_layout.xml deleted file mode 100644 index 1420ce15..00000000 --- a/TMessagesProj/src/main/res/layout/chat_outgoing_photo_layout.xml +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/menu/chat_enc_menu.xml b/TMessagesProj/src/main/res/menu/chat_enc_menu.xml deleted file mode 100644 index 44a62e3e..00000000 --- a/TMessagesProj/src/main/res/menu/chat_enc_menu.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/menu/chat_menu.xml b/TMessagesProj/src/main/res/menu/chat_menu.xml deleted file mode 100644 index f817aaad..00000000 --- a/TMessagesProj/src/main/res/menu/chat_menu.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/menu/contacts_menu.xml b/TMessagesProj/src/main/res/menu/contacts_menu.xml deleted file mode 100644 index 14193e5e..00000000 --- a/TMessagesProj/src/main/res/menu/contacts_menu.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/menu/gallery_menu.xml b/TMessagesProj/src/main/res/menu/gallery_menu.xml deleted file mode 100644 index 358039c6..00000000 --- a/TMessagesProj/src/main/res/menu/gallery_menu.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/menu/gallery_save_only_menu.xml b/TMessagesProj/src/main/res/menu/gallery_save_only_menu.xml deleted file mode 100644 index e1e85cbf..00000000 --- a/TMessagesProj/src/main/res/menu/gallery_save_only_menu.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/menu/group_create_menu.xml b/TMessagesProj/src/main/res/menu/group_create_menu.xml deleted file mode 100644 index 69268223..00000000 --- a/TMessagesProj/src/main/res/menu/group_create_menu.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/menu/group_profile_menu.xml b/TMessagesProj/src/main/res/menu/group_profile_menu.xml deleted file mode 100644 index 16c539f5..00000000 --- a/TMessagesProj/src/main/res/menu/group_profile_menu.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/menu/location_menu.xml b/TMessagesProj/src/main/res/menu/location_menu.xml deleted file mode 100644 index 52bd2970..00000000 --- a/TMessagesProj/src/main/res/menu/location_menu.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/menu/messages_deleteonly_menu.xml b/TMessagesProj/src/main/res/menu/messages_deleteonly_menu.xml deleted file mode 100644 index 05fd6643..00000000 --- a/TMessagesProj/src/main/res/menu/messages_deleteonly_menu.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/menu/messages_encrypted_menu.xml b/TMessagesProj/src/main/res/menu/messages_encrypted_menu.xml deleted file mode 100644 index bc5e54b5..00000000 --- a/TMessagesProj/src/main/res/menu/messages_encrypted_menu.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/menu/messages_full_menu.xml b/TMessagesProj/src/main/res/menu/messages_full_menu.xml deleted file mode 100644 index ec3808e0..00000000 --- a/TMessagesProj/src/main/res/menu/messages_full_menu.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/menu/messages_list_menu.xml b/TMessagesProj/src/main/res/menu/messages_list_menu.xml deleted file mode 100644 index b00ff8bd..00000000 --- a/TMessagesProj/src/main/res/menu/messages_list_menu.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/menu/messages_list_select_menu.xml b/TMessagesProj/src/main/res/menu/messages_list_select_menu.xml deleted file mode 100644 index 14193e5e..00000000 --- a/TMessagesProj/src/main/res/menu/messages_list_select_menu.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/menu/settings_block_users_bar_menu.xml b/TMessagesProj/src/main/res/menu/settings_block_users_bar_menu.xml deleted file mode 100644 index 8faba3ba..00000000 --- a/TMessagesProj/src/main/res/menu/settings_block_users_bar_menu.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/menu/user_profile_block_menu.xml b/TMessagesProj/src/main/res/menu/user_profile_block_menu.xml deleted file mode 100644 index 0954c42c..00000000 --- a/TMessagesProj/src/main/res/menu/user_profile_block_menu.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/menu/user_profile_contact_menu.xml b/TMessagesProj/src/main/res/menu/user_profile_contact_menu.xml deleted file mode 100644 index 7b4b570f..00000000 --- a/TMessagesProj/src/main/res/menu/user_profile_contact_menu.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/menu/user_profile_menu.xml b/TMessagesProj/src/main/res/menu/user_profile_menu.xml deleted file mode 100644 index 3aecfbd1..00000000 --- a/TMessagesProj/src/main/res/menu/user_profile_menu.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-ar/strings.xml b/TMessagesProj/src/main/res/values-ar/strings.xml index 8101ba20..b347218c 100644 --- a/TMessagesProj/src/main/res/values-ar/strings.xml +++ b/TMessagesProj/src/main/res/values-ar/strings.xml @@ -5,8 +5,9 @@ Telegram - العربية - Arabic + العربية + Arabic + ar رقم هاتفك المحمول @@ -110,7 +111,7 @@ حذف المجموعة قم بالسحب للإلغاء حفظ في الجهاز - Apply localization file + تطبيق ملف التعريب تم طلب محادثة سرية @@ -255,6 +256,12 @@ اشترك صديق في تيليجرام PEBBLE اللغة + نرجو الأخذ بالعلم أن الدعم الفني في تيليجرام يقوم به مجموعة من المتطوعين. نحاول الرد بسرعة قدر المستطاع، لكن ربما نستغرق القليل من الوقت.
]]>يرجى الإطلاع على صفحة الأسئلة الأكثر شيوعًا]]>: يوجد بها حلول للمشاكل وإجابات لمعظم الأسئلة.
+ اسأل متطوع + Telegram FAQ + https://telegram.org/faq/ar + Delete localization? + Incorrect localization file لا توجد وسائط بعد diff --git a/TMessagesProj/src/main/res/values-de/strings.xml b/TMessagesProj/src/main/res/values-de/strings.xml index 19a295c8..445a805d 100644 --- a/TMessagesProj/src/main/res/values-de/strings.xml +++ b/TMessagesProj/src/main/res/values-de/strings.xml @@ -5,8 +5,9 @@ Telegram - Deutsch - German + Deutsch + German + de Dein Telefon @@ -110,7 +111,7 @@ Diese Gruppe löschen WISCHEN UM ABZUBRECHEN In Ordner Downloads speichern - Apply localization file + Sprachdatei benutzen Geheimen Chat angefordert @@ -255,6 +256,12 @@ Kontakt ist Telegram beigetreten PEBBLE Sprache + Please note that Telegram Support is done by volunteers. We try to respond as quickly as possible, but it may take a while.
]]>Please take a look at the Telegram FAQ]]>: it has important troubleshooting tips]]> and answers to most questions.
+ Ask a volunteer + Telegram-FAQ + https://telegram.org/faq + Lokalisierung löschen? + Incorrect localization file Noch keine geteilten Medien vorhanden diff --git a/TMessagesProj/src/main/res/values-es/strings.xml b/TMessagesProj/src/main/res/values-es/strings.xml index 92dcea32..0a709c4f 100644 --- a/TMessagesProj/src/main/res/values-es/strings.xml +++ b/TMessagesProj/src/main/res/values-es/strings.xml @@ -5,8 +5,9 @@ Telegram - Español - Spanish + Español + Spanish + es Tu teléfono @@ -110,7 +111,7 @@ Eliminar este grupo DESLIZA PARA CANCELAR Guardar en descargas - Apply localization file + Aplicar fichero de localización Chat secreto solicitado @@ -255,6 +256,12 @@ Un contacto se unió a Telegram PEBBLE Idioma + Por favor, ten en cuenta que el Soporte de Telegram está hecho por voluntarios. Intentamos responder lo antes posible, pero puede tomar un tiempo.
]]>Por favor, echa un vistazo a las Preguntas Frecuentes de Telegram]]>: tiene importantes Soluciones a problemas]]> y respuestas para la mayoría de las preguntas.
+ Preguntar + Preguntas frecuentes + https://telegram.org/faq/es + ¿Eliminar localización? + Fichero de localización incorrecto No hay fotos ni vídeos compartidos aún diff --git a/TMessagesProj/src/main/res/values-it/strings.xml b/TMessagesProj/src/main/res/values-it/strings.xml index 0703954a..1edff811 100644 --- a/TMessagesProj/src/main/res/values-it/strings.xml +++ b/TMessagesProj/src/main/res/values-it/strings.xml @@ -5,8 +5,9 @@ Telegram - Italiano - Italian + Italiano + Italian + it Il tuo telefono @@ -110,7 +111,7 @@ Elimina questo gruppo TRASCINA PER ANNULLARE Salva in download - Apply localization file + Applica file di localizzazione Chat segreta richiesta @@ -255,6 +256,12 @@ Un contatto si è collegato a Telegram PEBBLE Lingua + Nota che il supporto di Telegram è fornito da volontari. Proviamo a rispondere non appena possibile, ma potrebbe richiedere del tempo.
]]>Dai un\'occhiata alle Domande frequenti]]>: troverai risposte alla maggior parte delle domande e suggerimenti importanti per l\'individuazione del problema]]>.
+ Chiedi a un volontario + Domande frequenti + https://telegram.org/faq + Eliminare la localizzazione? + File della localizzazione non valido Nessun media condiviso diff --git a/TMessagesProj/src/main/res/values-nl/strings.xml b/TMessagesProj/src/main/res/values-nl/strings.xml index 6e04af47..3913b251 100644 --- a/TMessagesProj/src/main/res/values-nl/strings.xml +++ b/TMessagesProj/src/main/res/values-nl/strings.xml @@ -5,8 +5,9 @@ Telegram - Nederlands - Dutch + Nederlands + Dutch + nl Uw telefoon @@ -110,7 +111,7 @@ Deze groep verwijderen SLEEP OM TE ANNULEREN Opslaan in downloads - Apply localization file + Vertaling toepassen Privégesprek aangevraagd @@ -255,6 +256,12 @@ Contact lid geworden van Telegram PEBBLE Taal + Please note that Telegram Support is done by volunteers. We try to respond as quickly as possible, but it may take a while.
]]>Please take a look at the Telegram FAQ]]>: it has important troubleshooting tips]]> and answers to most questions.
+ Ask a volunteer + Telegram FAQ + https://telegram.org/faq + Delete localization? + Incorrect localization file Nog geen media gedeeld diff --git a/TMessagesProj/src/main/res/values/strings.xml b/TMessagesProj/src/main/res/values/strings.xml index 700c6c73..c0bda50b 100644 --- a/TMessagesProj/src/main/res/values/strings.xml +++ b/TMessagesProj/src/main/res/values/strings.xml @@ -5,8 +5,9 @@ Telegram - English - English + English + English + en Your phone @@ -255,6 +256,12 @@ Contact joined Telegram PEBBLE Language + Please note that Telegram Support is done by volunteers. We try to respond as quickly as possible, but it may take a while.
]]>Please take a look at the Telegram FAQ]]>: it has answers to most questions and important tips for troubleshooting]]>.
+ Ask a volunteer + Telegram FAQ + https://telegram.org/faq + Delete localization? + Incorrect localization file No shared media yet