diff --git a/TMessagesProj/build.gradle b/TMessagesProj/build.gradle index 13e954910..f7f70ff79 100644 --- a/TMessagesProj/build.gradle +++ b/TMessagesProj/build.gradle @@ -13,7 +13,7 @@ configurations { dependencies { compileOnly 'org.checkerframework:checker-qual:2.5.2' compileOnly 'org.checkerframework:checker-compat-qual:2.5.0' - implementation 'com.google.firebase:firebase-core:16.0.6' + implementation 'com.google.firebase:firebase-core:16.0.7' implementation 'com.google.firebase:firebase-messaging:17.3.4' implementation 'com.google.firebase:firebase-config:16.1.3' implementation 'com.google.android.gms:play-services-maps:16.0.0' @@ -86,6 +86,16 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } + debugMultidex { + initWith debug + minifyEnabled false + multiDexEnabled true + dependencies{ + implementation 'com.android.support:multidex:1.0.3' + } + manifestPlaceholders = [applicationClassName:"MultiDexApplicationLoader"] + } + HA { debuggable false jniDebuggable false @@ -103,20 +113,19 @@ android { shrinkResources false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } - - foss { - debuggable false - jniDebuggable false - signingConfig signingConfigs.release - } } - defaultConfig.versionCode = 1497 + defaultConfig.versionCode = 1517 sourceSets.debug { manifest.srcFile 'config/debug/AndroidManifest.xml' } + sourceSets.debugMultidex { + manifest.srcFile 'config/debug/AndroidManifest.xml' + java.srcDirs = ['src/multidex/java'] + } + sourceSets.HA { manifest.srcFile 'config/debug/AndroidManifest.xml' } @@ -125,10 +134,6 @@ android { manifest.srcFile 'config/release/AndroidManifest.xml' } - sourceSets.foss { - manifest.srcFile 'config/foss/AndroidManifest.xml' - } - flavorDimensions "minApi" productFlavors { @@ -249,10 +254,17 @@ android { } } + variantFilter { variant -> + def names = variant.flavors*.name + if(variant.buildType.name!="release" && !names.contains("afat")){ + setIgnore(true) + } + } + defaultConfig { minSdkVersion 16 targetSdkVersion 27 - versionName "5.3.1" + versionName "5.4.0" vectorDrawables.generatedDensities = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi'] @@ -262,6 +274,8 @@ android { abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64" } } + + manifestPlaceholders = [applicationClassName:"ApplicationLoader"] } } diff --git a/TMessagesProj/jni/Android.mk b/TMessagesProj/jni/Android.mk index f7129d885..ca5c0955f 100755 --- a/TMessagesProj/jni/Android.mk +++ b/TMessagesProj/jni/Android.mk @@ -65,6 +65,22 @@ include $(PREBUILT_STATIC_LIBRARY) include $(CLEAR_VARS) +LOCAL_MODULE := swscale + +ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) + LOCAL_SRC_FILES := ./ffmpeg/armv7-a/libswscale.a +else ifeq ($(TARGET_ARCH_ABI),arm64-v8a) + LOCAL_SRC_FILES := ./ffmpeg/arm64/libswscale.a +else ifeq ($(TARGET_ARCH_ABI),x86) + LOCAL_SRC_FILES := ./ffmpeg/i686/libswscale.a +else ifeq ($(TARGET_ARCH_ABI),x86_64) + LOCAL_SRC_FILES := ./ffmpeg/x86_64/libswscale.a +endif + +include $(PREBUILT_STATIC_LIBRARY) + +include $(CLEAR_VARS) + LOCAL_MODULE := crypto ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) @@ -255,13 +271,13 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_PRELINK_MODULE := false -LOCAL_MODULE := tmessages.29 +LOCAL_MODULE := tmessages.30 LOCAL_CFLAGS := -w -std=c11 -Os -DNULL=0 -DSOCKLEN_T=socklen_t -DLOCALE_NOT_USED -D_LARGEFILE_SOURCE=1 LOCAL_CFLAGS += -Drestrict='' -D__EMX__ -DOPUS_BUILD -DFIXED_POINT -DUSE_ALLOCA -DHAVE_LRINT -DHAVE_LRINTF -fno-math-errno LOCAL_CFLAGS += -DANDROID_NDK -DDISABLE_IMPORTGL -fno-strict-aliasing -fprefetch-loop-arrays -DAVOID_TABLES -DANDROID_TILE_BASED_DECODE -DANDROID_ARMV6_IDCT -ffast-math -D__STDC_CONSTANT_MACROS LOCAL_CPPFLAGS := -DBSD=1 -ffast-math -Os -funroll-loops -std=c++11 LOCAL_LDLIBS := -ljnigraphics -llog -lz -latomic -lEGL -lGLESv2 -landroid -LOCAL_STATIC_LIBRARIES := webp sqlite tgnet avformat avcodec avresample avutil voip flac +LOCAL_STATIC_LIBRARIES := webp sqlite tgnet swscale avformat avcodec avresample avutil voip flac LOCAL_SRC_FILES := \ ./opus/src/opus.c \ @@ -523,10 +539,10 @@ endif LOCAL_SRC_FILES += \ ./jni.c \ ./audio.c \ -./utils.c \ ./image.c \ ./video.c \ ./intro/IntroRenderer.c \ +./utilities.cpp \ ./gifvideo.cpp \ ./SqliteWrapper.cpp \ ./TgNetWrapper.cpp \ diff --git a/TMessagesProj/jni/audio.c b/TMessagesProj/jni/audio.c index b2290cceb..a82e5f3a0 100644 --- a/TMessagesProj/jni/audio.c +++ b/TMessagesProj/jni/audio.c @@ -6,7 +6,7 @@ #include #include #include -#include "utils.h" +#include "c_utils.h" typedef struct { int version; @@ -335,7 +335,7 @@ int initRecorder(const char *path) { } #ifdef OPUS_SET_LSB_DEPTH - result = opus_encoder_ctl(_encoder, OPUS_SET_LSB_DEPTH(max(8, min(24, inopt.samplesize)))); + result = opus_encoder_ctl(_encoder, OPUS_SET_LSB_DEPTH(MAX(8, MIN(24, inopt.samplesize)))); if (result != OPUS_OK) { LOGE("Warning OPUS_SET_LSB_DEPTH returned: %s", opus_strerror(result)); } @@ -447,7 +447,7 @@ int writeFrame(uint8_t *framePcmBytes, uint32_t frameByteCount) { enc_granulepos += cur_frame_size * 48000 / coding_rate; size_segments = (nbBytes + 255) / 255; - min_bytes = min(nbBytes, min_bytes); + min_bytes = MIN(nbBytes, min_bytes); } while ((((size_segments <= 255) && (last_segments + size_segments > 255)) || (enc_granulepos - last_granulepos > max_ogg_delay)) && ogg_stream_flush_fill(&os, &og, 255 * 255)) { @@ -548,7 +548,7 @@ JNIEXPORT jbyteArray Java_org_telegram_messenger_MediaController_getWaveform2(JN uint16_t *samples = malloc(100 * 2); uint64_t sampleIndex = 0; uint16_t peakSample = 0; - int32_t sampleRate = (int32_t) max(1, length / resultSamples); + int32_t sampleRate = (int32_t) MAX(1, length / resultSamples); int32_t index = 0; for (int32_t i = 0; i < length; i++) { @@ -588,7 +588,7 @@ JNIEXPORT jbyteArray Java_org_telegram_messenger_MediaController_getWaveform2(JN uint8_t *bytes = malloc(bitstreamLength + 4); memset(bytes, 0, bitstreamLength + 4); for (int32_t i = 0; i < resultSamples; i++) { - int32_t value = min(31, abs((int32_t) samples[i]) * 31 / peak); + int32_t value = MIN(31, abs((int32_t) samples[i]) * 31 / peak); set_bits(bytes, i * 5, value & 31); } (*env)->SetByteArrayRegion(env, result, 0, bitstreamLength, (jbyte *) bytes); @@ -609,7 +609,7 @@ JNIEXPORT jbyteArray Java_org_telegram_messenger_MediaController_getWaveform(JNI if (opusFile != NULL && error == OPUS_OK) { int64_t totalSamples = op_pcm_total(opusFile, -1); const uint32_t resultSamples = 100; - int32_t sampleRate = max(1, (int32_t) (totalSamples / resultSamples)); + int32_t sampleRate = MAX(1, (int32_t) (totalSamples / resultSamples)); uint16_t *samples = malloc(100 * 2); @@ -667,7 +667,7 @@ JNIEXPORT jbyteArray Java_org_telegram_messenger_MediaController_getWaveform(JNI memset(bytes, 0, bitstreamLength + 4); for (int32_t i = 0; i < resultSamples; i++) { - int32_t value = min(31, abs((int32_t) samples[i]) * 31 / peak); + int32_t value = MIN(31, abs((int32_t) samples[i]) * 31 / peak); set_bits(bytes, i * 5, value & 31); } diff --git a/TMessagesProj/jni/utils.h b/TMessagesProj/jni/c_utils.h similarity index 77% rename from TMessagesProj/jni/utils.h rename to TMessagesProj/jni/c_utils.h index 34805a1d7..0656fd8c9 100644 --- a/TMessagesProj/jni/utils.h +++ b/TMessagesProj/jni/c_utils.h @@ -17,13 +17,11 @@ #define LOGV(...) #endif -#ifndef max -#define max(x, y) ((x) > (y)) ? (x) : (y) +#ifndef MAX +#define MAX(x, y) ((x) > (y)) ? (x) : (y) #endif -#ifndef min -#define min(x, y) ((x) < (y)) ? (x) : (y) +#ifndef MIN +#define MIN(x, y) ((x) < (y)) ? (x) : (y) #endif -void throwException(JNIEnv *env, char *format, ...); - #endif diff --git a/TMessagesProj/jni/ffmpeg/arm64/libavcodec.a b/TMessagesProj/jni/ffmpeg/arm64/libavcodec.a index 235bd3445..c70f0b413 100644 Binary files a/TMessagesProj/jni/ffmpeg/arm64/libavcodec.a and b/TMessagesProj/jni/ffmpeg/arm64/libavcodec.a differ diff --git a/TMessagesProj/jni/ffmpeg/arm64/libavformat.a b/TMessagesProj/jni/ffmpeg/arm64/libavformat.a index ebdd57ad9..bdcf3088d 100644 Binary files a/TMessagesProj/jni/ffmpeg/arm64/libavformat.a and b/TMessagesProj/jni/ffmpeg/arm64/libavformat.a differ diff --git a/TMessagesProj/jni/ffmpeg/arm64/libavresample.a b/TMessagesProj/jni/ffmpeg/arm64/libavresample.a index af238a40d..beb952491 100644 Binary files a/TMessagesProj/jni/ffmpeg/arm64/libavresample.a and b/TMessagesProj/jni/ffmpeg/arm64/libavresample.a differ diff --git a/TMessagesProj/jni/ffmpeg/arm64/libavutil.a b/TMessagesProj/jni/ffmpeg/arm64/libavutil.a index 203b0bdfb..b02dc9bd8 100644 Binary files a/TMessagesProj/jni/ffmpeg/arm64/libavutil.a and b/TMessagesProj/jni/ffmpeg/arm64/libavutil.a differ diff --git a/TMessagesProj/jni/ffmpeg/arm64/libswscale.a b/TMessagesProj/jni/ffmpeg/arm64/libswscale.a new file mode 100644 index 000000000..59f17885f Binary files /dev/null and b/TMessagesProj/jni/ffmpeg/arm64/libswscale.a differ diff --git a/TMessagesProj/jni/ffmpeg/armv7-a/libavcodec.a b/TMessagesProj/jni/ffmpeg/armv7-a/libavcodec.a index dd1f083a4..decd1f45d 100644 Binary files a/TMessagesProj/jni/ffmpeg/armv7-a/libavcodec.a and b/TMessagesProj/jni/ffmpeg/armv7-a/libavcodec.a differ diff --git a/TMessagesProj/jni/ffmpeg/armv7-a/libavformat.a b/TMessagesProj/jni/ffmpeg/armv7-a/libavformat.a index 11831dab4..0f1e870f8 100644 Binary files a/TMessagesProj/jni/ffmpeg/armv7-a/libavformat.a and b/TMessagesProj/jni/ffmpeg/armv7-a/libavformat.a differ diff --git a/TMessagesProj/jni/ffmpeg/armv7-a/libavresample.a b/TMessagesProj/jni/ffmpeg/armv7-a/libavresample.a index 3b92e489c..82940c810 100644 Binary files a/TMessagesProj/jni/ffmpeg/armv7-a/libavresample.a and b/TMessagesProj/jni/ffmpeg/armv7-a/libavresample.a differ diff --git a/TMessagesProj/jni/ffmpeg/armv7-a/libavutil.a b/TMessagesProj/jni/ffmpeg/armv7-a/libavutil.a index 420b7bdc3..3a2367eae 100644 Binary files a/TMessagesProj/jni/ffmpeg/armv7-a/libavutil.a and b/TMessagesProj/jni/ffmpeg/armv7-a/libavutil.a differ diff --git a/TMessagesProj/jni/ffmpeg/armv7-a/libswscale.a b/TMessagesProj/jni/ffmpeg/armv7-a/libswscale.a new file mode 100644 index 000000000..957ac45d2 Binary files /dev/null and b/TMessagesProj/jni/ffmpeg/armv7-a/libswscale.a differ diff --git a/TMessagesProj/jni/ffmpeg/i686/libavcodec.a b/TMessagesProj/jni/ffmpeg/i686/libavcodec.a index 94d5181d1..ebe9e35f9 100644 Binary files a/TMessagesProj/jni/ffmpeg/i686/libavcodec.a and b/TMessagesProj/jni/ffmpeg/i686/libavcodec.a differ diff --git a/TMessagesProj/jni/ffmpeg/i686/libavformat.a b/TMessagesProj/jni/ffmpeg/i686/libavformat.a index 8ec8b7eca..ff11db278 100644 Binary files a/TMessagesProj/jni/ffmpeg/i686/libavformat.a and b/TMessagesProj/jni/ffmpeg/i686/libavformat.a differ diff --git a/TMessagesProj/jni/ffmpeg/i686/libavresample.a b/TMessagesProj/jni/ffmpeg/i686/libavresample.a index e3c3099fd..dc8326607 100644 Binary files a/TMessagesProj/jni/ffmpeg/i686/libavresample.a and b/TMessagesProj/jni/ffmpeg/i686/libavresample.a differ diff --git a/TMessagesProj/jni/ffmpeg/i686/libavutil.a b/TMessagesProj/jni/ffmpeg/i686/libavutil.a index ba1393ccb..401b5a72e 100644 Binary files a/TMessagesProj/jni/ffmpeg/i686/libavutil.a and b/TMessagesProj/jni/ffmpeg/i686/libavutil.a differ diff --git a/TMessagesProj/jni/ffmpeg/i686/libswscale.a b/TMessagesProj/jni/ffmpeg/i686/libswscale.a new file mode 100644 index 000000000..dfddfc133 Binary files /dev/null and b/TMessagesProj/jni/ffmpeg/i686/libswscale.a differ diff --git a/TMessagesProj/jni/ffmpeg/include/libavcodec/avcodec.h b/TMessagesProj/jni/ffmpeg/include/libavcodec/avcodec.h index c41779ad4..0ce22ec4f 100644 --- a/TMessagesProj/jni/ffmpeg/include/libavcodec/avcodec.h +++ b/TMessagesProj/jni/ffmpeg/include/libavcodec/avcodec.h @@ -409,6 +409,7 @@ enum AVCodecID { AV_CODEC_ID_DXV, AV_CODEC_ID_SCREENPRESSO, AV_CODEC_ID_RSCC, + AV_CODEC_ID_AVS2, AV_CODEC_ID_Y41P = 0x8000, AV_CODEC_ID_AVRP, @@ -446,6 +447,13 @@ enum AVCodecID { AV_CODEC_ID_SVG, AV_CODEC_ID_GDV, AV_CODEC_ID_FITS, + AV_CODEC_ID_IMM4, + AV_CODEC_ID_PROSUMER, + AV_CODEC_ID_MWSC, + AV_CODEC_ID_WCMV, + AV_CODEC_ID_RASC, + AV_CODEC_ID_HYMT, + AV_CODEC_ID_ARBC, /* various PCM "codecs" */ AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs @@ -485,6 +493,7 @@ enum AVCodecID { AV_CODEC_ID_PCM_S64BE, AV_CODEC_ID_PCM_F16LE, AV_CODEC_ID_PCM_F24LE, + AV_CODEC_ID_PCM_VIDC, /* various ADPCM codecs */ AV_CODEC_ID_ADPCM_IMA_QT = 0x11000, @@ -615,6 +624,7 @@ enum AVCodecID { AV_CODEC_ID_PAF_AUDIO, AV_CODEC_ID_ON2AVC, AV_CODEC_ID_DSS_SP, + AV_CODEC_ID_CODEC2, AV_CODEC_ID_FFWAVESYNTH = 0x15800, AV_CODEC_ID_SONIC, @@ -634,6 +644,10 @@ enum AVCodecID { AV_CODEC_ID_ATRAC3PAL, AV_CODEC_ID_DOLBY_E, AV_CODEC_ID_APTX, + AV_CODEC_ID_APTX_HD, + AV_CODEC_ID_SBC, + AV_CODEC_ID_ATRAC9, + AV_CODEC_ID_HCOM, /* subtitle codecs */ AV_CODEC_ID_FIRST_SUBTITLE = 0x17000, ///< A dummy ID pointing at the start of subtitle codecs. @@ -662,6 +676,8 @@ enum AVCodecID { AV_CODEC_ID_PJS, AV_CODEC_ID_ASS, AV_CODEC_ID_HDMV_TEXT_SUBTITLE, + AV_CODEC_ID_TTML, + AV_CODEC_ID_ARIB_CAPTION, /* other specific kind of codecs (generally used for attachments) */ AV_CODEC_ID_FIRST_UNKNOWN = 0x18000, ///< A dummy ID pointing at the start of various fake codecs. @@ -1059,6 +1075,13 @@ typedef struct RcOverride{ */ #define AV_CODEC_CAP_HYBRID (1 << 19) +/** + * This codec takes the reordered_opaque field from input AVFrames + * and returns it in the corresponding field in AVCodecContext after + * encoding. + */ +#define AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE (1 << 20) + /** * Pan Scan area. * This specifies the area which should be displayed. @@ -1098,17 +1121,29 @@ typedef struct AVCPBProperties { * Maximum bitrate of the stream, in bits per second. * Zero if unknown or unspecified. */ +#if FF_API_UNSANITIZED_BITRATES int max_bitrate; +#else + int64_t max_bitrate; +#endif /** * Minimum bitrate of the stream, in bits per second. * Zero if unknown or unspecified. */ +#if FF_API_UNSANITIZED_BITRATES int min_bitrate; +#else + int64_t min_bitrate; +#endif /** * Average bitrate of the stream, in bits per second. * Zero if unknown or unspecified. */ +#if FF_API_UNSANITIZED_BITRATES int avg_bitrate; +#else + int64_t avg_bitrate; +#endif /** * The size of the buffer to which the ratecontrol is applied, in bits. @@ -1309,7 +1344,7 @@ enum AVPacketSideDataType { AV_PKT_DATA_METADATA_UPDATE, /** - * MPEGTS stream ID, this is required to pass the stream ID + * MPEGTS stream ID as uint8_t, this is required to pass the stream ID * information from the demuxer to the corresponding muxer. */ AV_PKT_DATA_MPEGTS_STREAM_ID, @@ -1341,6 +1376,25 @@ enum AVPacketSideDataType { */ AV_PKT_DATA_A53_CC, + /** + * This side data is encryption initialization data. + * The format is not part of ABI, use av_encryption_init_info_* methods to + * access. + */ + AV_PKT_DATA_ENCRYPTION_INIT_INFO, + + /** + * This side data contains encryption info for how to decrypt the packet. + * The format is not part of ABI, use av_encryption_info_* methods to access. + */ + AV_PKT_DATA_ENCRYPTION_INFO, + + /** + * Active Format Description data consisting of a single byte as specified + * in ETSI TS 101 154 using AVActiveFormatDescription enum. + */ + AV_PKT_DATA_AFD, + /** * The number of side data types. * This is not part of the public API/ABI in the sense that it may @@ -1596,6 +1650,7 @@ typedef struct AVCodecContext { * The allocated memory should be AV_INPUT_BUFFER_PADDING_SIZE bytes larger * than extradata_size to avoid problems if it is read with the bitstream reader. * The bytewise contents of extradata must not depend on the architecture or CPU endianness. + * Must be allocated with the av_malloc() family of functions. * - encoding: Set/allocated/freed by libavcodec. * - decoding: Set/allocated/freed by user. */ @@ -2645,7 +2700,10 @@ typedef struct AVCodecContext { /** * opaque 64-bit number (generally a PTS) that will be reordered and * output in AVFrame.reordered_opaque - * - encoding: unused + * - encoding: Set by libavcodec to the reordered_opaque of the input + * frame corresponding to the last returned packet. Only + * supported by encoders with the + * AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE capability. * - decoding: Set by user. */ int64_t reordered_opaque; @@ -2917,6 +2975,28 @@ typedef struct AVCodecContext { #define FF_PROFILE_HEVC_MAIN_STILL_PICTURE 3 #define FF_PROFILE_HEVC_REXT 4 +#define FF_PROFILE_AV1_MAIN 0 +#define FF_PROFILE_AV1_HIGH 1 +#define FF_PROFILE_AV1_PROFESSIONAL 2 + +#define FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT 0xc0 +#define FF_PROFILE_MJPEG_HUFFMAN_EXTENDED_SEQUENTIAL_DCT 0xc1 +#define FF_PROFILE_MJPEG_HUFFMAN_PROGRESSIVE_DCT 0xc2 +#define FF_PROFILE_MJPEG_HUFFMAN_LOSSLESS 0xc3 +#define FF_PROFILE_MJPEG_JPEG_LS 0xf7 + +#define FF_PROFILE_SBC_MSBC 1 + +#define FF_PROFILE_PRORES_PROXY 0 +#define FF_PROFILE_PRORES_LT 1 +#define FF_PROFILE_PRORES_STANDARD 2 +#define FF_PROFILE_PRORES_HQ 3 +#define FF_PROFILE_PRORES_4444 4 +#define FF_PROFILE_PRORES_XQ 5 + +#define FF_PROFILE_ARIB_PROFILE_A 0 +#define FF_PROFILE_ARIB_PROFILE_C 1 + /** * level * - encoding: Set by user. @@ -3068,6 +3148,7 @@ typedef struct AVCodecContext { #define FF_SUB_CHARENC_MODE_DO_NOTHING -1 ///< do nothing (demuxer outputs a stream supposed to be already in UTF-8, or the codec is bitmap for instance) #define FF_SUB_CHARENC_MODE_AUTOMATIC 0 ///< libavcodec will select the mode itself #define FF_SUB_CHARENC_MODE_PRE_DECODER 1 ///< the AVPacket data needs to be recoded to UTF-8 before being fed to the decoder, requires iconv +#define FF_SUB_CHARENC_MODE_IGNORE 2 ///< neither convert the subtitles, nor check them for valid UTF-8 /** * Skip processing alpha if supported by codec. @@ -3254,6 +3335,28 @@ typedef struct AVCodecContext { * (with the display dimensions being determined by the crop_* fields). */ int apply_cropping; + + /* + * Video decoding only. Sets the number of extra hardware frames which + * the decoder will allocate for use by the caller. This must be set + * before avcodec_open2() is called. + * + * Some hardware decoders require all frames that they will use for + * output to be defined in advance before decoding starts. For such + * decoders, the hardware frame pool must therefore be of a fixed size. + * The extra frames set here are on top of any number that the decoder + * needs internally in order to operate normally (for example, frames + * used as reference pictures). + */ + int extra_hw_frames; + + /** + * The percentage of damaged samples to discard a frame. + * + * - decoding: set by user + * - encoding: unused + */ + int discard_damaged_percentage; } AVCodecContext; #if FF_API_CODEC_GET_SET @@ -3439,6 +3542,9 @@ typedef struct AVCodec { /** * Initialize codec static data, called from avcodec_register(). + * + * This is not intended for time consuming operations as it is + * run for every codec regardless of that codec being used. */ void (*init_static_data)(struct AVCodec *codec); @@ -4319,7 +4425,7 @@ int av_packet_from_data(AVPacket *pkt, uint8_t *data, int size); * @warning This is a hack - the packet memory allocation stuff is broken. The * packet is allocated if it was not really allocated. * - * @deprecated Use av_packet_ref + * @deprecated Use av_packet_ref or av_packet_make_refcounted */ attribute_deprecated int av_dup_packet(AVPacket *pkt); @@ -4490,6 +4596,33 @@ void av_packet_move_ref(AVPacket *dst, AVPacket *src); */ int av_packet_copy_props(AVPacket *dst, const AVPacket *src); +/** + * Ensure the data described by a given packet is reference counted. + * + * @note This function does not ensure that the reference will be writable. + * Use av_packet_make_writable instead for that purpose. + * + * @see av_packet_ref + * @see av_packet_make_writable + * + * @param pkt packet whose data should be made reference counted. + * + * @return 0 on success, a negative AVERROR on error. On failure, the + * packet is unchanged. + */ +int av_packet_make_refcounted(AVPacket *pkt); + +/** + * Create a writable reference for the data described by a given packet, + * avoiding data copy if possible. + * + * @param pkt Packet whose data should be made writable. + * + * @return 0 on success, a negative AVERROR on failure. On failure, the + * packet is unchanged. + */ +int av_packet_make_writable(AVPacket *pkt); + /** * Convert valid timing fields (timestamps / durations) in a packet from one * timebase to another. Timestamps with unknown values (AV_NOPTS_VALUE) will be @@ -5693,6 +5826,7 @@ typedef struct AVBitStreamFilter { int (*init)(AVBSFContext *ctx); int (*filter)(AVBSFContext *ctx, AVPacket *pkt); void (*close)(AVBSFContext *ctx); + void (*flush)(AVBSFContext *ctx); } AVBitStreamFilter; #if FF_API_OLD_BSF @@ -5819,6 +5953,11 @@ int av_bsf_send_packet(AVBSFContext *ctx, AVPacket *pkt); */ int av_bsf_receive_packet(AVBSFContext *ctx, AVPacket *pkt); +/** + * Reset the internal bitstream filter state / flush internal buffers. + */ +void av_bsf_flush(AVBSFContext *ctx); + /** * Free a bitstream filter context and everything associated with it; write NULL * into the supplied pointer. diff --git a/TMessagesProj/jni/ffmpeg/include/libavcodec/mediacodec.h b/TMessagesProj/jni/ffmpeg/include/libavcodec/mediacodec.h index 5606d24a1..4c8545df0 100644 --- a/TMessagesProj/jni/ffmpeg/include/libavcodec/mediacodec.h +++ b/TMessagesProj/jni/ffmpeg/include/libavcodec/mediacodec.h @@ -85,4 +85,17 @@ typedef struct MediaCodecBuffer AVMediaCodecBuffer; */ int av_mediacodec_release_buffer(AVMediaCodecBuffer *buffer, int render); +/** + * Release a MediaCodec buffer and render it at the given time to the surface + * that is associated with the decoder. The timestamp must be within one second + * of the current java/lang/System#nanoTime() (which is implemented using + * CLOCK_MONOTONIC on Android). See the Android MediaCodec documentation + * of android/media/MediaCodec#releaseOutputBuffer(int,long) for more details. + * + * @param buffer the buffer to render + * @param time timestamp in nanoseconds of when to render the buffer + * @return 0 on success, < 0 otherwise + */ +int av_mediacodec_render_buffer_at_time(AVMediaCodecBuffer *buffer, int64_t time); + #endif /* AVCODEC_MEDIACODEC_H */ diff --git a/TMessagesProj/jni/ffmpeg/include/libavcodec/version.h b/TMessagesProj/jni/ffmpeg/include/libavcodec/version.h index 8cc0f9bf9..7c3897e2d 100644 --- a/TMessagesProj/jni/ffmpeg/include/libavcodec/version.h +++ b/TMessagesProj/jni/ffmpeg/include/libavcodec/version.h @@ -28,8 +28,8 @@ #include "libavutil/version.h" #define LIBAVCODEC_VERSION_MAJOR 58 -#define LIBAVCODEC_VERSION_MINOR 9 -#define LIBAVCODEC_VERSION_MICRO 100 +#define LIBAVCODEC_VERSION_MINOR 47 +#define LIBAVCODEC_VERSION_MICRO 102 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ LIBAVCODEC_VERSION_MINOR, \ @@ -132,6 +132,9 @@ #ifndef FF_API_NEXT #define FF_API_NEXT (LIBAVCODEC_VERSION_MAJOR < 59) #endif +#ifndef FF_API_UNSANITIZED_BITRATES +#define FF_API_UNSANITIZED_BITRATES (LIBAVCODEC_VERSION_MAJOR < 59) +#endif #endif /* AVCODEC_VERSION_H */ diff --git a/TMessagesProj/jni/ffmpeg/include/libavformat/avformat.h b/TMessagesProj/jni/ffmpeg/include/libavformat/avformat.h index 4ea1b5ab7..fdaffa5bf 100644 --- a/TMessagesProj/jni/ffmpeg/include/libavformat/avformat.h +++ b/TMessagesProj/jni/ffmpeg/include/libavformat/avformat.h @@ -845,6 +845,8 @@ typedef struct AVStreamInternal AVStreamInternal; #define AV_DISPOSITION_CAPTIONS 0x10000 #define AV_DISPOSITION_DESCRIPTIONS 0x20000 #define AV_DISPOSITION_METADATA 0x40000 +#define AV_DISPOSITION_DEPENDENT 0x80000 ///< dependent audio stream (mix_type=0 in mpegts) +#define AV_DISPOSITION_STILL_IMAGE 0x100000 ///< still images in video stream (still_picture_flag=1 in mpegts) /** * Options for behavior on timestamp wrap detection. @@ -1101,6 +1103,13 @@ typedef struct AVStream { */ int stream_identifier; + /** + * Details of the MPEG-TS program which created this stream. + */ + int program_num; + int pmt_version; + int pmt_stream_idx; + int64_t interleaver_chunk_size; int64_t interleaver_chunk_duration; @@ -1258,6 +1267,7 @@ typedef struct AVProgram { int program_num; int pmt_pid; int pcr_pid; + int pmt_version; /***************************************************************** * All fields below this line are not part of the public API. They @@ -1473,7 +1483,9 @@ typedef struct AVFormatContext { * This flag is mainly intended for testing. */ #define AVFMT_FLAG_BITEXACT 0x0400 -#define AVFMT_FLAG_MP4A_LATM 0x8000 ///< Enable RTP MP4A-LATM payload +#if FF_API_LAVF_MP4A_LATM +#define AVFMT_FLAG_MP4A_LATM 0x8000 ///< Deprecated, does nothing. +#endif #define AVFMT_FLAG_SORT_DTS 0x10000 ///< try to interleave outputted packets by dts (using this flag can slow demuxing down) #define AVFMT_FLAG_PRIV_OPT 0x20000 ///< Enable use of private options by delaying codec open (this could be made default once all code is converted) #if FF_API_LAVF_KEEPSIDE_FLAG @@ -1925,6 +1937,13 @@ typedef struct AVFormatContext { * - decoding: set by user */ int max_streams; + + /** + * Skip duration calcuation in estimate_timings_from_pts. + * - encoding: unused + * - decoding: set by user + */ + int skip_estimate_duration_from_pts; } AVFormatContext; #if FF_API_FORMAT_GET_SET diff --git a/TMessagesProj/jni/ffmpeg/include/libavformat/avio.h b/TMessagesProj/jni/ffmpeg/include/libavformat/avio.h index 75912ce6b..dcb8dcdf9 100644 --- a/TMessagesProj/jni/ffmpeg/include/libavformat/avio.h +++ b/TMessagesProj/jni/ffmpeg/include/libavformat/avio.h @@ -236,7 +236,7 @@ typedef struct AVIOContext { int (*write_packet)(void *opaque, uint8_t *buf, int buf_size); int64_t (*seek)(void *opaque, int64_t offset, int whence); int64_t pos; /**< position in the file of the current buffer */ - int eof_reached; /**< true if eof reached */ + int eof_reached; /**< true if was unable to read due to error or eof */ int write_flag; /**< true if open for writing */ int max_packet_size; unsigned long checksum; @@ -566,8 +566,8 @@ static av_always_inline int64_t avio_tell(AVIOContext *s) int64_t avio_size(AVIOContext *s); /** - * feof() equivalent for AVIOContext. - * @return non zero if and only if end of file + * Similar to feof() but also returns nonzero on read errors. + * @return non zero if and only if at end of file or a read error happened when reading. */ int avio_feof(AVIOContext *s); diff --git a/TMessagesProj/jni/ffmpeg/include/libavformat/version.h b/TMessagesProj/jni/ffmpeg/include/libavformat/version.h index 7669c2262..2e83eb4f2 100644 --- a/TMessagesProj/jni/ffmpeg/include/libavformat/version.h +++ b/TMessagesProj/jni/ffmpeg/include/libavformat/version.h @@ -32,8 +32,8 @@ // Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium) // Also please add any ticket numbers that you believe might be affected here #define LIBAVFORMAT_VERSION_MAJOR 58 -#define LIBAVFORMAT_VERSION_MINOR 7 -#define LIBAVFORMAT_VERSION_MICRO 100 +#define LIBAVFORMAT_VERSION_MINOR 26 +#define LIBAVFORMAT_VERSION_MICRO 101 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ LIBAVFORMAT_VERSION_MINOR, \ @@ -70,6 +70,9 @@ #ifndef FF_API_HLS_WRAP #define FF_API_HLS_WRAP (LIBAVFORMAT_VERSION_MAJOR < 59) #endif +#ifndef FF_API_HLS_USE_LOCALTIME +#define FF_API_HLS_USE_LOCALTIME (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif #ifndef FF_API_LAVF_KEEPSIDE_FLAG #define FF_API_LAVF_KEEPSIDE_FLAG (LIBAVFORMAT_VERSION_MAJOR < 59) #endif @@ -94,6 +97,12 @@ #ifndef FF_API_NEXT #define FF_API_NEXT (LIBAVFORMAT_VERSION_MAJOR < 59) #endif +#ifndef FF_API_DASH_MIN_SEG_DURATION +#define FF_API_DASH_MIN_SEG_DURATION (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif +#ifndef FF_API_LAVF_MP4A_LATM +#define FF_API_LAVF_MP4A_LATM (LIBAVFORMAT_VERSION_MAJOR < 59) +#endif #ifndef FF_API_R_FRAME_RATE diff --git a/TMessagesProj/jni/ffmpeg/include/libavutil/avassert.h b/TMessagesProj/jni/ffmpeg/include/libavutil/avassert.h index 46f3fea58..9abeadea4 100644 --- a/TMessagesProj/jni/ffmpeg/include/libavutil/avassert.h +++ b/TMessagesProj/jni/ffmpeg/include/libavutil/avassert.h @@ -66,7 +66,7 @@ #endif /** - * Assert that floating point opperations can be executed. + * Assert that floating point operations can be executed. * * This will av_assert0() that the cpu is not in MMX state on X86 */ diff --git a/TMessagesProj/jni/ffmpeg/include/libavutil/avstring.h b/TMessagesProj/jni/ffmpeg/include/libavutil/avstring.h index 04d269564..37dd4e2da 100644 --- a/TMessagesProj/jni/ffmpeg/include/libavutil/avstring.h +++ b/TMessagesProj/jni/ffmpeg/include/libavutil/avstring.h @@ -400,6 +400,12 @@ int av_utf8_decode(int32_t *codep, const uint8_t **bufp, const uint8_t *buf_end, */ int av_match_list(const char *name, const char *list, char separator); +/** + * See libc sscanf manual for more information. + * Locale-independent sscanf implementation. + */ +int av_sscanf(const char *string, const char *format, ...); + /** * @} */ diff --git a/TMessagesProj/jni/ffmpeg/include/libavutil/common.h b/TMessagesProj/jni/ffmpeg/include/libavutil/common.h index 5e0382827..8db029117 100644 --- a/TMessagesProj/jni/ffmpeg/include/libavutil/common.h +++ b/TMessagesProj/jni/ffmpeg/include/libavutil/common.h @@ -158,7 +158,7 @@ static av_always_inline av_const int64_t av_clip64_c(int64_t a, int64_t amin, in */ static av_always_inline av_const uint8_t av_clip_uint8_c(int a) { - if (a&(~0xFF)) return (-a)>>31; + if (a&(~0xFF)) return (~a)>>31; else return a; } @@ -180,7 +180,7 @@ static av_always_inline av_const int8_t av_clip_int8_c(int a) */ static av_always_inline av_const uint16_t av_clip_uint16_c(int a) { - if (a&(~0xFFFF)) return (-a)>>31; + if (a&(~0xFFFF)) return (~a)>>31; else return a; } @@ -228,7 +228,7 @@ static av_always_inline av_const int av_clip_intp2_c(int a, int p) */ static av_always_inline av_const unsigned av_clip_uintp2_c(int a, int p) { - if (a & ~((1<> 31 & ((1<> 31 & ((1< +#include + +typedef struct AVSubsampleEncryptionInfo { + /** The number of bytes that are clear. */ + unsigned int bytes_of_clear_data; + + /** + * The number of bytes that are protected. If using pattern encryption, + * the pattern applies to only the protected bytes; if not using pattern + * encryption, all these bytes are encrypted. + */ + unsigned int bytes_of_protected_data; +} AVSubsampleEncryptionInfo; + +/** + * This describes encryption info for a packet. This contains frame-specific + * info for how to decrypt the packet before passing it to the decoder. + * + * The size of this struct is not part of the public ABI. + */ +typedef struct AVEncryptionInfo { + /** The fourcc encryption scheme, in big-endian byte order. */ + uint32_t scheme; + + /** + * Only used for pattern encryption. This is the number of 16-byte blocks + * that are encrypted. + */ + uint32_t crypt_byte_block; + + /** + * Only used for pattern encryption. This is the number of 16-byte blocks + * that are clear. + */ + uint32_t skip_byte_block; + + /** + * The ID of the key used to encrypt the packet. This should always be + * 16 bytes long, but may be changed in the future. + */ + uint8_t *key_id; + uint32_t key_id_size; + + /** + * The initialization vector. This may have been zero-filled to be the + * correct block size. This should always be 16 bytes long, but may be + * changed in the future. + */ + uint8_t *iv; + uint32_t iv_size; + + /** + * An array of subsample encryption info specifying how parts of the sample + * are encrypted. If there are no subsamples, then the whole sample is + * encrypted. + */ + AVSubsampleEncryptionInfo *subsamples; + uint32_t subsample_count; +} AVEncryptionInfo; + +/** + * This describes info used to initialize an encryption key system. + * + * The size of this struct is not part of the public ABI. + */ +typedef struct AVEncryptionInitInfo { + /** + * A unique identifier for the key system this is for, can be NULL if it + * is not known. This should always be 16 bytes, but may change in the + * future. + */ + uint8_t* system_id; + uint32_t system_id_size; + + /** + * An array of key IDs this initialization data is for. All IDs are the + * same length. Can be NULL if there are no known key IDs. + */ + uint8_t** key_ids; + /** The number of key IDs. */ + uint32_t num_key_ids; + /** + * The number of bytes in each key ID. This should always be 16, but may + * change in the future. + */ + uint32_t key_id_size; + + /** + * Key-system specific initialization data. This data is copied directly + * from the file and the format depends on the specific key system. This + * can be NULL if there is no initialization data; in that case, there + * will be at least one key ID. + */ + uint8_t* data; + uint32_t data_size; + + /** + * An optional pointer to the next initialization info in the list. + */ + struct AVEncryptionInitInfo *next; +} AVEncryptionInitInfo; + +/** + * Allocates an AVEncryptionInfo structure and sub-pointers to hold the given + * number of subsamples. This will allocate pointers for the key ID, IV, + * and subsample entries, set the size members, and zero-initialize the rest. + * + * @param subsample_count The number of subsamples. + * @param key_id_size The number of bytes in the key ID, should be 16. + * @param iv_size The number of bytes in the IV, should be 16. + * + * @return The new AVEncryptionInfo structure, or NULL on error. + */ +AVEncryptionInfo *av_encryption_info_alloc(uint32_t subsample_count, uint32_t key_id_size, uint32_t iv_size); + +/** + * Allocates an AVEncryptionInfo structure with a copy of the given data. + * @return The new AVEncryptionInfo structure, or NULL on error. + */ +AVEncryptionInfo *av_encryption_info_clone(const AVEncryptionInfo *info); + +/** + * Frees the given encryption info object. This MUST NOT be used to free the + * side-data data pointer, that should use normal side-data methods. + */ +void av_encryption_info_free(AVEncryptionInfo *info); + +/** + * Creates a copy of the AVEncryptionInfo that is contained in the given side + * data. The resulting object should be passed to av_encryption_info_free() + * when done. + * + * @return The new AVEncryptionInfo structure, or NULL on error. + */ +AVEncryptionInfo *av_encryption_info_get_side_data(const uint8_t *side_data, size_t side_data_size); + +/** + * Allocates and initializes side data that holds a copy of the given encryption + * info. The resulting pointer should be either freed using av_free or given + * to av_packet_add_side_data(). + * + * @return The new side-data pointer, or NULL. + */ +uint8_t *av_encryption_info_add_side_data( + const AVEncryptionInfo *info, size_t *side_data_size); + + +/** + * Allocates an AVEncryptionInitInfo structure and sub-pointers to hold the + * given sizes. This will allocate pointers and set all the fields. + * + * @return The new AVEncryptionInitInfo structure, or NULL on error. + */ +AVEncryptionInitInfo *av_encryption_init_info_alloc( + uint32_t system_id_size, uint32_t num_key_ids, uint32_t key_id_size, uint32_t data_size); + +/** + * Frees the given encryption init info object. This MUST NOT be used to free + * the side-data data pointer, that should use normal side-data methods. + */ +void av_encryption_init_info_free(AVEncryptionInitInfo* info); + +/** + * Creates a copy of the AVEncryptionInitInfo that is contained in the given + * side data. The resulting object should be passed to + * av_encryption_init_info_free() when done. + * + * @return The new AVEncryptionInitInfo structure, or NULL on error. + */ +AVEncryptionInitInfo *av_encryption_init_info_get_side_data( + const uint8_t* side_data, size_t side_data_size); + +/** + * Allocates and initializes side data that holds a copy of the given encryption + * init info. The resulting pointer should be either freed using av_free or + * given to av_packet_add_side_data(). + * + * @return The new side-data pointer, or NULL. + */ +uint8_t *av_encryption_init_info_add_side_data( + const AVEncryptionInitInfo *info, size_t *side_data_size); + +#endif /* AVUTIL_ENCRYPTION_INFO_H */ diff --git a/TMessagesProj/jni/ffmpeg/include/libavutil/ffversion.h b/TMessagesProj/jni/ffmpeg/include/libavutil/ffversion.h index f9e7fe1c0..7bd516feb 100644 --- a/TMessagesProj/jni/ffmpeg/include/libavutil/ffversion.h +++ b/TMessagesProj/jni/ffmpeg/include/libavutil/ffversion.h @@ -1,5 +1,5 @@ /* Automatically generated by version.sh, do not manually edit! */ #ifndef AVUTIL_FFVERSION_H #define AVUTIL_FFVERSION_H -#define FFMPEG_VERSION "3.4.git" +#define FFMPEG_VERSION "N-93231-g96d79ff5b5" #endif /* AVUTIL_FFVERSION_H */ diff --git a/TMessagesProj/jni/ffmpeg/include/libavutil/file.h b/TMessagesProj/jni/ffmpeg/include/libavutil/file.h index 8666c7b1d..3ef4a6022 100644 --- a/TMessagesProj/jni/ffmpeg/include/libavutil/file.h +++ b/TMessagesProj/jni/ffmpeg/include/libavutil/file.h @@ -33,6 +33,8 @@ * allocated buffer or map it with mmap() when available. * In case of success set *bufptr to the read or mmapped buffer, and * *size to the size in bytes of the buffer in *bufptr. + * Unlike mmap this function succeeds with zero sized files, in this + * case *bufptr will be set to NULL and *size will be set to 0. * The returned buffer must be released with av_file_unmap(). * * @param log_offset loglevel offset used for logging diff --git a/TMessagesProj/jni/ffmpeg/include/libavutil/frame.h b/TMessagesProj/jni/ffmpeg/include/libavutil/frame.h index d54bd9a35..8aa3e8836 100644 --- a/TMessagesProj/jni/ffmpeg/include/libavutil/frame.h +++ b/TMessagesProj/jni/ffmpeg/include/libavutil/frame.h @@ -141,6 +141,44 @@ enum AVFrameSideDataType { * metadata key entry "name". */ AV_FRAME_DATA_ICC_PROFILE, + +#if FF_API_FRAME_QP + /** + * Implementation-specific description of the format of AV_FRAME_QP_TABLE_DATA. + * The contents of this side data are undocumented and internal; use + * av_frame_set_qp_table() and av_frame_get_qp_table() to access this in a + * meaningful way instead. + */ + AV_FRAME_DATA_QP_TABLE_PROPERTIES, + + /** + * Raw QP table data. Its format is described by + * AV_FRAME_DATA_QP_TABLE_PROPERTIES. Use av_frame_set_qp_table() and + * av_frame_get_qp_table() to access this instead. + */ + AV_FRAME_DATA_QP_TABLE_DATA, +#endif + + /** + * Timecode which conforms to SMPTE ST 12-1. The data is an array of 4 uint32_t + * where the first uint32_t describes how many (1-3) of the other timecodes are used. + * The timecode format is described in the av_timecode_get_smpte_from_framenum() + * function in libavutil/timecode.c. + */ + AV_FRAME_DATA_S12M_TIMECODE, + + /** + * HDR dynamic metadata associated with a video frame. The payload is + * an AVDynamicHDRPlus type and contains information for color + * volume transform - application 4 of SMPTE 2094-40:2016 standard. + */ + AV_FRAME_DATA_DYNAMIC_HDR_PLUS, + + /** + * Regions Of Interest, the data is an array of AVRegionOfInterest type, the number of + * array element is implied by AVFrameSideData.size / AVRegionOfInterest.self_size. + */ + AV_FRAME_DATA_REGIONS_OF_INTEREST, }; enum AVActiveFormatDescription { @@ -168,6 +206,35 @@ typedef struct AVFrameSideData { AVBufferRef *buf; } AVFrameSideData; +/** + * Structure to hold Region Of Interest. + * + * self_size specifies the size of this data structure. This value + * should be set to sizeof(AVRegionOfInterest). EINVAL is returned if self_size is zero. + * + * Number of pixels to discard from the top/bottom/left/right border of + * the frame to obtain the region of interest of the frame. + * They are encoder dependent and will be extended internally + * if the codec requires an alignment. + * If the regions overlap, the last value in the list will be used. + * + * qoffset is quant offset, and base rule here: + * returns EINVAL if AVRational.den is zero. + * the value (num/den) range is [-1.0, 1.0], clamp to +-1.0 if out of range. + * 0 means no picture quality change, + * negative offset asks for better quality (and the best with value -1.0), + * positive offset asks for worse quality (and the worst with value 1.0). + * How to explain/implement the different quilaity requirement is encoder dependent. + */ +typedef struct AVRegionOfInterest { + uint32_t self_size; + int top; + int bottom; + int left; + int right; + AVRational qoffset; +} AVRegionOfInterest; + /** * This structure describes decoded (raw) audio or video data. * @@ -364,7 +431,6 @@ typedef struct AVFrame { * that time, * the decoder reorders values as needed and sets AVFrame.reordered_opaque * to exactly one of the values provided by the user through AVCodecContext.reordered_opaque - * @deprecated in favor of pkt_pts */ int64_t reordered_opaque; @@ -529,6 +595,7 @@ typedef struct AVFrame { attribute_deprecated int qscale_type; + attribute_deprecated AVBufferRef *qp_table_buf; #endif /** @@ -800,6 +867,22 @@ AVFrameSideData *av_frame_new_side_data(AVFrame *frame, enum AVFrameSideDataType type, int size); +/** + * Add a new side data to a frame from an existing AVBufferRef + * + * @param frame a frame to which the side data should be added + * @param type the type of the added side data + * @param buf an AVBufferRef to add as side data. The ownership of + * the reference is transferred to the frame. + * + * @return newly added side data on success, NULL on error. On failure + * the frame is unchanged and the AVBufferRef remains owned by + * the caller. + */ +AVFrameSideData *av_frame_new_side_data_from_buf(AVFrame *frame, + enum AVFrameSideDataType type, + AVBufferRef *buf); + /** * @return a pointer to the side data of a given type on success, NULL if there * is no side data with such type in this frame. diff --git a/TMessagesProj/jni/ffmpeg/include/libavutil/hash.h b/TMessagesProj/jni/ffmpeg/include/libavutil/hash.h index a20b8934f..7693e6bf0 100644 --- a/TMessagesProj/jni/ffmpeg/include/libavutil/hash.h +++ b/TMessagesProj/jni/ffmpeg/include/libavutil/hash.h @@ -29,6 +29,8 @@ #include +#include "version.h" + /** * @defgroup lavu_hash Hash Functions * @ingroup lavu_crypto @@ -179,7 +181,11 @@ void av_hash_init(struct AVHashContext *ctx); * @param[in] src Data to be added to the hash context * @param[in] len Size of the additional data */ +#if FF_API_CRYPTO_SIZE_T void av_hash_update(struct AVHashContext *ctx, const uint8_t *src, int len); +#else +void av_hash_update(struct AVHashContext *ctx, const uint8_t *src, size_t len); +#endif /** * Finalize a hash context and compute the actual hash value. diff --git a/TMessagesProj/jni/ffmpeg/include/libavutil/hdr_dynamic_metadata.h b/TMessagesProj/jni/ffmpeg/include/libavutil/hdr_dynamic_metadata.h new file mode 100644 index 000000000..2d72de56a --- /dev/null +++ b/TMessagesProj/jni/ffmpeg/include/libavutil/hdr_dynamic_metadata.h @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2018 Mohammad Izadi + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HDR_DYNAMIC_METADATA_H +#define AVUTIL_HDR_DYNAMIC_METADATA_H + +#include "frame.h" +#include "rational.h" + +/** + * Option for overlapping elliptical pixel selectors in an image. + */ +enum AVHDRPlusOverlapProcessOption { + AV_HDR_PLUS_OVERLAP_PROCESS_WEIGHTED_AVERAGING = 0, + AV_HDR_PLUS_OVERLAP_PROCESS_LAYERING = 1, +}; + +/** + * Represents the percentile at a specific percentage in + * a distribution. + */ +typedef struct AVHDRPlusPercentile { + /** + * The percentage value corresponding to a specific percentile linearized + * RGB value in the processing window in the scene. The value shall be in + * the range of 0 to100, inclusive. + */ + uint8_t percentage; + + /** + * The linearized maxRGB value at a specific percentile in the processing + * window in the scene. The value shall be in the range of 0 to 1, inclusive + * and in multiples of 0.00001. + */ + AVRational percentile; +} AVHDRPlusPercentile; + +/** + * Color transform parameters at a processing window in a dynamic metadata for + * SMPTE 2094-40. + */ +typedef struct AVHDRPlusColorTransformParams { + /** + * The relative x coordinate of the top left pixel of the processing + * window. The value shall be in the range of 0 and 1, inclusive and + * in multiples of 1/(width of Picture - 1). The value 1 corresponds + * to the absolute coordinate of width of Picture - 1. The value for + * first processing window shall be 0. + */ + AVRational window_upper_left_corner_x; + + /** + * The relative y coordinate of the top left pixel of the processing + * window. The value shall be in the range of 0 and 1, inclusive and + * in multiples of 1/(height of Picture - 1). The value 1 corresponds + * to the absolute coordinate of height of Picture - 1. The value for + * first processing window shall be 0. + */ + AVRational window_upper_left_corner_y; + + /** + * The relative x coordinate of the bottom right pixel of the processing + * window. The value shall be in the range of 0 and 1, inclusive and + * in multiples of 1/(width of Picture - 1). The value 1 corresponds + * to the absolute coordinate of width of Picture - 1. The value for + * first processing window shall be 1. + */ + AVRational window_lower_right_corner_x; + + /** + * The relative y coordinate of the bottom right pixel of the processing + * window. The value shall be in the range of 0 and 1, inclusive and + * in multiples of 1/(height of Picture - 1). The value 1 corresponds + * to the absolute coordinate of height of Picture - 1. The value for + * first processing window shall be 1. + */ + AVRational window_lower_right_corner_y; + + /** + * The x coordinate of the center position of the concentric internal and + * external ellipses of the elliptical pixel selector in the processing + * window. The value shall be in the range of 0 to (width of Picture - 1), + * inclusive and in multiples of 1 pixel. + */ + uint16_t center_of_ellipse_x; + + /** + * The y coordinate of the center position of the concentric internal and + * external ellipses of the elliptical pixel selector in the processing + * window. The value shall be in the range of 0 to (height of Picture - 1), + * inclusive and in multiples of 1 pixel. + */ + uint16_t center_of_ellipse_y; + + /** + * The clockwise rotation angle in degree of arc with respect to the + * positive direction of the x-axis of the concentric internal and external + * ellipses of the elliptical pixel selector in the processing window. The + * value shall be in the range of 0 to 180, inclusive and in multiples of 1. + */ + uint8_t rotation_angle; + + /** + * The semi-major axis value of the internal ellipse of the elliptical pixel + * selector in amount of pixels in the processing window. The value shall be + * in the range of 1 to 65535, inclusive and in multiples of 1 pixel. + */ + uint16_t semimajor_axis_internal_ellipse; + + /** + * The semi-major axis value of the external ellipse of the elliptical pixel + * selector in amount of pixels in the processing window. The value + * shall not be less than semimajor_axis_internal_ellipse of the current + * processing window. The value shall be in the range of 1 to 65535, + * inclusive and in multiples of 1 pixel. + */ + uint16_t semimajor_axis_external_ellipse; + + /** + * The semi-minor axis value of the external ellipse of the elliptical pixel + * selector in amount of pixels in the processing window. The value shall be + * in the range of 1 to 65535, inclusive and in multiples of 1 pixel. + */ + uint16_t semiminor_axis_external_ellipse; + + /** + * Overlap process option indicates one of the two methods of combining + * rendered pixels in the processing window in an image with at least one + * elliptical pixel selector. For overlapping elliptical pixel selectors + * in an image, overlap_process_option shall have the same value. + */ + enum AVHDRPlusOverlapProcessOption overlap_process_option; + + /** + * The maximum of the color components of linearized RGB values in the + * processing window in the scene. The values should be in the range of 0 to + * 1, inclusive and in multiples of 0.00001. maxscl[ 0 ], maxscl[ 1 ], and + * maxscl[ 2 ] are corresponding to R, G, B color components respectively. + */ + AVRational maxscl[3]; + + /** + * The average of linearized maxRGB values in the processing window in the + * scene. The value should be in the range of 0 to 1, inclusive and in + * multiples of 0.00001. + */ + AVRational average_maxrgb; + + /** + * The number of linearized maxRGB values at given percentiles in the + * processing window in the scene. The maximum value shall be 15. + */ + uint8_t num_distribution_maxrgb_percentiles; + + /** + * The linearized maxRGB values at given percentiles in the + * processing window in the scene. + */ + AVHDRPlusPercentile distribution_maxrgb[15]; + + /** + * The fraction of selected pixels in the image that contains the brightest + * pixel in the scene. The value shall be in the range of 0 to 1, inclusive + * and in multiples of 0.001. + */ + AVRational fraction_bright_pixels; + + /** + * This flag indicates that the metadata for the tone mapping function in + * the processing window is present (for value of 1). + */ + uint8_t tone_mapping_flag; + + /** + * The x coordinate of the separation point between the linear part and the + * curved part of the tone mapping function. The value shall be in the range + * of 0 to 1, excluding 0 and in multiples of 1/4095. + */ + AVRational knee_point_x; + + /** + * The y coordinate of the separation point between the linear part and the + * curved part of the tone mapping function. The value shall be in the range + * of 0 to 1, excluding 0 and in multiples of 1/4095. + */ + AVRational knee_point_y; + + /** + * The number of the intermediate anchor parameters of the tone mapping + * function in the processing window. The maximum value shall be 15. + */ + uint8_t num_bezier_curve_anchors; + + /** + * The intermediate anchor parameters of the tone mapping function in the + * processing window in the scene. The values should be in the range of 0 + * to 1, inclusive and in multiples of 1/1023. + */ + AVRational bezier_curve_anchors[15]; + + /** + * This flag shall be equal to 0 in bitstreams conforming to this version of + * this Specification. Other values are reserved for future use. + */ + uint8_t color_saturation_mapping_flag; + + /** + * The color saturation gain in the processing window in the scene. The + * value shall be in the range of 0 to 63/8, inclusive and in multiples of + * 1/8. The default value shall be 1. + */ + AVRational color_saturation_weight; +} AVHDRPlusColorTransformParams; + +/** + * This struct represents dynamic metadata for color volume transform - + * application 4 of SMPTE 2094-40:2016 standard. + * + * To be used as payload of a AVFrameSideData or AVPacketSideData with the + * appropriate type. + * + * @note The struct should be allocated with + * av_dynamic_hdr_plus_alloc() and its size is not a part of + * the public ABI. + */ +typedef struct AVDynamicHDRPlus { + /** + * Country code by Rec. ITU-T T.35 Annex A. The value shall be 0xB5. + */ + uint8_t itu_t_t35_country_code; + + /** + * Application version in the application defining document in ST-2094 + * suite. The value shall be set to 0. + */ + uint8_t application_version; + + /** + * The number of processing windows. The value shall be in the range + * of 1 to 3, inclusive. + */ + uint8_t num_windows; + + /** + * The color transform parameters for every processing window. + */ + AVHDRPlusColorTransformParams params[3]; + + /** + * The nominal maximum display luminance of the targeted system display, + * in units of 0.0001 candelas per square metre. The value shall be in + * the range of 0 to 10000, inclusive. + */ + AVRational targeted_system_display_maximum_luminance; + + /** + * This flag shall be equal to 0 in bit streams conforming to this version + * of this Specification. The value 1 is reserved for future use. + */ + uint8_t targeted_system_display_actual_peak_luminance_flag; + + /** + * The number of rows in the targeted system_display_actual_peak_luminance + * array. The value shall be in the range of 2 to 25, inclusive. + */ + uint8_t num_rows_targeted_system_display_actual_peak_luminance; + + /** + * The number of columns in the + * targeted_system_display_actual_peak_luminance array. The value shall be + * in the range of 2 to 25, inclusive. + */ + uint8_t num_cols_targeted_system_display_actual_peak_luminance; + + /** + * The normalized actual peak luminance of the targeted system display. The + * values should be in the range of 0 to 1, inclusive and in multiples of + * 1/15. + */ + AVRational targeted_system_display_actual_peak_luminance[25][25]; + + /** + * This flag shall be equal to 0 in bitstreams conforming to this version of + * this Specification. The value 1 is reserved for future use. + */ + uint8_t mastering_display_actual_peak_luminance_flag; + + /** + * The number of rows in the mastering_display_actual_peak_luminance array. + * The value shall be in the range of 2 to 25, inclusive. + */ + uint8_t num_rows_mastering_display_actual_peak_luminance; + + /** + * The number of columns in the mastering_display_actual_peak_luminance + * array. The value shall be in the range of 2 to 25, inclusive. + */ + uint8_t num_cols_mastering_display_actual_peak_luminance; + + /** + * The normalized actual peak luminance of the mastering display used for + * mastering the image essence. The values should be in the range of 0 to 1, + * inclusive and in multiples of 1/15. + */ + AVRational mastering_display_actual_peak_luminance[25][25]; +} AVDynamicHDRPlus; + +/** + * Allocate an AVDynamicHDRPlus structure and set its fields to + * default values. The resulting struct can be freed using av_freep(). + * + * @return An AVDynamicHDRPlus filled with default values or NULL + * on failure. + */ +AVDynamicHDRPlus *av_dynamic_hdr_plus_alloc(size_t *size); + +/** + * Allocate a complete AVDynamicHDRPlus and add it to the frame. + * @param frame The frame which side data is added to. + * + * @return The AVDynamicHDRPlus structure to be filled by caller or NULL + * on failure. + */ +AVDynamicHDRPlus *av_dynamic_hdr_plus_create_side_data(AVFrame *frame); + +#endif /* AVUTIL_HDR_DYNAMIC_METADATA_H */ diff --git a/TMessagesProj/jni/ffmpeg/include/libavutil/hwcontext_cuda.h b/TMessagesProj/jni/ffmpeg/include/libavutil/hwcontext_cuda.h index 12dae8449..81a0552ca 100644 --- a/TMessagesProj/jni/ffmpeg/include/libavutil/hwcontext_cuda.h +++ b/TMessagesProj/jni/ffmpeg/include/libavutil/hwcontext_cuda.h @@ -41,6 +41,7 @@ typedef struct AVCUDADeviceContextInternal AVCUDADeviceContextInternal; */ typedef struct AVCUDADeviceContext { CUcontext cuda_ctx; + CUstream stream; AVCUDADeviceContextInternal *internal; } AVCUDADeviceContext; diff --git a/TMessagesProj/jni/ffmpeg/include/libavutil/hwcontext_drm.h b/TMessagesProj/jni/ffmpeg/include/libavutil/hwcontext_drm.h index 2e225451e..42709f215 100644 --- a/TMessagesProj/jni/ffmpeg/include/libavutil/hwcontext_drm.h +++ b/TMessagesProj/jni/ffmpeg/include/libavutil/hwcontext_drm.h @@ -58,6 +58,9 @@ typedef struct AVDRMObjectDescriptor { size_t size; /** * Format modifier applied to the object (DRM_FORMAT_MOD_*). + * + * If the format modifier is unknown then this should be set to + * DRM_FORMAT_MOD_INVALID. */ uint64_t format_modifier; } AVDRMObjectDescriptor; diff --git a/TMessagesProj/jni/ffmpeg/include/libavutil/intreadwrite.h b/TMessagesProj/jni/ffmpeg/include/libavutil/intreadwrite.h index 67c763b13..4c8413a53 100644 --- a/TMessagesProj/jni/ffmpeg/include/libavutil/intreadwrite.h +++ b/TMessagesProj/jni/ffmpeg/include/libavutil/intreadwrite.h @@ -542,6 +542,21 @@ union unaligned_16 { uint16_t l; } __attribute__((packed)) av_alias; # define AV_WN64A(p, v) AV_WNA(64, p, v) #endif +#if AV_HAVE_BIGENDIAN +# define AV_RLA(s, p) av_bswap##s(AV_RN##s##A(p)) +# define AV_WLA(s, p, v) AV_WN##s##A(p, av_bswap##s(v)) +#else +# define AV_RLA(s, p) AV_RN##s##A(p) +# define AV_WLA(s, p, v) AV_WN##s##A(p, v) +#endif + +#ifndef AV_RL64A +# define AV_RL64A(p) AV_RLA(64, p) +#endif +#ifndef AV_WL64A +# define AV_WL64A(p, v) AV_WLA(64, p, v) +#endif + /* * The AV_COPYxxU macros are suitable for copying data to/from unaligned * memory locations. diff --git a/TMessagesProj/jni/ffmpeg/include/libavutil/mem.h b/TMessagesProj/jni/ffmpeg/include/libavutil/mem.h index 7e0b12a8a..5fb1a02dd 100644 --- a/TMessagesProj/jni/ffmpeg/include/libavutil/mem.h +++ b/TMessagesProj/jni/ffmpeg/include/libavutil/mem.h @@ -339,7 +339,7 @@ av_alloc_size(2, 3) void *av_realloc_array(void *ptr, size_t nmemb, size_t size) * @warning Unlike av_malloc(), the allocated memory is not guaranteed to be * correctly aligned. */ -av_alloc_size(2, 3) int av_reallocp_array(void *ptr, size_t nmemb, size_t size); +int av_reallocp_array(void *ptr, size_t nmemb, size_t size); /** * Reallocate the given buffer if it is not large enough, otherwise do nothing. @@ -363,10 +363,10 @@ av_alloc_size(2, 3) int av_reallocp_array(void *ptr, size_t nmemb, size_t size); * @endcode * * @param[in,out] ptr Already allocated buffer, or `NULL` - * @param[in,out] size Pointer to current size of buffer `ptr`. `*size` is - * changed to `min_size` in case of success or 0 in - * case of failure - * @param[in] min_size New size of buffer `ptr` + * @param[in,out] size Pointer to the size of buffer `ptr`. `*size` is + * updated to the new allocated size, in particular 0 + * in case of failure. + * @param[in] min_size Desired minimal size of buffer `ptr` * @return `ptr` if the buffer is large enough, a pointer to newly reallocated * buffer if the buffer was not large enough, or `NULL` in case of * error @@ -397,10 +397,10 @@ void *av_fast_realloc(void *ptr, unsigned int *size, size_t min_size); * @param[in,out] ptr Pointer to pointer to an already allocated buffer. * `*ptr` will be overwritten with pointer to new * buffer on success or `NULL` on failure - * @param[in,out] size Pointer to current size of buffer `*ptr`. `*size` is - * changed to `min_size` in case of success or 0 in - * case of failure - * @param[in] min_size New size of buffer `*ptr` + * @param[in,out] size Pointer to the size of buffer `*ptr`. `*size` is + * updated to the new allocated size, in particular 0 + * in case of failure. + * @param[in] min_size Desired minimal size of buffer `*ptr` * @see av_realloc() * @see av_fast_mallocz() */ @@ -418,10 +418,10 @@ void av_fast_malloc(void *ptr, unsigned int *size, size_t min_size); * @param[in,out] ptr Pointer to pointer to an already allocated buffer. * `*ptr` will be overwritten with pointer to new * buffer on success or `NULL` on failure - * @param[in,out] size Pointer to current size of buffer `*ptr`. `*size` is - * changed to `min_size` in case of success or 0 in - * case of failure - * @param[in] min_size New size of buffer `*ptr` + * @param[in,out] size Pointer to the size of buffer `*ptr`. `*size` is + * updated to the new allocated size, in particular 0 + * in case of failure. + * @param[in] min_size Desired minimal size of buffer `*ptr` * @see av_fast_malloc() */ void av_fast_mallocz(void *ptr, unsigned int *size, size_t min_size); diff --git a/TMessagesProj/jni/ffmpeg/include/libavutil/murmur3.h b/TMessagesProj/jni/ffmpeg/include/libavutil/murmur3.h index 6a1694c08..1b09175c1 100644 --- a/TMessagesProj/jni/ffmpeg/include/libavutil/murmur3.h +++ b/TMessagesProj/jni/ffmpeg/include/libavutil/murmur3.h @@ -29,6 +29,8 @@ #include +#include "version.h" + /** * @defgroup lavu_murmur3 Murmur3 * @ingroup lavu_hash @@ -97,7 +99,11 @@ void av_murmur3_init(struct AVMurMur3 *c); * @param[in] src Input data to update hash with * @param[in] len Number of bytes to read from `src` */ +#if FF_API_CRYPTO_SIZE_T void av_murmur3_update(struct AVMurMur3 *c, const uint8_t *src, int len); +#else +void av_murmur3_update(struct AVMurMur3 *c, const uint8_t *src, size_t len); +#endif /** * Finish hashing and output digest value. diff --git a/TMessagesProj/jni/ffmpeg/include/libavutil/opt.h b/TMessagesProj/jni/ffmpeg/include/libavutil/opt.h index 555929dd6..39f4a8dda 100644 --- a/TMessagesProj/jni/ffmpeg/include/libavutil/opt.h +++ b/TMessagesProj/jni/ffmpeg/include/libavutil/opt.h @@ -229,15 +229,15 @@ enum AVOptionType{ AV_OPT_TYPE_BINARY, ///< offset must point to a pointer immediately followed by an int for the length AV_OPT_TYPE_DICT, AV_OPT_TYPE_UINT64, - AV_OPT_TYPE_CONST = 128, - AV_OPT_TYPE_IMAGE_SIZE = MKBETAG('S','I','Z','E'), ///< offset must point to two consecutive integers - AV_OPT_TYPE_PIXEL_FMT = MKBETAG('P','F','M','T'), - AV_OPT_TYPE_SAMPLE_FMT = MKBETAG('S','F','M','T'), - AV_OPT_TYPE_VIDEO_RATE = MKBETAG('V','R','A','T'), ///< offset must point to AVRational - AV_OPT_TYPE_DURATION = MKBETAG('D','U','R',' '), - AV_OPT_TYPE_COLOR = MKBETAG('C','O','L','R'), - AV_OPT_TYPE_CHANNEL_LAYOUT = MKBETAG('C','H','L','A'), - AV_OPT_TYPE_BOOL = MKBETAG('B','O','O','L'), + AV_OPT_TYPE_CONST, + AV_OPT_TYPE_IMAGE_SIZE, ///< offset must point to two consecutive integers + AV_OPT_TYPE_PIXEL_FMT, + AV_OPT_TYPE_SAMPLE_FMT, + AV_OPT_TYPE_VIDEO_RATE, ///< offset must point to AVRational + AV_OPT_TYPE_DURATION, + AV_OPT_TYPE_COLOR, + AV_OPT_TYPE_CHANNEL_LAYOUT, + AV_OPT_TYPE_BOOL, }; /** @@ -287,7 +287,9 @@ typedef struct AVOption { * This flag only makes sense when AV_OPT_FLAG_EXPORT is also set. */ #define AV_OPT_FLAG_READONLY 128 +#define AV_OPT_FLAG_BSF_PARAM (1<<8) ///< a generic parameter which can be set by the user for bit stream filtering #define AV_OPT_FLAG_FILTERING_PARAM (1<<16) ///< a generic parameter which can be set by the user for filtering +#define AV_OPT_FLAG_DEPRECATED (1<<17) ///< set if option is deprecated, users should refer to AVOption.help text for more information //FIXME think about enc-audio, ... style flags /** diff --git a/TMessagesProj/jni/ffmpeg/include/libavutil/pixdesc.h b/TMessagesProj/jni/ffmpeg/include/libavutil/pixdesc.h index ea046033a..c055810ae 100644 --- a/TMessagesProj/jni/ffmpeg/include/libavutil/pixdesc.h +++ b/TMessagesProj/jni/ffmpeg/include/libavutil/pixdesc.h @@ -154,17 +154,21 @@ typedef struct AVPixFmtDescriptor { * in some cases be simpler. Or the data can be interpreted purely based on * the pixel format without using the palette. * An example of a pseudo-paletted format is AV_PIX_FMT_GRAY8 + * + * @deprecated This flag is deprecated, and will be removed. When it is removed, + * the extra palette allocation in AVFrame.data[1] is removed as well. Only + * actual paletted formats (as indicated by AV_PIX_FMT_FLAG_PAL) will have a + * palette. Starting with FFmpeg versions which have this flag deprecated, the + * extra "pseudo" palette is already ignored, and API users are not required to + * allocate a palette for AV_PIX_FMT_FLAG_PSEUDOPAL formats (it was required + * before the deprecation, though). */ #define AV_PIX_FMT_FLAG_PSEUDOPAL (1 << 6) /** * The pixel format has an alpha channel. This is set on all formats that - * support alpha in some way. The exception is AV_PIX_FMT_PAL8, which can - * carry alpha as part of the palette. Details are explained in the - * AVPixelFormat enum, and are also encoded in the corresponding - * AVPixFmtDescriptor. - * - * The alpha is always straight, never pre-multiplied. + * support alpha in some way, including AV_PIX_FMT_PAL8. The alpha is always + * straight, never pre-multiplied. * * If a codec or a filter does not support alpha, it should set all alpha to * opaque, or use the equivalent pixel formats without alpha component, e.g. @@ -339,7 +343,13 @@ char *av_get_pix_fmt_string(char *buf, int buf_size, * format writes the values corresponding to the palette * component c in data[1] to dst, rather than the palette indexes in * data[0]. The behavior is undefined if the format is not paletted. + * @param dst_element_size size of elements in dst array (2 or 4 byte) */ +void av_read_image_line2(void *dst, const uint8_t *data[4], + const int linesize[4], const AVPixFmtDescriptor *desc, + int x, int y, int c, int w, int read_pal_component, + int dst_element_size); + void av_read_image_line(uint16_t *dst, const uint8_t *data[4], const int linesize[4], const AVPixFmtDescriptor *desc, int x, int y, int c, int w, int read_pal_component); @@ -357,7 +367,12 @@ void av_read_image_line(uint16_t *dst, const uint8_t *data[4], * @param y the vertical coordinate of the first pixel to write * @param w the width of the line to write, that is the number of * values to write to the image line + * @param src_element_size size of elements in src array (2 or 4 byte) */ +void av_write_image_line2(const void *src, uint8_t *data[4], + const int linesize[4], const AVPixFmtDescriptor *desc, + int x, int y, int c, int w, int src_element_size); + void av_write_image_line(const uint16_t *src, uint8_t *data[4], const int linesize[4], const AVPixFmtDescriptor *desc, int x, int y, int c, int w); diff --git a/TMessagesProj/jni/ffmpeg/include/libavutil/pixfmt.h b/TMessagesProj/jni/ffmpeg/include/libavutil/pixfmt.h index e184a5667..24d1b7e41 100644 --- a/TMessagesProj/jni/ffmpeg/include/libavutil/pixfmt.h +++ b/TMessagesProj/jni/ffmpeg/include/libavutil/pixfmt.h @@ -42,6 +42,10 @@ * This is stored as BGRA on little-endian CPU architectures and ARGB on * big-endian CPUs. * + * @note + * If the resolution is not a multiple of the chroma subsampling factor + * then the chroma plane resolution must be rounded up. + * * @par * When the pixel format is palettized RGB32 (AV_PIX_FMT_PAL8), the palettized * image data is stored in AVFrame.data[0]. The palette is transported in @@ -330,6 +334,17 @@ enum AVPixelFormat { */ AV_PIX_FMT_OPENCL, + AV_PIX_FMT_GRAY14BE, ///< Y , 14bpp, big-endian + AV_PIX_FMT_GRAY14LE, ///< Y , 14bpp, little-endian + + AV_PIX_FMT_GRAYF32BE, ///< IEEE-754 single precision Y, 32bpp, big-endian + AV_PIX_FMT_GRAYF32LE, ///< IEEE-754 single precision Y, 32bpp, little-endian + + AV_PIX_FMT_YUVA422P12BE, ///< planar YUV 4:2:2,24bpp, (1 Cr & Cb sample per 2x1 Y samples), 12b alpha, big-endian + AV_PIX_FMT_YUVA422P12LE, ///< planar YUV 4:2:2,24bpp, (1 Cr & Cb sample per 2x1 Y samples), 12b alpha, little-endian + AV_PIX_FMT_YUVA444P12BE, ///< planar YUV 4:4:4,36bpp, (1 Cr & Cb sample per 1x1 Y samples), 12b alpha, big-endian + AV_PIX_FMT_YUVA444P12LE, ///< planar YUV 4:4:4,36bpp, (1 Cr & Cb sample per 1x1 Y samples), 12b alpha, little-endian + AV_PIX_FMT_NB ///< number of pixel formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions }; @@ -349,6 +364,7 @@ enum AVPixelFormat { #define AV_PIX_FMT_GRAY9 AV_PIX_FMT_NE(GRAY9BE, GRAY9LE) #define AV_PIX_FMT_GRAY10 AV_PIX_FMT_NE(GRAY10BE, GRAY10LE) #define AV_PIX_FMT_GRAY12 AV_PIX_FMT_NE(GRAY12BE, GRAY12LE) +#define AV_PIX_FMT_GRAY14 AV_PIX_FMT_NE(GRAY14BE, GRAY14LE) #define AV_PIX_FMT_GRAY16 AV_PIX_FMT_NE(GRAY16BE, GRAY16LE) #define AV_PIX_FMT_YA16 AV_PIX_FMT_NE(YA16BE, YA16LE) #define AV_PIX_FMT_RGB48 AV_PIX_FMT_NE(RGB48BE, RGB48LE) @@ -397,12 +413,16 @@ enum AVPixelFormat { #define AV_PIX_FMT_GBRPF32 AV_PIX_FMT_NE(GBRPF32BE, GBRPF32LE) #define AV_PIX_FMT_GBRAPF32 AV_PIX_FMT_NE(GBRAPF32BE, GBRAPF32LE) +#define AV_PIX_FMT_GRAYF32 AV_PIX_FMT_NE(GRAYF32BE, GRAYF32LE) + #define AV_PIX_FMT_YUVA420P9 AV_PIX_FMT_NE(YUVA420P9BE , YUVA420P9LE) #define AV_PIX_FMT_YUVA422P9 AV_PIX_FMT_NE(YUVA422P9BE , YUVA422P9LE) #define AV_PIX_FMT_YUVA444P9 AV_PIX_FMT_NE(YUVA444P9BE , YUVA444P9LE) #define AV_PIX_FMT_YUVA420P10 AV_PIX_FMT_NE(YUVA420P10BE, YUVA420P10LE) #define AV_PIX_FMT_YUVA422P10 AV_PIX_FMT_NE(YUVA422P10BE, YUVA422P10LE) #define AV_PIX_FMT_YUVA444P10 AV_PIX_FMT_NE(YUVA444P10BE, YUVA444P10LE) +#define AV_PIX_FMT_YUVA422P12 AV_PIX_FMT_NE(YUVA422P12BE, YUVA422P12LE) +#define AV_PIX_FMT_YUVA444P12 AV_PIX_FMT_NE(YUVA444P12BE, YUVA444P12LE) #define AV_PIX_FMT_YUVA420P16 AV_PIX_FMT_NE(YUVA420P16BE, YUVA420P16LE) #define AV_PIX_FMT_YUVA422P16 AV_PIX_FMT_NE(YUVA422P16BE, YUVA422P16LE) #define AV_PIX_FMT_YUVA444P16 AV_PIX_FMT_NE(YUVA444P16BE, YUVA444P16LE) diff --git a/TMessagesProj/jni/ffmpeg/include/libavutil/ripemd.h b/TMessagesProj/jni/ffmpeg/include/libavutil/ripemd.h index 6d6bb3208..0db6858ff 100644 --- a/TMessagesProj/jni/ffmpeg/include/libavutil/ripemd.h +++ b/TMessagesProj/jni/ffmpeg/include/libavutil/ripemd.h @@ -66,7 +66,11 @@ int av_ripemd_init(struct AVRIPEMD* context, int bits); * @param data input data to update hash with * @param len input data length */ +#if FF_API_CRYPTO_SIZE_T void av_ripemd_update(struct AVRIPEMD* context, const uint8_t* data, unsigned int len); +#else +void av_ripemd_update(struct AVRIPEMD* context, const uint8_t* data, size_t len); +#endif /** * Finish hashing and output digest value. diff --git a/TMessagesProj/jni/ffmpeg/include/libavutil/threadmessage.h b/TMessagesProj/jni/ffmpeg/include/libavutil/threadmessage.h index 8480a0a3d..42ce655f3 100644 --- a/TMessagesProj/jni/ffmpeg/include/libavutil/threadmessage.h +++ b/TMessagesProj/jni/ffmpeg/include/libavutil/threadmessage.h @@ -95,6 +95,14 @@ void av_thread_message_queue_set_err_recv(AVThreadMessageQueue *mq, void av_thread_message_queue_set_free_func(AVThreadMessageQueue *mq, void (*free_func)(void *msg)); +/** + * Return the current number of messages in the queue. + * + * @return the current number of messages or AVERROR(ENOSYS) if lavu was built + * without thread support + */ +int av_thread_message_queue_nb_elems(AVThreadMessageQueue *mq); + /** * Flush the message queue * diff --git a/TMessagesProj/jni/ffmpeg/include/libavutil/version.h b/TMessagesProj/jni/ffmpeg/include/libavutil/version.h index d81ec6fa7..1fcdea95b 100644 --- a/TMessagesProj/jni/ffmpeg/include/libavutil/version.h +++ b/TMessagesProj/jni/ffmpeg/include/libavutil/version.h @@ -79,7 +79,7 @@ */ #define LIBAVUTIL_VERSION_MAJOR 56 -#define LIBAVUTIL_VERSION_MINOR 7 +#define LIBAVUTIL_VERSION_MINOR 26 #define LIBAVUTIL_VERSION_MICRO 100 #define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ @@ -126,6 +126,9 @@ #ifndef FF_API_FRAME_GET_SET #define FF_API_FRAME_GET_SET (LIBAVUTIL_VERSION_MAJOR < 57) #endif +#ifndef FF_API_PSEUDOPAL +#define FF_API_PSEUDOPAL (LIBAVUTIL_VERSION_MAJOR < 57) +#endif /** diff --git a/TMessagesProj/jni/ffmpeg/include/libswscale/swscale.h b/TMessagesProj/jni/ffmpeg/include/libswscale/swscale.h new file mode 100644 index 000000000..7713f51ec --- /dev/null +++ b/TMessagesProj/jni/ffmpeg/include/libswscale/swscale.h @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2001-2011 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SWSCALE_SWSCALE_H +#define SWSCALE_SWSCALE_H + +/** + * @file + * @ingroup libsws + * external API header + */ + +#include + +#include "libavutil/avutil.h" +#include "libavutil/log.h" +#include "libavutil/pixfmt.h" +#include "version.h" + +/** + * @defgroup libsws libswscale + * Color conversion and scaling library. + * + * @{ + * + * Return the LIBSWSCALE_VERSION_INT constant. + */ +unsigned swscale_version(void); + +/** + * Return the libswscale build-time configuration. + */ +const char *swscale_configuration(void); + +/** + * Return the libswscale license. + */ +const char *swscale_license(void); + +/* values for the flags, the stuff on the command line is different */ +#define SWS_FAST_BILINEAR 1 +#define SWS_BILINEAR 2 +#define SWS_BICUBIC 4 +#define SWS_X 8 +#define SWS_POINT 0x10 +#define SWS_AREA 0x20 +#define SWS_BICUBLIN 0x40 +#define SWS_GAUSS 0x80 +#define SWS_SINC 0x100 +#define SWS_LANCZOS 0x200 +#define SWS_SPLINE 0x400 + +#define SWS_SRC_V_CHR_DROP_MASK 0x30000 +#define SWS_SRC_V_CHR_DROP_SHIFT 16 + +#define SWS_PARAM_DEFAULT 123456 + +#define SWS_PRINT_INFO 0x1000 + +//the following 3 flags are not completely implemented +//internal chrominance subsampling info +#define SWS_FULL_CHR_H_INT 0x2000 +//input subsampling info +#define SWS_FULL_CHR_H_INP 0x4000 +#define SWS_DIRECT_BGR 0x8000 +#define SWS_ACCURATE_RND 0x40000 +#define SWS_BITEXACT 0x80000 +#define SWS_ERROR_DIFFUSION 0x800000 + +#define SWS_MAX_REDUCE_CUTOFF 0.002 + +#define SWS_CS_ITU709 1 +#define SWS_CS_FCC 4 +#define SWS_CS_ITU601 5 +#define SWS_CS_ITU624 5 +#define SWS_CS_SMPTE170M 5 +#define SWS_CS_SMPTE240M 7 +#define SWS_CS_DEFAULT 5 +#define SWS_CS_BT2020 9 + +/** + * Return a pointer to yuv<->rgb coefficients for the given colorspace + * suitable for sws_setColorspaceDetails(). + * + * @param colorspace One of the SWS_CS_* macros. If invalid, + * SWS_CS_DEFAULT is used. + */ +const int *sws_getCoefficients(int colorspace); + +// when used for filters they must have an odd number of elements +// coeffs cannot be shared between vectors +typedef struct SwsVector { + double *coeff; ///< pointer to the list of coefficients + int length; ///< number of coefficients in the vector +} SwsVector; + +// vectors can be shared +typedef struct SwsFilter { + SwsVector *lumH; + SwsVector *lumV; + SwsVector *chrH; + SwsVector *chrV; +} SwsFilter; + +struct SwsContext; + +/** + * Return a positive value if pix_fmt is a supported input format, 0 + * otherwise. + */ +int sws_isSupportedInput(enum AVPixelFormat pix_fmt); + +/** + * Return a positive value if pix_fmt is a supported output format, 0 + * otherwise. + */ +int sws_isSupportedOutput(enum AVPixelFormat pix_fmt); + +/** + * @param[in] pix_fmt the pixel format + * @return a positive value if an endianness conversion for pix_fmt is + * supported, 0 otherwise. + */ +int sws_isSupportedEndiannessConversion(enum AVPixelFormat pix_fmt); + +/** + * Allocate an empty SwsContext. This must be filled and passed to + * sws_init_context(). For filling see AVOptions, options.c and + * sws_setColorspaceDetails(). + */ +struct SwsContext *sws_alloc_context(void); + +/** + * Initialize the swscaler context sws_context. + * + * @return zero or positive value on success, a negative value on + * error + */ +av_warn_unused_result +int sws_init_context(struct SwsContext *sws_context, SwsFilter *srcFilter, SwsFilter *dstFilter); + +/** + * Free the swscaler context swsContext. + * If swsContext is NULL, then does nothing. + */ +void sws_freeContext(struct SwsContext *swsContext); + +/** + * Allocate and return an SwsContext. You need it to perform + * scaling/conversion operations using sws_scale(). + * + * @param srcW the width of the source image + * @param srcH the height of the source image + * @param srcFormat the source image format + * @param dstW the width of the destination image + * @param dstH the height of the destination image + * @param dstFormat the destination image format + * @param flags specify which algorithm and options to use for rescaling + * @param param extra parameters to tune the used scaler + * For SWS_BICUBIC param[0] and [1] tune the shape of the basis + * function, param[0] tunes f(1) and param[1] f´(1) + * For SWS_GAUSS param[0] tunes the exponent and thus cutoff + * frequency + * For SWS_LANCZOS param[0] tunes the width of the window function + * @return a pointer to an allocated context, or NULL in case of error + * @note this function is to be removed after a saner alternative is + * written + */ +struct SwsContext *sws_getContext(int srcW, int srcH, enum AVPixelFormat srcFormat, + int dstW, int dstH, enum AVPixelFormat dstFormat, + int flags, SwsFilter *srcFilter, + SwsFilter *dstFilter, const double *param); + +/** + * Scale the image slice in srcSlice and put the resulting scaled + * slice in the image in dst. A slice is a sequence of consecutive + * rows in an image. + * + * Slices have to be provided in sequential order, either in + * top-bottom or bottom-top order. If slices are provided in + * non-sequential order the behavior of the function is undefined. + * + * @param c the scaling context previously created with + * sws_getContext() + * @param srcSlice the array containing the pointers to the planes of + * the source slice + * @param srcStride the array containing the strides for each plane of + * the source image + * @param srcSliceY the position in the source image of the slice to + * process, that is the number (counted starting from + * zero) in the image of the first row of the slice + * @param srcSliceH the height of the source slice, that is the number + * of rows in the slice + * @param dst the array containing the pointers to the planes of + * the destination image + * @param dstStride the array containing the strides for each plane of + * the destination image + * @return the height of the output slice + */ +int sws_scale(struct SwsContext *c, const uint8_t *const srcSlice[], + const int srcStride[], int srcSliceY, int srcSliceH, + uint8_t *const dst[], const int dstStride[]); + +/** + * @param dstRange flag indicating the while-black range of the output (1=jpeg / 0=mpeg) + * @param srcRange flag indicating the while-black range of the input (1=jpeg / 0=mpeg) + * @param table the yuv2rgb coefficients describing the output yuv space, normally ff_yuv2rgb_coeffs[x] + * @param inv_table the yuv2rgb coefficients describing the input yuv space, normally ff_yuv2rgb_coeffs[x] + * @param brightness 16.16 fixed point brightness correction + * @param contrast 16.16 fixed point contrast correction + * @param saturation 16.16 fixed point saturation correction + * @return -1 if not supported + */ +int sws_setColorspaceDetails(struct SwsContext *c, const int inv_table[4], + int srcRange, const int table[4], int dstRange, + int brightness, int contrast, int saturation); + +/** + * @return -1 if not supported + */ +int sws_getColorspaceDetails(struct SwsContext *c, int **inv_table, + int *srcRange, int **table, int *dstRange, + int *brightness, int *contrast, int *saturation); + +/** + * Allocate and return an uninitialized vector with length coefficients. + */ +SwsVector *sws_allocVec(int length); + +/** + * Return a normalized Gaussian curve used to filter stuff + * quality = 3 is high quality, lower is lower quality. + */ +SwsVector *sws_getGaussianVec(double variance, double quality); + +/** + * Scale all the coefficients of a by the scalar value. + */ +void sws_scaleVec(SwsVector *a, double scalar); + +/** + * Scale all the coefficients of a so that their sum equals height. + */ +void sws_normalizeVec(SwsVector *a, double height); + +#if FF_API_SWS_VECTOR +attribute_deprecated SwsVector *sws_getConstVec(double c, int length); +attribute_deprecated SwsVector *sws_getIdentityVec(void); +attribute_deprecated void sws_convVec(SwsVector *a, SwsVector *b); +attribute_deprecated void sws_addVec(SwsVector *a, SwsVector *b); +attribute_deprecated void sws_subVec(SwsVector *a, SwsVector *b); +attribute_deprecated void sws_shiftVec(SwsVector *a, int shift); +attribute_deprecated SwsVector *sws_cloneVec(SwsVector *a); +attribute_deprecated void sws_printVec2(SwsVector *a, AVClass *log_ctx, int log_level); +#endif + +void sws_freeVec(SwsVector *a); + +SwsFilter *sws_getDefaultFilter(float lumaGBlur, float chromaGBlur, + float lumaSharpen, float chromaSharpen, + float chromaHShift, float chromaVShift, + int verbose); +void sws_freeFilter(SwsFilter *filter); + +/** + * Check if context can be reused, otherwise reallocate a new one. + * + * If context is NULL, just calls sws_getContext() to get a new + * context. Otherwise, checks if the parameters are the ones already + * saved in context. If that is the case, returns the current + * context. Otherwise, frees context and gets a new context with + * the new parameters. + * + * Be warned that srcFilter and dstFilter are not checked, they + * are assumed to remain the same. + */ +struct SwsContext *sws_getCachedContext(struct SwsContext *context, + int srcW, int srcH, enum AVPixelFormat srcFormat, + int dstW, int dstH, enum AVPixelFormat dstFormat, + int flags, SwsFilter *srcFilter, + SwsFilter *dstFilter, const double *param); + +/** + * Convert an 8-bit paletted frame into a frame with a color depth of 32 bits. + * + * The output frame will have the same packed format as the palette. + * + * @param src source frame buffer + * @param dst destination frame buffer + * @param num_pixels number of pixels to convert + * @param palette array with [256] entries, which must match color arrangement (RGB or BGR) of src + */ +void sws_convertPalette8ToPacked32(const uint8_t *src, uint8_t *dst, int num_pixels, const uint8_t *palette); + +/** + * Convert an 8-bit paletted frame into a frame with a color depth of 24 bits. + * + * With the palette format "ABCD", the destination frame ends up with the format "ABC". + * + * @param src source frame buffer + * @param dst destination frame buffer + * @param num_pixels number of pixels to convert + * @param palette array with [256] entries, which must match color arrangement (RGB or BGR) of src + */ +void sws_convertPalette8ToPacked24(const uint8_t *src, uint8_t *dst, int num_pixels, const uint8_t *palette); + +/** + * Get the AVClass for swsContext. It can be used in combination with + * AV_OPT_SEARCH_FAKE_OBJ for examining options. + * + * @see av_opt_find(). + */ +const AVClass *sws_get_class(void); + +/** + * @} + */ + +#endif /* SWSCALE_SWSCALE_H */ diff --git a/TMessagesProj/jni/ffmpeg/include/libswscale/version.h b/TMessagesProj/jni/ffmpeg/include/libswscale/version.h new file mode 100644 index 000000000..0e28a76e6 --- /dev/null +++ b/TMessagesProj/jni/ffmpeg/include/libswscale/version.h @@ -0,0 +1,53 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SWSCALE_VERSION_H +#define SWSCALE_VERSION_H + +/** + * @file + * swscale version macros + */ + +#include "libavutil/version.h" + +#define LIBSWSCALE_VERSION_MAJOR 5 +#define LIBSWSCALE_VERSION_MINOR 4 +#define LIBSWSCALE_VERSION_MICRO 100 + +#define LIBSWSCALE_VERSION_INT AV_VERSION_INT(LIBSWSCALE_VERSION_MAJOR, \ + LIBSWSCALE_VERSION_MINOR, \ + LIBSWSCALE_VERSION_MICRO) +#define LIBSWSCALE_VERSION AV_VERSION(LIBSWSCALE_VERSION_MAJOR, \ + LIBSWSCALE_VERSION_MINOR, \ + LIBSWSCALE_VERSION_MICRO) +#define LIBSWSCALE_BUILD LIBSWSCALE_VERSION_INT + +#define LIBSWSCALE_IDENT "SwS" AV_STRINGIFY(LIBSWSCALE_VERSION) + +/** + * FF_API_* defines may be placed below to indicate public API that will be + * dropped at a future version bump. The defines themselves are not part of + * the public API and may change, break or disappear at any time. + */ + +#ifndef FF_API_SWS_VECTOR +#define FF_API_SWS_VECTOR (LIBSWSCALE_VERSION_MAJOR < 6) +#endif + +#endif /* SWSCALE_VERSION_H */ diff --git a/TMessagesProj/jni/ffmpeg/x86_64/libavcodec.a b/TMessagesProj/jni/ffmpeg/x86_64/libavcodec.a index 237149ae5..b7a8e733f 100644 Binary files a/TMessagesProj/jni/ffmpeg/x86_64/libavcodec.a and b/TMessagesProj/jni/ffmpeg/x86_64/libavcodec.a differ diff --git a/TMessagesProj/jni/ffmpeg/x86_64/libavformat.a b/TMessagesProj/jni/ffmpeg/x86_64/libavformat.a index 36b8eebae..211ea97d5 100644 Binary files a/TMessagesProj/jni/ffmpeg/x86_64/libavformat.a and b/TMessagesProj/jni/ffmpeg/x86_64/libavformat.a differ diff --git a/TMessagesProj/jni/ffmpeg/x86_64/libavresample.a b/TMessagesProj/jni/ffmpeg/x86_64/libavresample.a index cdcc59d5b..00f258687 100644 Binary files a/TMessagesProj/jni/ffmpeg/x86_64/libavresample.a and b/TMessagesProj/jni/ffmpeg/x86_64/libavresample.a differ diff --git a/TMessagesProj/jni/ffmpeg/x86_64/libavutil.a b/TMessagesProj/jni/ffmpeg/x86_64/libavutil.a index 806fed109..4f29a7591 100644 Binary files a/TMessagesProj/jni/ffmpeg/x86_64/libavutil.a and b/TMessagesProj/jni/ffmpeg/x86_64/libavutil.a differ diff --git a/TMessagesProj/jni/ffmpeg/x86_64/libswscale.a b/TMessagesProj/jni/ffmpeg/x86_64/libswscale.a new file mode 100644 index 000000000..27f57efe3 Binary files /dev/null and b/TMessagesProj/jni/ffmpeg/x86_64/libswscale.a differ diff --git a/TMessagesProj/jni/gifvideo.cpp b/TMessagesProj/jni/gifvideo.cpp index c44e63187..cae9401d4 100644 --- a/TMessagesProj/jni/gifvideo.cpp +++ b/TMessagesProj/jni/gifvideo.cpp @@ -1,14 +1,20 @@ #include -#include -#include #include #include #include #include +#include +#include +#include +#include +#include +#include "tgnet/ConnectionsManager.h" +#include "c_utils.h" extern "C" { #include #include +#include static const std::string av_make_error_str(int errnum) { char errbuf[AV_ERROR_MAX_STRING_SIZE]; @@ -18,6 +24,11 @@ static const std::string av_make_error_str(int errnum) { #undef av_err2str #define av_err2str(errnum) av_make_error_str(errnum).c_str() +#define FFMPEG_AVSEEK_SIZE 0x10000 + +jclass jclass_AnimatedFileDrawableStream; +jmethodID jclass_AnimatedFileDrawableStream_read; +jmethodID jclass_AnimatedFileDrawableStream_cancel; typedef struct VideoInfo { @@ -38,12 +49,47 @@ typedef struct VideoInfo { delete [] src; src = nullptr; } + if (stream != nullptr) { + JNIEnv *jniEnv = nullptr; + JavaVMAttachArgs jvmArgs; + jvmArgs.version = JNI_VERSION_1_6; + + bool attached; + if (JNI_EDETACHED == javaVm->GetEnv((void **) &jniEnv, JNI_VERSION_1_6)) { + javaVm->AttachCurrentThread(&jniEnv, &jvmArgs); + attached = true; + } else { + attached = false; + } + jniEnv->DeleteGlobalRef(stream); + if (attached) { + javaVm->DetachCurrentThread(); + } + stream = nullptr; + } + /*if (ioBuffer) { TODO memleak? + av_free(ioBuffer); + ioBuffer = nullptr; + }*/ + if (ioContext != nullptr) { + avio_context_free(&ioContext); + ioContext = nullptr; + } + if (sws_ctx != nullptr) { + sws_freeContext(sws_ctx); + sws_ctx = nullptr; + } + if (fd >= 0) { + close(fd); + fd = -1; + } + av_packet_unref(&orig_pkt); video_stream_idx = -1; video_stream = nullptr; } - + AVFormatContext *fmt_ctx = nullptr; char *src = nullptr; int video_stream_idx = -1; @@ -53,14 +99,22 @@ typedef struct VideoInfo { bool has_decoded_frames = false; AVPacket pkt; AVPacket orig_pkt; -}; + bool stopped = false; + bool seeking = false; -jobject makeGlobarRef(JNIEnv *env, jobject object) { - if (object) { - return env->NewGlobalRef(object); - } - return 0; -} + uint8_t __attribute__ ((aligned (16))) *dst_data[1]; + int32_t dst_linesize[1]; + + struct SwsContext *sws_ctx = nullptr; + + AVIOContext *ioContext = nullptr; + unsigned char *ioBuffer = nullptr; + jobject stream = nullptr; + int32_t account = 0; + int fd = -1; + int64_t file_size = 0; + int64_t last_seek_p = 0; +}; int open_codec_context(int *stream_idx, AVCodecContext **dec_ctx, AVFormatContext *fmt_ctx, enum AVMediaType type) { int ret, stream_index; @@ -119,7 +173,82 @@ int decode_packet(VideoInfo *info, int *got_frame) { return decoded; } -jlong Java_org_telegram_ui_Components_AnimatedFileDrawable_createDecoder(JNIEnv *env, jclass clazz, jstring src, jintArray data) { +void requestFd(VideoInfo *info) { + JNIEnv *jniEnv = nullptr; + + JavaVMAttachArgs jvmArgs; + jvmArgs.version = JNI_VERSION_1_6; + + bool attached; + if (JNI_EDETACHED == javaVm->GetEnv((void **) &jniEnv, JNI_VERSION_1_6)) { + javaVm->AttachCurrentThread(&jniEnv, &jvmArgs); + attached = true; + } else { + attached = false; + } + jniEnv->CallIntMethod(info->stream, jclass_AnimatedFileDrawableStream_read, (jint) 0, (jint) 1); + if (attached) { + javaVm->DetachCurrentThread(); + } + info->fd = open(info->src, O_RDONLY, S_IRUSR); +} + +int readCallback(void *opaque, uint8_t *buf, int buf_size) { + VideoInfo *info = (VideoInfo *) opaque; + if (!info->stopped) { + if (info->fd < 0) { + requestFd(info); + } + if (info->fd >= 0) { + if (info->last_seek_p + buf_size > info->file_size) { + buf_size = (int) (info->file_size - info->last_seek_p); + } + if (buf_size > 0) { + JNIEnv *jniEnv = nullptr; + + JavaVMAttachArgs jvmArgs; + jvmArgs.version = JNI_VERSION_1_6; + + bool attached; + if (JNI_EDETACHED == javaVm->GetEnv((void **) &jniEnv, JNI_VERSION_1_6)) { + javaVm->AttachCurrentThread(&jniEnv, &jvmArgs); + attached = true; + } else { + attached = false; + } + + buf_size = jniEnv->CallIntMethod(info->stream, jclass_AnimatedFileDrawableStream_read, (jint) info->last_seek_p, (jint) buf_size); + info->last_seek_p += buf_size; + if (attached) { + javaVm->DetachCurrentThread(); + } + return (int) read(info->fd, buf, (size_t) buf_size); + } + } + } + return 0; +} + +int64_t seekCallback(void *opaque, int64_t offset, int whence) { + VideoInfo *info = (VideoInfo *) opaque; + if (!info->stopped) { + if (info->fd < 0) { + requestFd(info); + } + if (info->fd >= 0) { + if (whence & FFMPEG_AVSEEK_SIZE) { + return info->file_size; + } else { + info->last_seek_p = offset; + lseek(info->fd, off_t(offset), SEEK_SET); + return offset; + } + } + } + return 0; +} + +jlong Java_org_telegram_ui_Components_AnimatedFileDrawable_createDecoder(JNIEnv *env, jclass clazz, jstring src, jintArray data, jint account, jlong streamFileSize, jobject stream) { VideoInfo *info = new VideoInfo(); char const *srcString = env->GetStringUTFChars(src, 0); @@ -130,12 +259,40 @@ jlong Java_org_telegram_ui_Components_AnimatedFileDrawable_createDecoder(JNIEnv if (srcString != 0) { env->ReleaseStringUTFChars(src, srcString); } - + int ret; - if ((ret = avformat_open_input(&info->fmt_ctx, info->src, NULL, NULL)) < 0) { - LOGE("can't open source file %s, %s", info->src, av_err2str(ret)); - delete info; - return 0; + if (streamFileSize != 0) { + info->file_size = streamFileSize; + info->stream = env->NewGlobalRef(stream); + info->account = account; + info->fd = open(info->src, O_RDONLY, S_IRUSR); + + info->ioBuffer = (unsigned char *) av_malloc(64 * 1024); + info->ioContext = avio_alloc_context(info->ioBuffer, 64 * 1024, 0, info, readCallback, nullptr, seekCallback); + if (info->ioContext == nullptr) { + delete info; + return 0; + } + + info->fmt_ctx = avformat_alloc_context(); + info->fmt_ctx->pb = info->ioContext; + + AVDictionary *options = NULL; + av_dict_set(&options, "usetoc", "1", 0); + ret = avformat_open_input(&info->fmt_ctx, "http://localhost/file", NULL, &options); + av_dict_free(&options); + if (ret < 0) { + LOGE("can't open source file %s, %s", info->src, av_err2str(ret)); + delete info; + return 0; + } + info->fmt_ctx->flags |= AVFMT_FLAG_FAST_SEEK; + } else { + if ((ret = avformat_open_input(&info->fmt_ctx, info->src, NULL, NULL)) < 0) { + LOGE("can't open source file %s, %s", info->src, av_err2str(ret)); + delete info; + return 0; + } } if ((ret = avformat_find_stream_info(info->fmt_ctx, NULL)) < 0) { @@ -164,11 +321,12 @@ jlong Java_org_telegram_ui_Components_AnimatedFileDrawable_createDecoder(JNIEnv av_init_packet(&info->pkt); info->pkt.data = NULL; info->pkt.size = 0; - + jint *dataArr = env->GetIntArrayElements(data, 0); if (dataArr != nullptr) { dataArr[0] = info->video_dec_ctx->width; dataArr[1] = info->video_dec_ctx->height; + //float pixelWidthHeightRatio = info->video_dec_ctx->sample_aspect_ratio.num / info->video_dec_ctx->sample_aspect_ratio.den; TODO support AVDictionaryEntry *rotate_tag = av_dict_get(info->video_stream->metadata, "rotate", NULL, 0); if (rotate_tag && *rotate_tag->value && strcmp(rotate_tag->value, "0")) { char *tail; @@ -179,6 +337,8 @@ jlong Java_org_telegram_ui_Components_AnimatedFileDrawable_createDecoder(JNIEnv } else { dataArr[2] = 0; } + dataArr[4] = (int32_t) (info->fmt_ctx->duration * 1000 / AV_TIME_BASE); + //(int32_t) (1000 * info->video_stream->duration * av_q2d(info->video_stream->time_base)); env->ReleaseIntArrayElements(data, dataArr, 0); } @@ -192,19 +352,123 @@ void Java_org_telegram_ui_Components_AnimatedFileDrawable_destroyDecoder(JNIEnv return; } VideoInfo *info = (VideoInfo *) (intptr_t) ptr; + if (info->stream != nullptr) { + JNIEnv *jniEnv = nullptr; + JavaVMAttachArgs jvmArgs; + jvmArgs.version = JNI_VERSION_1_6; + + bool attached; + if (JNI_EDETACHED == javaVm->GetEnv((void **) &jniEnv, JNI_VERSION_1_6)) { + javaVm->AttachCurrentThread(&jniEnv, &jvmArgs); + attached = true; + } else { + attached = false; + } + jniEnv->CallVoidMethod(info->stream, jclass_AnimatedFileDrawableStream_cancel); + if (attached) { + javaVm->DetachCurrentThread(); + } + } delete info; } +void Java_org_telegram_ui_Components_AnimatedFileDrawable_stopDecoder(JNIEnv *env, jclass clazz, jlong ptr) { + if (ptr == NULL) { + return; + } + VideoInfo *info = (VideoInfo *) (intptr_t) ptr; + info->stopped = true; +} + +void Java_org_telegram_ui_Components_AnimatedFileDrawable_prepareToSeek(JNIEnv *env, jclass clazz, jlong ptr) { + if (ptr == NULL) { + return; + } + VideoInfo *info = (VideoInfo *) (intptr_t) ptr; + info->seeking = true; +} + +void Java_org_telegram_ui_Components_AnimatedFileDrawable_seekToMs(JNIEnv *env, jclass clazz, jlong ptr, jlong ms) { + if (ptr == NULL) { + return; + } + VideoInfo *info = (VideoInfo *) (intptr_t) ptr; + info->seeking = false; + int64_t pts = (int64_t) (ms / av_q2d(info->video_stream->time_base) / 1000); + int ret = 0; + if ((ret = av_seek_frame(info->fmt_ctx, info->video_stream_idx, pts, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME)) < 0) { + LOGE("can't seek file %s, %s", info->src, av_err2str(ret)); + return; + } else { + avcodec_flush_buffers(info->video_dec_ctx); + int got_frame = 0; + int32_t tries = 1000; + while (tries > 0) { + if (info->pkt.size == 0) { + ret = av_read_frame(info->fmt_ctx, &info->pkt); + if (ret >= 0) { + info->orig_pkt = info->pkt; + } + } + + if (info->pkt.size > 0) { + ret = decode_packet(info, &got_frame); + if (ret < 0) { + if (info->has_decoded_frames) { + ret = 0; + } + info->pkt.size = 0; + } else { + info->pkt.data += ret; + info->pkt.size -= ret; + } + if (info->pkt.size == 0) { + av_packet_unref(&info->orig_pkt); + } + } else { + info->pkt.data = NULL; + info->pkt.size = 0; + ret = decode_packet(info, &got_frame); + if (ret < 0) { + return; + } + if (got_frame == 0) { + av_seek_frame(info->fmt_ctx, info->video_stream_idx, 0, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME); + return; + } + } + if (ret < 0) { + return; + } + if (got_frame) { + if (info->frame->format == AV_PIX_FMT_YUV420P || info->frame->format == AV_PIX_FMT_BGRA || info->frame->format == AV_PIX_FMT_YUVJ420P) { + int64_t pkt_pts; + if (info->frame->pts != AV_NOPTS_VALUE) { + pkt_pts = info->frame->pts; + } else { + pkt_pts = info->frame->pkt_dts; + } + if (pkt_pts >= pts) { + return; + } + } + av_frame_unref(info->frame); + } + tries--; + } + } +} -jint Java_org_telegram_ui_Components_AnimatedFileDrawable_getVideoFrame(JNIEnv *env, jclass clazz, jlong ptr, jobject bitmap, jintArray data) { +jint Java_org_telegram_ui_Components_AnimatedFileDrawable_getVideoFrame(JNIEnv *env, jclass clazz, jlong ptr, jobject bitmap, jintArray data, jint stride) { if (ptr == NULL || bitmap == nullptr) { return 0; } + //int64_t time = ConnectionsManager::getInstance(0).getCurrentTimeMonotonicMillis(); VideoInfo *info = (VideoInfo *) (intptr_t) ptr; int ret = 0; int got_frame = 0; int32_t triesCount = 6; - while (triesCount != 0) { + while (!info->stopped && triesCount != 0) { if (info->pkt.size == 0) { ret = av_read_frame(info->fmt_ctx, &info->pkt); //LOGD("got packet with size %d", info->pkt.size); @@ -212,7 +476,7 @@ jint Java_org_telegram_ui_Components_AnimatedFileDrawable_getVideoFrame(JNIEnv * info->orig_pkt = info->pkt; } } - + if (info->pkt.size > 0) { ret = decode_packet(info, &got_frame); if (ret < 0) { @@ -225,7 +489,7 @@ jint Java_org_telegram_ui_Components_AnimatedFileDrawable_getVideoFrame(JNIEnv * info->pkt.data += ret; info->pkt.size -= ret; } - + if (info->pkt.size == 0) { av_packet_unref(&info->orig_pkt); } @@ -239,8 +503,7 @@ jint Java_org_telegram_ui_Components_AnimatedFileDrawable_getVideoFrame(JNIEnv * } if (got_frame == 0) { if (info->has_decoded_frames) { - //LOGD("file end reached %s", info->src); - if ((ret = avformat_seek_file(info->fmt_ctx, -1, std::numeric_limits::min(), 0, std::numeric_limits::max(), 0)) < 0) { + if ((ret = av_seek_frame(info->fmt_ctx, info->video_stream_idx, 0, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME)) < 0) { LOGE("can't seek to begin of file %s, %s", info->src, av_err2str(ret)); return 0; } else { @@ -249,7 +512,7 @@ jint Java_org_telegram_ui_Components_AnimatedFileDrawable_getVideoFrame(JNIEnv * } } } - if (ret < 0) { + if (ret < 0 || info->seeking) { return 0; } if (got_frame) { @@ -261,7 +524,11 @@ jint Java_org_telegram_ui_Components_AnimatedFileDrawable_getVideoFrame(JNIEnv * if (dataArr != nullptr) { wantedWidth = dataArr[0]; wantedHeight = dataArr[1]; - dataArr[3] = (jint) (1000 * info->frame->pts * av_q2d(info->video_stream->time_base)); + if (info->frame->pts != AV_NOPTS_VALUE) { + dataArr[3] = (jint) (1000 * info->frame->pts * av_q2d(info->video_stream->time_base)); + } else { + dataArr[3] = (jint) (1000 * info->frame->pkt_dts * av_q2d(info->video_stream->time_base)); + } env->ReleaseIntArrayElements(data, dataArr, 0); } else { AndroidBitmapInfo bitmapInfo; @@ -269,26 +536,64 @@ jint Java_org_telegram_ui_Components_AnimatedFileDrawable_getVideoFrame(JNIEnv * wantedWidth = bitmapInfo.width; wantedHeight = bitmapInfo.height; } - + void *pixels; if (AndroidBitmap_lockPixels(env, bitmap, &pixels) >= 0) { - if (info->frame->format == AV_PIX_FMT_YUV420P || info->frame->format == AV_PIX_FMT_YUVJ420P) { - //LOGD("y %d, u %d, v %d, width %d, height %d", info->frame->linesize[0], info->frame->linesize[2], info->frame->linesize[1], info->frame->width, info->frame->height); - if (wantedWidth == info->frame->width && wantedHeight == info->frame->height || wantedWidth == info->frame->height && wantedHeight == info->frame->width) { - libyuv::I420ToARGB(info->frame->data[0], info->frame->linesize[0], info->frame->data[2], info->frame->linesize[2], info->frame->data[1], info->frame->linesize[1], (uint8_t *) pixels, info->frame->width * 4, info->frame->width, info->frame->height); + if (wantedWidth == info->frame->width && wantedHeight == info->frame->height || wantedWidth == info->frame->height && wantedHeight == info->frame->width) { + if (info->sws_ctx == nullptr) { + if (info->frame->format > AV_PIX_FMT_NONE && info->frame->format < AV_PIX_FMT_NB) { + info->sws_ctx = sws_getContext(info->frame->width, info->frame->height, (AVPixelFormat) info->frame->format, info->frame->width, info->frame->height, AV_PIX_FMT_RGBA, SWS_BILINEAR, NULL, NULL, NULL); + } else if (info->video_dec_ctx->pix_fmt > AV_PIX_FMT_NONE && info->video_dec_ctx->pix_fmt < AV_PIX_FMT_NB) { + info->sws_ctx = sws_getContext(info->video_dec_ctx->width, info->video_dec_ctx->height, info->video_dec_ctx->pix_fmt, info->video_dec_ctx->width, info->video_dec_ctx->height, AV_PIX_FMT_RGBA, SWS_BILINEAR, NULL, NULL, NULL); + } + } + if (info->sws_ctx == nullptr || ((intptr_t) pixels) % 16 != 0) { + if (info->frame->format == AV_PIX_FMT_YUV420P || info->frame->format == AV_PIX_FMT_YUVJ420P) { + if (info->frame->colorspace == AVColorSpace::AVCOL_SPC_BT709) { + libyuv::H420ToARGB(info->frame->data[0], info->frame->linesize[0], info->frame->data[2], info->frame->linesize[2], info->frame->data[1], info->frame->linesize[1], (uint8_t *) pixels, info->frame->width * 4, info->frame->width, info->frame->height); + } else { + libyuv::I420ToARGB(info->frame->data[0], info->frame->linesize[0], info->frame->data[2], info->frame->linesize[2], info->frame->data[1], info->frame->linesize[1], (uint8_t *) pixels, info->frame->width * 4, info->frame->width, info->frame->height); + } + } else if (info->frame->format == AV_PIX_FMT_BGRA) { + libyuv::ABGRToARGB(info->frame->data[0], info->frame->linesize[0], (uint8_t *) pixels, info->frame->width * 4, info->frame->width, info->frame->height); + } + } else { + info->dst_data[0] = (uint8_t *) pixels; + info->dst_linesize[0] = stride; + sws_scale(info->sws_ctx, info->frame->data, info->frame->linesize, 0, info->frame->height, info->dst_data, info->dst_linesize); } - } else if (info->frame->format == AV_PIX_FMT_BGRA) { - libyuv::ABGRToARGB(info->frame->data[0], info->frame->linesize[0], (uint8_t *) pixels, info->frame->width * 4, info->frame->width, info->frame->height); } AndroidBitmap_unlockPixels(env, bitmap); } } info->has_decoded_frames = true; av_frame_unref(info->frame); + + //LOGD("frame time %lld ms", ConnectionsManager::getInstance(0).getCurrentTimeMonotonicMillis() - time); + return 1; } - triesCount--; + if (!info->has_decoded_frames) { + triesCount--; + } } return 0; } + +jint videoOnJNILoad(JavaVM *vm, JNIEnv *env) { + jclass_AnimatedFileDrawableStream = (jclass) env->NewGlobalRef(env->FindClass("org/telegram/messenger/AnimatedFileDrawableStream")); + if (jclass_AnimatedFileDrawableStream == 0) { + return JNI_FALSE; + } + jclass_AnimatedFileDrawableStream_read = env->GetMethodID(jclass_AnimatedFileDrawableStream, "read", "(II)I"); + if (jclass_AnimatedFileDrawableStream_read == 0) { + return JNI_FALSE; + } + jclass_AnimatedFileDrawableStream_cancel = env->GetMethodID(jclass_AnimatedFileDrawableStream, "cancel", "()V"); + if (jclass_AnimatedFileDrawableStream_cancel == 0) { + return JNI_FALSE; + } + + return JNI_TRUE; +} } diff --git a/TMessagesProj/jni/image.c b/TMessagesProj/jni/image.c index 00618d5b7..5aa93f93c 100644 --- a/TMessagesProj/jni/image.c +++ b/TMessagesProj/jni/image.c @@ -7,7 +7,7 @@ #include #include #include -#include "utils.h" +#include "c_utils.h" #include "image.h" jclass jclass_NullPointerException; @@ -28,34 +28,34 @@ jclass createGlobarRef(JNIEnv *env, jclass class) { return 0; } -jint imageOnJNILoad(JavaVM *vm, void *reserved, JNIEnv *env) { +jint imageOnJNILoad(JavaVM *vm, JNIEnv *env) { jclass_NullPointerException = createGlobarRef(env, (*env)->FindClass(env, "java/lang/NullPointerException")); if (jclass_NullPointerException == 0) { - return -1; + return JNI_FALSE; } jclass_RuntimeException = createGlobarRef(env, (*env)->FindClass(env, "java/lang/RuntimeException")); if (jclass_RuntimeException == 0) { - return -1; + return JNI_FALSE; } jclass_Options = createGlobarRef(env, (*env)->FindClass(env, "android/graphics/BitmapFactory$Options")); if (jclass_Options == 0) { - return -1; + return JNI_FALSE; } jclass_Options_inJustDecodeBounds = (*env)->GetFieldID(env, jclass_Options, "inJustDecodeBounds", "Z"); if (jclass_Options_inJustDecodeBounds == 0) { - return -1; + return JNI_FALSE; } jclass_Options_outHeight = (*env)->GetFieldID(env, jclass_Options, "outHeight", "I"); if (jclass_Options_outHeight == 0) { - return -1; + return JNI_FALSE; } jclass_Options_outWidth = (*env)->GetFieldID(env, jclass_Options, "outWidth", "I"); if (jclass_Options_outWidth == 0) { - return -1; + return JNI_FALSE; } - return JNI_VERSION_1_6; + return JNI_TRUE; } static inline uint64_t getColors(const uint8_t *p) { @@ -545,7 +545,7 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_calcCDT(JNIEnv *env, jclass uint32_t totalSegments = PGPhotoEnhanceSegments * PGPhotoEnhanceSegments; uint32_t tileArea = (uint32_t) (floorf(imageWidth / PGPhotoEnhanceSegments) * floorf(imageHeight / PGPhotoEnhanceSegments)); - uint32_t clipLimit = (uint32_t) max(1, _clipLimit * tileArea / (float) PGPhotoEnhanceHistogramBins); + uint32_t clipLimit = (uint32_t) MAX(1, _clipLimit * tileArea / (float) PGPhotoEnhanceHistogramBins); float scale = 255.0f / (float) tileArea; unsigned char *bytes = (*env)->GetDirectBufferAddress(env, hsvBuffer); @@ -609,7 +609,7 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_calcCDT(JNIEnv *env, jclass uint32_t cdf = 0; for (uint32_t j = hMin; j < PGPhotoEnhanceHistogramBins; ++j) { cdf += cdfs[i][j]; - cdfs[i][j] = (uint8_t) min(255, cdf * scale); + cdfs[i][j] = (uint8_t) MIN(255, cdf * scale); } cdfsMin[i] = cdfs[i][hMin]; @@ -709,8 +709,6 @@ JNIEXPORT jboolean Java_org_telegram_messenger_Utilities_loadWebpImage(JNIEnv *e } #define SQUARE(i) ((i)*(i)) -#define MAX(a, b) (a>b ? a : b) -#define MIN(a, b) (a>b ? b : a) inline static void zeroClearInt(int* p, size_t count) { memset(p, 0, sizeof(int) * count); } JNIEXPORT void Java_org_telegram_messenger_Utilities_stackBlurBitmap(JNIEnv* env, jclass class, jobject bitmap, jint radius){ @@ -724,6 +722,7 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_stackBlurBitmap(JNIEnv* env int w=info.width; int h=info.height; + int stride=info.stride; unsigned char* pixels=0; AndroidBitmap_lockPixels(env, bitmap, (void **) &pixels); @@ -777,7 +776,7 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_stackBlurBitmap(JNIEnv* env for(i = -radius;i <= radius;i++){ sir = &stack[(i + radius)*3]; - int offset = (yi + MIN(wm, MAX(i, 0)))*4; + int offset = (y*stride + (MIN(wm, MAX(i, 0)))*4); sir[0] = pixels[offset]; sir[1] = pixels[offset + 1]; sir[2] = pixels[offset + 2]; @@ -818,7 +817,7 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_stackBlurBitmap(JNIEnv* env vmin[x] = MIN(x + radius + 1, wm); } - int offset = (yw + vmin[x])*4; + int offset = (y*stride + vmin[x]*4); sir[0] = pixels[offset]; sir[1] = pixels[offset + 1]; sir[2] = pixels[offset + 2]; @@ -878,10 +877,9 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_stackBlurBitmap(JNIEnv* env yp += w; } } - yi = x; stackpointer = radius; for (y = 0;y < h;y++) { - int offset = yi*4; + int offset = stride*y+x*4; pixels[offset] = dv[rsum]; pixels[offset + 1] = dv[gsum]; pixels[offset + 2] = dv[bsum]; @@ -897,7 +895,7 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_stackBlurBitmap(JNIEnv* env boutsum -= sir[2]; if (x == 0){ - vmin[y] = MIN(y + r1, hm)*w; + vmin[y] = (MIN(y + r1, hm))*w; } p = x + vmin[y]; diff --git a/TMessagesProj/jni/image.h b/TMessagesProj/jni/image.h index 58dd5385b..ebe736c32 100644 --- a/TMessagesProj/jni/image.h +++ b/TMessagesProj/jni/image.h @@ -3,6 +3,6 @@ #include -jint imageOnJNILoad(JavaVM *vm, void *reserved, JNIEnv *env); +jint imageOnJNILoad(JavaVM *vm, JNIEnv *env); #endif diff --git a/TMessagesProj/jni/jni.c b/TMessagesProj/jni/jni.c index d84d2fe9f..748ff2873 100644 --- a/TMessagesProj/jni/jni.c +++ b/TMessagesProj/jni/jni.c @@ -13,6 +13,7 @@ #include "libtgvoip/client/android/tg_voip_jni.h" int registerNativeTgNetFunctions(JavaVM *vm, JNIEnv *env); +int videoOnJNILoad(JavaVM *vm, JNIEnv *env); jint JNI_OnLoad(JavaVM *vm, void *reserved) { JNIEnv *env = 0; @@ -22,7 +23,11 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) { return -1; } - if (imageOnJNILoad(vm, reserved, env) == -1) { + if (imageOnJNILoad(vm, env) != JNI_TRUE) { + return -1; + } + + if (videoOnJNILoad(vm, env) != JNI_TRUE) { return -1; } @@ -163,19 +168,6 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_aesCbcEncryption(JNIEnv *en (*env)->ReleaseByteArrayElements(env, iv, ivBuff, JNI_ABORT); } -JNIEXPORT jstring Java_org_telegram_messenger_Utilities_readlink(JNIEnv *env, jclass class, jstring path) { - static char buf[PATH_MAX + 1]; - const char *fileName = (*env)->GetStringUTFChars(env, path, NULL); - ssize_t result = readlink(fileName, buf, PATH_MAX); - jstring value = 0; - if (result != -1) { - buf[result] = '\0'; - value = (*env)->NewStringUTF(env, buf); - } - (*env)->ReleaseStringUTFChars(env, path, fileName); - return value; -} - int64_t listdir(const char *fileName, int32_t mode, int32_t docType, int64_t time) { int64_t value = 0; DIR *dir; @@ -209,7 +201,7 @@ int64_t listdir(const char *fileName, int32_t mode, int32_t docType, int64_t tim } else { stat(buff, &attrib); if (mode == 0) { - value += attrib.st_size; + value += 512 * attrib.st_blocks; } else if (mode == 1) { if (attrib.st_atim.tv_sec != 0) { if (attrib.st_atim.tv_sec < time) { diff --git a/TMessagesProj/jni/libtgvoip b/TMessagesProj/jni/libtgvoip index fc13464b3..ce74c9216 160000 --- a/TMessagesProj/jni/libtgvoip +++ b/TMessagesProj/jni/libtgvoip @@ -1 +1 @@ -Subproject commit fc13464b35aeb373844bafc7a9a33b18ca7afa3b +Subproject commit ce74c9216f599874571061f39c2dc31632b3004b diff --git a/TMessagesProj/jni/sqlite/sqlite3.c b/TMessagesProj/jni/sqlite/sqlite3.c index 92ff396ab..0ceb49a45 100644 --- a/TMessagesProj/jni/sqlite/sqlite3.c +++ b/TMessagesProj/jni/sqlite/sqlite3.c @@ -1,6 +1,6 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.26.0. By combining all the individual C code files into this +** version 3.27.2. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements @@ -1162,9 +1162,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.26.0" -#define SQLITE_VERSION_NUMBER 3026000 -#define SQLITE_SOURCE_ID "2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238b4f9" +#define SQLITE_VERSION "3.27.2" +#define SQLITE_VERSION_NUMBER 3027002 +#define SQLITE_SOURCE_ID "2019-02-25 16:06:06 bd49a8271d650fa89e446b42e513b595a717b9212c91dd384aab871fc1d0f6d7" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -1862,6 +1862,15 @@ struct sqlite3_io_methods { ** file space based on this hint in order to help writes to the database ** file run faster. ** +**
  • [[SQLITE_FCNTL_SIZE_LIMIT]] +** The [SQLITE_FCNTL_SIZE_LIMIT] opcode is used by in-memory VFS that +** implements [sqlite3_deserialize()] to set an upper bound on the size +** of the in-memory database. The argument is a pointer to a [sqlite3_int64]. +** If the integer pointed to is negative, then it is filled in with the +** current limit. Otherwise the limit is set to the larger of the value +** of the integer pointed to and the current database size. The integer +** pointed to is set to the new limit. +** **
  • [[SQLITE_FCNTL_CHUNK_SIZE]] ** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS ** extends and truncates the database file in chunks of a size specified @@ -2170,6 +2179,7 @@ struct sqlite3_io_methods { #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33 #define SQLITE_FCNTL_LOCK_TIMEOUT 34 #define SQLITE_FCNTL_DATA_VERSION 35 +#define SQLITE_FCNTL_SIZE_LIMIT 36 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE @@ -3011,6 +3021,17 @@ struct sqlite3_mem_methods { ** negative value for this option restores the default behaviour. ** This option is only available if SQLite is compiled with the ** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option. +** +** [[SQLITE_CONFIG_MEMDB_MAXSIZE]] +**
    SQLITE_CONFIG_MEMDB_MAXSIZE +**
    The SQLITE_CONFIG_MEMDB_MAXSIZE option accepts a single parameter +** [sqlite3_int64] parameter which is the default maximum size for an in-memory +** database created using [sqlite3_deserialize()]. This default maximum +** size can be adjusted up or down for individual databases using the +** [SQLITE_FCNTL_SIZE_LIMIT] [sqlite3_file_control|file-control]. If this +** configuration setting is never used, then the default maximum is determined +** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that +** compile-time option is not set, then the default maximum is 1073741824. ** */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ @@ -3041,6 +3062,7 @@ struct sqlite3_mem_methods { #define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ +#define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */ /* ** CAPI3REF: Database Connection Configuration Options @@ -3386,7 +3408,7 @@ SQLITE_API int sqlite3_changes(sqlite3*); ** not. ^Changes to a view that are intercepted by INSTEAD OF triggers ** are not counted. ** -** This the [sqlite3_total_changes(D)] interface only reports the number +** The [sqlite3_total_changes(D)] interface only reports the number ** of rows that changed due to SQL statement run against database ** connection D. Any changes by other database connections are ignored. ** To detect changes against a database file from other database @@ -4030,9 +4052,9 @@ SQLITE_API int sqlite3_set_authorizer( ** time is in units of nanoseconds, however the current implementation ** is only capable of millisecond resolution so the six least significant ** digits in the time are meaningless. Future versions of SQLite -** might provide greater resolution on the profiler callback. The -** sqlite3_profile() function is considered experimental and is -** subject to change in future versions of SQLite. +** might provide greater resolution on the profiler callback. Invoking +** either [sqlite3_trace()] or [sqlite3_trace_v2()] will cancel the +** profile callback. */ SQLITE_API SQLITE_DEPRECATED void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); @@ -4446,6 +4468,8 @@ SQLITE_API int sqlite3_open_v2( ** is not a database file pathname pointer that SQLite passed into the xOpen ** VFS method, then the behavior of this routine is undefined and probably ** undesirable. +** +** See the [URI filename] documentation for additional information. */ SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); @@ -4668,18 +4692,23 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** deplete the limited store of lookaside memory. Future versions of ** SQLite may act on this hint differently. ** -** [[SQLITE_PREPARE_NORMALIZE]] ^(
    SQLITE_PREPARE_NORMALIZE
    -**
    The SQLITE_PREPARE_NORMALIZE flag indicates that a normalized -** representation of the SQL statement should be calculated and then -** associated with the prepared statement, which can be obtained via -** the [sqlite3_normalized_sql()] interface.)^ The semantics used to -** normalize a SQL statement are unspecified and subject to change. -** At a minimum, literal values will be replaced with suitable -** placeholders. +** [[SQLITE_PREPARE_NORMALIZE]]
    SQLITE_PREPARE_NORMALIZE
    +**
    The SQLITE_PREPARE_NORMALIZE flag is a no-op. This flag used +** to be required for any prepared statement that wanted to use the +** [sqlite3_normalized_sql()] interface. However, the +** [sqlite3_normalized_sql()] interface is now available to all +** prepared statements, regardless of whether or not they use this +** flag. +** +** [[SQLITE_PREPARE_NO_VTAB]]
    SQLITE_PREPARE_NO_VTAB
    +**
    The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler +** to return an error (error code SQLITE_ERROR) if the statement uses +** any virtual tables. ** */ #define SQLITE_PREPARE_PERSISTENT 0x01 #define SQLITE_PREPARE_NORMALIZE 0x02 +#define SQLITE_PREPARE_NO_VTAB 0x04 /* ** CAPI3REF: Compiling An SQL Statement @@ -11035,7 +11064,7 @@ SQLITE_API int sqlite3changeset_next(sqlite3_changeset_iter *pIter); ** sqlite3changeset_next() is called on the iterator or until the ** conflict-handler function returns. If pnCol is not NULL, then *pnCol is ** set to the number of columns in the table affected by the change. If -** pbIncorrect is not NULL, then *pbIndirect is set to true (1) if the change +** pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change ** is an indirect change, or false (0) otherwise. See the documentation for ** [sqlite3session_indirect()] for a description of direct and indirect ** changes. Finally, if pOp is not NULL, then *pOp is set to one of @@ -12269,12 +12298,8 @@ struct Fts5PhraseIter { ** ** Usually, output parameter *piPhrase is set to the phrase number, *piCol ** to the column in which it occurs and *piOff the token offset of the -** first token of the phrase. The exception is if the table was created -** with the offsets=0 option specified. In this case *piOff is always -** set to -1. -** -** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM) -** if an error occurs. +** first token of the phrase. Returns SQLITE_OK if successful, or an error +** code (i.e. SQLITE_NOMEM) if an error occurs. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. @@ -12563,11 +12588,11 @@ struct Fts5ExtensionApi { ** the tokenizer substitutes "first" for "1st" and the query works ** as expected. ** -**
  • By adding multiple synonyms for a single term to the FTS index. -** In this case, when tokenizing query text, the tokenizer may -** provide multiple synonyms for a single term within the document. -** FTS5 then queries the index for each synonym individually. For -** example, faced with the query: +**
  • By querying the index for all synonyms of each query term +** separately. In this case, when tokenizing query text, the +** tokenizer may provide multiple synonyms for a single term +** within the document. FTS5 then queries the index for each +** synonym individually. For example, faced with the query: ** ** ** ... MATCH 'first place' @@ -12591,7 +12616,7 @@ struct Fts5ExtensionApi { ** "place". ** ** This way, even if the tokenizer does not provide synonyms -** when tokenizing query text (it should not - to do would be +** when tokenizing query text (it should not - to do so would be ** inefficient), it doesn't matter if the user queries for ** 'first + place' or '1st + place', as there are entries in the ** FTS index corresponding to both forms of the first token. @@ -14535,6 +14560,7 @@ SQLITE_PRIVATE i64 sqlite3BtreeOffset(BtCursor*); SQLITE_PRIVATE int sqlite3BtreePayload(BtCursor*, u32 offset, u32 amt, void*); SQLITE_PRIVATE const void *sqlite3BtreePayloadFetch(BtCursor*, u32 *pAmt); SQLITE_PRIVATE u32 sqlite3BtreePayloadSize(BtCursor*); +SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeMaxRecordSize(BtCursor*); SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*); SQLITE_PRIVATE struct Pager *sqlite3BtreePager(Btree*); @@ -14774,12 +14800,11 @@ typedef struct VdbeOpList VdbeOpList; #endif /* -** The following macro converts a relative address in the p2 field -** of a VdbeOp structure into a negative number so that -** sqlite3VdbeAddOpList() knows that the address is relative. Calling -** the macro again restores the address. +** The following macro converts a label returned by sqlite3VdbeMakeLabel() +** into an index into the Parse.aLabel[] array that contains the resolved +** address of that label. */ -#define ADDR(X) (-1-(X)) +#define ADDR(X) (~(X)) /* ** The makefile scans the vdbe.c source file and creates the "opcodes.h" @@ -14912,57 +14937,56 @@ typedef struct VdbeOpList VdbeOpList; #define OP_Sequence 120 /* synopsis: r[P2]=cursor[P1].ctr++ */ #define OP_NewRowid 121 /* synopsis: r[P2]=rowid */ #define OP_Insert 122 /* synopsis: intkey=r[P3] data=r[P2] */ -#define OP_InsertInt 123 /* synopsis: intkey=P3 data=r[P2] */ -#define OP_Delete 124 -#define OP_ResetCount 125 -#define OP_SorterCompare 126 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */ -#define OP_SorterData 127 /* synopsis: r[P2]=data */ -#define OP_RowData 128 /* synopsis: r[P2]=data */ -#define OP_Rowid 129 /* synopsis: r[P2]=rowid */ -#define OP_NullRow 130 -#define OP_SeekEnd 131 -#define OP_SorterInsert 132 /* synopsis: key=r[P2] */ -#define OP_IdxInsert 133 /* synopsis: key=r[P2] */ -#define OP_IdxDelete 134 /* synopsis: key=r[P2@P3] */ -#define OP_DeferredSeek 135 /* synopsis: Move P3 to P1.rowid if needed */ -#define OP_IdxRowid 136 /* synopsis: r[P2]=rowid */ -#define OP_Destroy 137 -#define OP_Clear 138 -#define OP_ResetSorter 139 -#define OP_CreateBtree 140 /* synopsis: r[P2]=root iDb=P1 flags=P3 */ +#define OP_Delete 123 +#define OP_ResetCount 124 +#define OP_SorterCompare 125 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */ +#define OP_SorterData 126 /* synopsis: r[P2]=data */ +#define OP_RowData 127 /* synopsis: r[P2]=data */ +#define OP_Rowid 128 /* synopsis: r[P2]=rowid */ +#define OP_NullRow 129 +#define OP_SeekEnd 130 +#define OP_SorterInsert 131 /* synopsis: key=r[P2] */ +#define OP_IdxInsert 132 /* synopsis: key=r[P2] */ +#define OP_IdxDelete 133 /* synopsis: key=r[P2@P3] */ +#define OP_DeferredSeek 134 /* synopsis: Move P3 to P1.rowid if needed */ +#define OP_IdxRowid 135 /* synopsis: r[P2]=rowid */ +#define OP_Destroy 136 +#define OP_Clear 137 +#define OP_ResetSorter 138 +#define OP_CreateBtree 139 /* synopsis: r[P2]=root iDb=P1 flags=P3 */ +#define OP_SqlExec 140 #define OP_Real 141 /* same as TK_FLOAT, synopsis: r[P2]=P4 */ -#define OP_SqlExec 142 -#define OP_ParseSchema 143 -#define OP_LoadAnalysis 144 -#define OP_DropTable 145 -#define OP_DropIndex 146 -#define OP_DropTrigger 147 -#define OP_IntegrityCk 148 -#define OP_RowSetAdd 149 /* synopsis: rowset(P1)=r[P2] */ -#define OP_Param 150 -#define OP_FkCounter 151 /* synopsis: fkctr[P1]+=P2 */ -#define OP_MemMax 152 /* synopsis: r[P1]=max(r[P1],r[P2]) */ -#define OP_OffsetLimit 153 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */ -#define OP_AggInverse 154 /* synopsis: accum=r[P3] inverse(r[P2@P5]) */ -#define OP_AggStep 155 /* synopsis: accum=r[P3] step(r[P2@P5]) */ -#define OP_AggStep1 156 /* synopsis: accum=r[P3] step(r[P2@P5]) */ -#define OP_AggValue 157 /* synopsis: r[P3]=value N=P2 */ -#define OP_AggFinal 158 /* synopsis: accum=r[P1] N=P2 */ -#define OP_Expire 159 -#define OP_TableLock 160 /* synopsis: iDb=P1 root=P2 write=P3 */ -#define OP_VBegin 161 -#define OP_VCreate 162 -#define OP_VDestroy 163 -#define OP_VOpen 164 -#define OP_VColumn 165 /* synopsis: r[P3]=vcolumn(P2) */ -#define OP_VRename 166 -#define OP_Pagecount 167 -#define OP_MaxPgcnt 168 -#define OP_Trace 169 -#define OP_CursorHint 170 -#define OP_Noop 171 -#define OP_Explain 172 -#define OP_Abortable 173 +#define OP_ParseSchema 142 +#define OP_LoadAnalysis 143 +#define OP_DropTable 144 +#define OP_DropIndex 145 +#define OP_DropTrigger 146 +#define OP_IntegrityCk 147 +#define OP_RowSetAdd 148 /* synopsis: rowset(P1)=r[P2] */ +#define OP_Param 149 +#define OP_FkCounter 150 /* synopsis: fkctr[P1]+=P2 */ +#define OP_MemMax 151 /* synopsis: r[P1]=max(r[P1],r[P2]) */ +#define OP_OffsetLimit 152 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */ +#define OP_AggInverse 153 /* synopsis: accum=r[P3] inverse(r[P2@P5]) */ +#define OP_AggStep 154 /* synopsis: accum=r[P3] step(r[P2@P5]) */ +#define OP_AggStep1 155 /* synopsis: accum=r[P3] step(r[P2@P5]) */ +#define OP_AggValue 156 /* synopsis: r[P3]=value N=P2 */ +#define OP_AggFinal 157 /* synopsis: accum=r[P1] N=P2 */ +#define OP_Expire 158 +#define OP_TableLock 159 /* synopsis: iDb=P1 root=P2 write=P3 */ +#define OP_VBegin 160 +#define OP_VCreate 161 +#define OP_VDestroy 162 +#define OP_VOpen 163 +#define OP_VColumn 164 /* synopsis: r[P3]=vcolumn(P2) */ +#define OP_VRename 165 +#define OP_Pagecount 166 +#define OP_MaxPgcnt 167 +#define OP_Trace 168 +#define OP_CursorHint 169 +#define OP_Noop 170 +#define OP_Explain 171 +#define OP_Abortable 172 /* Properties such as "out2" or "jump" that are specified in ** comments following the "case" for each opcode in the vdbe.c @@ -14991,12 +15015,12 @@ typedef struct VdbeOpList VdbeOpList; /* 104 */ 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,\ /* 112 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ /* 120 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 128 */ 0x00, 0x10, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00,\ -/* 136 */ 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00,\ -/* 144 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x00,\ -/* 152 */ 0x04, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ -/* 160 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,\ -/* 168 */ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,} +/* 128 */ 0x10, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x10,\ +/* 136 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00,\ +/* 144 */ 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x00, 0x04,\ +/* 152 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 160 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10,\ +/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00,} /* The sqlite3P2Values() routine is able to run faster if it knows ** the value of the largest JUMP opcode. The smaller the maximum @@ -15055,6 +15079,12 @@ SQLITE_PRIVATE int sqlite3VdbeExplainParent(Parse*); # define ExplainQueryPlan(P) # define ExplainQueryPlanPop(P) # define ExplainQueryPlanParent(P) 0 +# define sqlite3ExplainBreakpoint(A,B) /*no-op*/ +#endif +#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_EXPLAIN) +SQLITE_PRIVATE void sqlite3ExplainBreakpoint(const char*,const char*); +#else +# define sqlite3ExplainBreakpoint(A,B) /*no-op*/ #endif SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*); SQLITE_PRIVATE void sqlite3VdbeChangeOpcode(Vdbe*, u32 addr, u8); @@ -15070,7 +15100,7 @@ SQLITE_PRIVATE void sqlite3VdbeAppendP4(Vdbe*, void *pP4, int p4type); SQLITE_PRIVATE void sqlite3VdbeSetP4KeyInfo(Parse*, Index*); SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe*, int); SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe*, int); -SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe*); +SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Parse*); SQLITE_PRIVATE void sqlite3VdbeRunOnlyOnce(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeReusable(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe*); @@ -15091,6 +15121,10 @@ SQLITE_PRIVATE void sqlite3VdbeCountChanges(Vdbe*); SQLITE_PRIVATE sqlite3 *sqlite3VdbeDb(Vdbe*); SQLITE_PRIVATE u8 sqlite3VdbePrepareFlags(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, u8); +#ifdef SQLITE_ENABLE_NORMALIZE +SQLITE_PRIVATE void sqlite3VdbeAddDblquoteStr(sqlite3*,Vdbe*,const char*); +SQLITE_PRIVATE int sqlite3VdbeUsesDoubleQuotedString(Vdbe*,const char*); +#endif SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe*,Vdbe*); SQLITE_PRIVATE VdbeOp *sqlite3VdbeTakeOpArray(Vdbe*, int*, int*); SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetBoundValue(Vdbe*, int, u8); @@ -16216,10 +16250,13 @@ SQLITE_PRIVATE void sqlite3CryptFunc(sqlite3_context*,int,sqlite3_value**); /* This is an extra SQLITE_TRACE macro that indicates "legacy" tracing ** in the style of sqlite3_trace() */ -#define SQLITE_TRACE_LEGACY 0x80 +#define SQLITE_TRACE_LEGACY 0x40 /* Use the legacy xTrace */ +#define SQLITE_TRACE_XPROFILE 0x80 /* Use the legacy xProfile */ #else -#define SQLITE_TRACE_LEGACY 0 +#define SQLITE_TRACE_LEGACY 0 +#define SQLITE_TRACE_XPROFILE 0 #endif /* SQLITE_OMIT_DEPRECATED */ +#define SQLITE_TRACE_NONLEGACY_MASK 0x0f /* Normal flags */ /* @@ -16278,8 +16315,10 @@ struct sqlite3 { void **aExtension; /* Array of shared library handles */ int (*xTrace)(u32,void*,void*,void*); /* Trace function */ void *pTraceArg; /* Argument to the trace function */ +#ifndef SQLITE_OMIT_DEPRECATED void (*xProfile)(void*,const char*,u64); /* Profiling function */ void *pProfileArg; /* Argument to profile function */ +#endif void *pCommitArg; /* Argument to xCommitCallback() */ int (*xCommitCallback)(void*); /* Invoked at every commit. */ void *pRollbackArg; /* Argument to xRollbackCallback() */ @@ -16410,6 +16449,7 @@ struct sqlite3 { #define SQLITE_VdbeTrace HI(0x0004) /* True to trace VDBE execution */ #define SQLITE_VdbeAddopTrace HI(0x0008) /* Trace sqlite3VdbeAddOp() calls */ #define SQLITE_VdbeEQP HI(0x0010) /* Debug EXPLAIN QUERY PLAN */ +#define SQLITE_ParserTrace HI(0x0020) /* PRAGMA parser_trace=ON */ #endif /* @@ -16812,9 +16852,6 @@ struct VTable { struct Table { char *zName; /* Name of the table or view */ Column *aCol; /* Information about each column */ -#ifdef SQLITE_ENABLE_NORMALIZE - Hash *pColHash; /* All columns indexed by name */ -#endif Index *pIndex; /* List of SQL indexes on this table. */ Select *pSelect; /* NULL for tables. Points to definition if a view. */ FKey *pFKey; /* Linked list of all foreign keys in this table */ @@ -17101,7 +17138,7 @@ struct Index { u16 nKeyCol; /* Number of columns forming the key */ u16 nColumn; /* Number of columns stored in the index */ u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ - unsigned idxType:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */ + unsigned idxType:2; /* 0:Normal 1:UNIQUE, 2:PRIMARY KEY, 3:IPK */ unsigned bUnordered:1; /* Use this index for == or IN queries only */ unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */ unsigned isResized:1; /* True if resizeIndexObject() has been called */ @@ -17126,6 +17163,7 @@ struct Index { #define SQLITE_IDXTYPE_APPDEF 0 /* Created using CREATE INDEX */ #define SQLITE_IDXTYPE_UNIQUE 1 /* Implements a UNIQUE constraint */ #define SQLITE_IDXTYPE_PRIMARYKEY 2 /* Is the PRIMARY KEY for the table */ +#define SQLITE_IDXTYPE_IPK 3 /* INTEGER PRIMARY KEY index */ /* Return true if index X is a PRIMARY KEY index */ #define IsPrimaryKeyIndex(X) ((X)->idxType==SQLITE_IDXTYPE_PRIMARYKEY) @@ -17343,6 +17381,10 @@ struct Expr { Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL ** for a column of an index on an expression */ Window *pWin; /* TK_FUNCTION: Window definition for the func */ + struct { /* TK_IN, TK_SELECT, and TK_EXISTS */ + int iAddr; /* Subroutine entry address */ + int regReturn; /* Register used to hold return address */ + } sub; } y; }; @@ -17374,6 +17416,8 @@ struct Expr { #define EP_Alias 0x400000 /* Is an alias for a result set column */ #define EP_Leaf 0x800000 /* Expr.pLeft, .pRight, .u.pSelect all NULL */ #define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */ +#define EP_Subrtn 0x2000000 /* Uses Expr.y.sub. TK_IN, _SELECT, or _EXISTS */ +#define EP_Quoted 0x4000000 /* TK_ID was originally quoted */ /* ** The EP_Propagate mask is a set of properties that automatically propagate @@ -17917,16 +17961,17 @@ struct Parse { u8 hasCompound; /* Need to invoke convertCompoundSelectToSubquery() */ u8 okConstFactor; /* OK to factor out constants */ u8 disableLookaside; /* Number of times lookaside has been disabled */ + u8 disableVtab; /* Disable all virtual tables for this parse */ int nRangeReg; /* Size of the temporary register block */ int iRangeReg; /* First register in temporary register block */ int nErr; /* Number of errors seen */ int nTab; /* Number of previously allocated VDBE cursors */ int nMem; /* Number of memory cells used so far */ - int nOpAlloc; /* Number of slots allocated for Vdbe.aOp[] */ int szOpAlloc; /* Bytes of memory space allocated for Vdbe.aOp[] */ int iSelfTab; /* Table associated with an index on expr, or negative ** of the base register during check-constraint eval */ - int nLabel; /* Number of labels used */ + int nLabel; /* The *negative* of the number of labels used */ + int nLabelAlloc; /* Number of slots in aLabel */ int *aLabel; /* Space to hold the labels */ ExprList *pConstExpr;/* Constant expressions */ Token constraintName;/* Name of the constraint currently being parsed */ @@ -17986,7 +18031,9 @@ struct Parse { Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */ const char *zTail; /* All SQL text past the last semicolon parsed */ Table *pNewTable; /* A table being constructed by CREATE TABLE */ - Index *pNewIndex; /* An index being constructed by CREATE INDEX */ + Index *pNewIndex; /* An index being constructed by CREATE INDEX. + ** Also used to hold redundant UNIQUE constraints + ** during a RENAME COLUMN */ Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */ const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */ #ifndef SQLITE_OMIT_VIRTUALTABLE @@ -18214,6 +18261,7 @@ typedef struct { int iDb; /* 0 for main database. 1 for TEMP, 2.. for ATTACHed */ int rc; /* Result code stored here */ u32 mInitFlags; /* Flags controlling error messages */ + u32 nInitRow; /* Number of rows processed */ } InitData; /* @@ -18274,6 +18322,9 @@ struct Sqlite3Config { void (*xVdbeBranch)(void*,unsigned iSrcLine,u8 eThis,u8 eMx); /* Callback */ void *pVdbeBranchArg; /* 1st argument */ #endif +#ifdef SQLITE_ENABLE_DESERIALIZE + sqlite3_int64 mxMemdbSize; /* Default max memdb size */ +#endif #ifndef SQLITE_UNTESTABLE int (*xTestCallback)(int); /* Invoked by sqlite3FaultSim() */ #endif @@ -18662,6 +18713,7 @@ SQLITE_PRIVATE void sqlite3TreeViewWinFunc(TreeView*, const Window*, u8); SQLITE_PRIVATE void sqlite3SetString(char **, sqlite3*, const char*); SQLITE_PRIVATE void sqlite3ErrorMsg(Parse*, const char*, ...); SQLITE_PRIVATE void sqlite3Dequote(char*); +SQLITE_PRIVATE void sqlite3DequoteExpr(Expr*); SQLITE_PRIVATE void sqlite3TokenInit(Token*,char*); SQLITE_PRIVATE int sqlite3KeywordCode(const unsigned char*, int); SQLITE_PRIVATE int sqlite3RunParser(Parse*, const char*, char **); @@ -18690,6 +18742,7 @@ SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,Token*,int); SQLITE_PRIVATE void sqlite3ExprListSetSpan(Parse*,ExprList*,const char*,const char*); SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3*, ExprList*); SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList*); +SQLITE_PRIVATE int sqlite3IndexHasDuplicateRootPage(Index*); SQLITE_PRIVATE int sqlite3Init(sqlite3*, char**); SQLITE_PRIVATE int sqlite3InitCallback(void*, int, char**, char**); SQLITE_PRIVATE int sqlite3InitOne(sqlite3*, int, char**, u32); @@ -18723,6 +18776,11 @@ SQLITE_PRIVATE void sqlite3AddCollateType(Parse*, Token*); SQLITE_PRIVATE void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*); SQLITE_PRIVATE int sqlite3ParseUri(const char*,const char*,unsigned int*, sqlite3_vfs**,char**,char **); +#ifdef SQLITE_HAS_CODEC +SQLITE_PRIVATE int sqlite3CodecQueryParameters(sqlite3*,const char*,const char*); +#else +# define sqlite3CodecQueryParameters(A,B,C) 0 +#endif SQLITE_PRIVATE Btree *sqlite3DbNameToBtree(sqlite3*,const char*); #ifdef SQLITE_UNTESTABLE @@ -18775,8 +18833,8 @@ SQLITE_PRIVATE void sqlite3Insert(Parse*, SrcList*, Select*, IdList*, int, Upser SQLITE_PRIVATE void *sqlite3ArrayAllocate(sqlite3*,void*,int,int*,int*); SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse*, IdList*, Token*); SQLITE_PRIVATE int sqlite3IdListIndex(IdList*,const char*); -SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(sqlite3*, SrcList*, int, int); -SQLITE_PRIVATE SrcList *sqlite3SrcListAppend(sqlite3*, SrcList*, Token*, Token*); +SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(Parse*, SrcList*, int, int); +SQLITE_PRIVATE SrcList *sqlite3SrcListAppend(Parse*, SrcList*, Token*, Token*); SQLITE_PRIVATE SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*, Token*, Select*, Expr*, IdList*); SQLITE_PRIVATE void sqlite3SrcListIndexedBy(Parse *, SrcList *, Token *); @@ -18843,8 +18901,8 @@ SQLITE_PRIVATE Table *sqlite3LocateTableItem(Parse*,u32 flags,struct SrcList_ite SQLITE_PRIVATE Index *sqlite3FindIndex(sqlite3*,const char*, const char*); SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*); SQLITE_PRIVATE void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*); -SQLITE_PRIVATE void sqlite3Vacuum(Parse*,Token*); -SQLITE_PRIVATE int sqlite3RunVacuum(char**, sqlite3*, int); +SQLITE_PRIVATE void sqlite3Vacuum(Parse*,Token*,Expr*); +SQLITE_PRIVATE int sqlite3RunVacuum(char**, sqlite3*, int, sqlite3_value*); SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3*, Token*); SQLITE_PRIVATE int sqlite3ExprCompare(Parse*,Expr*, Expr*, int); SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr*, Expr*, int); @@ -18882,9 +18940,6 @@ SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr*, int*); SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*); SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char); SQLITE_PRIVATE int sqlite3IsRowid(const char*); -#ifdef SQLITE_ENABLE_NORMALIZE -SQLITE_PRIVATE int sqlite3IsRowidN(const char*, int); -#endif SQLITE_PRIVATE void sqlite3GenerateRowDelete( Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8,int); SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int); @@ -18911,9 +18966,7 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3*,ExprList*,int); SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int); SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3*,IdList*); SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3*,Select*,int); -#ifdef SQLITE_ENABLE_NORMALIZE -SQLITE_PRIVATE FuncDef *sqlite3FunctionSearchN(int,const char*,int); -#endif +SQLITE_PRIVATE FuncDef *sqlite3FunctionSearch(int,const char*); SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs(FuncDef*,int); SQLITE_PRIVATE FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,u8,u8); SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void); @@ -19118,19 +19171,17 @@ SQLITE_PRIVATE void sqlite3AlterFunctions(void); SQLITE_PRIVATE void sqlite3AlterRenameTable(Parse*, SrcList*, Token*); SQLITE_PRIVATE void sqlite3AlterRenameColumn(Parse*, SrcList*, Token*, Token*); SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *); -#ifdef SQLITE_ENABLE_NORMALIZE -SQLITE_PRIVATE int sqlite3GetTokenNormalized(const unsigned char *, int *, int *); -#endif SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...); SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*, int); -SQLITE_PRIVATE int sqlite3CodeSubselect(Parse*, Expr *, int, int); +SQLITE_PRIVATE void sqlite3CodeRhsOfIN(Parse*, Expr*, int); +SQLITE_PRIVATE int sqlite3CodeSubselect(Parse*, Expr*); SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*); SQLITE_PRIVATE void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p); SQLITE_PRIVATE int sqlite3MatchSpanName(const char*, const char*, const char*, const char*); SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*); SQLITE_PRIVATE int sqlite3ResolveExprListNames(NameContext*, ExprList*); SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*); -SQLITE_PRIVATE void sqlite3ResolveSelfReference(Parse*,Table*,int,Expr*,ExprList*); +SQLITE_PRIVATE int sqlite3ResolveSelfReference(Parse*,Table*,int,Expr*,ExprList*); SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*); SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int, int); SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *); @@ -19279,7 +19330,7 @@ SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe*, const char*, int); SQLITE_PRIVATE int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *); SQLITE_PRIVATE void sqlite3ParserReset(Parse*); #ifdef SQLITE_ENABLE_NORMALIZE -SQLITE_PRIVATE void sqlite3Normalize(Vdbe*, const char*, int, u8); +SQLITE_PRIVATE char *sqlite3Normalize(Vdbe*, const char*); #endif SQLITE_PRIVATE int sqlite3Reprepare(Vdbe*); SQLITE_PRIVATE void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*); @@ -19375,7 +19426,7 @@ SQLITE_PRIVATE void sqlite3EndBenignMalloc(void); #define IN_INDEX_NOOP_OK 0x0001 /* OK to return IN_INDEX_NOOP */ #define IN_INDEX_MEMBERSHIP 0x0002 /* IN operator used for membership test */ #define IN_INDEX_LOOP 0x0004 /* IN operator used as a loop */ -SQLITE_PRIVATE int sqlite3FindInIndex(Parse *, Expr *, u32, int*, int*); +SQLITE_PRIVATE int sqlite3FindInIndex(Parse *, Expr *, u32, int*, int*, int*); SQLITE_PRIVATE int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int); SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *); @@ -19691,6 +19742,13 @@ SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[256] = { #endif +/* The default maximum size of an in-memory database created using +** sqlite3_deserialize() +*/ +#ifndef SQLITE_MEMDB_DEFAULT_MAXSIZE +# define SQLITE_MEMDB_DEFAULT_MAXSIZE 1073741824 +#endif + /* ** The following singleton contains the global configuration for ** the SQLite library. @@ -19738,13 +19796,16 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = { 0, /* xVdbeBranch */ 0, /* pVbeBranchArg */ #endif +#ifdef SQLITE_ENABLE_DESERIALIZE + SQLITE_MEMDB_DEFAULT_MAXSIZE, /* mxMemdbSize */ +#endif #ifndef SQLITE_UNTESTABLE 0, /* xTestCallback */ #endif 0, /* bLocaltimeFault */ 0, /* bInternalFunctions */ 0x7ffffffe, /* iOnceResetThreshold */ - SQLITE_DEFAULT_SORTERREF_SIZE /* szSorterRef */ + SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */ }; /* @@ -20163,6 +20224,9 @@ struct sqlite3_context { */ typedef unsigned bft; /* Bit Field Type */ +/* The ScanStatus object holds a single value for the +** sqlite3_stmt_scanstatus() interface. +*/ typedef struct ScanStatus ScanStatus; struct ScanStatus { int addrExplain; /* OP_Explain for loop */ @@ -20173,6 +20237,19 @@ struct ScanStatus { char *zName; /* Name of table or index */ }; +/* The DblquoteStr object holds the text of a double-quoted +** string for a prepared statement. A linked list of these objects +** is constructed during statement parsing and is held on Vdbe.pDblStr. +** When computing a normalized SQL statement for an SQL statement, that +** list is consulted for each double-quoted identifier to see if the +** identifier should really be a string literal. +*/ +typedef struct DblquoteStr DblquoteStr; +struct DblquoteStr { + DblquoteStr *pNextStr; /* Next string literal in the list */ + char z[8]; /* Dequoted value for the string */ +}; + /* ** An instance of the virtual machine. This structure contains the complete ** state of the virtual machine. @@ -20192,28 +20269,29 @@ struct Vdbe { int pc; /* The program counter */ int rc; /* Value to return */ int nChange; /* Number of db changes made since last reset */ - int iStatement; /* Statement number (or 0 if has not opened stmt) */ + int iStatement; /* Statement number (or 0 if has no opened stmt) */ i64 iCurrentTime; /* Value of julianday('now') for this statement */ i64 nFkConstraint; /* Number of imm. FK constraints this VM */ i64 nStmtDefCons; /* Number of def. constraints when stmt started */ i64 nStmtDefImmCons; /* Number of def. imm constraints when stmt started */ + Mem *aMem; /* The memory locations */ + Mem **apArg; /* Arguments to currently executing user function */ + VdbeCursor **apCsr; /* One element of this array for each open cursor */ + Mem *aVar; /* Values for the OP_Variable opcode. */ /* When allocating a new Vdbe object, all of the fields below should be ** initialized to zero or NULL */ Op *aOp; /* Space to hold the virtual machine's program */ - Mem *aMem; /* The memory locations */ - Mem **apArg; /* Arguments to currently executing user function */ + int nOp; /* Number of instructions in the program */ + int nOpAlloc; /* Slots allocated for aOp[] */ Mem *aColName; /* Column names to return */ Mem *pResultSet; /* Pointer to an array of results */ char *zErrMsg; /* Error message written here */ - VdbeCursor **apCsr; /* One element of this array for each open cursor */ - Mem *aVar; /* Values for the OP_Variable opcode. */ VList *pVList; /* Name of variables */ #ifndef SQLITE_OMIT_TRACE i64 startTime; /* Time when query started - used for profiling */ #endif - int nOp; /* Number of instructions in the program */ #ifdef SQLITE_DEBUG int rcApp; /* errcode set by sqlite3_result_error_code() */ u32 nWrite; /* Number of write operations that have occurred */ @@ -20236,6 +20314,7 @@ struct Vdbe { char *zSql; /* Text of the SQL statement that generated this */ #ifdef SQLITE_ENABLE_NORMALIZE char *zNormSql; /* Normalization of the associated SQL statement */ + DblquoteStr *pDblStr; /* List of double-quoted string literals */ #endif void *pFree; /* Free this when deleting the vdbe */ VdbeFrame *pFrame; /* Parent frame */ @@ -27253,6 +27332,27 @@ static char *getTextArg(PrintfArguments *p){ return (char*)sqlite3_value_text(p->apArg[p->nUsed++]); } +/* +** Allocate memory for a temporary buffer needed for printf rendering. +** +** If the requested size of the temp buffer is larger than the size +** of the output buffer in pAccum, then cause an SQLITE_TOOBIG error. +** Do the size check before the memory allocation to prevent rogue +** SQL from requesting large allocations using the precision or width +** field of the printf() function. +*/ +static char *printfTempBuf(sqlite3_str *pAccum, sqlite3_int64 n){ + char *z; + if( n>pAccum->nAlloc && n>pAccum->mxAlloc ){ + setStrAccumError(pAccum, SQLITE_TOOBIG); + return 0; + } + z = sqlite3DbMallocRaw(pAccum->db, n); + if( z==0 ){ + setStrAccumError(pAccum, SQLITE_NOMEM); + } + return z; +} /* ** On machines with a small stack size, you can redefine the @@ -27335,6 +27435,9 @@ SQLITE_API void sqlite3_str_vappendf( flag_leftjustify = flag_prefix = cThousand = flag_alternateform = flag_altform2 = flag_zeropad = 0; done = 0; + width = 0; + flag_long = 0; + precision = -1; do{ switch( c ){ case '-': flag_leftjustify = 1; break; @@ -27345,80 +27448,93 @@ SQLITE_API void sqlite3_str_vappendf( case '0': flag_zeropad = 1; break; case ',': cThousand = ','; break; default: done = 1; break; + case 'l': { + flag_long = 1; + c = *++fmt; + if( c=='l' ){ + c = *++fmt; + flag_long = 2; + } + done = 1; + break; + } + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': { + unsigned wx = c - '0'; + while( (c = *++fmt)>='0' && c<='9' ){ + wx = wx*10 + c - '0'; + } + testcase( wx>0x7fffffff ); + width = wx & 0x7fffffff; +#ifdef SQLITE_PRINTF_PRECISION_LIMIT + if( width>SQLITE_PRINTF_PRECISION_LIMIT ){ + width = SQLITE_PRINTF_PRECISION_LIMIT; + } +#endif + if( c!='.' && c!='l' ){ + done = 1; + }else{ + fmt--; + } + break; + } + case '*': { + if( bArgList ){ + width = (int)getIntArg(pArgList); + }else{ + width = va_arg(ap,int); + } + if( width<0 ){ + flag_leftjustify = 1; + width = width >= -2147483647 ? -width : 0; + } +#ifdef SQLITE_PRINTF_PRECISION_LIMIT + if( width>SQLITE_PRINTF_PRECISION_LIMIT ){ + width = SQLITE_PRINTF_PRECISION_LIMIT; + } +#endif + if( (c = fmt[1])!='.' && c!='l' ){ + c = *++fmt; + done = 1; + } + break; + } + case '.': { + c = *++fmt; + if( c=='*' ){ + if( bArgList ){ + precision = (int)getIntArg(pArgList); + }else{ + precision = va_arg(ap,int); + } + if( precision<0 ){ + precision = precision >= -2147483647 ? -precision : -1; + } + c = *++fmt; + }else{ + unsigned px = 0; + while( c>='0' && c<='9' ){ + px = px*10 + c - '0'; + c = *++fmt; + } + testcase( px>0x7fffffff ); + precision = px & 0x7fffffff; + } +#ifdef SQLITE_PRINTF_PRECISION_LIMIT + if( precision>SQLITE_PRINTF_PRECISION_LIMIT ){ + precision = SQLITE_PRINTF_PRECISION_LIMIT; + } +#endif + if( c=='l' ){ + --fmt; + }else{ + done = 1; + } + break; + } } }while( !done && (c=(*++fmt))!=0 ); - /* Get the field width */ - if( c=='*' ){ - if( bArgList ){ - width = (int)getIntArg(pArgList); - }else{ - width = va_arg(ap,int); - } - if( width<0 ){ - flag_leftjustify = 1; - width = width >= -2147483647 ? -width : 0; - } - c = *++fmt; - }else{ - unsigned wx = 0; - while( c>='0' && c<='9' ){ - wx = wx*10 + c - '0'; - c = *++fmt; - } - testcase( wx>0x7fffffff ); - width = wx & 0x7fffffff; - } - assert( width>=0 ); -#ifdef SQLITE_PRINTF_PRECISION_LIMIT - if( width>SQLITE_PRINTF_PRECISION_LIMIT ){ - width = SQLITE_PRINTF_PRECISION_LIMIT; - } -#endif - /* Get the precision */ - if( c=='.' ){ - c = *++fmt; - if( c=='*' ){ - if( bArgList ){ - precision = (int)getIntArg(pArgList); - }else{ - precision = va_arg(ap,int); - } - c = *++fmt; - if( precision<0 ){ - precision = precision >= -2147483647 ? -precision : -1; - } - }else{ - unsigned px = 0; - while( c>='0' && c<='9' ){ - px = px*10 + c - '0'; - c = *++fmt; - } - testcase( px>0x7fffffff ); - precision = px & 0x7fffffff; - } - }else{ - precision = -1; - } - assert( precision>=(-1) ); -#ifdef SQLITE_PRINTF_PRECISION_LIMIT - if( precision>SQLITE_PRINTF_PRECISION_LIMIT ){ - precision = SQLITE_PRINTF_PRECISION_LIMIT; - } -#endif - - - /* Get the conversion type modifier */ - if( c=='l' ){ - flag_long = 1; - c = *++fmt; - if( c=='l' ){ - flag_long = 2; - c = *++fmt; - } - }else{ - flag_long = 0; - } /* Fetch the info entry for the field */ infop = &fmtinfo[0]; xtype = etINVALID; @@ -27503,12 +27619,11 @@ SQLITE_API void sqlite3_str_vappendf( nOut = etBUFSIZE; zOut = buf; }else{ - u64 n = (u64)precision + 10 + precision/3; - zOut = zExtra = sqlite3Malloc( n ); - if( zOut==0 ){ - setStrAccumError(pAccum, SQLITE_NOMEM); - return; - } + u64 n; + n = (u64)precision + 10; + if( cThousand ) n += precision/3; + zOut = zExtra = printfTempBuf(pAccum, n); + if( zOut==0 ) return; nOut = (int)n; } bufpt = &zOut[nOut-1]; @@ -27627,12 +27742,12 @@ SQLITE_API void sqlite3_str_vappendf( }else{ e2 = exp; } - if( MAX(e2,0)+(i64)precision+(i64)width > etBUFSIZE - 15 ){ - bufpt = zExtra - = sqlite3Malloc( MAX(e2,0)+(i64)precision+(i64)width+15 ); - if( bufpt==0 ){ - setStrAccumError(pAccum, SQLITE_NOMEM); - return; + { + i64 szBufNeeded; /* Size of a temporary buffer needed */ + szBufNeeded = MAX(e2,0)+(i64)precision+(i64)width+15; + if( szBufNeeded > etBUFSIZE ){ + bufpt = zExtra = printfTempBuf(pAccum, szBufNeeded); + if( bufpt==0 ) return; } } zOut = bufpt; @@ -27856,11 +27971,8 @@ SQLITE_API void sqlite3_str_vappendf( needQuote = !isnull && xtype==etSQLESCAPE2; n += i + 3; if( n>etBUFSIZE ){ - bufpt = zExtra = sqlite3Malloc( n ); - if( bufpt==0 ){ - setStrAccumError(pAccum, SQLITE_NOMEM); - return; - } + bufpt = zExtra = printfTempBuf(pAccum, n); + if( bufpt==0 ) return; }else{ bufpt = buf; } @@ -28486,7 +28598,8 @@ SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc) sqlite3_str_appendf(&x, " %s", pItem->zName); } if( pItem->pTab ){ - sqlite3_str_appendf(&x, " tabname=%Q", pItem->pTab->zName); + sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p", + pItem->pTab->zName, pItem->pTab->nCol, pItem->pTab); } if( pItem->zAlias ){ sqlite3_str_appendf(&x, " (AS %s)", pItem->zAlias); @@ -30226,7 +30339,7 @@ SQLITE_PRIVATE void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){ ** dequoted string, exclusive of the zero terminator, if dequoting does ** occur. ** -** 2002-Feb-14: This routine is extended to remove MS-Access style +** 2002-02-14: This routine is extended to remove MS-Access style ** brackets from around identifiers. For example: "[a-b-c]" becomes ** "a-b-c". */ @@ -30252,6 +30365,11 @@ SQLITE_PRIVATE void sqlite3Dequote(char *z){ } z[j] = 0; } +SQLITE_PRIVATE void sqlite3DequoteExpr(Expr *p){ + assert( sqlite3Isquote(p->u.zToken[0]) ); + p->flags |= p->u.zToken[0]=='"' ? EP_Quoted|EP_DblQuoted : EP_Quoted; + sqlite3Dequote(p->u.zToken); +} /* ** Generate a Token object from a string @@ -31679,20 +31797,6 @@ static unsigned int strHash(const char *z){ } return h; } -#ifdef SQLITE_ENABLE_NORMALIZE -static unsigned int strHashN(const char *z, int n){ - unsigned int h = 0; - int i; - for(i=0; iht ){ /*OPTIMIZATION-IF-TRUE*/ - struct _ht *pEntry; - h = strHashN(pKey, nKey) % pH->htsize; - pEntry = &pH->ht[h]; - elem = pEntry->chain; - count = pEntry->count; - }else{ - h = 0; - elem = pH->first; - count = pH->count; - } - if( pHash ) *pHash = h; - while( count-- ){ - assert( elem!=0 ); - if( sqlite3StrNICmp(elem->pKey,pKey,nKey)==0 ){ - return elem; - } - elem = elem->next; - } - return &nullElement; -} -#endif /* SQLITE_ENABLE_NORMALIZE */ /* Remove a single entry from the hash table given a pointer to that ** element and a hash on the element's key. @@ -31882,14 +31952,6 @@ SQLITE_PRIVATE void *sqlite3HashFind(const Hash *pH, const char *pKey){ assert( pKey!=0 ); return findElementWithHash(pH, pKey, 0)->data; } -#ifdef SQLITE_ENABLE_NORMALIZE -SQLITE_PRIVATE void *sqlite3HashFindN(const Hash *pH, const char *pKey, int nKey){ - assert( pH!=0 ); - assert( pKey!=0 ); - assert( nKey>=0 ); - return findElementWithHashN(pH, pKey, nKey, 0)->data; -} -#endif /* SQLITE_ENABLE_NORMALIZE */ /* Insert an element into the hash table pH. The key is pKey ** and the data is "data". @@ -32076,57 +32138,56 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ /* 120 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"), /* 121 */ "NewRowid" OpHelp("r[P2]=rowid"), /* 122 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"), - /* 123 */ "InsertInt" OpHelp("intkey=P3 data=r[P2]"), - /* 124 */ "Delete" OpHelp(""), - /* 125 */ "ResetCount" OpHelp(""), - /* 126 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"), - /* 127 */ "SorterData" OpHelp("r[P2]=data"), - /* 128 */ "RowData" OpHelp("r[P2]=data"), - /* 129 */ "Rowid" OpHelp("r[P2]=rowid"), - /* 130 */ "NullRow" OpHelp(""), - /* 131 */ "SeekEnd" OpHelp(""), - /* 132 */ "SorterInsert" OpHelp("key=r[P2]"), - /* 133 */ "IdxInsert" OpHelp("key=r[P2]"), - /* 134 */ "IdxDelete" OpHelp("key=r[P2@P3]"), - /* 135 */ "DeferredSeek" OpHelp("Move P3 to P1.rowid if needed"), - /* 136 */ "IdxRowid" OpHelp("r[P2]=rowid"), - /* 137 */ "Destroy" OpHelp(""), - /* 138 */ "Clear" OpHelp(""), - /* 139 */ "ResetSorter" OpHelp(""), - /* 140 */ "CreateBtree" OpHelp("r[P2]=root iDb=P1 flags=P3"), + /* 123 */ "Delete" OpHelp(""), + /* 124 */ "ResetCount" OpHelp(""), + /* 125 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"), + /* 126 */ "SorterData" OpHelp("r[P2]=data"), + /* 127 */ "RowData" OpHelp("r[P2]=data"), + /* 128 */ "Rowid" OpHelp("r[P2]=rowid"), + /* 129 */ "NullRow" OpHelp(""), + /* 130 */ "SeekEnd" OpHelp(""), + /* 131 */ "SorterInsert" OpHelp("key=r[P2]"), + /* 132 */ "IdxInsert" OpHelp("key=r[P2]"), + /* 133 */ "IdxDelete" OpHelp("key=r[P2@P3]"), + /* 134 */ "DeferredSeek" OpHelp("Move P3 to P1.rowid if needed"), + /* 135 */ "IdxRowid" OpHelp("r[P2]=rowid"), + /* 136 */ "Destroy" OpHelp(""), + /* 137 */ "Clear" OpHelp(""), + /* 138 */ "ResetSorter" OpHelp(""), + /* 139 */ "CreateBtree" OpHelp("r[P2]=root iDb=P1 flags=P3"), + /* 140 */ "SqlExec" OpHelp(""), /* 141 */ "Real" OpHelp("r[P2]=P4"), - /* 142 */ "SqlExec" OpHelp(""), - /* 143 */ "ParseSchema" OpHelp(""), - /* 144 */ "LoadAnalysis" OpHelp(""), - /* 145 */ "DropTable" OpHelp(""), - /* 146 */ "DropIndex" OpHelp(""), - /* 147 */ "DropTrigger" OpHelp(""), - /* 148 */ "IntegrityCk" OpHelp(""), - /* 149 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"), - /* 150 */ "Param" OpHelp(""), - /* 151 */ "FkCounter" OpHelp("fkctr[P1]+=P2"), - /* 152 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"), - /* 153 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"), - /* 154 */ "AggInverse" OpHelp("accum=r[P3] inverse(r[P2@P5])"), - /* 155 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"), - /* 156 */ "AggStep1" OpHelp("accum=r[P3] step(r[P2@P5])"), - /* 157 */ "AggValue" OpHelp("r[P3]=value N=P2"), - /* 158 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), - /* 159 */ "Expire" OpHelp(""), - /* 160 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), - /* 161 */ "VBegin" OpHelp(""), - /* 162 */ "VCreate" OpHelp(""), - /* 163 */ "VDestroy" OpHelp(""), - /* 164 */ "VOpen" OpHelp(""), - /* 165 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), - /* 166 */ "VRename" OpHelp(""), - /* 167 */ "Pagecount" OpHelp(""), - /* 168 */ "MaxPgcnt" OpHelp(""), - /* 169 */ "Trace" OpHelp(""), - /* 170 */ "CursorHint" OpHelp(""), - /* 171 */ "Noop" OpHelp(""), - /* 172 */ "Explain" OpHelp(""), - /* 173 */ "Abortable" OpHelp(""), + /* 142 */ "ParseSchema" OpHelp(""), + /* 143 */ "LoadAnalysis" OpHelp(""), + /* 144 */ "DropTable" OpHelp(""), + /* 145 */ "DropIndex" OpHelp(""), + /* 146 */ "DropTrigger" OpHelp(""), + /* 147 */ "IntegrityCk" OpHelp(""), + /* 148 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"), + /* 149 */ "Param" OpHelp(""), + /* 150 */ "FkCounter" OpHelp("fkctr[P1]+=P2"), + /* 151 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"), + /* 152 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"), + /* 153 */ "AggInverse" OpHelp("accum=r[P3] inverse(r[P2@P5])"), + /* 154 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"), + /* 155 */ "AggStep1" OpHelp("accum=r[P3] step(r[P2@P5])"), + /* 156 */ "AggValue" OpHelp("r[P3]=value N=P2"), + /* 157 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), + /* 158 */ "Expire" OpHelp(""), + /* 159 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), + /* 160 */ "VBegin" OpHelp(""), + /* 161 */ "VCreate" OpHelp(""), + /* 162 */ "VDestroy" OpHelp(""), + /* 163 */ "VOpen" OpHelp(""), + /* 164 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), + /* 165 */ "VRename" OpHelp(""), + /* 166 */ "Pagecount" OpHelp(""), + /* 167 */ "MaxPgcnt" OpHelp(""), + /* 168 */ "Trace" OpHelp(""), + /* 169 */ "CursorHint" OpHelp(""), + /* 170 */ "Noop" OpHelp(""), + /* 171 */ "Explain" OpHelp(""), + /* 172 */ "Abortable" OpHelp(""), }; return azName[i]; } @@ -46577,7 +46638,8 @@ typedef struct MemFile MemFile; struct MemFile { sqlite3_file base; /* IO methods */ sqlite3_int64 sz; /* Size of the file */ - sqlite3_int64 szMax; /* Space allocated to aData */ + sqlite3_int64 szAlloc; /* Space allocated to aData */ + sqlite3_int64 szMax; /* Maximum allowed size of the file */ unsigned char *aData; /* content of the file */ int nMmap; /* Number of memory mapped pages */ unsigned mFlags; /* Flags */ @@ -46703,10 +46765,15 @@ static int memdbEnlarge(MemFile *p, sqlite3_int64 newSz){ if( (p->mFlags & SQLITE_DESERIALIZE_RESIZEABLE)==0 || p->nMmap>0 ){ return SQLITE_FULL; } + if( newSz>p->szMax ){ + return SQLITE_FULL; + } + newSz *= 2; + if( newSz>p->szMax ) newSz = p->szMax; pNew = sqlite3_realloc64(p->aData, newSz); if( pNew==0 ) return SQLITE_NOMEM; p->aData = pNew; - p->szMax = newSz; + p->szAlloc = newSz; return SQLITE_OK; } @@ -46720,10 +46787,11 @@ static int memdbWrite( sqlite_int64 iOfst ){ MemFile *p = (MemFile *)pFile; + if( NEVER(p->mFlags & SQLITE_DESERIALIZE_READONLY) ) return SQLITE_READONLY; if( iOfst+iAmt>p->sz ){ int rc; - if( iOfst+iAmt>p->szMax - && (rc = memdbEnlarge(p, (iOfst+iAmt)*2))!=SQLITE_OK + if( iOfst+iAmt>p->szAlloc + && (rc = memdbEnlarge(p, iOfst+iAmt))!=SQLITE_OK ){ return rc; } @@ -46769,6 +46837,11 @@ static int memdbFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ */ static int memdbLock(sqlite3_file *pFile, int eLock){ MemFile *p = (MemFile *)pFile; + if( eLock>SQLITE_LOCK_SHARED + && (p->mFlags & SQLITE_DESERIALIZE_READONLY)!=0 + ){ + return SQLITE_READONLY; + } p->eLock = eLock; return SQLITE_OK; } @@ -46793,6 +46866,19 @@ static int memdbFileControl(sqlite3_file *pFile, int op, void *pArg){ *(char**)pArg = sqlite3_mprintf("memdb(%p,%lld)", p->aData, p->sz); rc = SQLITE_OK; } + if( op==SQLITE_FCNTL_SIZE_LIMIT ){ + sqlite3_int64 iLimit = *(sqlite3_int64*)pArg; + if( iLimitsz ){ + if( iLimit<0 ){ + iLimit = p->szMax; + }else{ + iLimit = p->sz; + } + } + p->szMax = iLimit; + *(sqlite3_int64*)pArg = iLimit; + rc = SQLITE_OK; + } return rc; } @@ -46823,8 +46909,12 @@ static int memdbFetch( void **pp ){ MemFile *p = (MemFile *)pFile; - p->nMmap++; - *pp = (void*)(p->aData + iOfst); + if( iOfst+iAmt>p->sz ){ + *pp = 0; + }else{ + p->nMmap++; + *pp = (void*)(p->aData + iOfst); + } return SQLITE_OK; } @@ -46854,6 +46944,7 @@ static int memdbOpen( assert( pOutFlags!=0 ); /* True because flags==SQLITE_OPEN_MAIN_DB */ *pOutFlags = flags | SQLITE_OPEN_MEMORY; p->base.pMethods = &memdb_io_methods; + p->szMax = sqlite3GlobalConfig.mxMemdbSize; return SQLITE_OK; } @@ -47103,7 +47194,11 @@ SQLITE_API int sqlite3_deserialize( }else{ p->aData = pData; p->sz = szDb; + p->szAlloc = szBuf; p->szMax = szBuf; + if( p->szMaxszMax = sqlite3GlobalConfig.mxMemdbSize; + } p->mFlags = mFlags; rc = SQLITE_OK; } @@ -48524,16 +48619,27 @@ typedef struct PGroup PGroup; ** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of ** PgHdr1.pCache->szPage bytes is allocated directly before this structure ** in memory. +** +** Note: Variables isBulkLocal and isAnchor were once type "u8". That works, +** but causes a 2-byte gap in the structure for most architectures (since +** pointers must be either 4 or 8-byte aligned). As this structure is located +** in memory directly after the associated page data, if the database is +** corrupt, code at the b-tree layer may overread the page buffer and +** read part of this structure before the corruption is detected. This +** can cause a valgrind error if the unitialized gap is accessed. Using u16 +** ensures there is no such gap, and therefore no bytes of unitialized memory +** in the structure. */ struct PgHdr1 { sqlite3_pcache_page page; /* Base class. Must be first. pBuf & pExtra */ unsigned int iKey; /* Key value (page number) */ - u8 isBulkLocal; /* This page from bulk local storage */ - u8 isAnchor; /* This is the PGroup.lru element */ + u16 isBulkLocal; /* This page from bulk local storage */ + u16 isAnchor; /* This is the PGroup.lru element */ PgHdr1 *pNext; /* Next in hash table chain */ PCache1 *pCache; /* Cache that currently owns this page */ PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */ PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */ + /* NB: pLruPrev is only valid if pLruNext!=0 */ }; /* @@ -48599,6 +48705,7 @@ struct PCache1 { unsigned int nMax; /* Configured "cache_size" value */ unsigned int n90pct; /* nMax*9/10 */ unsigned int iMaxKey; /* Largest key seen since xTruncate() */ + unsigned int nPurgeableDummy; /* pnPurgeable points here when not used*/ /* Hash table of all pages. The following variables may only be accessed ** when the accessor is holding the PGroup mutex. @@ -48733,6 +48840,7 @@ static int pcache1InitBulk(PCache1 *pCache){ pX->isBulkLocal = 1; pX->isAnchor = 0; pX->pNext = pCache->pFree; + pX->pLruPrev = 0; /* Initializing this saves a valgrind error */ pCache->pFree = pX; zBulk += pCache->szAlloc; }while( --nBulk ); @@ -48908,6 +49016,9 @@ static void pcache1FreePage(PgHdr1 *p){ ** exists, this function falls back to sqlite3Malloc(). */ SQLITE_PRIVATE void *sqlite3PageMalloc(int sz){ + /* During rebalance operations on a corrupt database file, it is sometimes + ** (rarely) possible to overread the temporary page buffer by a few bytes. + ** Enlarge the allocation slightly so that this does not cause problems. */ return pcache1Alloc(sz); } @@ -49002,7 +49113,8 @@ static PgHdr1 *pcache1PinPage(PgHdr1 *pPage){ pPage->pLruPrev->pLruNext = pPage->pLruNext; pPage->pLruNext->pLruPrev = pPage->pLruPrev; pPage->pLruNext = 0; - pPage->pLruPrev = 0; + /* pPage->pLruPrev = 0; + ** No need to clear pLruPrev as it is never accessed if pLruNext is 0 */ assert( pPage->isAnchor==0 ); assert( pPage->pCache->pGroup->lru.isAnchor==1 ); pPage->pCache->nRecyclable--; @@ -49212,8 +49324,7 @@ static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){ pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; pCache->pnPurgeable = &pGroup->nPurgeable; }else{ - static unsigned int dummyCurrentPage; - pCache->pnPurgeable = &dummyCurrentPage; + pCache->pnPurgeable = &pCache->nPurgeableDummy; } pcache1LeaveMutex(pGroup); if( pCache->nHash==0 ){ @@ -49340,8 +49451,9 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2( pPage->iKey = iKey; pPage->pNext = pCache->apHash[h]; pPage->pCache = pCache; - pPage->pLruPrev = 0; pPage->pLruNext = 0; + /* pPage->pLruPrev = 0; + ** No need to clear pLruPrev since it is not accessed when pLruNext==0 */ *(void **)pPage->page.pExtra = 0; pCache->apHash[h] = pPage; if( iKey>pCache->iMaxKey ){ @@ -49501,7 +49613,7 @@ static void pcache1Unpin( /* It is an error to call this function if the page is already ** part of the PGroup LRU list. */ - assert( pPage->pLruPrev==0 && pPage->pLruNext==0 ); + assert( pPage->pLruNext==0 ); assert( PAGE_IS_PINNED(pPage) ); if( reuseUnlikely || pGroup->nPurgeable>pGroup->nMaxPage ){ @@ -54192,7 +54304,10 @@ SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager *pPager, int mxPage){ pPager->mxPgno = mxPage; } assert( pPager->eState!=PAGER_OPEN ); /* Called only by OP_MaxPgcnt */ - assert( pPager->mxPgno>=pPager->dbSize ); /* OP_MaxPgcnt enforces this */ + /* assert( pPager->mxPgno>=pPager->dbSize ); */ + /* OP_MaxPgcnt ensures that the parameter passed to this function is not + ** less than the total number of valid pages in the database. But this + ** may be less than Pager.dbSize, and so the assert() above is not valid */ return pPager->mxPgno; } @@ -62431,9 +62546,16 @@ struct CellInfo { ** found at self->pBt->mutex. ** ** skipNext meaning: -** eState==SKIPNEXT && skipNext>0: Next sqlite3BtreeNext() is no-op. -** eState==SKIPNEXT && skipNext<0: Next sqlite3BtreePrevious() is no-op. -** eState==FAULT: Cursor fault with skipNext as error code. +** The meaning of skipNext depends on the value of eState: +** +** eState Meaning of skipNext +** VALID skipNext is meaningless and is ignored +** INVALID skipNext is meaningless and is ignored +** SKIPNEXT sqlite3BtreeNext() is a no-op if skipNext>0 and +** sqlite3BtreePrevious() is no-op if skipNext<0. +** REQUIRESEEK restoreCursorPosition() restores the cursor to +** eState=SKIPNEXT if skipNext!=0 +** FAULT skipNext holds the cursor fault error code. */ struct BtCursor { u8 eState; /* One of the CURSOR_XXX constants (see below) */ @@ -63597,13 +63719,19 @@ static int saveCursorKey(BtCursor *pCur){ /* Only the rowid is required for a table btree */ pCur->nKey = sqlite3BtreeIntegerKey(pCur); }else{ - /* For an index btree, save the complete key content */ + /* For an index btree, save the complete key content. It is possible + ** that the current key is corrupt. In that case, it is possible that + ** the sqlite3VdbeRecordUnpack() function may overread the buffer by + ** up to the size of 1 varint plus 1 8-byte value when the cursor + ** position is restored. Hence the 17 bytes of padding allocated + ** below. */ void *pKey; pCur->nKey = sqlite3BtreePayloadSize(pCur); - pKey = sqlite3Malloc( pCur->nKey ); + pKey = sqlite3Malloc( pCur->nKey + 9 + 8 ); if( pKey ){ rc = sqlite3BtreePayload(pCur, 0, (int)pCur->nKey, pKey); if( rc==SQLITE_OK ){ + memset(((u8*)pKey)+pCur->nKey, 0, 9+8); pCur->pKey = pKey; }else{ sqlite3_free(pKey); @@ -63735,11 +63863,12 @@ static int btreeMoveto( UnpackedRecord *pIdxKey; /* Unpacked index key */ if( pKey ){ + KeyInfo *pKeyInfo = pCur->pKeyInfo; assert( nKey==(i64)(int)nKey ); - pIdxKey = sqlite3VdbeAllocUnpackedRecord(pCur->pKeyInfo); + pIdxKey = sqlite3VdbeAllocUnpackedRecord(pKeyInfo); if( pIdxKey==0 ) return SQLITE_NOMEM_BKPT; - sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, pIdxKey); - if( pIdxKey->nField==0 ){ + sqlite3VdbeRecordUnpack(pKeyInfo, (int)nKey, pKey, pIdxKey); + if( pIdxKey->nField==0 || pIdxKey->nField>pKeyInfo->nAllField ){ rc = SQLITE_CORRUPT_BKPT; goto moveto_done; } @@ -63775,7 +63904,7 @@ static int btreeRestoreCursorPosition(BtCursor *pCur){ sqlite3_free(pCur->pKey); pCur->pKey = 0; assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_INVALID ); - pCur->skipNext |= skipNext; + if( skipNext ) pCur->skipNext = skipNext; if( pCur->skipNext && pCur->eState==CURSOR_VALID ){ pCur->eState = CURSOR_SKIPNEXT; } @@ -63845,7 +63974,6 @@ SQLITE_PRIVATE int sqlite3BtreeCursorRestore(BtCursor *pCur, int *pDifferentRow) if( pCur->eState!=CURSOR_VALID ){ *pDifferentRow = 1; }else{ - assert( pCur->skipNext==0 ); *pDifferentRow = 0; } return SQLITE_OK; @@ -63929,6 +64057,13 @@ static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){ *pRC = rc; return; } + if( ((char*)sqlite3PagerGetExtra(pDbPage))[0]!=0 ){ + /* The first byte of the extra data is the MemPage.isInit byte. + ** If that byte is set, it means this page is also being used + ** as a btree page. */ + *pRC = SQLITE_CORRUPT_BKPT; + goto ptrmap_exit; + } offset = PTRMAP_PTROFFSET(iPtrmap, key); if( offset<0 ){ *pRC = SQLITE_CORRUPT_BKPT; @@ -63991,7 +64126,7 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){ #else /* if defined SQLITE_OMIT_AUTOVACUUM */ #define ptrmapPut(w,x,y,z,rc) #define ptrmapGet(w,x,y,z) SQLITE_OK - #define ptrmapPutOvflPtr(x, y, rc) + #define ptrmapPutOvflPtr(x, y, z, rc) #endif /* @@ -64284,17 +64419,24 @@ static u16 cellSize(MemPage *pPage, int iCell){ #ifndef SQLITE_OMIT_AUTOVACUUM /* -** If the cell pCell, part of page pPage contains a pointer -** to an overflow page, insert an entry into the pointer-map -** for the overflow page. +** The cell pCell is currently part of page pSrc but will ultimately be part +** of pPage. (pSrc and pPager are often the same.) If pCell contains a +** pointer to an overflow page, insert an entry into the pointer-map for +** the overflow page that will be valid after pCell has been moved to pPage. */ -static void ptrmapPutOvflPtr(MemPage *pPage, u8 *pCell, int *pRC){ +static void ptrmapPutOvflPtr(MemPage *pPage, MemPage *pSrc, u8 *pCell,int *pRC){ CellInfo info; if( *pRC ) return; assert( pCell!=0 ); pPage->xParseCell(pPage, pCell, &info); if( info.nLocalaDataEnd, pCell, pCell+info.nLocal) ){ + testcase( pSrc!=pPage ); + *pRC = SQLITE_CORRUPT_BKPT; + return; + } + ovfl = get4byte(&pCell[info.nSize-4]); ptrmapPut(pPage->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, pRC); } } @@ -64349,19 +64491,14 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ ** reconstruct the entire page. */ if( (int)data[hdr+7]<=nMaxFrag ){ int iFree = get2byte(&data[hdr+1]); + + /* If the initial freeblock offset were out of bounds, that would + ** have been detected by btreeInitPage() when it was computing the + ** number of free bytes on the page. */ + assert( iFree<=usableSize-4 ); if( iFree ){ int iFree2 = get2byte(&data[iFree]); - - /* pageFindSlot() has already verified that free blocks are sorted - ** in order of offset within the page, and that no block extends - ** past the end of the page. Provided the two free slots do not - ** overlap, this guarantees that the memmove() calls below will not - ** overwrite the usableSize byte buffer, even if the database page - ** is corrupt. */ - assert( iFree2==0 || iFree2>iFree ); - assert( iFree+get2byte(&data[iFree+2]) <= usableSize ); - assert( iFree2==0 || iFree2+get2byte(&data[iFree2+2]) <= usableSize ); - + if( iFree2>usableSize-4 ) return SQLITE_CORRUPT_PAGE(pPage); if( 0==iFree2 || (data[iFree2]==0 && data[iFree2+1]==0) ){ u8 *pEnd = &data[cellOffset + nCell*2]; u8 *pAddr; @@ -64372,9 +64509,9 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ return SQLITE_CORRUPT_PAGE(pPage); } if( iFree2 ){ - assert( iFree+sz<=iFree2 ); /* Verified by pageFindSlot() */ + if( iFree+sz>iFree2 ) return SQLITE_CORRUPT_PAGE(pPage); sz2 = get2byte(&data[iFree2+2]); - assert( iFree+sz+sz2+iFree2-(iFree+sz) <= usableSize ); + if( iFree2+sz2 > usableSize ) return SQLITE_CORRUPT_PAGE(pPage); memmove(&data[iFree+sz+sz2], &data[iFree+sz], iFree2-(iFree+sz)); sz += sz2; } @@ -65929,9 +66066,9 @@ static int newDatabase(BtShared*); static int lockBtree(BtShared *pBt){ int rc; /* Result code from subfunctions */ MemPage *pPage1; /* Page 1 of the database file */ - int nPage; /* Number of pages in the database */ - int nPageFile = 0; /* Number of pages in the database file */ - int nPageHeader; /* Number of pages in the database according to hdr */ + u32 nPage; /* Number of pages in the database */ + u32 nPageFile = 0; /* Number of pages in the database file */ + u32 nPageHeader; /* Number of pages in the database according to hdr */ assert( sqlite3_mutex_held(pBt->mutex) ); assert( pBt->pPage1==0 ); @@ -65944,7 +66081,7 @@ static int lockBtree(BtShared *pBt){ ** a valid database file. */ nPage = nPageHeader = get4byte(28+(u8*)pPage1->aData); - sqlite3PagerPagecount(pBt->pPager, &nPageFile); + sqlite3PagerPagecount(pBt->pPager, (int*)&nPageFile); if( nPage==0 || memcmp(24+(u8*)pPage1->aData, 92+(u8*)pPage1->aData,4)!=0 ){ nPage = nPageFile; } @@ -66025,6 +66162,7 @@ static int lockBtree(BtShared *pBt){ ){ goto page1_init_failed; } + pBt->btsFlags |= BTS_PAGESIZE_FIXED; assert( (pageSize & 7)==0 ); /* EVIDENCE-OF: R-59310-51205 The "reserved space" size in the 1-byte ** integer at offset 20 is the number of bytes of space at the end of @@ -66415,7 +66553,7 @@ static int setChildPtrmaps(MemPage *pPage){ for(i=0; ileaf ){ Pgno childPgno = get4byte(pCell); @@ -67341,6 +67479,7 @@ SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor *pCur){ sqlite3_free(pCur->aOverflow); sqlite3_free(pCur->pKey); sqlite3BtreeLeave(pBtree); + pCur->pBtree = 0; } return SQLITE_OK; } @@ -67439,6 +67578,25 @@ SQLITE_PRIVATE u32 sqlite3BtreePayloadSize(BtCursor *pCur){ return pCur->info.nPayload; } +/* +** Return an upper bound on the size of any record for the table +** that the cursor is pointing into. +** +** This is an optimization. Everything will still work if this +** routine always returns 2147483647 (which is the largest record +** that SQLite can handle) or more. But returning a smaller value might +** prevent large memory allocations when trying to interpret a +** corrupt datrabase. +** +** The current implementation merely returns the size of the underlying +** database file. +*/ +SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeMaxRecordSize(BtCursor *pCur){ + assert( cursorHoldsMutex(pCur) ); + assert( pCur->eState==CURSOR_VALID ); + return pCur->pBt->pageSize * (sqlite3_int64)pCur->pBt->nPage; +} + /* ** Given the page number of an overflow page in the database (parameter ** ovfl), this function finds the page number of the next page in the @@ -68253,7 +68411,7 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( ** try to get there using sqlite3BtreeNext() rather than a full ** binary search. This is an optimization only. The correct answer ** is still obtained without this case, only a little more slowely */ - if( pCur->info.nKey+1==intKey && !pCur->skipNext ){ + if( pCur->info.nKey+1==intKey ){ *pRes = 0; rc = sqlite3BtreeNext(pCur, 0); if( rc==SQLITE_OK ){ @@ -68395,7 +68553,7 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( testcase( nCell==0 ); /* Invalid key size: 0x80 0x80 0x00 */ testcase( nCell==1 ); /* Invalid key size: 0x80 0x80 0x01 */ testcase( nCell==2 ); /* Minimum legal index key size */ - if( nCell<2 ){ + if( nCell<2 || nCell/pCur->pBt->usableSize>pCur->pBt->nPage ){ rc = SQLITE_CORRUPT_PAGE(pPage); goto moveto_finish; } @@ -68527,7 +68685,6 @@ static SQLITE_NOINLINE int btreeNext(BtCursor *pCur){ MemPage *pPage; assert( cursorOwnsBtShared(pCur) ); - assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); if( pCur->eState!=CURSOR_VALID ){ assert( (pCur->curFlags & BTCF_ValidOvfl)==0 ); rc = restoreCursorPosition(pCur); @@ -68537,14 +68694,9 @@ static SQLITE_NOINLINE int btreeNext(BtCursor *pCur){ if( CURSOR_INVALID==pCur->eState ){ return SQLITE_DONE; } - if( pCur->skipNext ){ - assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_SKIPNEXT ); + if( pCur->eState==CURSOR_SKIPNEXT ){ pCur->eState = CURSOR_VALID; - if( pCur->skipNext>0 ){ - pCur->skipNext = 0; - return SQLITE_OK; - } - pCur->skipNext = 0; + if( pCur->skipNext>0 ) return SQLITE_OK; } } @@ -68599,7 +68751,6 @@ SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor *pCur, int flags){ UNUSED_PARAMETER( flags ); /* Used in COMDB2 but not native SQLite */ assert( cursorOwnsBtShared(pCur) ); assert( flags==0 || flags==1 ); - assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); pCur->info.nSize = 0; pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl); if( pCur->eState!=CURSOR_VALID ) return btreeNext(pCur); @@ -68640,7 +68791,6 @@ static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur){ MemPage *pPage; assert( cursorOwnsBtShared(pCur) ); - assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); assert( (pCur->curFlags & (BTCF_AtLast|BTCF_ValidOvfl|BTCF_ValidNKey))==0 ); assert( pCur->info.nSize==0 ); if( pCur->eState!=CURSOR_VALID ){ @@ -68651,14 +68801,9 @@ static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur){ if( CURSOR_INVALID==pCur->eState ){ return SQLITE_DONE; } - if( pCur->skipNext ){ - assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_SKIPNEXT ); + if( CURSOR_SKIPNEXT==pCur->eState ){ pCur->eState = CURSOR_VALID; - if( pCur->skipNext<0 ){ - pCur->skipNext = 0; - return SQLITE_OK; - } - pCur->skipNext = 0; + if( pCur->skipNext<0 ) return SQLITE_OK; } } @@ -68693,7 +68838,6 @@ static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur){ SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int flags){ assert( cursorOwnsBtShared(pCur) ); assert( flags==0 || flags==1 ); - assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID ); UNUSED_PARAMETER( flags ); /* Used in COMDB2 but not native SQLite */ pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidOvfl|BTCF_ValidNKey); pCur->info.nSize = 0; @@ -69029,7 +69173,7 @@ static int allocateBtreePage( TRACE(("ALLOCATE: %d from end of file\n", *pPgno)); } - assert( *pPgno!=PENDING_BYTE_PAGE(pBt) ); + assert( CORRUPT_DB || *pPgno!=PENDING_BYTE_PAGE(pBt) ); end_allocate_page: releasePage(pTrunk); @@ -69584,9 +69728,16 @@ static void insertCell( assert( idx >= pPage->cellOffset+2*pPage->nCell+2 || CORRUPT_DB ); assert( idx+sz <= (int)pPage->pBt->usableSize ); pPage->nFree -= (u16)(2 + sz); - memcpy(&data[idx], pCell, sz); if( iChild ){ + /* In a corrupt database where an entry in the cell index section of + ** a btree page has a value of 3 or less, the pCell value might point + ** as many as 4 bytes in front of the start of the aData buffer for + ** the source page. Make sure this does not cause problems by not + ** reading the first 4 bytes */ + memcpy(&data[idx+4], pCell+4, sz-4); put4byte(&data[idx], iChild); + }else{ + memcpy(&data[idx], pCell, sz); } pIns = pPage->aCellIdx + i*2; memmove(pIns+2, pIns, 2*(pPage->nCell - i)); @@ -69600,15 +69751,89 @@ static void insertCell( /* The cell may contain a pointer to an overflow page. If so, write ** the entry for the overflow page into the pointer map. */ - ptrmapPutOvflPtr(pPage, pCell, pRC); + ptrmapPutOvflPtr(pPage, pPage, pCell, pRC); } #endif } } +/* +** The following parameters determine how many adjacent pages get involved +** in a balancing operation. NN is the number of neighbors on either side +** of the page that participate in the balancing operation. NB is the +** total number of pages that participate, including the target page and +** NN neighbors on either side. +** +** The minimum value of NN is 1 (of course). Increasing NN above 1 +** (to 2 or 3) gives a modest improvement in SELECT and DELETE performance +** in exchange for a larger degradation in INSERT and UPDATE performance. +** The value of NN appears to give the best results overall. +** +** (Later:) The description above makes it seem as if these values are +** tunable - as if you could change them and recompile and it would all work. +** But that is unlikely. NB has been 3 since the inception of SQLite and +** we have never tested any other value. +*/ +#define NN 1 /* Number of neighbors on either side of pPage */ +#define NB 3 /* (NN*2+1): Total pages involved in the balance */ + /* ** A CellArray object contains a cache of pointers and sizes for a ** consecutive sequence of cells that might be held on multiple pages. +** +** The cells in this array are the divider cell or cells from the pParent +** page plus up to three child pages. There are a total of nCell cells. +** +** pRef is a pointer to one of the pages that contributes cells. This is +** used to access information such as MemPage.intKey and MemPage.pBt->pageSize +** which should be common to all pages that contribute cells to this array. +** +** apCell[] and szCell[] hold, respectively, pointers to the start of each +** cell and the size of each cell. Some of the apCell[] pointers might refer +** to overflow cells. In other words, some apCel[] pointers might not point +** to content area of the pages. +** +** A szCell[] of zero means the size of that cell has not yet been computed. +** +** The cells come from as many as four different pages: +** +** ----------- +** | Parent | +** ----------- +** / | \ +** / | \ +** --------- --------- --------- +** |Child-1| |Child-2| |Child-3| +** --------- --------- --------- +** +** The order of cells is in the array is for an index btree is: +** +** 1. All cells from Child-1 in order +** 2. The first divider cell from Parent +** 3. All cells from Child-2 in order +** 4. The second divider cell from Parent +** 5. All cells from Child-3 in order +** +** For a table-btree (with rowids) the items 2 and 4 are empty because +** content exists only in leaves and there are no divider cells. +** +** For an index btree, the apEnd[] array holds pointer to the end of page +** for Child-1, the Parent, Child-2, the Parent (again), and Child-3, +** respectively. The ixNx[] array holds the number of cells contained in +** each of these 5 stages, and all stages to the left. Hence: +** +** ixNx[0] = Number of cells in Child-1. +** ixNx[1] = Number of cells in Child-1 plus 1 for first divider. +** ixNx[2] = Number of cells in Child-1 and Child-2 + 1 for 1st divider. +** ixNx[3] = Number of cells in Child-1 and Child-2 + both divider cells +** ixNx[4] = Total number of cells. +** +** For a table-btree, the concept is similar, except only apEnd[0]..apEnd[2] +** are used and they point to the leaf pages only, and the ixNx value are: +** +** ixNx[0] = Number of cells in Child-1. +** ixNx[1] = Number of cells in Child-1 and Child-2 + 1 for 1st divider. +** ixNx[2] = Number of cells in Child-1 and Child-2 + both divider cells */ typedef struct CellArray CellArray; struct CellArray { @@ -69616,6 +69841,8 @@ struct CellArray { MemPage *pRef; /* Reference page */ u8 **apCell; /* All cells begin balanced */ u16 *szCell; /* Local size of all cells in apCell[] */ + u8 *apEnd[NB*2]; /* MemPage.aDataEnd values */ + int ixNx[NB*2]; /* Index of at which we move to the next apEnd[] */ }; /* @@ -69666,36 +69893,59 @@ static u16 cachedCellSize(CellArray *p, int N){ ** responsibility of the caller to set it correctly. */ static int rebuildPage( - MemPage *pPg, /* Edit this page */ + CellArray *pCArray, /* Content to be added to page pPg */ + int iFirst, /* First cell in pCArray to use */ int nCell, /* Final number of cells on page */ - u8 **apCell, /* Array of cells */ - u16 *szCell /* Array of cell sizes */ + MemPage *pPg /* The page to be reconstructed */ ){ const int hdr = pPg->hdrOffset; /* Offset of header on pPg */ u8 * const aData = pPg->aData; /* Pointer to data for pPg */ const int usableSize = pPg->pBt->usableSize; u8 * const pEnd = &aData[usableSize]; - int i; + int i = iFirst; /* Which cell to copy from pCArray*/ + u32 j; /* Start of cell content area */ + int iEnd = i+nCell; /* Loop terminator */ u8 *pCellptr = pPg->aCellIdx; u8 *pTmp = sqlite3PagerTempSpace(pPg->pBt->pPager); u8 *pData; + int k; /* Current slot in pCArray->apEnd[] */ + u8 *pSrcEnd; /* Current pCArray->apEnd[k] value */ - i = get2byte(&aData[hdr+5]); - memcpy(&pTmp[i], &aData[i], usableSize - i); + assert( i(u32)usableSize) ){ j = 0; } + memcpy(&pTmp[j], &aData[j], usableSize - j); + + for(k=0; pCArray->ixNx[k]<=i && ALWAYS(kapEnd[k]; pData = pEnd; - for(i=0; iapCell[i]; + u16 sz = pCArray->szCell[i]; + assert( sz>0 ); if( SQLITE_WITHIN(pCell,aData,pEnd) ){ + if( ((uptr)(pCell+sz))>(uptr)pEnd ) return SQLITE_CORRUPT_BKPT; pCell = &pTmp[pCell - aData]; + }else if( (uptr)(pCell+sz)>(uptr)pSrcEnd + && (uptr)(pCell)<(uptr)pSrcEnd + ){ + return SQLITE_CORRUPT_BKPT; } - pData -= szCell[i]; + + pData -= sz; put2byte(pCellptr, (pData - aData)); pCellptr += 2; if( pData < pCellptr ) return SQLITE_CORRUPT_BKPT; - memcpy(pData, pCell, szCell[i]); - assert( szCell[i]==pPg->xCellSize(pPg, pCell) || CORRUPT_DB ); - testcase( szCell[i]!=pPg->xCellSize(pPg,pCell) ); + memcpy(pData, pCell, sz); + assert( sz==pPg->xCellSize(pPg, pCell) || CORRUPT_DB ); + testcase( sz!=pPg->xCellSize(pPg,pCell) ); + i++; + if( i>=iEnd ) break; + if( pCArray->ixNx[k]<=i ){ + k++; + pSrcEnd = pCArray->apEnd[k]; + } } /* The pPg->nFree field is now set incorrectly. The caller will fix it. */ @@ -69710,12 +69960,11 @@ static int rebuildPage( } /* -** Array apCell[] contains nCell pointers to b-tree cells. Array szCell -** contains the size in bytes of each such cell. This function attempts to -** add the cells stored in the array to page pPg. If it cannot (because -** the page needs to be defragmented before the cells will fit), non-zero -** is returned. Otherwise, if the cells are added successfully, zero is -** returned. +** The pCArray objects contains pointers to b-tree cells and the cell sizes. +** This function attempts to add the cells stored in the array to page pPg. +** If it cannot (because the page needs to be defragmented before the cells +** will fit), non-zero is returned. Otherwise, if the cells are added +** successfully, zero is returned. ** ** Argument pCellptr points to the first entry in the cell-pointer array ** (part of page pPg) to populate. After cell apCell[0] is written to the @@ -69737,18 +69986,23 @@ static int rebuildPage( static int pageInsertArray( MemPage *pPg, /* Page to add cells to */ u8 *pBegin, /* End of cell-pointer array */ - u8 **ppData, /* IN/OUT: Page content -area pointer */ + u8 **ppData, /* IN/OUT: Page content-area pointer */ u8 *pCellptr, /* Pointer to cell-pointer area */ int iFirst, /* Index of first cell to add */ int nCell, /* Number of cells to add to pPg */ CellArray *pCArray /* Array of cells */ ){ - int i; - u8 *aData = pPg->aData; - u8 *pData = *ppData; - int iEnd = iFirst + nCell; + int i = iFirst; /* Loop counter - cell index to insert */ + u8 *aData = pPg->aData; /* Complete page */ + u8 *pData = *ppData; /* Content area. A subset of aData[] */ + int iEnd = iFirst + nCell; /* End of loop. One past last cell to ins */ + int k; /* Current slot in pCArray->apEnd[] */ + u8 *pEnd; /* Maximum extent of cell data */ assert( CORRUPT_DB || pPg->hdrOffset==0 ); /* Never called on page 1 */ - for(i=iFirst; iixNx[k]<=i && ALWAYS(kapEnd[k]; + while( 1 /*Exit by break*/ ){ int sz, rc; u8 *pSlot; sz = cachedCellSize(pCArray, i); @@ -69763,20 +70017,33 @@ static int pageInsertArray( assert( (pSlot+sz)<=pCArray->apCell[i] || pSlot>=(pCArray->apCell[i]+sz) || CORRUPT_DB ); + if( (uptr)(pCArray->apCell[i]+sz)>(uptr)pEnd + && (uptr)(pCArray->apCell[i])<(uptr)pEnd + ){ + assert( CORRUPT_DB ); + (void)SQLITE_CORRUPT_BKPT; + return 1; + } memmove(pSlot, pCArray->apCell[i], sz); put2byte(pCellptr, (pSlot - aData)); pCellptr += 2; + i++; + if( i>=iEnd ) break; + if( pCArray->ixNx[k]<=i ){ + k++; + pEnd = pCArray->apEnd[k]; + } } *ppData = pData; return 0; } /* -** Array apCell[] contains nCell pointers to b-tree cells. Array szCell -** contains the size in bytes of each such cell. This function adds the -** space associated with each cell in the array that is currently stored -** within the body of pPg to the pPg free-list. The cell-pointers and other -** fields of the page are not updated. +** The pCArray object contains pointers to b-tree cells and their sizes. +** +** This function adds the space associated with each cell in the array +** that is currently stored within the body of pPg to the pPg free-list. +** The cell-pointers and other fields of the page are not updated. ** ** This function returns the total number of cells added to the free-list. */ @@ -69826,9 +70093,9 @@ static int pageFreeArray( } /* -** apCell[] and szCell[] contains pointers to and sizes of all cells in the -** pages being balanced. The current page, pPg, has pPg->nCell cells starting -** with apCell[iOld]. After balancing, this page should hold nNew cells +** pCArray contains pointers to and sizes of all cells in the page being +** balanced. The current page, pPg, has pPg->nCell cells starting with +** pCArray->apCell[iOld]. After balancing, this page should hold nNew cells ** starting at apCell[iNew]. ** ** This routine makes the necessary adjustments to pPg so that it contains @@ -69860,13 +70127,17 @@ static int editPage( #endif /* Remove cells from the start and end of the page */ + assert( nCell>=0 ); if( iOldnCell ) return SQLITE_CORRUPT_BKPT; memmove(pPg->aCellIdx, &pPg->aCellIdx[nShift*2], nCell*2); nCell -= nShift; } if( iNewEnd < iOldEnd ){ - nCell -= pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray); + int nTail = pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray); + assert( nCell>=nTail ); + nCell -= nTail; } pData = &aData[get2byteNotZero(&aData[hdr+5])]; @@ -69876,6 +70147,7 @@ static int editPage( if( iNew=0 ); pCellptr = pPg->aCellIdx; memmove(&pCellptr[nAdd*2], pCellptr, nCell*2); if( pageInsertArray( @@ -69890,6 +70162,7 @@ static int editPage( int iCell = (iOld + pPg->aiOvfl[i]) - iNew; if( iCell>=0 && iCellaCellIdx[iCell * 2]; + assert( nCell>=iCell ); memmove(&pCellptr[2], pCellptr, (nCell - iCell) * 2); nCell++; if( pageInsertArray( @@ -69900,6 +70173,7 @@ static int editPage( } /* Append cells to the end of the page */ + assert( nCell>=0 ); pCellptr = &pPg->aCellIdx[nCell*2]; if( pageInsertArray( pPg, pBegin, &pData, pCellptr, @@ -69928,24 +70202,9 @@ static int editPage( editpage_fail: /* Unable to edit this page. Rebuild it from scratch instead. */ populateCellCache(pCArray, iNew, nNew); - return rebuildPage(pPg, nNew, &pCArray->apCell[iNew], &pCArray->szCell[iNew]); + return rebuildPage(pCArray, iNew, nNew, pPg); } -/* -** The following parameters determine how many adjacent pages get involved -** in a balancing operation. NN is the number of neighbors on either side -** of the page that participate in the balancing operation. NB is the -** total number of pages that participate, including the target page and -** NN neighbors on either side. -** -** The minimum value of NN is 1 (of course). Increasing NN above 1 -** (to 2 or 3) gives a modest improvement in SELECT and DELETE performance -** in exchange for a larger degradation in INSERT and UPDATE performance. -** The value of NN appears to give the best results overall. -*/ -#define NN 1 /* Number of neighbors on either side of pPage */ -#define NB (NN*2+1) /* Total pages involved in the balance */ - #ifndef SQLITE_OMIT_QUICKBALANCE /* @@ -69981,8 +70240,7 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){ assert( sqlite3PagerIswriteable(pParent->pDbPage) ); assert( pPage->nOverflow==1 ); - /* This error condition is now caught prior to reaching this function */ - if( NEVER(pPage->nCell==0) ) return SQLITE_CORRUPT_BKPT; + if( pPage->nCell==0 ) return SQLITE_CORRUPT_BKPT; /* dbfuzz001.test */ /* Allocate a new page. This page will become the right-sibling of ** pPage. Make the parent page writable, so that the new divider cell @@ -69996,12 +70254,22 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){ u8 *pCell = pPage->apOvfl[0]; u16 szCell = pPage->xCellSize(pPage, pCell); u8 *pStop; + CellArray b; assert( sqlite3PagerIswriteable(pNew->pDbPage) ); - assert( pPage->aData[0]==(PTF_INTKEY|PTF_LEAFDATA|PTF_LEAF) ); + assert( CORRUPT_DB || pPage->aData[0]==(PTF_INTKEY|PTF_LEAFDATA|PTF_LEAF) ); zeroPage(pNew, PTF_INTKEY|PTF_LEAFDATA|PTF_LEAF); - rc = rebuildPage(pNew, 1, &pCell, &szCell); - if( NEVER(rc) ) return rc; + b.nCell = 1; + b.pRef = pPage; + b.apCell = &pCell; + b.szCell = &szCell; + b.apEnd[0] = pPage->aDataEnd; + b.ixNx[0] = 2; + rc = rebuildPage(&b, 0, 1, pNew); + if( NEVER(rc) ){ + releasePage(pNew); + return rc; + } pNew->nFree = pBt->usableSize - pNew->cellOffset - 2 - szCell; /* If this is an auto-vacuum database, update the pointer map @@ -70016,7 +70284,7 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){ if( ISAUTOVACUUM ){ ptrmapPut(pBt, pgnoNew, PTRMAP_BTREE, pParent->pgno, &rc); if( szCell>pNew->minLocal ){ - ptrmapPutOvflPtr(pNew, pCell, &rc); + ptrmapPutOvflPtr(pNew, pNew, pCell, &rc); } } @@ -70239,10 +70507,6 @@ static int balance_nonroot( assert( sqlite3_mutex_held(pBt->mutex) ); assert( sqlite3PagerIswriteable(pParent->pDbPage) ); -#if 0 - TRACE(("BALANCE: begin page %d child of %d\n", pPage->pgno, pParent->pgno)); -#endif - /* At this point pParent may have at most one overflow cell. And if ** this overflow cell is present, it must be the cell with ** index iParentIdx. This scenario comes about when this function @@ -70483,8 +70747,15 @@ static int balance_nonroot( ** */ usableSpace = pBt->usableSize - 12 + leafCorrection; - for(i=0; iaDataEnd; + b.ixNx[k] = cntOld[i]; + if( !leafData ){ + k++; + b.apEnd[k] = pParent->aDataEnd; + b.ixNx[k] = cntOld[i]+1; + } szNew[i] = usableSpace - p->nFree; for(j=0; jnOverflow; j++){ szNew[i] += 2 + p->xCellSize(p, p->apOvfl[j]); @@ -70708,7 +70979,8 @@ static int balance_nonroot( ** populated, not here. */ if( ISAUTOVACUUM ){ - MemPage *pNew = apNew[0]; + MemPage *pOld; + MemPage *pNew = pOld = apNew[0]; u8 *aOld = pNew->aData; int cntOldNext = pNew->nCell + pNew->nOverflow; int usableSize = pBt->usableSize; @@ -70718,7 +70990,7 @@ static int balance_nonroot( for(i=0; inCell + pOld->nOverflow + !leafData; aOld = pOld->aData; } @@ -70741,7 +71013,7 @@ static int balance_nonroot( ptrmapPut(pBt, get4byte(pCell), PTRMAP_BTREE, pNew->pgno, &rc); } if( cachedCellSize(&b,i)>pNew->minLocal ){ - ptrmapPutOvflPtr(pNew, pCell, &rc); + ptrmapPutOvflPtr(pNew, pOld, pCell, &rc); } if( rc ) goto balance_cleanup; } @@ -71165,7 +71437,11 @@ static int btreeOverwriteContent( if( memcmp(pDest, ((u8*)pX->pData) + iOffset, iAmt)!=0 ){ int rc = sqlite3PagerWrite(pPage->pDbPage); if( rc ) return rc; - memcpy(pDest, ((u8*)pX->pData) + iOffset, iAmt); + /* In a corrupt database, it is possible for the source and destination + ** buffers to overlap. This is harmless since the database is already + ** corrupt but it does cause valgrind and ASAN warnings. So use + ** memmove(). */ + memmove(pDest, ((u8*)pX->pData) + iOffset, iAmt); } } return SQLITE_OK; @@ -71560,6 +71836,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){ if( bPreserve ){ if( !pPage->leaf || (pPage->nFree+cellSizePtr(pPage,pCell)+2)>(int)(pBt->usableSize*2/3) + || pPage->nCell==1 /* See dbfuzz001.test for a test case */ ){ /* A b-tree rebalance will be required after deleting this entry. ** Save the cursor key. */ @@ -72338,18 +72615,18 @@ static void checkList( } pOvflData = (unsigned char *)sqlite3PagerGetData(pOvflPage); if( isFreeList ){ - int n = get4byte(&pOvflData[4]); + u32 n = (u32)get4byte(&pOvflData[4]); #ifndef SQLITE_OMIT_AUTOVACUUM if( pCheck->pBt->autoVacuum ){ checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0); } #endif - if( n>(int)pCheck->pBt->usableSize/4-2 ){ + if( n>pCheck->pBt->usableSize/4-2 ){ checkAppendMsg(pCheck, "freelist leaf count too big on page %d", iPage); N--; }else{ - for(i=0; ipBt->autoVacuum ){ @@ -72726,7 +73003,7 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( Pgno i; IntegrityCk sCheck; BtShared *pBt = p->pBt; - int savedDbFlags = pBt->db->flags; + u64 savedDbFlags = pBt->db->flags; char zErr[100]; VVA_ONLY( int nRef ); @@ -72793,7 +73070,7 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck( } #endif testcase( pBt->db->flags & SQLITE_CellSizeCk ); - pBt->db->flags &= ~SQLITE_CellSizeCk; + pBt->db->flags &= ~(u64)SQLITE_CellSizeCk; for(i=0; (int)i0 ); + assert( CORRUPT_DB || szNew>0 ); assert( (pMem->flags & MEM_Dyn)==0 || pMem->szMalloc==0 ); if( pMem->szMallocflags = MEM_Null; + if( sqlite3BtreeMaxRecordSize(pCur)z); if( rc==SQLITE_OK ){ @@ -75468,9 +75748,11 @@ static int valueFromExpr( } #endif else if( op==TK_TRUEFALSE ){ - pVal = valueNew(db, pCtx); - pVal->flags = MEM_Int; - pVal->u.i = pExpr->u.zToken[4]==0; + pVal = valueNew(db, pCtx); + if( pVal ){ + pVal->flags = MEM_Int; + pVal->u.i = pExpr->u.zToken[4]==0; + } } *ppVal = pVal; @@ -75863,7 +76145,7 @@ SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(Parse *pParse){ pParse->pVdbe = p; assert( pParse->aLabel==0 ); assert( pParse->nLabel==0 ); - assert( pParse->nOpAlloc==0 ); + assert( p->nOpAlloc==0 ); assert( pParse->szOpAlloc==0 ); sqlite3VdbeAddOp2(p, OP_Init, 0, 1); return p; @@ -75891,15 +76173,45 @@ SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, u8 prepFlag } assert( p->zSql==0 ); p->zSql = sqlite3DbStrNDup(p->db, z, n); -#ifdef SQLITE_ENABLE_NORMALIZE - assert( p->zNormSql==0 ); - if( p->zSql && (prepFlags & SQLITE_PREPARE_NORMALIZE)!=0 ){ - sqlite3Normalize(p, p->zSql, n, prepFlags); - assert( p->zNormSql!=0 || p->db->mallocFailed ); - } -#endif } +#ifdef SQLITE_ENABLE_NORMALIZE +/* +** Add a new element to the Vdbe->pDblStr list. +*/ +SQLITE_PRIVATE void sqlite3VdbeAddDblquoteStr(sqlite3 *db, Vdbe *p, const char *z){ + if( p ){ + int n = sqlite3Strlen30(z); + DblquoteStr *pStr = sqlite3DbMallocRawNN(db, + sizeof(*pStr)+n+1-sizeof(pStr->z)); + if( pStr ){ + pStr->pNextStr = p->pDblStr; + p->pDblStr = pStr; + memcpy(pStr->z, z, n+1); + } + } +} +#endif + +#ifdef SQLITE_ENABLE_NORMALIZE +/* +** zId of length nId is a double-quoted identifier. Check to see if +** that identifier is really used as a string literal. +*/ +SQLITE_PRIVATE int sqlite3VdbeUsesDoubleQuotedString( + Vdbe *pVdbe, /* The prepared statement */ + const char *zId /* The double-quoted identifier, already dequoted */ +){ + DblquoteStr *pStr; + assert( zId!=0 ); + if( pVdbe->pDblStr==0 ) return 0; + for(pStr=pVdbe->pDblStr; pStr; pStr=pStr->pNextStr){ + if( strcmp(zId, pStr->z)==0 ) return 1; + } + return 0; +} +#endif + /* ** Swap all content between two VDBE structures. */ @@ -75919,7 +76231,7 @@ SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){ zTmp = pA->zSql; pA->zSql = pB->zSql; pB->zSql = zTmp; -#ifdef SQLITE_ENABLE_NORMALIZE +#if 0 zTmp = pA->zNormSql; pA->zNormSql = pB->zNormSql; pB->zNormSql = zTmp; @@ -75936,7 +76248,7 @@ SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){ ** to 1024/sizeof(Op). ** ** If an out-of-memory error occurs while resizing the array, return -** SQLITE_NOMEM. In this case Vdbe.aOp and Parse.nOpAlloc remain +** SQLITE_NOMEM. In this case Vdbe.aOp and Vdbe.nOpAlloc remain ** unchanged (this is so that any opcodes already allocated can be ** correctly deallocated along with the rest of the Vdbe). */ @@ -75952,9 +76264,9 @@ static int growOpArray(Vdbe *v, int nOp){ ** operation (without SQLITE_TEST_REALLOC_STRESS) is to double the current ** size of the op array or add 1KB of space, whichever is smaller. */ #ifdef SQLITE_TEST_REALLOC_STRESS - int nNew = (p->nOpAlloc>=512 ? p->nOpAlloc*2 : p->nOpAlloc+nOp); + int nNew = (v->nOpAlloc>=512 ? v->nOpAlloc*2 : v->nOpAlloc+nOp); #else - int nNew = (p->nOpAlloc ? p->nOpAlloc*2 : (int)(1024/sizeof(Op))); + int nNew = (v->nOpAlloc ? v->nOpAlloc*2 : (int)(1024/sizeof(Op))); UNUSED_PARAMETER(nOp); #endif @@ -75965,11 +76277,11 @@ static int growOpArray(Vdbe *v, int nOp){ } assert( nOp<=(1024/sizeof(Op)) ); - assert( nNew>=(p->nOpAlloc+nOp) ); + assert( nNew>=(v->nOpAlloc+nOp) ); pNew = sqlite3DbRealloc(p->db, v->aOp, nNew*sizeof(Op)); if( pNew ){ p->szOpAlloc = sqlite3DbMallocSize(p->db, pNew); - p->nOpAlloc = p->szOpAlloc/sizeof(Op); + v->nOpAlloc = p->szOpAlloc/sizeof(Op); v->aOp = pNew; } return (pNew ? SQLITE_OK : SQLITE_NOMEM_BKPT); @@ -76003,9 +76315,9 @@ static void test_addop_breakpoint(void){ ** operand. */ static SQLITE_NOINLINE int growOp3(Vdbe *p, int op, int p1, int p2, int p3){ - assert( p->pParse->nOpAlloc<=p->nOp ); + assert( p->nOpAlloc<=p->nOp ); if( growOpArray(p, 1) ) return 1; - assert( p->pParse->nOpAlloc>p->nOp ); + assert( p->nOpAlloc>p->nOp ); return sqlite3VdbeAddOp3(p, op, p1, p2, p3); } SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){ @@ -76015,7 +76327,7 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){ i = p->nOp; assert( p->magic==VDBE_MAGIC_INIT ); assert( op>=0 && op<0xff ); - if( p->pParse->nOpAlloc<=i ){ + if( p->nOpAlloc<=i ){ return growOp3(p, op, p1, p2, p3); } p->nOp++; @@ -76147,13 +76459,29 @@ SQLITE_PRIVATE int sqlite3VdbeExplainParent(Parse *pParse){ } /* -** Add a new OP_Explain opcode. +** Set a debugger breakpoint on the following routine in order to +** monitor the EXPLAIN QUERY PLAN code generation. +*/ +#if defined(SQLITE_DEBUG) +SQLITE_PRIVATE void sqlite3ExplainBreakpoint(const char *z1, const char *z2){ + (void)z1; + (void)z2; +} +#endif + +/* +** Add a new OP_ opcode. ** ** If the bPush flag is true, then make this opcode the parent for ** subsequent Explains until sqlite3VdbeExplainPop() is called. */ SQLITE_PRIVATE void sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){ - if( pParse->explain==2 ){ +#ifndef SQLITE_DEBUG + /* Always include the OP_Explain opcodes if SQLITE_DEBUG is defined. + ** But omit them (for performance) during production builds */ + if( pParse->explain==2 ) +#endif + { char *zMsg; Vdbe *v; va_list ap; @@ -76165,7 +76493,10 @@ SQLITE_PRIVATE void sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt iThis = v->nOp; sqlite3VdbeAddOp4(v, OP_Explain, iThis, pParse->addrExplain, 0, zMsg, P4_DYNAMIC); - if( bPush) pParse->addrExplain = iThis; + sqlite3ExplainBreakpoint(bPush?"PUSH":"", sqlite3VdbeGetOp(v,-1)->p4.z); + if( bPush){ + pParse->addrExplain = iThis; + } } } @@ -76173,6 +76504,7 @@ SQLITE_PRIVATE void sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt ** Pop the EXPLAIN QUERY PLAN stack one level. */ SQLITE_PRIVATE void sqlite3VdbeExplainPop(Parse *pParse){ + sqlite3ExplainBreakpoint("POP", 0); pParse->addrExplain = sqlite3VdbeExplainParent(pParse); } #endif /* SQLITE_OMIT_EXPLAIN */ @@ -76237,21 +76569,22 @@ SQLITE_PRIVATE void sqlite3VdbeEndCoroutine(Vdbe *v, int regYield){ ** The VDBE knows that a P2 value is a label because labels are ** always negative and P2 values are suppose to be non-negative. ** Hence, a negative P2 value is a label that has yet to be resolved. +** (Later:) This is only true for opcodes that have the OPFLG_JUMP +** property. ** -** Zero is returned if a malloc() fails. +** Variable usage notes: +** +** Parse.aLabel[x] Stores the address that the x-th label resolves +** into. For testing (SQLITE_DEBUG), unresolved +** labels stores -1, but that is not required. +** Parse.nLabelAlloc Number of slots allocated to Parse.aLabel[] +** Parse.nLabel The *negative* of the number of labels that have +** been issued. The negative is stored because +** that gives a performance improvement over storing +** the equivalent positive value. */ -SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe *v){ - Parse *p = v->pParse; - int i = p->nLabel++; - assert( v->magic==VDBE_MAGIC_INIT ); - if( (i & (i-1))==0 ){ - p->aLabel = sqlite3DbReallocOrFree(p->db, p->aLabel, - (i*2+1)*sizeof(p->aLabel[0])); - } - if( p->aLabel ){ - p->aLabel[i] = -1; - } - return ADDR(i); +SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Parse *pParse){ + return --pParse->nLabel; } /* @@ -76259,18 +76592,35 @@ SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe *v){ ** be inserted. The parameter "x" must have been obtained from ** a prior call to sqlite3VdbeMakeLabel(). */ +static SQLITE_NOINLINE void resizeResolveLabel(Parse *p, Vdbe *v, int j){ + int nNewSize = 10 - p->nLabel; + p->aLabel = sqlite3DbReallocOrFree(p->db, p->aLabel, + nNewSize*sizeof(p->aLabel[0])); + if( p->aLabel==0 ){ + p->nLabelAlloc = 0; + }else{ +#ifdef SQLITE_DEBUG + int i; + for(i=p->nLabelAlloc; iaLabel[i] = -1; +#endif + p->nLabelAlloc = nNewSize; + p->aLabel[j] = v->nOp; + } +} SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe *v, int x){ Parse *p = v->pParse; int j = ADDR(x); assert( v->magic==VDBE_MAGIC_INIT ); - assert( jnLabel ); + assert( j<-p->nLabel ); assert( j>=0 ); - if( p->aLabel ){ #ifdef SQLITE_DEBUG - if( p->db->flags & SQLITE_VdbeAddopTrace ){ - printf("RESOLVE LABEL %d to %d\n", x, v->nOp); - } + if( p->db->flags & SQLITE_VdbeAddopTrace ){ + printf("RESOLVE LABEL %d to %d\n", x, v->nOp); + } #endif + if( p->nLabelAlloc + p->nLabel < 0 ){ + resizeResolveLabel(p,v,j); + }else{ assert( p->aLabel[j]==(-1) ); /* Labels may only be resolved once */ p->aLabel[j] = v->nOp; } @@ -76395,8 +76745,9 @@ SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){ while( (pOp = opIterNext(&sIter))!=0 ){ int opcode = pOp->opcode; if( opcode==OP_Destroy || opcode==OP_VUpdate || opcode==OP_VRename + || opcode==OP_VDestroy || ((opcode==OP_Halt || opcode==OP_HaltIfNull) - && ((pOp->p1&0xff)==SQLITE_CONSTRAINT && pOp->p2==OE_Abort)) + && ((pOp->p1)!=SQLITE_OK && pOp->p2==OE_Abort)) ){ hasAbort = 1; break; @@ -76545,7 +76896,7 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ ** non-jump opcodes less than SQLITE_MX_JUMP_CODE are guaranteed to ** have non-negative values for P2. */ assert( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 ); - assert( ADDR(pOp->p2)nLabel ); + assert( ADDR(pOp->p2)<-pParse->nLabel ); pOp->p2 = aLabel[ADDR(pOp->p2)]; } break; @@ -76584,7 +76935,7 @@ SQLITE_PRIVATE int sqlite3VdbeCurrentAddr(Vdbe *p){ */ #if defined(SQLITE_DEBUG) && !defined(SQLITE_TEST_REALLOC_STRESS) SQLITE_PRIVATE void sqlite3VdbeVerifyNoMallocRequired(Vdbe *p, int N){ - assert( p->nOp + N <= p->pParse->nOpAlloc ); + assert( p->nOp + N <= p->nOpAlloc ); } #endif @@ -76656,7 +77007,7 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeAddOpList( VdbeOp *pOut, *pFirst; assert( nOp>0 ); assert( p->magic==VDBE_MAGIC_INIT ); - if( p->nOp + nOp > p->pParse->nOpAlloc && growOpArray(p, nOp) ){ + if( p->nOp + nOp > p->nOpAlloc && growOpArray(p, nOp) ){ return 0; } pFirst = pOut = &p->aOp[p->nOp]; @@ -77978,19 +78329,27 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady( ** the leftover memory at the end of the opcode array. This can significantly ** reduce the amount of memory held by a prepared statement. */ - do { - x.nNeeded = 0; - p->aMem = allocSpace(&x, p->aMem, nMem*sizeof(Mem)); - p->aVar = allocSpace(&x, p->aVar, nVar*sizeof(Mem)); - p->apArg = allocSpace(&x, p->apArg, nArg*sizeof(Mem*)); - p->apCsr = allocSpace(&x, p->apCsr, nCursor*sizeof(VdbeCursor*)); + x.nNeeded = 0; + p->aMem = allocSpace(&x, 0, nMem*sizeof(Mem)); + p->aVar = allocSpace(&x, 0, nVar*sizeof(Mem)); + p->apArg = allocSpace(&x, 0, nArg*sizeof(Mem*)); + p->apCsr = allocSpace(&x, 0, nCursor*sizeof(VdbeCursor*)); #ifdef SQLITE_ENABLE_STMT_SCANSTATUS - p->anExec = allocSpace(&x, p->anExec, p->nOp*sizeof(i64)); + p->anExec = allocSpace(&x, 0, p->nOp*sizeof(i64)); #endif - if( x.nNeeded==0 ) break; + if( x.nNeeded ){ x.pSpace = p->pFree = sqlite3DbMallocRawNN(db, x.nNeeded); x.nFree = x.nNeeded; - }while( !db->mallocFailed ); + if( !db->mallocFailed ){ + p->aMem = allocSpace(&x, p->aMem, nMem*sizeof(Mem)); + p->aVar = allocSpace(&x, p->aVar, nVar*sizeof(Mem)); + p->apArg = allocSpace(&x, p->apArg, nArg*sizeof(Mem*)); + p->apCsr = allocSpace(&x, p->apCsr, nCursor*sizeof(VdbeCursor*)); +#ifdef SQLITE_ENABLE_STMT_SCANSTATUS + p->anExec = allocSpace(&x, p->anExec, p->nOp*sizeof(i64)); +#endif + } + } p->pVList = pParse->pVList; pParse->pVList = 0; @@ -78682,7 +79041,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){ }else{ db->nDeferredCons = 0; db->nDeferredImmCons = 0; - db->flags &= ~SQLITE_DeferFKs; + db->flags &= ~(u64)SQLITE_DeferFKs; sqlite3CommitInternalChanges(db); } }else{ @@ -78997,6 +79356,13 @@ SQLITE_PRIVATE void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){ sqlite3DbFree(db, p->zSql); #ifdef SQLITE_ENABLE_NORMALIZE sqlite3DbFree(db, p->zNormSql); + { + DblquoteStr *pThis, *pNext; + for(pThis=p->pDblStr; pThis; pThis=pNext){ + pNext = pThis->pNextStr; + sqlite3DbFree(db, pThis); + } + } #endif #ifdef SQLITE_ENABLE_STMT_SCANSTATUS { @@ -79537,7 +79903,7 @@ SQLITE_PRIVATE void sqlite3VdbeRecordUnpack( UnpackedRecord *p /* Populate this structure before returning. */ ){ const unsigned char *aKey = (const unsigned char *)pKey; - int d; + u32 d; u32 idx; /* Offset in aKey[] to read from */ u16 u; /* Unsigned loop counter */ u32 szHdr; @@ -79548,7 +79914,7 @@ SQLITE_PRIVATE void sqlite3VdbeRecordUnpack( idx = getVarint32(aKey, szHdr); d = szHdr; u = 0; - while( idx=p->nField ) break; } + if( d>(u32)nKey && u ){ + assert( CORRUPT_DB ); + /* In a corrupt record entry, the last pMem might have been set up using + ** uninitialized memory. Overwrite its value with NULL, to prevent + ** warnings from MSAN. */ + sqlite3VdbeMemSetNull(pMem-1); + } assert( u<=pKeyInfo->nKeyField + 1 ); p->nField = u; } @@ -79626,8 +79999,8 @@ static int vdbeRecordCompareDebug( ** Use that approximation to avoid the more expensive call to ** sqlite3VdbeSerialTypeLen() in the common case. */ - if( d1+serial_type1+2>(u32)nKey1 - && d1+sqlite3VdbeSerialTypeLen(serial_type1)>(u32)nKey1 + if( d1+(u64)serial_type1+2>(u64)nKey1 + && d1+(u64)sqlite3VdbeSerialTypeLen(serial_type1)>(u64)nKey1 ){ break; } @@ -79638,7 +80011,8 @@ static int vdbeRecordCompareDebug( /* Do the comparison */ - rc = sqlite3MemCompare(&mem1, &pPKey2->aMem[i], pKeyInfo->aColl[i]); + rc = sqlite3MemCompare(&mem1, &pPKey2->aMem[i], + pKeyInfo->nAllField>i ? pKeyInfo->aColl[i] : 0); if( rc!=0 ){ assert( mem1.szMalloc==0 ); /* See comment below */ if( pKeyInfo->aSortOrder[i] ){ @@ -79994,12 +80368,12 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip( }else{ idx1 = getVarint32(aKey1, szHdr1); d1 = szHdr1; - if( d1>(unsigned)nKey1 ){ - pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT; - return 0; /* Corruption */ - } i = 0; } + if( d1>(unsigned)nKey1 ){ + pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT; + return 0; /* Corruption */ + } VVA_ONLY( mem1.szMalloc = 0; ) /* Only needed by assert() statements */ assert( pPKey2->pKeyInfo->nAllField>=pPKey2->nField @@ -80069,10 +80443,12 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip( mem1.n = (serial_type - 12) / 2; testcase( (d1+mem1.n)==(unsigned)nKey1 ); testcase( (d1+mem1.n+1)==(unsigned)nKey1 ); - if( (d1+mem1.n) > (unsigned)nKey1 ){ + if( (d1+mem1.n) > (unsigned)nKey1 + || (pKeyInfo = pPKey2->pKeyInfo)->nAllField<=i + ){ pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT; return 0; /* Corruption */ - }else if( (pKeyInfo = pPKey2->pKeyInfo)->aColl[i] ){ + }else if( pKeyInfo->aColl[i] ){ mem1.enc = pKeyInfo->enc; mem1.db = pKeyInfo->db; mem1.flags = MEM_Str; @@ -80772,14 +81148,16 @@ static SQLITE_NOINLINE void invokeProfileCallback(sqlite3 *db, Vdbe *p){ sqlite3_int64 iNow; sqlite3_int64 iElapse; assert( p->startTime>0 ); - assert( db->xProfile!=0 || (db->mTrace & SQLITE_TRACE_PROFILE)!=0 ); + assert( (db->mTrace & (SQLITE_TRACE_PROFILE|SQLITE_TRACE_XPROFILE))!=0 ); assert( db->init.busy==0 ); assert( p->zSql!=0 ); sqlite3OsCurrentTimeInt64(db->pVfs, &iNow); iElapse = (iNow - p->startTime)*1000000; +#ifndef SQLITE_OMIT_DEPRECATED if( db->xProfile ){ db->xProfile(db->pProfileArg, p->zSql, iElapse); } +#endif if( db->mTrace & SQLITE_TRACE_PROFILE ){ db->xTrace(SQLITE_TRACE_PROFILE, db->pTraceArg, p, (void*)&iElapse); } @@ -81293,7 +81671,7 @@ static int sqlite3Step(Vdbe *p){ return SQLITE_NOMEM_BKPT; } - if( p->pc<=0 && p->expired ){ + if( p->pc<0 && p->expired ){ p->rc = SQLITE_SCHEMA; rc = SQLITE_ERROR; goto end_of_step; @@ -81312,7 +81690,7 @@ static int sqlite3Step(Vdbe *p){ ); #ifndef SQLITE_OMIT_TRACE - if( (db->xProfile || (db->mTrace & SQLITE_TRACE_PROFILE)!=0) + if( (db->mTrace & (SQLITE_TRACE_PROFILE|SQLITE_TRACE_XPROFILE))!=0 && !db->init.busy && p->zSql ){ sqlite3OsCurrentTimeInt64(db->pVfs, &p->startTime); }else{ @@ -81339,16 +81717,18 @@ static int sqlite3Step(Vdbe *p){ db->nVdbeExec--; } + if( rc!=SQLITE_ROW ){ #ifndef SQLITE_OMIT_TRACE - /* If the statement completed successfully, invoke the profile callback */ - if( rc!=SQLITE_ROW ) checkProfileCallback(db, p); + /* If the statement completed successfully, invoke the profile callback */ + checkProfileCallback(db, p); #endif - if( rc==SQLITE_DONE && db->autoCommit ){ - assert( p->rc==SQLITE_OK ); - p->rc = doWalCallbacks(db); - if( p->rc!=SQLITE_OK ){ - rc = SQLITE_ERROR; + if( rc==SQLITE_DONE && db->autoCommit ){ + assert( p->rc==SQLITE_OK ); + p->rc = doWalCallbacks(db); + if( p->rc!=SQLITE_OK ){ + rc = SQLITE_ERROR; + } } } @@ -81368,9 +81748,9 @@ end_of_step: || (rc&0xff)==SQLITE_BUSY || rc==SQLITE_MISUSE ); assert( (p->rc!=SQLITE_ROW && p->rc!=SQLITE_DONE) || p->rc==p->rcApp ); - if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 - && rc!=SQLITE_ROW - && rc!=SQLITE_DONE + if( rc!=SQLITE_ROW + && rc!=SQLITE_DONE + && (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 ){ /* If this statement was prepared using saved SQL and an ** error has occurred, then return the error code in p->rc to the @@ -81992,7 +82372,7 @@ static int vdbeUnbind(Vdbe *p, int i){ pVar = &p->aVar[i]; sqlite3VdbeMemRelease(pVar); pVar->flags = MEM_Null; - sqlite3Error(p->db, SQLITE_OK); + p->db->errCode = SQLITE_OK; /* If the bit corresponding to this variable in Vdbe.expmask is set, then ** binding a new value to this variable invalidates the current query plan. @@ -82418,7 +82798,13 @@ SQLITE_API char *sqlite3_expanded_sql(sqlite3_stmt *pStmt){ */ SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt){ Vdbe *p = (Vdbe *)pStmt; - return p ? p->zNormSql : 0; + if( p==0 ) return 0; + if( p->zNormSql==0 && ALWAYS(p->zSql!=0) ){ + sqlite3_mutex_enter(p->db->mutex); + p->zNormSql = sqlite3Normalize(p, p->zSql); + sqlite3_mutex_leave(p->db->mutex); + } + return p->zNormSql; } #endif /* SQLITE_ENABLE_NORMALIZE */ @@ -83118,6 +83504,11 @@ static VdbeCursor *allocateCursor( assert( iCur>=0 && iCurnCursor ); if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/ + /* Before calling sqlite3VdbeFreeCursor(), ensure the isEphemeral flag + ** is clear. Otherwise, if this is an ephemeral cursor created by + ** OP_OpenDup, the cursor will not be closed and will still be part + ** of a BtShared.pCursor list. */ + p->apCsr[iCur]->isEphemeral = 0; sqlite3VdbeFreeCursor(p, p->apCsr[iCur]); p->apCsr[iCur] = 0; } @@ -83258,6 +83649,7 @@ SQLITE_PRIVATE void sqlite3ValueApplyAffinity( static u16 SQLITE_NOINLINE computeNumericType(Mem *pMem){ assert( (pMem->flags & (MEM_Int|MEM_Real))==0 ); assert( (pMem->flags & (MEM_Str|MEM_Blob))!=0 ); + ExpandBlob(pMem); if( sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc)==0 ){ return 0; } @@ -84545,8 +84937,8 @@ fp_math: break; } default: { - iA = (i64)rA; - iB = (i64)rB; + iA = sqlite3VdbeIntValue(pIn1); + iB = sqlite3VdbeIntValue(pIn2); if( iA==0 ) goto arithmetic_result_is_null; if( iA==-1 ) iA = 1; rB = (double)(iB % iA); @@ -84892,7 +85284,8 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ */ assert( pOp->opcode==OP_Eq || pOp->opcode==OP_Ne ); assert( (flags1 & MEM_Cleared)==0 ); - assert( (pOp->p5 & SQLITE_JUMPIFNULL)==0 ); + assert( (pOp->p5 & SQLITE_JUMPIFNULL)==0 || CORRUPT_DB ); + testcase( (pOp->p5 & SQLITE_JUMPIFNULL)!=0 ); if( (flags1&flags3&MEM_Null)!=0 && (flags3&MEM_Cleared)==0 ){ @@ -86576,7 +86969,8 @@ case OP_OpenDup: { pCx->isEphemeral = 1; pCx->pKeyInfo = pOrig->pKeyInfo; pCx->isTable = pOrig->isTable; - rc = sqlite3BtreeCursor(pOrig->pBtx, MASTER_ROOT, BTREE_WRCSR, + pCx->pgnoRoot = pOrig->pgnoRoot; + rc = sqlite3BtreeCursor(pOrig->pBtx, pCx->pgnoRoot, BTREE_WRCSR, pCx->pKeyInfo, pCx->uc.pCursor); /* The sqlite3BtreeCursor() routine can only fail for the first cursor ** opened for a database. Since there is already an open cursor when this @@ -86594,6 +86988,9 @@ case OP_OpenDup: { ** the main database is read-only. The ephemeral ** table is deleted automatically when the cursor is closed. ** +** If the cursor P1 is already opened on an ephemeral table, the table +** is cleared (all content is erased). +** ** P2 is the number of columns in the ephemeral table. ** The cursor points to a BTree table if P4==0 and to a BTree index ** if P4 is not 0. If P4 is not NULL, it points to a KeyInfo structure @@ -86625,41 +87022,50 @@ case OP_OpenEphemeral: { SQLITE_OPEN_TRANSIENT_DB; assert( pOp->p1>=0 ); assert( pOp->p2>=0 ); - pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, CURTYPE_BTREE); - if( pCx==0 ) goto no_mem; - pCx->nullRow = 1; - pCx->isEphemeral = 1; - rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBtx, - BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags); - if( rc==SQLITE_OK ){ - rc = sqlite3BtreeBeginTrans(pCx->pBtx, 1, 0); - } - if( rc==SQLITE_OK ){ - /* If a transient index is required, create it by calling - ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before - ** opening it. If a transient table is required, just use the - ** automatically created table with root-page 1 (an BLOB_INTKEY table). - */ - if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ - int pgno; - assert( pOp->p4type==P4_KEYINFO ); - rc = sqlite3BtreeCreateTable(pCx->pBtx, &pgno, BTREE_BLOBKEY | pOp->p5); - if( rc==SQLITE_OK ){ - assert( pgno==MASTER_ROOT+1 ); - assert( pKeyInfo->db==db ); - assert( pKeyInfo->enc==ENC(db) ); - rc = sqlite3BtreeCursor(pCx->pBtx, pgno, BTREE_WRCSR, - pKeyInfo, pCx->uc.pCursor); - } - pCx->isTable = 0; - }else{ - rc = sqlite3BtreeCursor(pCx->pBtx, MASTER_ROOT, BTREE_WRCSR, - 0, pCx->uc.pCursor); - pCx->isTable = 1; + pCx = p->apCsr[pOp->p1]; + if( pCx ){ + /* If the ephermeral table is already open, erase all existing content + ** so that the table is empty again, rather than creating a new table. */ + rc = sqlite3BtreeClearTable(pCx->pBtx, pCx->pgnoRoot, 0); + }else{ + pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, CURTYPE_BTREE); + if( pCx==0 ) goto no_mem; + pCx->nullRow = 1; + pCx->isEphemeral = 1; + rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBtx, + BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, + vfsFlags); + if( rc==SQLITE_OK ){ + rc = sqlite3BtreeBeginTrans(pCx->pBtx, 1, 0); } + if( rc==SQLITE_OK ){ + /* If a transient index is required, create it by calling + ** sqlite3BtreeCreateTable() with the BTREE_BLOBKEY flag before + ** opening it. If a transient table is required, just use the + ** automatically created table with root-page 1 (an BLOB_INTKEY table). + */ + if( (pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo)!=0 ){ + assert( pOp->p4type==P4_KEYINFO ); + rc = sqlite3BtreeCreateTable(pCx->pBtx, (int*)&pCx->pgnoRoot, + BTREE_BLOBKEY | pOp->p5); + if( rc==SQLITE_OK ){ + assert( pCx->pgnoRoot==MASTER_ROOT+1 ); + assert( pKeyInfo->db==db ); + assert( pKeyInfo->enc==ENC(db) ); + rc = sqlite3BtreeCursor(pCx->pBtx, pCx->pgnoRoot, BTREE_WRCSR, + pKeyInfo, pCx->uc.pCursor); + } + pCx->isTable = 0; + }else{ + pCx->pgnoRoot = MASTER_ROOT; + rc = sqlite3BtreeCursor(pCx->pBtx, MASTER_ROOT, BTREE_WRCSR, + 0, pCx->uc.pCursor); + pCx->isTable = 1; + } + } + pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); } if( rc ) goto abort_due_to_error; - pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED); break; } @@ -87309,7 +87715,7 @@ case OP_NotExists: /* jump, in3 */ pC = p->apCsr[pOp->p1]; assert( pC!=0 ); #ifdef SQLITE_DEBUG - pC->seekOp = OP_SeekRowid; + if( pOp->opcode==OP_SeekRowid ) pC->seekOp = OP_SeekRowid; #endif assert( pC->isTable ); assert( pC->eCurType==CURTYPE_BTREE ); @@ -87527,14 +87933,7 @@ case OP_NewRowid: { /* out2 */ ** This instruction only works on tables. The equivalent instruction ** for indices is OP_IdxInsert. */ -/* Opcode: InsertInt P1 P2 P3 P4 P5 -** Synopsis: intkey=P3 data=r[P2] -** -** This works exactly like OP_Insert except that the key is the -** integer value P3, not the value of the integer stored in register P3. -*/ -case OP_Insert: -case OP_InsertInt: { +case OP_Insert: { Mem *pData; /* MEM cell holding data for the record to be inserted */ Mem *pKey; /* MEM cell holding key for the record */ VdbeCursor *pC; /* Cursor to table into which insert is written */ @@ -87555,16 +87954,11 @@ case OP_InsertInt: { REGISTER_TRACE(pOp->p2, pData); sqlite3VdbeIncrWriteCounter(p, pC); - if( pOp->opcode==OP_Insert ){ - pKey = &aMem[pOp->p3]; - assert( pKey->flags & MEM_Int ); - assert( memIsValid(pKey) ); - REGISTER_TRACE(pOp->p3, pKey); - x.nKey = pKey->u.i; - }else{ - assert( pOp->opcode==OP_InsertInt ); - x.nKey = pOp->p3; - } + pKey = &aMem[pOp->p3]; + assert( pKey->flags & MEM_Int ); + assert( memIsValid(pKey) ); + REGISTER_TRACE(pOp->p3, pKey); + x.nKey = pKey->u.i; if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){ assert( pC->iDb>=0 ); @@ -88217,7 +88611,7 @@ case OP_Next: /* jump */ assert( pOp->opcode!=OP_Next || pC->seekOp==OP_SeekGT || pC->seekOp==OP_SeekGE || pC->seekOp==OP_Rewind || pC->seekOp==OP_Found - || pC->seekOp==OP_NullRow); + || pC->seekOp==OP_NullRow|| pC->seekOp==OP_SeekRowid); assert( pOp->opcode!=OP_Prev || pC->seekOp==OP_SeekLT || pC->seekOp==OP_SeekLE || pC->seekOp==OP_Last @@ -88747,9 +89141,16 @@ case OP_ParseSchema: { assert( db->init.busy==0 ); db->init.busy = 1; initData.rc = SQLITE_OK; + initData.nInitRow = 0; assert( !db->mallocFailed ); rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0); if( rc==SQLITE_OK ) rc = initData.rc; + if( rc==SQLITE_OK && initData.nInitRow==0 ){ + /* The OP_ParseSchema opcode with a non-NULL P4 argument should parse + ** at least one SQL statement. Any less than that indicates that + ** the sqlite_master table is corrupt. */ + rc = SQLITE_CORRUPT_BKPT; + } sqlite3DbFreeNN(db, zSql); db->init.busy = 0; } @@ -89112,6 +89513,17 @@ case OP_Program: { /* jump */ p->nOp = pProgram->nOp; #ifdef SQLITE_ENABLE_STMT_SCANSTATUS p->anExec = 0; +#endif +#ifdef SQLITE_DEBUG + /* Verify that second and subsequent executions of the same trigger do not + ** try to reuse register values from the first use. */ + { + int i; + for(i=0; inMem; i++){ + aMem[i].pScopyFrom = 0; /* Prevent false-positive AboutToChange() errs */ + aMem[i].flags |= MEM_Undefined; /* Cause a fault if this reg is reused */ + } + } #endif pOp = &aOp[-1]; @@ -89651,14 +90063,19 @@ case OP_JournalMode: { /* out2 */ #endif /* SQLITE_OMIT_PRAGMA */ #if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH) -/* Opcode: Vacuum P1 * * * * +/* Opcode: Vacuum P1 P2 * * * ** ** Vacuum the entire database P1. P1 is 0 for "main", and 2 or more ** for an attached database. The "temp" database may not be vacuumed. +** +** If P2 is not zero, then it is a register holding a string which is +** the file into which the result of vacuum should be written. When +** P2 is zero, the vacuum overwrites the original database. */ case OP_Vacuum: { assert( p->readOnly==0 ); - rc = sqlite3RunVacuum(&p->zErrMsg, db, pOp->p1); + rc = sqlite3RunVacuum(&p->zErrMsg, db, pOp->p1, + pOp->p2 ? &aMem[pOp->p2] : 0); if( rc ) goto abort_due_to_error; break; } @@ -89810,6 +90227,7 @@ case OP_VDestroy: { db->nVDestroy++; rc = sqlite3VtabCallDestroy(db, pOp->p1, pOp->p4.z); db->nVDestroy--; + assert( p->errorAction==OE_Abort && p->usesStmtJournal ); if( rc ) goto abort_due_to_error; break; } @@ -90053,7 +90471,7 @@ case OP_VRename: { rc = sqlite3VdbeChangeEncoding(pName, SQLITE_UTF8); if( rc ) goto abort_due_to_error; rc = pVtab->pModule->xRename(pVtab, pName->z); - if( isLegacy==0 ) db->flags &= ~SQLITE_LegacyAlter; + if( isLegacy==0 ) db->flags &= ~(u64)SQLITE_LegacyAlter; sqlite3VtabImportErrmsg(p, pVtab); p->expired = 0; if( rc ) goto abort_due_to_error; @@ -94280,6 +94698,22 @@ SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *pVfs){ /* #include */ +#if !defined(SQLITE_OMIT_WINDOWFUNC) +/* +** Walk all expressions linked into the list of Window objects passed +** as the second argument. +*/ +static int walkWindowList(Walker *pWalker, Window *pList){ + Window *pWin; + for(pWin=pList; pWin; pWin=pWin->pNextWin){ + if( sqlite3WalkExprList(pWalker, pWin->pOrderBy) ) return WRC_Abort; + if( sqlite3WalkExprList(pWalker, pWin->pPartition) ) return WRC_Abort; + if( sqlite3WalkExpr(pWalker, pWin->pFilter) ) return WRC_Abort; + } + return WRC_Continue; +} +#endif + /* ** Walk an expression tree. Invoke the callback once for each node ** of the expression, while descending. (In other words, the callback @@ -94319,10 +94753,7 @@ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){ } #ifndef SQLITE_OMIT_WINDOWFUNC if( ExprHasProperty(pExpr, EP_WinFunc) ){ - Window *pWin = pExpr->y.pWin; - if( sqlite3WalkExprList(pWalker, pWin->pPartition) ) return WRC_Abort; - if( sqlite3WalkExprList(pWalker, pWin->pOrderBy) ) return WRC_Abort; - if( sqlite3WalkExpr(pWalker, pWin->pFilter) ) return WRC_Abort; + if( walkWindowList(pWalker, pExpr->y.pWin) ) return WRC_Abort; } #endif } @@ -94362,6 +94793,16 @@ SQLITE_PRIVATE int sqlite3WalkSelectExpr(Walker *pWalker, Select *p){ if( sqlite3WalkExpr(pWalker, p->pHaving) ) return WRC_Abort; if( sqlite3WalkExprList(pWalker, p->pOrderBy) ) return WRC_Abort; if( sqlite3WalkExpr(pWalker, p->pLimit) ) return WRC_Abort; +#if !defined(SQLITE_OMIT_WINDOWFUNC) && !defined(SQLITE_OMIT_ALTERTABLE) + { + Parse *pParse = pWalker->pParse; + if( pParse && IN_RENAME_OBJECT ){ + int rc = walkWindowList(pWalker, p->pWinDefn); + assert( rc==WRC_Continue ); + return rc; + } + } +#endif return WRC_Continue; } @@ -94513,7 +94954,6 @@ static void resolveAlias( if( pExpr->op==TK_COLLATE ){ pDup = sqlite3ExprAddCollateString(pParse, pDup, pExpr->u.zToken); } - ExprSetProperty(pDup, EP_Alias); /* Before calling sqlite3ExprDelete(), set the EP_Static flag. This ** prevents ExprDelete() from deleting the Expr structure itself, @@ -94907,6 +95347,25 @@ static int lookupName( if( cnt==0 && zTab==0 ){ assert( pExpr->op==TK_ID ); if( ExprHasProperty(pExpr,EP_DblQuoted) ){ + /* If a double-quoted identifier does not match any known column name, + ** then treat it as a string. + ** + ** This hack was added in the early days of SQLite in a misguided attempt + ** to be compatible with MySQL 3.x, which used double-quotes for strings. + ** I now sorely regret putting in this hack. The effect of this hack is + ** that misspelled identifier names are silently converted into strings + ** rather than causing an error, to the frustration of countless + ** programmers. To all those frustrated programmers, my apologies. + ** + ** Someday, I hope to get rid of this hack. Unfortunately there is + ** a huge amount of legacy SQL that uses it. So for now, we just + ** issue a warning. + */ + sqlite3_log(SQLITE_WARNING, + "double-quoted string literal: \"%w\"", zCol); +#ifdef SQLITE_ENABLE_NORMALIZE + sqlite3VdbeAddDblquoteStr(db, pParse->pVdbe, zCol); +#endif pExpr->op = TK_STRING; pExpr->y.pTab = 0; return WRC_Prune; @@ -95273,10 +95732,10 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){ #ifndef SQLITE_OMIT_WINDOWFUNC if( pExpr->y.pWin ){ Select *pSel = pNC->pWinSelect; + sqlite3WindowUpdate(pParse, pSel->pWinDefn, pExpr->y.pWin, pDef); sqlite3WalkExprList(pWalker, pExpr->y.pWin->pPartition); sqlite3WalkExprList(pWalker, pExpr->y.pWin->pOrderBy); sqlite3WalkExpr(pWalker, pExpr->y.pWin->pFilter); - sqlite3WindowUpdate(pParse, pSel->pWinDefn, pExpr->y.pWin, pDef); if( 0==pSel->pWin || 0==sqlite3WindowCompare(pParse, pSel->pWin, pExpr->y.pWin) ){ @@ -95553,32 +96012,53 @@ static int resolveCompoundOrderBy( }else{ iCol = resolveAsName(pParse, pEList, pE); if( iCol==0 ){ - pDup = sqlite3ExprDup(db, pE, 0); + /* Now test if expression pE matches one of the values returned + ** by pSelect. In the usual case this is done by duplicating the + ** expression, resolving any symbols in it, and then comparing + ** it against each expression returned by the SELECT statement. + ** Once the comparisons are finished, the duplicate expression + ** is deleted. + ** + ** Or, if this is running as part of an ALTER TABLE operation, + ** resolve the symbols in the actual expression, not a duplicate. + ** And, if one of the comparisons is successful, leave the expression + ** as is instead of transforming it to an integer as in the usual + ** case. This allows the code in alter.c to modify column + ** refererences within the ORDER BY expression as required. */ + if( IN_RENAME_OBJECT ){ + pDup = pE; + }else{ + pDup = sqlite3ExprDup(db, pE, 0); + } if( !db->mallocFailed ){ assert(pDup); iCol = resolveOrderByTermToExprList(pParse, pSelect, pDup); } - sqlite3ExprDelete(db, pDup); + if( !IN_RENAME_OBJECT ){ + sqlite3ExprDelete(db, pDup); + } } } if( iCol>0 ){ /* Convert the ORDER BY term into an integer column number iCol, ** taking care to preserve the COLLATE clause if it exists */ - Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0); - if( pNew==0 ) return 1; - pNew->flags |= EP_IntValue; - pNew->u.iValue = iCol; - if( pItem->pExpr==pE ){ - pItem->pExpr = pNew; - }else{ - Expr *pParent = pItem->pExpr; - assert( pParent->op==TK_COLLATE ); - while( pParent->pLeft->op==TK_COLLATE ) pParent = pParent->pLeft; - assert( pParent->pLeft==pE ); - pParent->pLeft = pNew; + if( !IN_RENAME_OBJECT ){ + Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0); + if( pNew==0 ) return 1; + pNew->flags |= EP_IntValue; + pNew->u.iValue = iCol; + if( pItem->pExpr==pE ){ + pItem->pExpr = pNew; + }else{ + Expr *pParent = pItem->pExpr; + assert( pParent->op==TK_COLLATE ); + while( pParent->pLeft->op==TK_COLLATE ) pParent = pParent->pLeft; + assert( pParent->pLeft==pE ); + pParent->pLeft = pNew; + } + sqlite3ExprDelete(db, pE); + pItem->u.x.iOrderByCol = (u16)iCol; } - sqlite3ExprDelete(db, pE); - pItem->u.x.iOrderByCol = (u16)iCol; pItem->done = 1; }else{ moreToDo = 1; @@ -95637,6 +96117,38 @@ SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy( return 0; } +#ifndef SQLITE_OMIT_WINDOWFUNC +/* +** Walker callback for resolveRemoveWindows(). +*/ +static int resolveRemoveWindowsCb(Walker *pWalker, Expr *pExpr){ + if( ExprHasProperty(pExpr, EP_WinFunc) ){ + Window **pp; + for(pp=&pWalker->u.pSelect->pWin; *pp; pp=&(*pp)->pNextWin){ + if( *pp==pExpr->y.pWin ){ + *pp = (*pp)->pNextWin; + break; + } + } + } + return WRC_Continue; +} + +/* +** Remove any Window objects owned by the expression pExpr from the +** Select.pWin list of Select object pSelect. +*/ +static void resolveRemoveWindows(Select *pSelect, Expr *pExpr){ + Walker sWalker; + memset(&sWalker, 0, sizeof(Walker)); + sWalker.xExprCallback = resolveRemoveWindowsCb; + sWalker.u.pSelect = pSelect; + sqlite3WalkExpr(&sWalker, pExpr); +} +#else +# define resolveRemoveWindows(x,y) +#endif + /* ** pOrderBy is an ORDER BY or GROUP BY clause in SELECT statement pSelect. ** The Name context of the SELECT statement is pNC. zType is either @@ -95703,19 +96215,10 @@ static int resolveOrderGroupBy( } for(j=0; jpEList->nExpr; j++){ if( sqlite3ExprCompare(0, pE, pSelect->pEList->a[j].pExpr, -1)==0 ){ -#ifndef SQLITE_OMIT_WINDOWFUNC - if( ExprHasProperty(pE, EP_WinFunc) ){ - /* Since this window function is being changed into a reference - ** to the same window function the result set, remove the instance - ** of this window function from the Select.pWin list. */ - Window **pp; - for(pp=&pSelect->pWin; *pp; pp=&(*pp)->pNextWin){ - if( *pp==pE->y.pWin ){ - *pp = (*pp)->pNextWin; - } - } - } -#endif + /* Since this expresion is being changed into a reference + ** to an identical expression in the result set, remove all Window + ** objects belonging to the expression from the Select.pWin list. */ + resolveRemoveWindows(pSelect, pE); pItem->u.x.iOrderByCol = j+1; } } @@ -95927,6 +96430,17 @@ static int resolveSelectStep(Walker *pWalker, Select *p){ } } + if( IN_RENAME_OBJECT ){ + Window *pWin; + for(pWin=p->pWinDefn; pWin; pWin=pWin->pNextWin){ + if( sqlite3ResolveExprListNames(&sNC, pWin->pOrderBy) + || sqlite3ResolveExprListNames(&sNC, pWin->pPartition) + ){ + return WRC_Abort; + } + } + } + /* If this is part of a compound SELECT, check that it has the right ** number of expressions in the select list. */ if( p->pNext && p->pEList->nExpr!=p->pNext->pEList->nExpr ){ @@ -96077,38 +96591,47 @@ SQLITE_PRIVATE void sqlite3ResolveSelectNames( } /* -** Resolve names in expressions that can only reference a single table: +** Resolve names in expressions that can only reference a single table +** or which cannot reference any tables at all. Examples: ** -** * CHECK constraints -** * WHERE clauses on partial indices +** (1) CHECK constraints +** (2) WHERE clauses on partial indices +** (3) Expressions in indexes on expressions +** (4) Expression arguments to VACUUM INTO. ** -** The Expr.iTable value for Expr.op==TK_COLUMN nodes of the expression -** is set to -1 and the Expr.iColumn value is set to the column number. +** In all cases except (4), the Expr.iTable value for Expr.op==TK_COLUMN +** nodes of the expression is set to -1 and the Expr.iColumn value is +** set to the column number. In case (4), TK_COLUMN nodes cause an error. ** ** Any errors cause an error message to be set in pParse. */ -SQLITE_PRIVATE void sqlite3ResolveSelfReference( +SQLITE_PRIVATE int sqlite3ResolveSelfReference( Parse *pParse, /* Parsing context */ - Table *pTab, /* The table being referenced */ - int type, /* NC_IsCheck or NC_PartIdx or NC_IdxExpr */ + Table *pTab, /* The table being referenced, or NULL */ + int type, /* NC_IsCheck or NC_PartIdx or NC_IdxExpr, or 0 */ Expr *pExpr, /* Expression to resolve. May be NULL. */ ExprList *pList /* Expression list to resolve. May be NULL. */ ){ SrcList sSrc; /* Fake SrcList for pParse->pNewTable */ NameContext sNC; /* Name context for pParse->pNewTable */ + int rc; - assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr ); + assert( type==0 || pTab!=0 ); + assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr || pTab==0 ); memset(&sNC, 0, sizeof(sNC)); memset(&sSrc, 0, sizeof(sSrc)); - sSrc.nSrc = 1; - sSrc.a[0].zName = pTab->zName; - sSrc.a[0].pTab = pTab; - sSrc.a[0].iCursor = -1; + if( pTab ){ + sSrc.nSrc = 1; + sSrc.a[0].zName = pTab->zName; + sSrc.a[0].pTab = pTab; + sSrc.a[0].iCursor = -1; + } sNC.pParse = pParse; sNC.pSrcList = &sSrc; sNC.ncFlags = type; - if( sqlite3ResolveExprNames(&sNC, pExpr) ) return; - if( pList ) sqlite3ResolveExprListNames(&sNC, pList); + if( (rc = sqlite3ResolveExprNames(&sNC, pExpr))!=SQLITE_OK ) return rc; + if( pList ) rc = sqlite3ResolveExprListNames(&sNC, pList); + return rc; } /************** End of resolve.c *********************************************/ @@ -96256,8 +96779,8 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){ while( p ){ int op = p->op; if( p->flags & EP_Generic ) break; - if( (op==TK_AGG_COLUMN || op==TK_COLUMN - || op==TK_REGISTER || op==TK_TRIGGER) + if( op==TK_REGISTER ) op = p->op2; + if( (op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_TRIGGER) && p->y.pTab!=0 ){ /* op==TK_REGISTER && p->y.pTab!=0 happens when pExpr was originally @@ -96273,7 +96796,7 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){ p = p->pLeft; continue; } - if( op==TK_COLLATE || (op==TK_REGISTER && p->op2==TK_COLLATE) ){ + if( op==TK_COLLATE ){ pColl = sqlite3GetCollSeq(pParse, ENC(db), 0, p->u.zToken); break; } @@ -96580,6 +97103,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprForVectorField( }else{ if( pVector->op==TK_VECTOR ) pVector = pVector->x.pList->a[iField].pExpr; pRet = sqlite3ExprDup(pParse->db, pVector, 0); + sqlite3RenameTokenRemap(pParse, pRet, pVector); } return pRet; } @@ -96596,7 +97120,7 @@ static int exprCodeSubselect(Parse *pParse, Expr *pExpr){ int reg = 0; #ifndef SQLITE_OMIT_SUBQUERY if( pExpr->op==TK_SELECT ){ - reg = sqlite3CodeSubselect(pParse, pExpr, 0, 0); + reg = sqlite3CodeSubselect(pParse, pExpr); } #endif return reg; @@ -96668,7 +97192,7 @@ static void codeVectorCompare( int regLeft = 0; int regRight = 0; u8 opx = op; - int addrDone = sqlite3VdbeMakeLabel(v); + int addrDone = sqlite3VdbeMakeLabel(pParse); if( nLeft!=sqlite3ExprVectorSize(pRight) ){ sqlite3ErrorMsg(pParse, "row value misused"); @@ -96895,8 +97419,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprAlloc( if( pToken->n ) memcpy(pNew->u.zToken, pToken->z, pToken->n); pNew->u.zToken[pToken->n] = 0; if( dequote && sqlite3Isquote(pNew->u.zToken[0]) ){ - if( pNew->u.zToken[0]=='"' ) pNew->flags |= EP_DblQuoted; - sqlite3Dequote(pNew->u.zToken); + sqlite3DequoteExpr(pNew); } } } @@ -96965,7 +97488,7 @@ SQLITE_PRIVATE Expr *sqlite3PExpr( Expr *pRight /* Right operand */ ){ Expr *p; - if( op==TK_AND && pParse->nErr==0 ){ + if( op==TK_AND && pParse->nErr==0 && !IN_RENAME_OBJECT ){ /* Take advantage of short-circuit false optimization for AND */ p = sqlite3ExprAnd(pParse->db, pLeft, pRight); }else{ @@ -97214,6 +97737,16 @@ static int exprStructSize(Expr *p){ return EXPR_FULLSIZE; } +/* +** Copy the complete content of an Expr node, taking care not to read +** past the end of the structure for a reduced-size version of the source +** Expr. +*/ +static void exprNodeCopy(Expr *pDest, Expr *pSrc){ + memset(pDest, 0, sizeof(Expr)); + memcpy(pDest, pSrc, exprStructSize(pSrc)); +} + /* ** The dupedExpr*Size() routines each return the number of bytes required ** to store a copy of an expression or expression tree. They differ in @@ -97445,6 +97978,36 @@ static With *withDup(sqlite3 *db, With *p){ # define withDup(x,y) 0 #endif +#ifndef SQLITE_OMIT_WINDOWFUNC +/* +** The gatherSelectWindows() procedure and its helper routine +** gatherSelectWindowsCallback() are used to scan all the expressions +** an a newly duplicated SELECT statement and gather all of the Window +** objects found there, assembling them onto the linked list at Select->pWin. +*/ +static int gatherSelectWindowsCallback(Walker *pWalker, Expr *pExpr){ + if( pExpr->op==TK_FUNCTION && pExpr->y.pWin!=0 ){ + assert( ExprHasProperty(pExpr, EP_WinFunc) ); + pExpr->y.pWin->pNextWin = pWalker->u.pSelect->pWin; + pWalker->u.pSelect->pWin = pExpr->y.pWin; + } + return WRC_Continue; +} +static int gatherSelectWindowsSelectCallback(Walker *pWalker, Select *p){ + return p==pWalker->u.pSelect ? WRC_Continue : WRC_Prune; +} +static void gatherSelectWindows(Select *p){ + Walker w; + w.xExprCallback = gatherSelectWindowsCallback; + w.xSelectCallback = gatherSelectWindowsSelectCallback; + w.xSelectCallback2 = 0; + w.pParse = 0; + w.u.pSelect = p; + sqlite3WalkSelect(&w, p); +} +#endif + + /* ** The following group of routines make deep copies of expressions, ** expression lists, ID lists, and select statements. The copies can @@ -97612,6 +98175,7 @@ SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, Select *pDup, int flags){ #ifndef SQLITE_OMIT_WINDOWFUNC pNew->pWin = 0; pNew->pWinDefn = sqlite3WindowListDup(db, p->pWinDefn); + if( p->pWin ) gatherSelectWindows(pNew); #endif pNew->selId = p->selId; *pp = pNew; @@ -97744,6 +98308,9 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector( } vector_append_error: + if( IN_RENAME_OBJECT ){ + sqlite3RenameExprUnmap(pParse, pExpr); + } sqlite3ExprDelete(db, pExpr); sqlite3IdListDelete(db, pColumns); return pList; @@ -97887,8 +98454,9 @@ SQLITE_PRIVATE int sqlite3SelectWalkFail(Walker *pWalker, Select *NotUsed){ */ SQLITE_PRIVATE int sqlite3ExprIdToTrueFalse(Expr *pExpr){ assert( pExpr->op==TK_ID || pExpr->op==TK_STRING ); - if( sqlite3StrICmp(pExpr->u.zToken, "true")==0 - || sqlite3StrICmp(pExpr->u.zToken, "false")==0 + if( !ExprHasProperty(pExpr, EP_Quoted) + && (sqlite3StrICmp(pExpr->u.zToken, "true")==0 + || sqlite3StrICmp(pExpr->u.zToken, "false")==0) ){ pExpr->op = TK_TRUEFALSE; return 1; @@ -98197,7 +98765,9 @@ SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr *p, int *pValue){ */ SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){ u8 op; - while( p->op==TK_UPLUS || p->op==TK_UMINUS ){ p = p->pLeft; } + while( p->op==TK_UPLUS || p->op==TK_UMINUS ){ + p = p->pLeft; + } op = p->op; if( op==TK_REGISTER ) op = p->op2; switch( op ){ @@ -98264,14 +98834,6 @@ SQLITE_PRIVATE int sqlite3IsRowid(const char *z){ if( sqlite3StrICmp(z, "OID")==0 ) return 1; return 0; } -#ifdef SQLITE_ENABLE_NORMALIZE -SQLITE_PRIVATE int sqlite3IsRowidN(const char *z, int n){ - if( sqlite3StrNICmp(z, "_ROWID_", n)==0 ) return 1; - if( sqlite3StrNICmp(z, "ROWID", n)==0 ) return 1; - if( sqlite3StrNICmp(z, "OID", n)==0 ) return 1; - return 0; -} -#endif /* ** pX is the RHS of an IN operator. If pX is a SELECT statement @@ -98441,7 +99003,8 @@ SQLITE_PRIVATE int sqlite3FindInIndex( Expr *pX, /* The right-hand side (RHS) of the IN operator */ u32 inFlags, /* IN_INDEX_LOOP, _MEMBERSHIP, and/or _NOOP_OK */ int *prRhsHasNull, /* Register holding NULL status. See notes */ - int *aiMap /* Mapping from Index fields to RHS fields */ + int *aiMap, /* Mapping from Index fields to RHS fields */ + int *piTab /* OUT: index to use */ ){ Select *p; /* SELECT to the right of IN operator */ int eType = 0; /* Type of RHS table. IN_INDEX_* */ @@ -98536,6 +99099,7 @@ SQLITE_PRIVATE int sqlite3FindInIndex( Bitmask colUsed; /* Columns of the index used */ Bitmask mCol; /* Mask for the current column */ if( pIdx->nColumnpPartIdxWhere!=0 ) continue; /* Maximum nColumn is BMS-2, not BMS-1, so that we can compute ** BITMASK(nExpr) without overflowing */ testcase( pIdx->nColumn==BMS-2 ); @@ -98626,16 +99190,15 @@ SQLITE_PRIVATE int sqlite3FindInIndex( eType = IN_INDEX_EPH; if( inFlags & IN_INDEX_LOOP ){ pParse->nQueryLoop = 0; - if( pX->pLeft->iColumn<0 && !ExprHasProperty(pX, EP_xIsSelect) ){ - eType = IN_INDEX_ROWID; - } }else if( prRhsHasNull ){ *prRhsHasNull = rMayHaveNull = ++pParse->nMem; } - sqlite3CodeSubselect(pParse, pX, rMayHaveNull, eType==IN_INDEX_ROWID); + assert( pX->op==TK_IN ); + sqlite3CodeRhsOfIN(pParse, pX, iTab); + if( rMayHaveNull ){ + sqlite3SetHasNullFlag(v, iTab, rMayHaveNull); + } pParse->nQueryLoop = savedNQueryLoop; - }else{ - pX->iTable = iTab; } if( aiMap && eType!=IN_INDEX_INDEX_ASC && eType!=IN_INDEX_INDEX_DESC ){ @@ -98643,6 +99206,7 @@ SQLITE_PRIVATE int sqlite3FindInIndex( n = sqlite3ExprVectorSize(pX->pLeft); for(i=0; iiTable, +** however the cursor number returned might not be the same, as it might +** have been duplicated using OP_OpenDup. ** -** If parameter isRowid is non-zero, then expression pExpr is guaranteed -** to be of the form " IN (?, ?, ?)", where is a reference -** to some integer key column of a table B-Tree. In this case, use an -** intkey B-Tree to store the set of IN(...) values instead of the usual -** (slower) variable length keys B-Tree. +** If the LHS expression ("x" in the examples) is a column value, or +** the SELECT statement returns a column value, then the affinity of that +** column is used to build the index keys. If both 'x' and the +** SELECT... statement are columns, then numeric affinity is used +** if either column has NUMERIC or INTEGER affinity. If neither +** 'x' nor the SELECT... statement are columns, then numeric affinity +** is used. +*/ +SQLITE_PRIVATE void sqlite3CodeRhsOfIN( + Parse *pParse, /* Parsing context */ + Expr *pExpr, /* The IN operator */ + int iTab /* Use this cursor number */ +){ + int addrOnce = 0; /* Address of the OP_Once instruction at top */ + int addr; /* Address of OP_OpenEphemeral instruction */ + Expr *pLeft; /* the LHS of the IN operator */ + KeyInfo *pKeyInfo = 0; /* Key information */ + int nVal; /* Size of vector pLeft */ + Vdbe *v; /* The prepared statement under construction */ + + v = pParse->pVdbe; + assert( v!=0 ); + + /* The evaluation of the IN must be repeated every time it + ** is encountered if any of the following is true: + ** + ** * The right-hand side is a correlated subquery + ** * The right-hand side is an expression list containing variables + ** * We are inside a trigger + ** + ** If all of the above are false, then we can compute the RHS just once + ** and reuse it many names. + */ + if( !ExprHasProperty(pExpr, EP_VarSelect) && pParse->iSelfTab==0 ){ + /* Reuse of the RHS is allowed */ + /* If this routine has already been coded, but the previous code + ** might not have been invoked yet, so invoke it now as a subroutine. + */ + if( ExprHasProperty(pExpr, EP_Subrtn) ){ + addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); + if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + ExplainQueryPlan((pParse, 0, "REUSE LIST SUBQUERY %d", + pExpr->x.pSelect->selId)); + } + sqlite3VdbeAddOp2(v, OP_Gosub, pExpr->y.sub.regReturn, + pExpr->y.sub.iAddr); + sqlite3VdbeAddOp2(v, OP_OpenDup, iTab, pExpr->iTable); + sqlite3VdbeJumpHere(v, addrOnce); + return; + } + + /* Begin coding the subroutine */ + ExprSetProperty(pExpr, EP_Subrtn); + pExpr->y.sub.regReturn = ++pParse->nMem; + pExpr->y.sub.iAddr = + sqlite3VdbeAddOp2(v, OP_Integer, 0, pExpr->y.sub.regReturn) + 1; + VdbeComment((v, "return address")); + + addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); + } + + /* Check to see if this is a vector IN operator */ + pLeft = pExpr->pLeft; + nVal = sqlite3ExprVectorSize(pLeft); + + /* Construct the ephemeral table that will contain the content of + ** RHS of the IN operator. + */ + pExpr->iTable = iTab; + addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, nVal); +#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS + if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + VdbeComment((v, "Result of SELECT %u", pExpr->x.pSelect->selId)); + }else{ + VdbeComment((v, "RHS of IN operator")); + } +#endif + pKeyInfo = sqlite3KeyInfoAlloc(pParse->db, nVal, 1); + + if( ExprHasProperty(pExpr, EP_xIsSelect) ){ + /* Case 1: expr IN (SELECT ...) + ** + ** Generate code to write the results of the select into the temporary + ** table allocated and opened above. + */ + Select *pSelect = pExpr->x.pSelect; + ExprList *pEList = pSelect->pEList; + + ExplainQueryPlan((pParse, 1, "%sLIST SUBQUERY %d", + addrOnce?"":"CORRELATED ", pSelect->selId + )); + /* If the LHS and RHS of the IN operator do not match, that + ** error will have been caught long before we reach this point. */ + if( ALWAYS(pEList->nExpr==nVal) ){ + SelectDest dest; + int i; + sqlite3SelectDestInit(&dest, SRT_Set, iTab); + dest.zAffSdst = exprINAffinity(pParse, pExpr); + pSelect->iLimit = 0; + testcase( pSelect->selFlags & SF_Distinct ); + testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */ + if( sqlite3Select(pParse, pSelect, &dest) ){ + sqlite3DbFree(pParse->db, dest.zAffSdst); + sqlite3KeyInfoUnref(pKeyInfo); + return; + } + sqlite3DbFree(pParse->db, dest.zAffSdst); + assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */ + assert( pEList!=0 ); + assert( pEList->nExpr>0 ); + assert( sqlite3KeyInfoIsWriteable(pKeyInfo) ); + for(i=0; iaColl[i] = sqlite3BinaryCompareCollSeq( + pParse, p, pEList->a[i].pExpr + ); + } + } + }else if( ALWAYS(pExpr->x.pList!=0) ){ + /* Case 2: expr IN (exprlist) + ** + ** For each expression, build an index key from the evaluation and + ** store it in the temporary table. If is a column, then use + ** that columns affinity when building index keys. If is not + ** a column, use numeric affinity. + */ + char affinity; /* Affinity of the LHS of the IN */ + int i; + ExprList *pList = pExpr->x.pList; + struct ExprList_item *pItem; + int r1, r2, r3; + affinity = sqlite3ExprAffinity(pLeft); + if( !affinity ){ + affinity = SQLITE_AFF_BLOB; + } + if( pKeyInfo ){ + assert( sqlite3KeyInfoIsWriteable(pKeyInfo) ); + pKeyInfo->aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft); + } + + /* Loop through each expression in . */ + r1 = sqlite3GetTempReg(pParse); + r2 = sqlite3GetTempReg(pParse); + for(i=pList->nExpr, pItem=pList->a; i>0; i--, pItem++){ + Expr *pE2 = pItem->pExpr; + + /* If the expression is not constant then we will need to + ** disable the test that was generated above that makes sure + ** this code only executes once. Because for a non-constant + ** expression we need to rerun this code each time. + */ + if( addrOnce && !sqlite3ExprIsConstant(pE2) ){ + sqlite3VdbeChangeToNoop(v, addrOnce); + addrOnce = 0; + } + + /* Evaluate the expression and insert it into the temp table */ + r3 = sqlite3ExprCodeTarget(pParse, pE2, r1); + sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1); + sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iTab, r2, r3, 1); + } + sqlite3ReleaseTempReg(pParse, r1); + sqlite3ReleaseTempReg(pParse, r2); + } + if( pKeyInfo ){ + sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO); + } + if( addrOnce ){ + sqlite3VdbeJumpHere(v, addrOnce); + /* Subroutine return */ + sqlite3VdbeAddOp1(v, OP_Return, pExpr->y.sub.regReturn); + sqlite3VdbeChangeP1(v, pExpr->y.sub.iAddr-1, sqlite3VdbeCurrentAddr(v)-1); + } +} +#endif /* SQLITE_OMIT_SUBQUERY */ + +/* +** Generate code for scalar subqueries used as a subquery expression +** or EXISTS operator: ** -** If rMayHaveNull is non-zero, that means that the operation is an IN -** (not a SELECT or EXISTS) and that the RHS might contains NULLs. -** All this routine does is initialize the register given by rMayHaveNull -** to NULL. Calling routines will take care of changing this register -** value to non-NULL if the RHS is NULL-free. +** (SELECT a FROM b) -- subquery +** EXISTS (SELECT a FROM b) -- EXISTS subquery ** -** For a SELECT or EXISTS operator, return the register that holds the -** result. For a multi-column SELECT, the result is stored in a contiguous -** array of registers and the return value is the register of the left-most -** result column. Return 0 for IN operators or if an error occurs. +** The pExpr parameter is the SELECT or EXISTS operator to be coded. +** +** The register that holds the result. For a multi-column SELECT, +** the result is stored in a contiguous array of registers and the +** return value is the register of the left-most result column. +** Return 0 if an error occurs. */ #ifndef SQLITE_OMIT_SUBQUERY -SQLITE_PRIVATE int sqlite3CodeSubselect( - Parse *pParse, /* Parsing context */ - Expr *pExpr, /* The IN, SELECT, or EXISTS operator */ - int rHasNullFlag, /* Register that records whether NULLs exist in RHS */ - int isRowid /* If true, LHS of IN operator is a rowid */ -){ - int jmpIfDynamic = -1; /* One-time test address */ - int rReg = 0; /* Register storing resulting */ - Vdbe *v = sqlite3GetVdbe(pParse); - if( NEVER(v==0) ) return 0; +SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ + int addrOnce = 0; /* Address of OP_Once at top of subroutine */ + int rReg = 0; /* Register storing resulting */ + Select *pSel; /* SELECT statement to encode */ + SelectDest dest; /* How to deal with SELECT result */ + int nReg; /* Registers to allocate */ + Expr *pLimit; /* New limit expression */ - /* The evaluation of the IN/EXISTS/SELECT must be repeated every time it + Vdbe *v = pParse->pVdbe; + assert( v!=0 ); + testcase( pExpr->op==TK_EXISTS ); + testcase( pExpr->op==TK_SELECT ); + assert( pExpr->op==TK_EXISTS || pExpr->op==TK_SELECT ); + assert( ExprHasProperty(pExpr, EP_xIsSelect) ); + pSel = pExpr->x.pSelect; + + /* The evaluation of the EXISTS/SELECT must be repeated every time it ** is encountered if any of the following is true: ** ** * The right-hand side is a correlated subquery @@ -98768,208 +99513,70 @@ SQLITE_PRIVATE int sqlite3CodeSubselect( ** save the results, and reuse the same result on subsequent invocations. */ if( !ExprHasProperty(pExpr, EP_VarSelect) ){ - jmpIfDynamic = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); - } - - switch( pExpr->op ){ - case TK_IN: { - int addr; /* Address of OP_OpenEphemeral instruction */ - Expr *pLeft = pExpr->pLeft; /* the LHS of the IN operator */ - KeyInfo *pKeyInfo = 0; /* Key information */ - int nVal; /* Size of vector pLeft */ - - nVal = sqlite3ExprVectorSize(pLeft); - assert( !isRowid || nVal==1 ); - - /* Whether this is an 'x IN(SELECT...)' or an 'x IN()' - ** expression it is handled the same way. An ephemeral table is - ** filled with index keys representing the results from the - ** SELECT or the . - ** - ** If the 'x' expression is a column value, or the SELECT... - ** statement returns a column value, then the affinity of that - ** column is used to build the index keys. If both 'x' and the - ** SELECT... statement are columns, then numeric affinity is used - ** if either column has NUMERIC or INTEGER affinity. If neither - ** 'x' nor the SELECT... statement are columns, then numeric affinity - ** is used. - */ - pExpr->iTable = pParse->nTab++; - addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, - pExpr->iTable, (isRowid?0:nVal)); - pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, nVal, 1); - - if( ExprHasProperty(pExpr, EP_xIsSelect) ){ - /* Case 1: expr IN (SELECT ...) - ** - ** Generate code to write the results of the select into the temporary - ** table allocated and opened above. - */ - Select *pSelect = pExpr->x.pSelect; - ExprList *pEList = pSelect->pEList; - - ExplainQueryPlan((pParse, 1, "%sLIST SUBQUERY", - jmpIfDynamic>=0?"":"CORRELATED " - )); - assert( !isRowid ); - /* If the LHS and RHS of the IN operator do not match, that - ** error will have been caught long before we reach this point. */ - if( ALWAYS(pEList->nExpr==nVal) ){ - SelectDest dest; - int i; - sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable); - dest.zAffSdst = exprINAffinity(pParse, pExpr); - pSelect->iLimit = 0; - testcase( pSelect->selFlags & SF_Distinct ); - testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */ - if( sqlite3Select(pParse, pSelect, &dest) ){ - sqlite3DbFree(pParse->db, dest.zAffSdst); - sqlite3KeyInfoUnref(pKeyInfo); - return 0; - } - sqlite3DbFree(pParse->db, dest.zAffSdst); - assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */ - assert( pEList!=0 ); - assert( pEList->nExpr>0 ); - assert( sqlite3KeyInfoIsWriteable(pKeyInfo) ); - for(i=0; iaColl[i] = sqlite3BinaryCompareCollSeq( - pParse, p, pEList->a[i].pExpr - ); - } - } - }else if( ALWAYS(pExpr->x.pList!=0) ){ - /* Case 2: expr IN (exprlist) - ** - ** For each expression, build an index key from the evaluation and - ** store it in the temporary table. If is a column, then use - ** that columns affinity when building index keys. If is not - ** a column, use numeric affinity. - */ - char affinity; /* Affinity of the LHS of the IN */ - int i; - ExprList *pList = pExpr->x.pList; - struct ExprList_item *pItem; - int r1, r2, r3; - affinity = sqlite3ExprAffinity(pLeft); - if( !affinity ){ - affinity = SQLITE_AFF_BLOB; - } - if( pKeyInfo ){ - assert( sqlite3KeyInfoIsWriteable(pKeyInfo) ); - pKeyInfo->aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft); - } - - /* Loop through each expression in . */ - r1 = sqlite3GetTempReg(pParse); - r2 = sqlite3GetTempReg(pParse); - if( isRowid ) sqlite3VdbeAddOp4(v, OP_Blob, 0, r2, 0, "", P4_STATIC); - for(i=pList->nExpr, pItem=pList->a; i>0; i--, pItem++){ - Expr *pE2 = pItem->pExpr; - int iValToIns; - - /* If the expression is not constant then we will need to - ** disable the test that was generated above that makes sure - ** this code only executes once. Because for a non-constant - ** expression we need to rerun this code each time. - */ - if( jmpIfDynamic>=0 && !sqlite3ExprIsConstant(pE2) ){ - sqlite3VdbeChangeToNoop(v, jmpIfDynamic); - jmpIfDynamic = -1; - } - - /* Evaluate the expression and insert it into the temp table */ - if( isRowid && sqlite3ExprIsInteger(pE2, &iValToIns) ){ - sqlite3VdbeAddOp3(v, OP_InsertInt, pExpr->iTable, r2, iValToIns); - }else{ - r3 = sqlite3ExprCodeTarget(pParse, pE2, r1); - if( isRowid ){ - sqlite3VdbeAddOp2(v, OP_MustBeInt, r3, - sqlite3VdbeCurrentAddr(v)+2); - VdbeCoverage(v); - sqlite3VdbeAddOp3(v, OP_Insert, pExpr->iTable, r2, r3); - }else{ - sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1); - sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pExpr->iTable, r2, r3, 1); - } - } - } - sqlite3ReleaseTempReg(pParse, r1); - sqlite3ReleaseTempReg(pParse, r2); - } - if( pKeyInfo ){ - sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO); - } - break; + /* If this routine has already been coded, then invoke it as a + ** subroutine. */ + if( ExprHasProperty(pExpr, EP_Subrtn) ){ + ExplainQueryPlan((pParse, 0, "REUSE SUBQUERY %d", pSel->selId)); + sqlite3VdbeAddOp2(v, OP_Gosub, pExpr->y.sub.regReturn, + pExpr->y.sub.iAddr); + return pExpr->iTable; } - case TK_EXISTS: - case TK_SELECT: - default: { - /* Case 3: (SELECT ... FROM ...) - ** or: EXISTS(SELECT ... FROM ...) - ** - ** For a SELECT, generate code to put the values for all columns of - ** the first row into an array of registers and return the index of - ** the first register. - ** - ** If this is an EXISTS, write an integer 0 (not exists) or 1 (exists) - ** into a register and return that register number. - ** - ** In both cases, the query is augmented with "LIMIT 1". Any - ** preexisting limit is discarded in place of the new LIMIT 1. - */ - Select *pSel; /* SELECT statement to encode */ - SelectDest dest; /* How to deal with SELECT result */ - int nReg; /* Registers to allocate */ - Expr *pLimit; /* New limit expression */ + /* Begin coding the subroutine */ + ExprSetProperty(pExpr, EP_Subrtn); + pExpr->y.sub.regReturn = ++pParse->nMem; + pExpr->y.sub.iAddr = + sqlite3VdbeAddOp2(v, OP_Integer, 0, pExpr->y.sub.regReturn) + 1; + VdbeComment((v, "return address")); - testcase( pExpr->op==TK_EXISTS ); - testcase( pExpr->op==TK_SELECT ); - assert( pExpr->op==TK_EXISTS || pExpr->op==TK_SELECT ); - assert( ExprHasProperty(pExpr, EP_xIsSelect) ); - - pSel = pExpr->x.pSelect; - ExplainQueryPlan((pParse, 1, "%sSCALAR SUBQUERY", - jmpIfDynamic>=0?"":"CORRELATED ")); - nReg = pExpr->op==TK_SELECT ? pSel->pEList->nExpr : 1; - sqlite3SelectDestInit(&dest, 0, pParse->nMem+1); - pParse->nMem += nReg; - if( pExpr->op==TK_SELECT ){ - dest.eDest = SRT_Mem; - dest.iSdst = dest.iSDParm; - dest.nSdst = nReg; - sqlite3VdbeAddOp3(v, OP_Null, 0, dest.iSDParm, dest.iSDParm+nReg-1); - VdbeComment((v, "Init subquery result")); - }else{ - dest.eDest = SRT_Exists; - sqlite3VdbeAddOp2(v, OP_Integer, 0, dest.iSDParm); - VdbeComment((v, "Init EXISTS result")); - } - pLimit = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[1], 0); - if( pSel->pLimit ){ - sqlite3ExprDelete(pParse->db, pSel->pLimit->pLeft); - pSel->pLimit->pLeft = pLimit; - }else{ - pSel->pLimit = sqlite3PExpr(pParse, TK_LIMIT, pLimit, 0); - } - pSel->iLimit = 0; - if( sqlite3Select(pParse, pSel, &dest) ){ - return 0; - } - rReg = dest.iSDParm; - ExprSetVVAProperty(pExpr, EP_NoReduce); - break; - } + addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); } - - if( rHasNullFlag ){ - sqlite3SetHasNullFlag(v, pExpr->iTable, rHasNullFlag); + + /* For a SELECT, generate code to put the values for all columns of + ** the first row into an array of registers and return the index of + ** the first register. + ** + ** If this is an EXISTS, write an integer 0 (not exists) or 1 (exists) + ** into a register and return that register number. + ** + ** In both cases, the query is augmented with "LIMIT 1". Any + ** preexisting limit is discarded in place of the new LIMIT 1. + */ + ExplainQueryPlan((pParse, 1, "%sSCALAR SUBQUERY %d", + addrOnce?"":"CORRELATED ", pSel->selId)); + nReg = pExpr->op==TK_SELECT ? pSel->pEList->nExpr : 1; + sqlite3SelectDestInit(&dest, 0, pParse->nMem+1); + pParse->nMem += nReg; + if( pExpr->op==TK_SELECT ){ + dest.eDest = SRT_Mem; + dest.iSdst = dest.iSDParm; + dest.nSdst = nReg; + sqlite3VdbeAddOp3(v, OP_Null, 0, dest.iSDParm, dest.iSDParm+nReg-1); + VdbeComment((v, "Init subquery result")); + }else{ + dest.eDest = SRT_Exists; + sqlite3VdbeAddOp2(v, OP_Integer, 0, dest.iSDParm); + VdbeComment((v, "Init EXISTS result")); } + pLimit = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[1], 0); + if( pSel->pLimit ){ + sqlite3ExprDelete(pParse->db, pSel->pLimit->pLeft); + pSel->pLimit->pLeft = pLimit; + }else{ + pSel->pLimit = sqlite3PExpr(pParse, TK_LIMIT, pLimit, 0); + } + pSel->iLimit = 0; + if( sqlite3Select(pParse, pSel, &dest) ){ + return 0; + } + pExpr->iTable = rReg = dest.iSDParm; + ExprSetVVAProperty(pExpr, EP_NoReduce); + if( addrOnce ){ + sqlite3VdbeJumpHere(v, addrOnce); - if( jmpIfDynamic>=0 ){ - sqlite3VdbeJumpHere(v, jmpIfDynamic); + /* Subroutine return */ + sqlite3VdbeAddOp1(v, OP_Return, pExpr->y.sub.regReturn); + sqlite3VdbeChangeP1(v, pExpr->y.sub.iAddr-1, sqlite3VdbeCurrentAddr(v)-1); } return rReg; @@ -99046,6 +99653,7 @@ static void sqlite3ExprCodeIN( int addrTruthOp; /* Address of opcode that determines the IN is true */ int destNotNull; /* Jump here if a comparison is not true in step 6 */ int addrTop; /* Top of the step-6 loop */ + int iTab = 0; /* Index to use */ pLeft = pExpr->pLeft; if( sqlite3ExprCheckIN(pParse, pExpr) ) return; @@ -99057,7 +99665,7 @@ static void sqlite3ExprCodeIN( if( pParse->db->mallocFailed ) goto sqlite3ExprCodeIN_oom_error; /* Attempt to compute the RHS. After this step, if anything other than - ** IN_INDEX_NOOP is returned, the table opened ith cursor pExpr->iTable + ** IN_INDEX_NOOP is returned, the table opened with cursor iTab ** contains the values that make up the RHS. If IN_INDEX_NOOP is returned, ** the RHS has not yet been coded. */ v = pParse->pVdbe; @@ -99065,7 +99673,8 @@ static void sqlite3ExprCodeIN( VdbeNoopComment((v, "begin IN expr")); eType = sqlite3FindInIndex(pParse, pExpr, IN_INDEX_MEMBERSHIP | IN_INDEX_NOOP_OK, - destIfFalse==destIfNull ? 0 : &rRhsHasNull, aiMap); + destIfFalse==destIfNull ? 0 : &rRhsHasNull, + aiMap, &iTab); assert( pParse->nErr || nVector==1 || eType==IN_INDEX_EPH || eType==IN_INDEX_INDEX_ASC || eType==IN_INDEX_INDEX_DESC @@ -99111,7 +99720,7 @@ static void sqlite3ExprCodeIN( if( eType==IN_INDEX_NOOP ){ ExprList *pList = pExpr->x.pList; CollSeq *pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft); - int labelOk = sqlite3VdbeMakeLabel(v); + int labelOk = sqlite3VdbeMakeLabel(pParse); int r2, regToFree; int regCkNull = 0; int ii; @@ -99155,7 +99764,7 @@ static void sqlite3ExprCodeIN( if( destIfNull==destIfFalse ){ destStep2 = destIfFalse; }else{ - destStep2 = destStep6 = sqlite3VdbeMakeLabel(v); + destStep2 = destStep6 = sqlite3VdbeMakeLabel(pParse); } for(i=0; ipLeft, i); @@ -99173,19 +99782,19 @@ static void sqlite3ExprCodeIN( /* In this case, the RHS is the ROWID of table b-tree and so we also ** know that the RHS is non-NULL. Hence, we combine steps 3 and 4 ** into a single opcode. */ - sqlite3VdbeAddOp3(v, OP_SeekRowid, pExpr->iTable, destIfFalse, rLhs); + sqlite3VdbeAddOp3(v, OP_SeekRowid, iTab, destIfFalse, rLhs); VdbeCoverage(v); addrTruthOp = sqlite3VdbeAddOp0(v, OP_Goto); /* Return True */ }else{ sqlite3VdbeAddOp4(v, OP_Affinity, rLhs, nVector, 0, zAff, nVector); if( destIfFalse==destIfNull ){ /* Combine Step 3 and Step 5 into a single opcode */ - sqlite3VdbeAddOp4Int(v, OP_NotFound, pExpr->iTable, destIfFalse, + sqlite3VdbeAddOp4Int(v, OP_NotFound, iTab, destIfFalse, rLhs, nVector); VdbeCoverage(v); goto sqlite3ExprCodeIN_finished; } /* Ordinary Step 3, for the case where FALSE and NULL are distinct */ - addrTruthOp = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, + addrTruthOp = sqlite3VdbeAddOp4Int(v, OP_Found, iTab, 0, rLhs, nVector); VdbeCoverage(v); } @@ -99210,10 +99819,10 @@ static void sqlite3ExprCodeIN( ** of the RHS. */ if( destStep6 ) sqlite3VdbeResolveLabel(v, destStep6); - addrTop = sqlite3VdbeAddOp2(v, OP_Rewind, pExpr->iTable, destIfFalse); + addrTop = sqlite3VdbeAddOp2(v, OP_Rewind, iTab, destIfFalse); VdbeCoverage(v); if( nVector>1 ){ - destNotNull = sqlite3VdbeMakeLabel(v); + destNotNull = sqlite3VdbeMakeLabel(pParse); }else{ /* For nVector==1, combine steps 6 and 7 by immediately returning ** FALSE if the first comparison is not NULL */ @@ -99225,7 +99834,7 @@ static void sqlite3ExprCodeIN( int r3 = sqlite3GetTempReg(pParse); p = sqlite3VectorFieldSubexpr(pLeft, i); pColl = sqlite3ExprCollSeq(pParse, p); - sqlite3VdbeAddOp3(v, OP_Column, pExpr->iTable, i, r3); + sqlite3VdbeAddOp3(v, OP_Column, iTab, i, r3); sqlite3VdbeAddOp4(v, OP_Ne, rLhs+i, destNotNull, r3, (void*)pColl, P4_COLLSEQ); VdbeCoverage(v); @@ -99234,7 +99843,7 @@ static void sqlite3ExprCodeIN( sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfNull); if( nVector>1 ){ sqlite3VdbeResolveLabel(v, destNotNull); - sqlite3VdbeAddOp2(v, OP_Next, pExpr->iTable, addrTop+1); + sqlite3VdbeAddOp2(v, OP_Next, iTab, addrTop+1); VdbeCoverage(v); /* Step 7: If we reach this point, we know that the result must @@ -99433,7 +100042,7 @@ static int exprCodeVector(Parse *pParse, Expr *p, int *piFreeable){ #if SQLITE_OMIT_SUBQUERY iResult = 0; #else - iResult = sqlite3CodeSubselect(pParse, p, 0, 0); + iResult = sqlite3CodeSubselect(pParse, p); #endif }else{ int i; @@ -99778,7 +100387,7 @@ expr_code_doover: ** arguments past the first non-NULL argument. */ if( pDef->funcFlags & SQLITE_FUNC_COALESCE ){ - int endCoalesce = sqlite3VdbeMakeLabel(v); + int endCoalesce = sqlite3VdbeMakeLabel(pParse); assert( nFarg>=2 ); sqlite3ExprCode(pParse, pFarg->a[0].pExpr, target); for(i=1; ix.pSelect->pEList->nExpr)!=1 ){ sqlite3SubselectError(pParse, nCol, 1); }else{ - return sqlite3CodeSubselect(pParse, pExpr, 0, 0); + return sqlite3CodeSubselect(pParse, pExpr); } break; } case TK_SELECT_COLUMN: { int n; if( pExpr->pLeft->iTable==0 ){ - pExpr->pLeft->iTable = sqlite3CodeSubselect(pParse, pExpr->pLeft, 0, 0); + pExpr->pLeft->iTable = sqlite3CodeSubselect(pParse, pExpr->pLeft); } assert( pExpr->iTable==0 || pExpr->pLeft->op==TK_SELECT ); if( pExpr->iTable @@ -99926,8 +100535,8 @@ expr_code_doover: return pExpr->pLeft->iTable + pExpr->iColumn; } case TK_IN: { - int destIfFalse = sqlite3VdbeMakeLabel(v); - int destIfNull = sqlite3VdbeMakeLabel(v); + int destIfFalse = sqlite3VdbeMakeLabel(pParse); + int destIfNull = sqlite3VdbeMakeLabel(pParse); sqlite3VdbeAddOp2(v, OP_Null, 0, target); sqlite3ExprCodeIN(pParse, pExpr, destIfFalse, destIfNull); sqlite3VdbeAddOp2(v, OP_Integer, 1, target); @@ -100067,9 +100676,9 @@ expr_code_doover: pEList = pExpr->x.pList; aListelem = pEList->a; nExpr = pEList->nExpr; - endLabel = sqlite3VdbeMakeLabel(v); + endLabel = sqlite3VdbeMakeLabel(pParse); if( (pX = pExpr->pLeft)!=0 ){ - tempX = *pX; + exprNodeCopy(&tempX, pX); testcase( pX->op==TK_COLUMN ); exprToRegister(&tempX, exprCodeVector(pParse, &tempX, ®Free1)); testcase( regFree1==0 ); @@ -100090,7 +100699,7 @@ expr_code_doover: }else{ pTest = aListelem[i].pExpr; } - nextCase = sqlite3VdbeMakeLabel(v); + nextCase = sqlite3VdbeMakeLabel(pParse); testcase( pTest->op==TK_COLUMN ); sqlite3ExprIfFalse(pParse, pTest, nextCase, SQLITE_JUMPIFNULL); testcase( aListelem[i+1].pExpr->op==TK_COLUMN ); @@ -100390,13 +100999,12 @@ static void exprCodeBetween( Expr exprX; /* The x subexpression */ int regFree1 = 0; /* Temporary use register */ - memset(&compLeft, 0, sizeof(Expr)); memset(&compRight, 0, sizeof(Expr)); memset(&exprAnd, 0, sizeof(Expr)); assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); - exprX = *pExpr->pLeft; + exprNodeCopy(&exprX, pExpr->pLeft); exprAnd.op = TK_AND; exprAnd.pLeft = &compLeft; exprAnd.pRight = &compRight; @@ -100459,7 +101067,7 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int op = pExpr->op; switch( op ){ case TK_AND: { - int d2 = sqlite3VdbeMakeLabel(v); + int d2 = sqlite3VdbeMakeLabel(pParse); testcase( jumpIfNull==0 ); sqlite3ExprIfFalse(pParse, pExpr->pLeft, d2,jumpIfNull^SQLITE_JUMPIFNULL); sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull); @@ -100545,7 +101153,7 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int } #ifndef SQLITE_OMIT_SUBQUERY case TK_IN: { - int destIfFalse = sqlite3VdbeMakeLabel(v); + int destIfFalse = sqlite3VdbeMakeLabel(pParse); int destIfNull = jumpIfNull ? dest : destIfFalse; sqlite3ExprCodeIN(pParse, pExpr, destIfFalse, destIfNull); sqlite3VdbeGoto(v, dest); @@ -100632,7 +101240,7 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int break; } case TK_OR: { - int d2 = sqlite3VdbeMakeLabel(v); + int d2 = sqlite3VdbeMakeLabel(pParse); testcase( jumpIfNull==0 ); sqlite3ExprIfTrue(pParse, pExpr->pLeft, d2, jumpIfNull^SQLITE_JUMPIFNULL); sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull); @@ -100716,7 +101324,7 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int if( jumpIfNull ){ sqlite3ExprCodeIN(pParse, pExpr, dest, dest); }else{ - int destIfNull = sqlite3VdbeMakeLabel(v); + int destIfNull = sqlite3VdbeMakeLabel(pParse); sqlite3ExprCodeIN(pParse, pExpr, dest, destIfNull); sqlite3VdbeResolveLabel(v, destIfNull); } @@ -100837,7 +101445,7 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTa } return 2; } - if( pA->op!=pB->op ){ + if( pA->op!=pB->op || pA->op==TK_RAISE ){ if( pA->op==TK_COLLATE && sqlite3ExprCompare(pParse, pA->pLeft,pB,iTab)<2 ){ return 1; } @@ -100863,21 +101471,25 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTa if( sqlite3WindowCompare(pParse,pA->y.pWin,pB->y.pWin)!=0 ) return 2; } #endif + }else if( pA->op==TK_NULL ){ + return 0; }else if( pA->op==TK_COLLATE ){ if( sqlite3_stricmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2; - }else if( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){ + }else if( ALWAYS(pB->u.zToken!=0) && strcmp(pA->u.zToken,pB->u.zToken)!=0 ){ return 2; } } if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 2; - if( ALWAYS((combinedFlags & EP_TokenOnly)==0) ){ + if( (combinedFlags & EP_TokenOnly)==0 ){ if( combinedFlags & EP_xIsSelect ) return 2; if( (combinedFlags & EP_FixedCol)==0 && sqlite3ExprCompare(pParse, pA->pLeft, pB->pLeft, iTab) ) return 2; if( sqlite3ExprCompare(pParse, pA->pRight, pB->pRight, iTab) ) return 2; if( sqlite3ExprListCompare(pA->x.pList, pB->x.pList, iTab) ) return 2; - assert( (combinedFlags & EP_Reduced)==0 ); - if( pA->op!=TK_STRING && pA->op!=TK_TRUEFALSE ){ + if( pA->op!=TK_STRING + && pA->op!=TK_TRUEFALSE + && (combinedFlags & EP_Reduced)==0 + ){ if( pA->iColumn!=pB->iColumn ) return 2; if( pA->iTable!=pB->iTable && (pA->iTable!=iTab || NEVER(pB->iTable>=0)) ) return 2; @@ -100986,6 +101598,7 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ case TK_ISNOT: case TK_NOT: case TK_ISNULL: + case TK_NOTNULL: case TK_IS: case TK_OR: case TK_CASE: @@ -100994,6 +101607,7 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){ testcase( pExpr->op==TK_ISNOT ); testcase( pExpr->op==TK_NOT ); testcase( pExpr->op==TK_ISNULL ); + testcase( pExpr->op==TK_NOTNULL ); testcase( pExpr->op==TK_IS ); testcase( pExpr->op==TK_OR ); testcase( pExpr->op==TK_CASE ); @@ -101367,6 +101981,7 @@ SQLITE_PRIVATE void sqlite3ExprAnalyzeAggregates(NameContext *pNC, Expr *pExpr){ w.xSelectCallback2 = analyzeAggregatesInSelectEnd; w.walkerDepth = 0; w.u.pNC = pNC; + w.pParse = 0; assert( pNC->pSrcList!=0 ); sqlite3WalkExpr(&w, pExpr); } @@ -101498,9 +102113,16 @@ SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse *pParse, int iFirst, int iLast){ ** ** Or, if zName is not a system table, zero is returned. */ -static int isSystemTable(Parse *pParse, const char *zName){ - if( 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){ - sqlite3ErrorMsg(pParse, "table %s may not be altered", zName); +static int isAlterableTable(Parse *pParse, Table *pTab){ + if( 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7) +#ifndef SQLITE_OMIT_VIRTUALTABLE + || ( (pTab->tabFlags & TF_Shadow) + && (pParse->db->flags & SQLITE_Defensive) + && pParse->db->nVdbeExec==0 + ) +#endif + ){ + sqlite3ErrorMsg(pParse, "table %s may not be altered", pTab->zName); return 1; } return 0; @@ -101596,7 +102218,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable( /* Make sure it is not a system table being altered, or a reserved name ** that the table is being renamed to. */ - if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ){ + if( SQLITE_OK!=isAlterableTable(pParse, pTab) ){ goto exit_rename_table; } if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ goto @@ -101894,7 +102516,7 @@ SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ sqlite3ErrorMsg(pParse, "Cannot add a column to a view"); goto exit_begin_add_column; } - if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ){ + if( SQLITE_OK!=isAlterableTable(pParse, pTab) ){ goto exit_begin_add_column; } @@ -101996,7 +102618,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameColumn( if( !pTab ) goto exit_rename_column; /* Cannot alter a system table */ - if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ) goto exit_rename_column; + if( SQLITE_OK!=isAlterableTable(pParse, pTab) ) goto exit_rename_column; if( SQLITE_OK!=isRealTable(pParse, pTab) ) goto exit_rename_column; /* Which schema holds the table to be altered */ @@ -102250,14 +102872,31 @@ static void renameTokenFind(Parse *pParse, struct RenameCtx *pCtx, void *pPtr){ } } +/* +** Iterate through the Select objects that are part of WITH clauses attached +** to select statement pSelect. +*/ +static void renameWalkWith(Walker *pWalker, Select *pSelect){ + if( pSelect->pWith ){ + int i; + for(i=0; ipWith->nCte; i++){ + Select *p = pSelect->pWith->a[i].pSelect; + NameContext sNC; + memset(&sNC, 0, sizeof(sNC)); + sNC.pParse = pWalker->pParse; + sqlite3SelectPrep(sNC.pParse, p, &sNC); + sqlite3WalkSelect(pWalker, p); + } + } +} + /* ** This is a Walker select callback. It does nothing. It is only required ** because without a dummy callback, sqlite3WalkExpr() and similar do not ** descend into sub-select statements. */ static int renameColumnSelectCb(Walker *pWalker, Select *p){ - UNUSED_PARAMETER(pWalker); - UNUSED_PARAMETER(p); + renameWalkWith(pWalker, p); return WRC_Continue; } @@ -102407,7 +103046,6 @@ static int renameParseSql( rc = sqlite3RunParser(p, zSql, &zErr); assert( p->zErrMsg==0 ); assert( rc!=SQLITE_OK || zErr==0 ); - assert( (0!=p->pNewTable) + (0!=p->pNewIndex) + (0!=p->pNewTrigger)<2 ); p->zErrMsg = zErr; if( db->mallocFailed ) rc = SQLITE_NOMEM; if( rc==SQLITE_OK @@ -102590,6 +103228,7 @@ static int renameResolveTrigger(Parse *pParse, const char *zDb){ } sNC.ncFlags = 0; } + sNC.pSrcList = 0; } } } @@ -102627,11 +103266,15 @@ static void renameWalkTrigger(Walker *pWalker, Trigger *pTrigger){ */ static void renameParseCleanup(Parse *pParse){ sqlite3 *db = pParse->db; + Index *pIdx; if( pParse->pVdbe ){ sqlite3VdbeFinalize(pParse->pVdbe); } sqlite3DeleteTable(db, pParse->pNewTable); - if( pParse->pNewIndex ) sqlite3FreeIndex(db, pParse->pNewIndex); + while( (pIdx = pParse->pNewIndex)!=0 ){ + pParse->pNewIndex = pIdx->pNext; + sqlite3FreeIndex(db, pIdx); + } sqlite3DeleteTrigger(db, pParse->pNewTrigger); sqlite3DbFree(db, pParse->zErrMsg); renameTokenFree(db, pParse->pRename); @@ -102742,6 +103385,9 @@ static void renameColumnFunc( for(pIdx=sParse.pNewTable->pIndex; pIdx; pIdx=pIdx->pNext){ sqlite3WalkExprList(&sWalker, pIdx->aColExpr); } + for(pIdx=sParse.pNewIndex; pIdx; pIdx=pIdx->pNext){ + sqlite3WalkExprList(&sWalker, pIdx->aColExpr); + } } for(pFKey=sParse.pNewTable->pFKey; pFKey; pFKey=pFKey->pNextFrom){ @@ -102828,12 +103474,17 @@ static int renameTableSelectCb(Walker *pWalker, Select *pSelect){ int i; RenameCtx *p = pWalker->u.pRename; SrcList *pSrc = pSelect->pSrc; + if( pSrc==0 ){ + assert( pWalker->pParse->db->mallocFailed ); + return WRC_Abort; + } for(i=0; inSrc; i++){ struct SrcList_item *pItem = &pSrc->a[i]; if( pItem->pTab==p->pTab ){ renameTokenFind(pWalker->pParse, p, pItem->zName); } } + renameWalkWith(pWalker, pSelect); return WRC_Continue; } @@ -104235,7 +104886,7 @@ static void analyzeOneTable( addrNextRow = sqlite3VdbeCurrentAddr(v); if( nColTest>0 ){ - int endDistinctTest = sqlite3VdbeMakeLabel(v); + int endDistinctTest = sqlite3VdbeMakeLabel(pParse); int *aGotoChng; /* Array of jump instruction addresses */ aGotoChng = sqlite3DbMallocRawNN(db, sizeof(int)*nColTest); if( aGotoChng==0 ) continue; @@ -105173,8 +105824,8 @@ static void attachFunc( assert( pVfs ); flags |= SQLITE_OPEN_MAIN_DB; rc = sqlite3BtreeOpen(pVfs, zPath, db, &pNew->pBt, 0, flags); - sqlite3_free( zPath ); db->nDb++; + pNew->zDbSName = sqlite3DbStrDup(db, zName); } db->noSharedCache = 0; if( rc==SQLITE_CONSTRAINT ){ @@ -105202,7 +105853,6 @@ static void attachFunc( sqlite3BtreeLeave(pNew->pBt); } pNew->safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1; - if( !REOPEN_AS_MEMDB(db) ) pNew->zDbSName = sqlite3DbStrDup(db, zName); if( rc==SQLITE_OK && pNew->zDbSName==0 ){ rc = SQLITE_NOMEM_BKPT; } @@ -105230,15 +105880,19 @@ static void attachFunc( break; case SQLITE_NULL: - /* No key specified. Use the key from the main database */ - sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey); - if( nKey || sqlite3BtreeGetOptimalReserve(db->aDb[0].pBt)>0 ){ - rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); + /* No key specified. Use the key from URI filename, or if none, + ** use the key from the main database. */ + if( sqlite3CodecQueryParameters(db, zName, zPath)==0 ){ + sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey); + if( nKey || sqlite3BtreeGetOptimalReserve(db->aDb[0].pBt)>0 ){ + rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); + } } break; } } #endif + sqlite3_free( zPath ); /* If the file was opened successfully, read the schema for the new database. ** If this fails, or if opening the file failed, then close the file and @@ -106150,7 +106804,7 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){ if( v && pParse->nErr==0 && !db->mallocFailed ){ /* A minimum of one cursor is required if autoincrement is used * See ticket [a696379c1f08866] */ - if( pParse->pAinc!=0 && pParse->nTab==0 ) pParse->nTab = 1; + assert( pParse->pAinc==0 || pParse->nTab>0 ); sqlite3VdbeMakeReady(v, pParse); pParse->rc = SQLITE_DONE; }else{ @@ -106277,26 +106931,32 @@ SQLITE_PRIVATE Table *sqlite3LocateTable( p = sqlite3FindTable(db, zName, zDbase); if( p==0 ){ - const char *zMsg = flags & LOCATE_VIEW ? "no such view" : "no such table"; #ifndef SQLITE_OMIT_VIRTUALTABLE /* If zName is the not the name of a table in the schema created using ** CREATE, then check to see if it is the name of an virtual table that ** can be an eponymous virtual table. */ - Module *pMod = (Module*)sqlite3HashFind(&db->aModule, zName); - if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){ - pMod = sqlite3PragmaVtabRegister(db, zName); - } - if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){ - return pMod->pEpoTab; + if( pParse->disableVtab==0 ){ + Module *pMod = (Module*)sqlite3HashFind(&db->aModule, zName); + if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){ + pMod = sqlite3PragmaVtabRegister(db, zName); + } + if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){ + return pMod->pEpoTab; + } } #endif - if( (flags & LOCATE_NOERR)==0 ){ - if( zDbase ){ - sqlite3ErrorMsg(pParse, "%s: %s.%s", zMsg, zDbase, zName); - }else{ - sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName); - } - pParse->checkSchema = 1; + if( flags & LOCATE_NOERR ) return 0; + pParse->checkSchema = 1; + }else if( IsVirtual(p) && pParse->disableVtab ){ + p = 0; + } + + if( p==0 ){ + const char *zMsg = flags & LOCATE_VIEW ? "no such view" : "no such table"; + if( zDbase ){ + sqlite3ErrorMsg(pParse, "%s: %s.%s", zMsg, zDbase, zName); + }else{ + sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName); } } @@ -106559,12 +107219,6 @@ static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){ /* Delete the Table structure itself. */ -#ifdef SQLITE_ENABLE_NORMALIZE - if( pTable->pColHash ){ - sqlite3HashClear(pTable->pColHash); - sqlite3_free(pTable->pColHash); - } -#endif sqlite3DeleteColumnNames(db, pTable); sqlite3DbFree(db, pTable->zName); sqlite3DbFree(db, pTable->zColAff); @@ -108561,6 +109215,7 @@ SQLITE_PRIVATE void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, in */ if( IsVirtual(pTab) ){ sqlite3VdbeAddOp4(v, OP_VDestroy, iDb, 0, 0, pTab->zName, 0); + sqlite3MayAbort(pParse); } sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0); sqlite3ChangeCookie(pParse, iDb); @@ -109389,6 +110044,11 @@ SQLITE_PRIVATE void sqlite3CreateIndex( } } if( idxType==SQLITE_IDXTYPE_PRIMARYKEY ) pIdx->idxType = idxType; + if( IN_RENAME_OBJECT ){ + pIndex->pNext = pParse->pNewIndex; + pParse->pNewIndex = pIndex; + pIndex = 0; + } goto exit_create_index; } } @@ -109404,6 +110064,14 @@ SQLITE_PRIVATE void sqlite3CreateIndex( Index *p; assert( !IN_SPECIAL_PARSE ); assert( sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) ); + if( pTblName!=0 ){ + pIndex->tnum = db->init.newTnum; + if( sqlite3IndexHasDuplicateRootPage(pIndex) ){ + sqlite3ErrorMsg(pParse, "invalid rootpage"); + pParse->rc = SQLITE_CORRUPT_BKPT; + goto exit_create_index; + } + } p = sqlite3HashInsert(&pIndex->pSchema->idxHash, pIndex->zName, pIndex); if( p ){ @@ -109412,9 +110080,6 @@ SQLITE_PRIVATE void sqlite3CreateIndex( goto exit_create_index; } db->mDbFlags |= DBFLAG_SchemaChange; - if( pTblName!=0 ){ - pIndex->tnum = db->init.newTnum; - } } /* If this is the initial CREATE INDEX statement (or CREATE TABLE if the @@ -109740,6 +110405,18 @@ SQLITE_PRIVATE int sqlite3IdListIndex(IdList *pList, const char *zName){ return -1; } +/* +** Maximum size of a SrcList object. +** The SrcList object is used to represent the FROM clause of a +** SELECT statement, and the query planner cannot deal with more +** than 64 tables in a join. So any value larger than 64 here +** is sufficient for most uses. Smaller values, like say 10, are +** appropriate for small and memory-limited applications. +*/ +#ifndef SQLITE_MAX_SRCLIST +# define SQLITE_MAX_SRCLIST 200 +#endif + /* ** Expand the space allocated for the given SrcList object by ** creating nExtra new slots beginning at iStart. iStart is zero based. @@ -109756,11 +110433,12 @@ SQLITE_PRIVATE int sqlite3IdListIndex(IdList *pList, const char *zName){ ** the iStart value would be 0. The result then would ** be: nil, nil, nil, A, B. ** -** If a memory allocation fails the SrcList is unchanged. The -** db->mallocFailed flag will be set to true. +** If a memory allocation fails or the SrcList becomes too large, leave +** the original SrcList unchanged, return NULL, and leave an error message +** in pParse. */ SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge( - sqlite3 *db, /* Database connection to notify of OOM errors */ + Parse *pParse, /* Parsing context into which errors are reported */ SrcList *pSrc, /* The SrcList to be enlarged */ int nExtra, /* Number of new slots to add to pSrc->a[] */ int iStart /* Index in pSrc->a[] of first new slot */ @@ -109777,16 +110455,22 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge( if( (u32)pSrc->nSrc+nExtra>pSrc->nAlloc ){ SrcList *pNew; int nAlloc = pSrc->nSrc*2+nExtra; - int nGot; + sqlite3 *db = pParse->db; + + if( pSrc->nSrc+nExtra>=SQLITE_MAX_SRCLIST ){ + sqlite3ErrorMsg(pParse, "too many FROM clause terms, max: %d", + SQLITE_MAX_SRCLIST); + return 0; + } + if( nAlloc>SQLITE_MAX_SRCLIST ) nAlloc = SQLITE_MAX_SRCLIST; pNew = sqlite3DbRealloc(db, pSrc, sizeof(*pSrc) + (nAlloc-1)*sizeof(pSrc->a[0]) ); if( pNew==0 ){ assert( db->mallocFailed ); - return pSrc; + return 0; } pSrc = pNew; - nGot = (sqlite3DbMallocSize(db, pNew) - sizeof(*pSrc))/sizeof(pSrc->a[0])+1; - pSrc->nAlloc = nGot; + pSrc->nAlloc = nAlloc; } /* Move existing slots that come after the newly inserted slots @@ -109811,7 +110495,8 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge( ** Append a new table name to the given SrcList. Create a new SrcList if ** need be. A new entry is created in the SrcList even if pTable is NULL. ** -** A SrcList is returned, or NULL if there is an OOM error. The returned +** A SrcList is returned, or NULL if there is an OOM error or if the +** SrcList grows to large. The returned ** SrcList might be the same as the SrcList that was input or it might be ** a new one. If an OOM error does occurs, then the prior value of pList ** that is input to this routine is automatically freed. @@ -109842,27 +110527,32 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge( ** before being added to the SrcList. */ SQLITE_PRIVATE SrcList *sqlite3SrcListAppend( - sqlite3 *db, /* Connection to notify of malloc failures */ + Parse *pParse, /* Parsing context, in which errors are reported */ SrcList *pList, /* Append to this SrcList. NULL creates a new SrcList */ Token *pTable, /* Table to append */ Token *pDatabase /* Database of the table */ ){ struct SrcList_item *pItem; + sqlite3 *db; assert( pDatabase==0 || pTable!=0 ); /* Cannot have C without B */ - assert( db!=0 ); + assert( pParse!=0 ); + assert( pParse->db!=0 ); + db = pParse->db; if( pList==0 ){ - pList = sqlite3DbMallocRawNN(db, sizeof(SrcList) ); + pList = sqlite3DbMallocRawNN(pParse->db, sizeof(SrcList) ); if( pList==0 ) return 0; pList->nAlloc = 1; pList->nSrc = 1; memset(&pList->a[0], 0, sizeof(pList->a[0])); pList->a[0].iCursor = -1; }else{ - pList = sqlite3SrcListEnlarge(db, pList, 1, pList->nSrc); - } - if( db->mallocFailed ){ - sqlite3SrcListDelete(db, pList); - return 0; + SrcList *pNew = sqlite3SrcListEnlarge(pParse, pList, 1, pList->nSrc); + if( pNew==0 ){ + sqlite3SrcListDelete(db, pList); + return 0; + }else{ + pList = pNew; + } } pItem = &pList->a[pList->nSrc-1]; if( pDatabase && pDatabase->z==0 ){ @@ -109951,7 +110641,7 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppendFromTerm( ); goto append_from_error; } - p = sqlite3SrcListAppend(db, p, pTable, pDatabase); + p = sqlite3SrcListAppend(pParse, p, pTable, pDatabase); if( p==0 ){ goto append_from_error; } @@ -110340,13 +111030,15 @@ static int collationMatch(const char *zColl, Index *pIndex){ */ #ifndef SQLITE_OMIT_REINDEX static void reindexTable(Parse *pParse, Table *pTab, char const *zColl){ - Index *pIndex; /* An index associated with pTab */ + if( !IsVirtual(pTab) ){ + Index *pIndex; /* An index associated with pTab */ - for(pIndex=pTab->pIndex; pIndex; pIndex=pIndex->pNext){ - if( zColl==0 || collationMatch(zColl, pIndex) ){ - int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); - sqlite3BeginWriteOperation(pParse, 0, iDb); - sqlite3RefillIndex(pParse, pIndex, -1); + for(pIndex=pTab->pIndex; pIndex; pIndex=pIndex->pNext){ + if( zColl==0 || collationMatch(zColl, pIndex) ){ + int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); + sqlite3BeginWriteOperation(pParse, 0, iDb); + sqlite3RefillIndex(pParse, pIndex, -1); + } } } } @@ -110845,7 +111537,7 @@ static int matchQuality( ** Search a FuncDefHash for a function with the given name. Return ** a pointer to the matching FuncDef if found, or 0 if there is no match. */ -static FuncDef *functionSearch( +SQLITE_PRIVATE FuncDef *sqlite3FunctionSearch( int h, /* Hash of the name */ const char *zFunc /* Name of function */ ){ @@ -110857,21 +111549,6 @@ static FuncDef *functionSearch( } return 0; } -#ifdef SQLITE_ENABLE_NORMALIZE -SQLITE_PRIVATE FuncDef *sqlite3FunctionSearchN( - int h, /* Hash of the name */ - const char *zFunc, /* Name of function */ - int nFunc /* Length of the name */ -){ - FuncDef *p; - for(p=sqlite3BuiltinFunctions.a[h]; p; p=p->u.pHash){ - if( sqlite3StrNICmp(p->zName, zFunc, nFunc)==0 ){ - return p; - } - } - return 0; -} -#endif /* SQLITE_ENABLE_NORMALIZE */ /* ** Insert a new FuncDef into a FuncDefHash hash table. @@ -110887,7 +111564,7 @@ SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs( int nName = sqlite3Strlen30(zName); int h = SQLITE_FUNC_HASH(zName[0], nName); assert( zName[0]>='a' && zName[0]<='z' ); - pOther = functionSearch(h, zName); + pOther = sqlite3FunctionSearch(h, zName); if( pOther ){ assert( pOther!=&aDef[i] && pOther->pNext!=&aDef[i] ); aDef[i].pNext = pOther->pNext; @@ -110965,7 +111642,7 @@ SQLITE_PRIVATE FuncDef *sqlite3FindFunction( if( !createFlag && (pBest==0 || (db->mDbFlags & DBFLAG_PreferBuiltin)!=0) ){ bestScore = 0; h = SQLITE_FUNC_HASH(sqlite3UpperToLower[(u8)zName[0]], nName); - p = functionSearch(h, zName); + p = sqlite3FunctionSearch(h, zName); while( p ){ int score = matchQuality(p, nArg, enc); if( score>bestScore ){ @@ -111185,7 +111862,7 @@ SQLITE_PRIVATE void sqlite3MaterializeView( sqlite3 *db = pParse->db; int iDb = sqlite3SchemaToIndex(db, pView->pSchema); pWhere = sqlite3ExprDup(db, pWhere, 0); - pFrom = sqlite3SrcListAppend(db, 0, 0, 0); + pFrom = sqlite3SrcListAppend(pParse, 0, 0, 0); if( pFrom ){ assert( pFrom->nSrc==1 ); pFrom->a[0].zName = sqlite3DbStrDup(db, pView->zName); @@ -111585,7 +112262,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom( /* If this DELETE cannot use the ONEPASS strategy, this is the ** end of the WHERE loop */ if( eOnePass!=ONEPASS_OFF ){ - addrBypass = sqlite3VdbeMakeLabel(v); + addrBypass = sqlite3VdbeMakeLabel(pParse); }else{ sqlite3WhereEnd(pWInfo); } @@ -111774,7 +112451,7 @@ SQLITE_PRIVATE void sqlite3GenerateRowDelete( /* Seek cursor iCur to the row to delete. If this row no longer exists ** (this can happen if a trigger program has already deleted it), do ** not attempt to delete it or fire any DELETE triggers. */ - iLabel = sqlite3VdbeMakeLabel(v); + iLabel = sqlite3VdbeMakeLabel(pParse); opSeek = HasRowid(pTab) ? OP_NotExists : OP_NotFound; if( eMode==ONEPASS_OFF ){ sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk); @@ -111980,7 +112657,7 @@ SQLITE_PRIVATE int sqlite3GenerateIndexKey( if( piPartIdxLabel ){ if( pIdx->pPartIdxWhere ){ - *piPartIdxLabel = sqlite3VdbeMakeLabel(v); + *piPartIdxLabel = sqlite3VdbeMakeLabel(pParse); pParse->iSelfTab = iDataCur + 1; sqlite3ExprIfFalseDup(pParse, pIdx->pPartIdxWhere, *piPartIdxLabel, SQLITE_JUMPIFNULL); @@ -112236,6 +112913,7 @@ static void instrFunc( int typeHaystack, typeNeedle; int N = 1; int isText; + unsigned char firstChar; UNUSED_PARAMETER(argc); typeHaystack = sqlite3_value_type(argv[0]); @@ -112254,7 +112932,10 @@ static void instrFunc( isText = 1; } if( zNeedle==0 || (nHaystack && zHaystack==0) ) return; - while( nNeedle<=nHaystack && memcmp(zHaystack, zNeedle, nNeedle)!=0 ){ + firstChar = zNeedle[0]; + while( nNeedle<=nHaystack + && (zHaystack[0]!=firstChar || memcmp(zHaystack, zNeedle, nNeedle)!=0) + ){ N++; do{ nHaystack--; @@ -112545,11 +113226,11 @@ static void randomBlob( int argc, sqlite3_value **argv ){ - int n; + sqlite3_int64 n; unsigned char *p; assert( argc==1 ); UNUSED_PARAMETER(argc); - n = sqlite3_value_int(argv[0]); + n = sqlite3_value_int64(argv[0]); if( n<1 ){ n = 1; } @@ -114385,7 +115066,7 @@ static void fkLookupParent( int i; /* Iterator variable */ Vdbe *v = sqlite3GetVdbe(pParse); /* Vdbe to add code to */ int iCur = pParse->nTab - 1; /* Cursor number to use */ - int iOk = sqlite3VdbeMakeLabel(v); /* jump here if parent key found */ + int iOk = sqlite3VdbeMakeLabel(pParse); /* jump here if parent key found */ sqlite3VdbeVerifyAbortable(v, (!pFKey->isDeferred @@ -114658,8 +115339,11 @@ static void fkScanChildren( ** NOT( $current_a==a AND $current_b==b AND ... ) ** ** The first form is used for rowid tables. The second form is used - ** for WITHOUT ROWID tables. In the second form, the primary key is - ** (a,b,...) + ** for WITHOUT ROWID tables. In the second form, the *parent* key is + ** (a,b,...). Either the parent or primary key could be used to + ** uniquely identify the current row, but the parent key is more convenient + ** as the required values have already been loaded into registers + ** by the caller. */ if( pTab==pFKey->pFrom && nIncr>0 ){ Expr *pNe; /* Expression (pLeft != pRight) */ @@ -114671,14 +115355,13 @@ static void fkScanChildren( pNe = sqlite3PExpr(pParse, TK_NE, pLeft, pRight); }else{ Expr *pEq, *pAll = 0; - Index *pPk = sqlite3PrimaryKeyIndex(pTab); assert( pIdx!=0 ); - for(i=0; inKeyCol; i++){ + for(i=0; inKeyCol; i++){ i16 iCol = pIdx->aiColumn[i]; assert( iCol>=0 ); pLeft = exprTableRegister(pParse, pTab, regData, iCol); - pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, iCol); - pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight); + pRight = sqlite3Expr(db, TK_ID, pTab->aCol[iCol].zName); + pEq = sqlite3PExpr(pParse, TK_IS, pLeft, pRight); pAll = sqlite3ExprAnd(db, pAll, pEq); } pNe = sqlite3PExpr(pParse, TK_NOT, pAll, 0); @@ -114783,7 +115466,7 @@ SQLITE_PRIVATE void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTa if( p->isDeferred || (db->flags & SQLITE_DeferFKs) ) break; } if( !p ) return; - iSkip = sqlite3VdbeMakeLabel(v); + iSkip = sqlite3VdbeMakeLabel(pParse); sqlite3VdbeAddOp2(v, OP_FkIfZero, 1, iSkip); VdbeCoverage(v); } @@ -115068,7 +115751,7 @@ SQLITE_PRIVATE void sqlite3FkCheck( /* Create a SrcList structure containing the child table. We need the ** child table as a SrcList for sqlite3WhereBegin() */ - pSrc = sqlite3SrcListAppend(db, 0, 0, 0); + pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); if( pSrc ){ struct SrcList_item *pItem = pSrc->a; pItem->pTab = pFKey->pFrom; @@ -115345,7 +116028,7 @@ static Trigger *fkActionTrigger( } pSelect = sqlite3SelectNew(pParse, sqlite3ExprListAppend(pParse, 0, pRaise), - sqlite3SrcListAppend(db, 0, &tFrom, 0), + sqlite3SrcListAppend(pParse, 0, &tFrom, 0), pWhere, 0, 0, 0, 0, 0 ); @@ -115807,6 +116490,7 @@ SQLITE_PRIVATE void sqlite3AutoincrementBegin(Parse *pParse){ aOp[7].p2 = memId+2; aOp[7].p1 = memId; aOp[10].p2 = memId; + if( pParse->nTab==0 ) pParse->nTab = 1; } } @@ -116313,6 +116997,11 @@ SQLITE_PRIVATE void sqlite3Insert( } #ifndef SQLITE_OMIT_UPSERT if( pUpsert ){ + if( IsVirtual(pTab) ){ + sqlite3ErrorMsg(pParse, "UPSERT not implemented for virtual table \"%s\"", + pTab->zName); + goto insert_cleanup; + } pTabList->a[0].iCursor = iDataCur; pUpsert->pUpsertSrc = pTabList; pUpsert->regData = regData; @@ -116353,7 +117042,7 @@ SQLITE_PRIVATE void sqlite3Insert( /* Run the BEFORE and INSTEAD OF triggers, if there are any */ - endOfLoop = sqlite3VdbeMakeLabel(v); + endOfLoop = sqlite3VdbeMakeLabel(pParse); if( tmask & TRIGGER_BEFORE ){ int regCols = sqlite3GetTempRange(pParse, pTab->nCol+1); @@ -116435,16 +117124,12 @@ SQLITE_PRIVATE void sqlite3Insert( }else if( pSelect ){ sqlite3VdbeAddOp2(v, OP_Copy, regFromSelect+ipkColumn, regRowid); }else{ - VdbeOp *pOp; - sqlite3ExprCode(pParse, pList->a[ipkColumn].pExpr, regRowid); - pOp = sqlite3VdbeGetOp(v, -1); - assert( pOp!=0 ); - if( pOp->opcode==OP_Null && !IsVirtual(pTab) ){ + Expr *pIpk = pList->a[ipkColumn].pExpr; + if( pIpk->op==TK_NULL && !IsVirtual(pTab) ){ + sqlite3VdbeAddOp3(v, OP_NewRowid, iDataCur, regRowid, regAutoinc); appendFlag = 1; - pOp->opcode = OP_NewRowid; - pOp->p1 = iDataCur; - pOp->p2 = regRowid; - pOp->p3 = regAutoinc; + }else{ + sqlite3ExprCode(pParse, pList->a[ipkColumn].pExpr, regRowid); } } /* If the PRIMARY KEY expression is NULL, then use OP_NewRowid @@ -116839,7 +117524,20 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( } assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail || onError==OE_Ignore || onError==OE_Replace ); + addr1 = 0; switch( onError ){ + case OE_Replace: { + assert( onError==OE_Replace ); + addr1 = sqlite3VdbeMakeLabel(pParse); + sqlite3VdbeAddOp2(v, OP_NotNull, regNewData+1+i, addr1); + VdbeCoverage(v); + sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regNewData+1+i); + sqlite3VdbeAddOp2(v, OP_NotNull, regNewData+1+i, addr1); + VdbeCoverage(v); + onError = OE_Abort; + /* Fall through into the OE_Abort case to generate code that runs + ** if both the input and the default value are NULL */ + } case OE_Abort: sqlite3MayAbort(pParse); /* Fall through */ @@ -116852,19 +117550,13 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( sqlite3VdbeAppendP4(v, zMsg, P4_DYNAMIC); sqlite3VdbeChangeP5(v, P5_ConstraintNotNull); VdbeCoverage(v); - break; - } - case OE_Ignore: { - sqlite3VdbeAddOp2(v, OP_IsNull, regNewData+1+i, ignoreDest); - VdbeCoverage(v); + if( addr1 ) sqlite3VdbeResolveLabel(v, addr1); break; } default: { - assert( onError==OE_Replace ); - addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, regNewData+1+i); - VdbeCoverage(v); - sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regNewData+1+i); - sqlite3VdbeJumpHere(v, addr1); + assert( onError==OE_Ignore ); + sqlite3VdbeAddOp2(v, OP_IsNull, regNewData+1+i, ignoreDest); + VdbeCoverage(v); break; } } @@ -116887,7 +117579,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( ** updated so there is no point it verifying the check constraint */ continue; } - allOk = sqlite3VdbeMakeLabel(v); + allOk = sqlite3VdbeMakeLabel(pParse); sqlite3VdbeVerifyAbortable(v, onError); sqlite3ExprIfTrue(pParse, pExpr, allOk, SQLITE_JUMPIFNULL); if( onError==OE_Ignore ){ @@ -116954,7 +117646,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( ** exist in the table. */ if( pkChng && pPk==0 ){ - int addrRowidOk = sqlite3VdbeMakeLabel(v); + int addrRowidOk = sqlite3VdbeMakeLabel(pParse); /* Figure out what action to take in case of a rowid collision */ onError = pTab->keyConf; @@ -117104,7 +117796,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( VdbeComment((v, "Skip upsert subroutine")); sqlite3VdbeJumpHere(v, upsertJump); }else{ - addrUniqueOk = sqlite3VdbeMakeLabel(v); + addrUniqueOk = sqlite3VdbeMakeLabel(pParse); } if( bAffinityDone==0 && (pUpIdx==0 || pUpIdx==pIdx) ){ sqlite3TableAffinity(v, pTab, regNewData+1); @@ -117187,7 +117879,11 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( ** (3) There are no secondary indexes on the table ** (4) No delete triggers need to be fired if there is a conflict ** (5) No FK constraint counters need to be updated if a conflict occurs. - */ + ** + ** This is not possible for ENABLE_PREUPDATE_HOOK builds, as the row + ** must be explicitly deleted in order to ensure any pre-update hook + ** is invoked. */ +#ifndef SQLITE_ENABLE_PREUPDATE_HOOK if( (ix==0 && pIdx->pNext==0) /* Condition 3 */ && pPk==pIdx /* Condition 2 */ && onError==OE_Replace /* Condition 1 */ @@ -117199,6 +117895,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( sqlite3VdbeResolveLabel(v, addrUniqueOk); continue; } +#endif /* ifndef SQLITE_ENABLE_PREUPDATE_HOOK */ /* Check to see if the new index entry will be unique */ sqlite3VdbeVerifyAbortable(v, onError); @@ -117312,7 +118009,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( /* If the IPK constraint is a REPLACE, run it last */ if( ipkTop ){ - sqlite3VdbeGoto(v, ipkTop+1); + sqlite3VdbeGoto(v, ipkTop); VdbeComment((v, "Do IPK REPLACE")); sqlite3VdbeJumpHere(v, ipkBottom); } @@ -117393,10 +118090,13 @@ SQLITE_PRIVATE void sqlite3CompleteInsertion( pik_flags |= (update_flags & OPFLAG_SAVEPOSITION); #ifdef SQLITE_ENABLE_PREUPDATE_HOOK if( update_flags==0 ){ - sqlite3VdbeAddOp4(v, OP_InsertInt, - iIdxCur+i, aRegIdx[i], 0, (char*)pTab, P4_TABLE + int r = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp2(v, OP_Integer, 0, r); + sqlite3VdbeAddOp4(v, OP_Insert, + iIdxCur+i, aRegIdx[i], r, (char*)pTab, P4_TABLE ); sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP); + sqlite3ReleaseTempReg(pParse, r); } #endif } @@ -117682,7 +118382,8 @@ static int xferOptimization( if( pSrc==0 ){ return 0; /* FROM clause does not contain a real table */ } - if( pSrc==pDest ){ + if( pSrc->tnum==pDest->tnum && pSrc->pSchema==pDest->pSchema ){ + testcase( pSrc!=pDest ); /* Possible due to bad sqlite_master.rootpage */ return 0; /* tab1 and tab2 may not be the same table */ } if( HasRowid(pDest)!=HasRowid(pSrc) ){ @@ -117958,7 +118659,7 @@ SQLITE_API int sqlite3_exec( sqlite3_mutex_enter(db->mutex); sqlite3Error(db, SQLITE_OK); while( rc==SQLITE_OK && zSql[0] ){ - int nCol; + int nCol = 0; char **azVals = 0; pStmt = 0; @@ -117972,9 +118673,7 @@ SQLITE_API int sqlite3_exec( zSql = zLeftover; continue; } - callbackIsInit = 0; - nCol = sqlite3_column_count(pStmt); while( 1 ){ int i; @@ -117985,6 +118684,7 @@ SQLITE_API int sqlite3_exec( (SQLITE_DONE==rc && !callbackIsInit && db->flags&SQLITE_NullCallback)) ){ if( !callbackIsInit ){ + nCol = sqlite3_column_count(pStmt); azCols = sqlite3DbMallocRaw(db, (2*nCol+1)*sizeof(const char*)); if( azCols==0 ){ goto exec_out; @@ -119339,7 +120039,7 @@ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff){ if( onoff ){ db->flags |= SQLITE_LoadExtension|SQLITE_LoadExtFunc; }else{ - db->flags &= ~(SQLITE_LoadExtension|SQLITE_LoadExtFunc); + db->flags &= ~(u64)(SQLITE_LoadExtension|SQLITE_LoadExtFunc); } sqlite3_mutex_leave(db->mutex); return SQLITE_OK; @@ -119598,8 +120298,7 @@ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){ #define PragTyp_HEXKEY 41 #define PragTyp_KEY 42 #define PragTyp_LOCK_STATUS 43 -#define PragTyp_PARSER_TRACE 44 -#define PragTyp_STATS 45 +#define PragTyp_STATS 44 /* Property flags associated with various pragma. */ #define PragFlg_NeedSchema 0x01 /* Force schema load before running */ @@ -120010,12 +120709,14 @@ static const PragmaName aPragmaName[] = { /* ColNames: */ 0, 0, /* iArg: */ 0 }, #endif -#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_PARSER_TRACE) +#if !defined(SQLITE_OMIT_FLAG_PRAGMAS) +#if defined(SQLITE_DEBUG) {/* zName: */ "parser_trace", - /* ePragTyp: */ PragTyp_PARSER_TRACE, - /* ePragFlg: */ 0, + /* ePragTyp: */ PragTyp_FLAG, + /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, - /* iArg: */ 0 }, + /* iArg: */ SQLITE_ParserTrace }, +#endif #endif #if defined(SQLITE_INTROSPECTION_PRAGMAS) {/* zName: */ "pragma_list", @@ -121006,7 +121707,7 @@ SQLITE_PRIVATE void sqlite3Pragma( if( sqlite3GetBoolean(zRight, size!=0) ){ db->flags |= SQLITE_CacheSpill; }else{ - db->flags &= ~SQLITE_CacheSpill; + db->flags &= ~(u64)SQLITE_CacheSpill; } setAllPagerFlags(db); } @@ -121566,7 +122267,7 @@ SQLITE_PRIVATE void sqlite3Pragma( x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, &aiCols); assert( x==0 ); } - addrOk = sqlite3VdbeMakeLabel(v); + addrOk = sqlite3VdbeMakeLabel(pParse); /* Generate code to read the child key values into registers ** regRow..regRow+n. If any of the child key values are NULL, this @@ -121611,19 +122312,6 @@ SQLITE_PRIVATE void sqlite3Pragma( #endif /* !defined(SQLITE_OMIT_TRIGGER) */ #endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */ -#ifndef NDEBUG - case PragTyp_PARSER_TRACE: { - if( zRight ){ - if( sqlite3GetBoolean(zRight, 0) ){ - sqlite3ParserTrace(stdout, "parser: "); - }else{ - sqlite3ParserTrace(0, 0); - } - } - } - break; -#endif - /* Reinstall the LIKE and GLOB functions. The variant of LIKE ** used will be case sensitive or not depending on the RHS. */ @@ -121786,8 +122474,8 @@ SQLITE_PRIVATE void sqlite3Pragma( if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){ ExprList *pCheck = sqlite3ExprListDup(db, pTab->pCheck, 0); if( db->mallocFailed==0 ){ - int addrCkFault = sqlite3VdbeMakeLabel(v); - int addrCkOk = sqlite3VdbeMakeLabel(v); + int addrCkFault = sqlite3VdbeMakeLabel(pParse); + int addrCkOk = sqlite3VdbeMakeLabel(pParse); char *zErr; int k; pParse->iSelfTab = iDataCur + 1; @@ -121810,7 +122498,7 @@ SQLITE_PRIVATE void sqlite3Pragma( /* Validate index entries for the current row */ for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ int jmp2, jmp3, jmp4, jmp5; - int ckUniq = sqlite3VdbeMakeLabel(v); + int ckUniq = sqlite3VdbeMakeLabel(pParse); if( pPk==pIdx ) continue; r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 0, &jmp3, pPrior, r1); @@ -121831,7 +122519,7 @@ SQLITE_PRIVATE void sqlite3Pragma( ** current key. The entry is unique if (1) any column is NULL ** or (2) the next entry has a different key */ if( IsUniqueIndex(pIdx) ){ - int uniqOk = sqlite3VdbeMakeLabel(v); + int uniqOk = sqlite3VdbeMakeLabel(pParse); int jmp6; int kk; for(kk=0; kknKeyCol; kk++){ @@ -122745,6 +123433,19 @@ static void corruptSchema( } } +/* +** Check to see if any sibling index (another index on the same table) +** of pIndex has the same root page number, and if it does, return true. +** This would indicate a corrupt schema. +*/ +SQLITE_PRIVATE int sqlite3IndexHasDuplicateRootPage(Index *pIndex){ + Index *p; + for(p=pIndex->pTable->pIndex; p; p=p->pNext){ + if( p->tnum==pIndex->tnum && p!=pIndex ) return 1; + } + return 0; +} + /* ** This is the callback routine for the code that initializes the ** database. See sqlite3Init() below for additional information. @@ -122766,6 +123467,7 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char UNUSED_PARAMETER2(NotUsed, argc); assert( sqlite3_mutex_held(db->mutex) ); DbClearProperty(db, iDb, DB_Empty); + pData->nInitRow++; if( db->mallocFailed ){ corruptSchema(pData, argv[0], 0); return 1; @@ -122819,15 +123521,12 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char */ Index *pIndex; pIndex = sqlite3FindIndex(db, argv[0], db->aDb[iDb].zDbSName); - if( pIndex==0 ){ - /* This can occur if there exists an index on a TEMP table which - ** has the same name as another index on a permanent index. Since - ** the permanent table is hidden by the TEMP table, we can also - ** safely ignore the index on the permanent table. - */ - /* Do Nothing */; - }else if( sqlite3GetInt32(argv[1], &pIndex->tnum)==0 ){ - corruptSchema(pData, argv[0], "invalid rootpage"); + if( pIndex==0 + || sqlite3GetInt32(argv[1],&pIndex->tnum)==0 + || pIndex->tnum<2 + || sqlite3IndexHasDuplicateRootPage(pIndex) + ){ + corruptSchema(pData, argv[0], pIndex?"invalid rootpage":"orphan index"); } } return 0; @@ -122877,6 +123576,7 @@ SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFl initData.rc = SQLITE_OK; initData.pzErrMsg = pzErrMsg; initData.mInitFlags = mFlags; + initData.nInitRow = 0; sqlite3InitCallback(&initData, 3, (char **)azArg, 0); if( initData.rc ){ rc = initData.rc; @@ -122994,7 +123694,7 @@ SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFl ** indices that the user might have created. */ if( iDb==0 && meta[BTREE_FILE_FORMAT-1]>=4 ){ - db->flags &= ~SQLITE_LegacyFileFmt; + db->flags &= ~(u64)SQLITE_LegacyFileFmt; } /* Read the schema information out of the schema tables @@ -123246,6 +123946,7 @@ static int sqlite3Prepare( sParse.disableLookaside++; db->lookaside.bDisable++; } + sParse.disableVtab = (prepFlags & SQLITE_PREPARE_NO_VTAB)!=0; /* Check to verify that it is possible to get a read lock on all ** database schemas. The inability to get a read lock indicates that @@ -123410,293 +124111,6 @@ static int sqlite3LockAndPrepare( return rc; } -#ifdef SQLITE_ENABLE_NORMALIZE -/* -** Checks if the specified token is a table, column, or function name, -** based on the databases associated with the statement being prepared. -** If the function fails, zero is returned and pRc is filled with the -** error code. -*/ -static int shouldTreatAsIdentifier( - sqlite3 *db, /* Database handle. */ - const char *zToken, /* Pointer to start of token to be checked */ - int nToken, /* Length of token to be checked */ - int *pRc /* Pointer to error code upon failure */ -){ - int bFound = 0; /* Non-zero if token is an identifier name. */ - int i, j; /* Database and column loop indexes. */ - Schema *pSchema; /* Schema for current database. */ - Hash *pHash; /* Hash table of tables for current database. */ - HashElem *e; /* Hash element for hash table iteration. */ - Table *pTab; /* Database table for columns being checked. */ - - if( sqlite3IsRowidN(zToken, nToken) ){ - return 1; - } - if( nToken>0 ){ - int hash = SQLITE_FUNC_HASH(sqlite3UpperToLower[(u8)zToken[0]], nToken); - if( sqlite3FunctionSearchN(hash, zToken, nToken) ) return 1; - } - assert( db!=0 ); - sqlite3_mutex_enter(db->mutex); - sqlite3BtreeEnterAll(db); - for(i=0; inDb; i++){ - pHash = &db->aFunc; - if( sqlite3HashFindN(pHash, zToken, nToken) ){ - bFound = 1; - break; - } - pSchema = db->aDb[i].pSchema; - if( pSchema==0 ) continue; - pHash = &pSchema->tblHash; - if( sqlite3HashFindN(pHash, zToken, nToken) ){ - bFound = 1; - break; - } - for(e=sqliteHashFirst(pHash); e; e=sqliteHashNext(e)){ - pTab = sqliteHashData(e); - if( pTab==0 ) continue; - pHash = pTab->pColHash; - if( pHash==0 ){ - pTab->pColHash = pHash = sqlite3_malloc(sizeof(Hash)); - if( pHash ){ - sqlite3HashInit(pHash); - for(j=0; jnCol; j++){ - Column *pCol = &pTab->aCol[j]; - sqlite3HashInsert(pHash, pCol->zName, pCol); - } - }else{ - *pRc = SQLITE_NOMEM_BKPT; - bFound = 0; - goto done; - } - } - if( pHash && sqlite3HashFindN(pHash, zToken, nToken) ){ - bFound = 1; - goto done; - } - } - } -done: - sqlite3BtreeLeaveAll(db); - sqlite3_mutex_leave(db->mutex); - return bFound; -} - -/* -** Attempt to estimate the final output buffer size needed for the fully -** normalized version of the specified SQL string. This should take into -** account any potential expansion that could occur (e.g. via IN clauses -** being expanded, etc). This size returned is the total number of bytes -** including the NUL terminator. -*/ -static int estimateNormalizedSize( - const char *zSql, /* The original SQL string */ - int nSql, /* Length of original SQL string */ - u8 prepFlags /* The flags passed to sqlite3_prepare_v3() */ -){ - int nOut = nSql + 4; - const char *z = zSql; - while( nOut0 ){ - zOut[j++] = '"'; - continue; - }else if( k==nToken-1 ){ - zOut[j++] = '"'; - continue; - } - } - if( bKeyword ){ - zOut[j++] = sqlite3Toupper(zSql[iIn+k]); - }else{ - zOut[j++] = sqlite3Tolower(zSql[iIn+k]); - } - } - *piOut = j; -} - -/* -** Perform normalization of the SQL contained in the prepared statement and -** store the result in the zNormSql field. The schema for the associated -** databases are consulted while performing the normalization in order to -** determine if a token appears to be an identifier. All identifiers are -** left intact in the normalized SQL and all literals are replaced with a -** single '?'. -*/ -SQLITE_PRIVATE void sqlite3Normalize( - Vdbe *pVdbe, /* VM being reprepared */ - const char *zSql, /* The original SQL string */ - int nSql, /* Size of the input string in bytes */ - u8 prepFlags /* The flags passed to sqlite3_prepare_v3() */ -){ - sqlite3 *db; /* Database handle. */ - char *z; /* The output string */ - int nZ; /* Size of the output string in bytes */ - int i; /* Next character to read from zSql[] */ - int j; /* Next character to fill in on z[] */ - int tokenType = 0; /* Type of the next token */ - int prevTokenType = 0; /* Type of the previous token, except spaces */ - int n; /* Size of the next token */ - int nParen = 0; /* Nesting level of parenthesis */ - Hash inHash; /* Table of parenthesis levels to output index. */ - - db = sqlite3VdbeDb(pVdbe); - assert( db!=0 ); - assert( pVdbe->zNormSql==0 ); - if( zSql==0 ) return; - nZ = estimateNormalizedSize(zSql, nSql, prepFlags); - z = sqlite3DbMallocRawNN(db, nZ); - if( z==0 ) return; - sqlite3HashInit(&inHash); - for(i=j=0; i0 ){ - sqlite3HashInsert(&inHash, zSql+nParen, 0); - assert( jj+6=0 ); - assert( nZ-1-j=0 ); - /* Fall through */ - } - case TK_MINUS: - case TK_SEMI: - case TK_PLUS: - case TK_STAR: - case TK_SLASH: - case TK_REM: - case TK_EQ: - case TK_LE: - case TK_NE: - case TK_LSHIFT: - case TK_LT: - case TK_RSHIFT: - case TK_GT: - case TK_GE: - case TK_BITOR: - case TK_CONCAT: - case TK_COMMA: - case TK_BITAND: - case TK_BITNOT: - case TK_DOT: - case TK_IN: - case TK_IS: - case TK_NOT: - case TK_NULL: - case TK_ID: { - if( tokenType==TK_NULL ){ - if( prevTokenType==TK_IS || prevTokenType==TK_NOT ){ - /* NULL is a keyword in this case, not a literal value */ - }else{ - /* Here the NULL is a literal value */ - z[j++] = '?'; - break; - } - } - if( j>0 && sqlite3IsIdChar(z[j-1]) && sqlite3IsIdChar(zSql[i]) ){ - z[j++] = ' '; - } - if( tokenType==TK_ID ){ - int i2 = i, n2 = n, rc = SQLITE_OK; - if( nParen>0 ){ - assert( nParen0 && z[j-1]==' ' ){ j--; } - if( j>0 && z[j-1]!=';' ){ z[j++] = ';'; } - z[j] = 0; - assert( jzNormSql = z; - sqlite3HashClear(&inHash); -} -#endif /* SQLITE_ENABLE_NORMALIZE */ /* ** Rerun the compilation of a statement after a schema change. @@ -124538,7 +124952,7 @@ static void pushOntoSorter( } assert( pSelect->iOffset==0 || pSelect->iLimit!=0 ); iLimit = pSelect->iOffset ? pSelect->iOffset+1 : pSelect->iLimit; - pSort->labelDone = sqlite3VdbeMakeLabel(v); + pSort->labelDone = sqlite3VdbeMakeLabel(pParse); sqlite3ExprCodeExprList(pParse, pSort->pOrderBy, regBase, regOrigData, SQLITE_ECEL_DUP | (regOrigData? SQLITE_ECEL_REF : 0)); if( bSeq ){ @@ -124577,7 +124991,7 @@ static void pushOntoSorter( pKI->nAllField-pKI->nKeyField-1); addrJmp = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v); - pSort->labelBkOut = sqlite3VdbeMakeLabel(v); + pSort->labelBkOut = sqlite3VdbeMakeLabel(pParse); pSort->regReturn = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut); sqlite3VdbeAddOp1(v, OP_ResetSorter, pSort->iECursor); @@ -125324,7 +125738,7 @@ static void generateSortTail( ){ Vdbe *v = pParse->pVdbe; /* The prepared statement */ int addrBreak = pSort->labelDone; /* Jump here to exit loop */ - int addrContinue = sqlite3VdbeMakeLabel(v); /* Jump here for next cycle */ + int addrContinue = sqlite3VdbeMakeLabel(pParse);/* Jump here for next cycle */ int addr; /* Top of output loop. Jump for Next. */ int addrOnce = 0; int iTab; @@ -125364,7 +125778,12 @@ static void generateSortTail( regRow = pDest->iSdst; }else{ regRowid = sqlite3GetTempReg(pParse); - regRow = sqlite3GetTempRange(pParse, nColumn); + if( eDest==SRT_EphemTab || eDest==SRT_Table ){ + regRow = sqlite3GetTempReg(pParse); + nColumn = 0; + }else{ + regRow = sqlite3GetTempRange(pParse, nColumn); + } } nKey = pOrderBy->nExpr - pSort->nOBSat; if( pSort->sortFlags & SORTFLAG_UseSorter ){ @@ -125444,6 +125863,7 @@ static void generateSortTail( switch( eDest ){ case SRT_Table: case SRT_EphemTab: { + sqlite3VdbeAddOp3(v, OP_Column, iSortTab, nKey+bSeq, regRow); sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, regRowid); sqlite3VdbeAddOp3(v, OP_Insert, iParm, regRow, regRowid); sqlite3VdbeChangeP5(v, OPFLAG_APPEND); @@ -125984,15 +126404,15 @@ SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation( SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){ Table *pTab; sqlite3 *db = pParse->db; - int savedFlags; + u64 savedFlags; savedFlags = db->flags; - db->flags &= ~SQLITE_FullColNames; + db->flags &= ~(u64)SQLITE_FullColNames; db->flags |= SQLITE_ShortColNames; sqlite3SelectPrep(pParse, pSelect, 0); + db->flags = savedFlags; if( pParse->nErr ) return 0; while( pSelect->pPrior ) pSelect = pSelect->pPrior; - db->flags = savedFlags; pTab = sqlite3DbMallocZero(db, sizeof(Table) ); if( pTab==0 ){ return 0; @@ -126236,7 +126656,7 @@ static void generateWithRecursiveQuery( if( sqlite3AuthCheck(pParse, SQLITE_RECURSIVE, 0, 0, 0) ) return; /* Process the LIMIT and OFFSET clauses, if they exist */ - addrBreak = sqlite3VdbeMakeLabel(v); + addrBreak = sqlite3VdbeMakeLabel(pParse); p->nSelectRow = 320; /* 4 billion rows */ computeLimitRegisters(pParse, p, addrBreak); pLimit = p->pLimit; @@ -126306,7 +126726,7 @@ static void generateWithRecursiveQuery( sqlite3VdbeAddOp1(v, OP_Delete, iQueue); /* Output the single row in Current */ - addrCont = sqlite3VdbeMakeLabel(v); + addrCont = sqlite3VdbeMakeLabel(pParse); codeOffset(v, regOffset, addrCont); selectInnerLoop(pParse, p, iCurrent, 0, 0, pDest, addrCont, addrBreak); @@ -126614,8 +127034,8 @@ static int multiSelect( if( dest.eDest!=priorOp ){ int iCont, iBreak, iStart; assert( p->pEList ); - iBreak = sqlite3VdbeMakeLabel(v); - iCont = sqlite3VdbeMakeLabel(v); + iBreak = sqlite3VdbeMakeLabel(pParse); + iCont = sqlite3VdbeMakeLabel(pParse); computeLimitRegisters(pParse, p, iBreak); sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak); VdbeCoverage(v); iStart = sqlite3VdbeCurrentAddr(v); @@ -126683,8 +127103,8 @@ static int multiSelect( ** tables. */ assert( p->pEList ); - iBreak = sqlite3VdbeMakeLabel(v); - iCont = sqlite3VdbeMakeLabel(v); + iBreak = sqlite3VdbeMakeLabel(pParse); + iCont = sqlite3VdbeMakeLabel(pParse); computeLimitRegisters(pParse, p, iBreak); sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak); VdbeCoverage(v); r1 = sqlite3GetTempReg(pParse); @@ -126814,7 +127234,7 @@ static int generateOutputSubroutine( int addr; addr = sqlite3VdbeCurrentAddr(v); - iContinue = sqlite3VdbeMakeLabel(v); + iContinue = sqlite3VdbeMakeLabel(pParse); /* Suppress duplicates for UNION, EXCEPT, and INTERSECT */ @@ -127051,8 +127471,8 @@ static int multiSelectOrderBy( db = pParse->db; v = pParse->pVdbe; assert( v!=0 ); /* Already thrown the error if VDBE alloc failed */ - labelEnd = sqlite3VdbeMakeLabel(v); - labelCmpr = sqlite3VdbeMakeLabel(v); + labelEnd = sqlite3VdbeMakeLabel(pParse); + labelCmpr = sqlite3VdbeMakeLabel(pParse); /* Patch up the ORDER BY clause @@ -127368,6 +127788,7 @@ static Expr *substExpr( ifNullRow.iTable = pSubst->iNewTable; pCopy = &ifNullRow; } + testcase( ExprHasProperty(pCopy, EP_Subquery) ); pNew = sqlite3ExprDup(db, pCopy, 0); if( pNew && pSubst->isLeftJoin ){ ExprSetProperty(pNew, EP_CanBeNull); @@ -127860,11 +128281,9 @@ static int flattenSubquery( jointype = pSubitem->fg.jointype; }else{ assert( pParent!=p ); /* 2nd and subsequent times through the loop */ - pSrc = pParent->pSrc = sqlite3SrcListAppend(db, 0, 0, 0); - if( pSrc==0 ){ - assert( db->mallocFailed ); - break; - } + pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); + if( pSrc==0 ) break; + pParent->pSrc = pSrc; } /* The subquery uses a single slot of the FROM clause of the outer @@ -127883,10 +128302,9 @@ static int flattenSubquery( ** for the two elements in the FROM clause of the subquery. */ if( nSubSrc>1 ){ - pParent->pSrc = pSrc = sqlite3SrcListEnlarge(db, pSrc, nSubSrc-1,iFrom+1); - if( db->mallocFailed ){ - break; - } + pSrc = sqlite3SrcListEnlarge(pParse, pSrc, nSubSrc-1,iFrom+1); + if( pSrc==0 ) break; + pParent->pSrc = pSrc; } /* Transfer the FROM clause terms from the subquery into the @@ -127932,7 +128350,8 @@ static int flattenSubquery( pParent->pOrderBy = pOrderBy; pSub->pOrderBy = 0; } - pWhere = sqlite3ExprDup(db, pSub->pWhere, 0); + pWhere = pSub->pWhere; + pSub->pWhere = 0; if( isLeftJoin>0 ){ setJoinExpr(pWhere, iNewParent); } @@ -129235,7 +129654,7 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){ regAgg = 0; } if( pF->iDistinct>=0 ){ - addrNext = sqlite3VdbeMakeLabel(v); + addrNext = sqlite3VdbeMakeLabel(pParse); testcase( nArg==0 ); /* Error condition */ testcase( nArg>1 ); /* Also an error */ codeDistinct(pParse, pF->iDistinct, addrNext, 1, regAgg); @@ -129371,14 +129790,19 @@ static struct SrcList_item *isSelfJoinView( ){ struct SrcList_item *pItem; for(pItem = pTabList->a; pItempSelect==0 ) continue; if( pItem->fg.viaCoroutine ) continue; if( pItem->zName==0 ) continue; if( sqlite3_stricmp(pItem->zDatabase, pThis->zDatabase)!=0 ) continue; if( sqlite3_stricmp(pItem->zName, pThis->zName)!=0 ) continue; - if( sqlite3ExprCompare(0, - pThis->pSelect->pWhere, pItem->pSelect->pWhere, -1) - ){ + pS1 = pItem->pSelect; + if( pThis->pSelect->selId!=pS1->selId ){ + /* The query flattener left two different CTE tables with identical + ** names in the same FROM clause. */ + continue; + } + if( sqlite3ExprCompare(0, pThis->pSelect->pWhere, pS1->pWhere, -1) ){ /* The view was modified by some other optimization such as ** pushDownWhereTerms() */ continue; @@ -129640,6 +130064,7 @@ SQLITE_PRIVATE int sqlite3Select( } if( flattenSubquery(pParse, p, i, isAgg) ){ + if( pParse->nErr ) goto select_end; /* This subquery can be absorbed into its parent. */ i = -1; } @@ -129735,22 +130160,12 @@ SQLITE_PRIVATE int sqlite3Select( pSub = pItem->pSelect; if( pSub==0 ) continue; - /* Sometimes the code for a subquery will be generated more than - ** once, if the subquery is part of the WHERE clause in a LEFT JOIN, - ** for example. In that case, do not regenerate the code to manifest - ** a view or the co-routine to implement a view. The first instance - ** is sufficient, though the subroutine to manifest the view does need - ** to be invoked again. */ - if( pItem->addrFillSub ){ - if( pItem->fg.viaCoroutine==0 ){ - /* The subroutine that manifests the view might be a one-time routine, - ** or it might need to be rerun on each iteration because it - ** encodes a correlated subquery. */ - testcase( sqlite3VdbeGetOp(v, pItem->addrFillSub)->opcode==OP_Once ); - sqlite3VdbeAddOp2(v, OP_Gosub, pItem->regReturn, pItem->addrFillSub); - } - continue; - } + /* The code for a subquery should only be generated once, though it is + ** technically harmless for it to be generated multiple times. The + ** following assert() will detect if something changes to cause + ** the same subquery to be coded multiple times, as a signal to the + ** developers to try to optimize the situation. */ + assert( pItem->addrFillSub==0 ); /* Increment Parse.nHeight by the height of the largest expression ** tree referred to by this, the parent select. The child select @@ -129938,7 +130353,7 @@ SQLITE_PRIVATE int sqlite3Select( /* Set the limiter. */ - iEnd = sqlite3VdbeMakeLabel(v); + iEnd = sqlite3VdbeMakeLabel(pParse); if( (p->selFlags & SF_FixedLimit)==0 ){ p->nSelectRow = 320; /* 4 billion rows */ } @@ -130005,9 +130420,9 @@ SQLITE_PRIVATE int sqlite3Select( assert( p->pEList==pEList ); #ifndef SQLITE_OMIT_WINDOWFUNC if( pWin ){ - int addrGosub = sqlite3VdbeMakeLabel(v); - int iCont = sqlite3VdbeMakeLabel(v); - int iBreak = sqlite3VdbeMakeLabel(v); + int addrGosub = sqlite3VdbeMakeLabel(pParse); + int iCont = sqlite3VdbeMakeLabel(pParse); + int iBreak = sqlite3VdbeMakeLabel(pParse); int regGosub = ++pParse->nMem; sqlite3WindowCodeStep(pParse, p, pWInfo, regGosub, addrGosub); @@ -130082,7 +130497,7 @@ SQLITE_PRIVATE int sqlite3Select( } /* Create a label to jump to when we want to abort the query */ - addrEnd = sqlite3VdbeMakeLabel(v); + addrEnd = sqlite3VdbeMakeLabel(pParse); /* Convert TK_COLUMN nodes into TK_AGG_COLUMN and make entries in ** sAggInfo for all TK_AGG_FUNCTION nodes in expressions of the @@ -130171,9 +130586,9 @@ SQLITE_PRIVATE int sqlite3Select( iUseFlag = ++pParse->nMem; iAbortFlag = ++pParse->nMem; regOutputRow = ++pParse->nMem; - addrOutputRow = sqlite3VdbeMakeLabel(v); + addrOutputRow = sqlite3VdbeMakeLabel(pParse); regReset = ++pParse->nMem; - addrReset = sqlite3VdbeMakeLabel(v); + addrReset = sqlite3VdbeMakeLabel(pParse); iAMem = pParse->nMem + 1; pParse->nMem += pGroupBy->nExpr; iBMem = pParse->nMem + 1; @@ -131460,7 +131875,7 @@ static SrcList *targetSrcList( int iDb; /* Index of the database to use */ SrcList *pSrc; /* SrcList to be returned */ - pSrc = sqlite3SrcListAppend(db, 0, 0, 0); + pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); if( pSrc ){ assert( pSrc->nSrc>0 ); pSrc->a[pSrc->nSrc-1].zName = sqlite3DbStrDup(db, pStep->zTarget); @@ -131645,6 +132060,7 @@ static TriggerPrg *codeRowTrigger( pSubParse->zAuthContext = pTrigger->zName; pSubParse->eTriggerOp = pTrigger->op; pSubParse->nQueryLoop = pParse->nQueryLoop; + pSubParse->disableVtab = pParse->disableVtab; v = sqlite3GetVdbe(pSubParse); if( v ){ @@ -131672,7 +132088,7 @@ static TriggerPrg *codeRowTrigger( if( SQLITE_OK==sqlite3ResolveExprNames(&sNC, pWhen) && db->mallocFailed==0 ){ - iEndTrigger = sqlite3VdbeMakeLabel(v); + iEndTrigger = sqlite3VdbeMakeLabel(pSubParse); sqlite3ExprIfFalse(pSubParse, pWhen, iEndTrigger, SQLITE_JUMPIFNULL); } sqlite3ExprDelete(db, pWhen); @@ -132271,6 +132687,7 @@ SQLITE_PRIVATE void sqlite3Update( ** being updated. Fill in aRegIdx[] with a register number that will hold ** the key for accessing each index. */ + if( onError==OE_Replace ) bReplace = 1; for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ int reg; if( chngKey || hasFK>1 || pIdx==pPk @@ -132284,9 +132701,7 @@ SQLITE_PRIVATE void sqlite3Update( if( indexColumnIsBeingUpdated(pIdx, i, aXRef, chngRowid) ){ reg = ++pParse->nMem; pParse->nMem += pIdx->nColumn; - if( (onError==OE_Replace) - || (onError==OE_Default && pIdx->onError==OE_Replace) - ){ + if( onError==OE_Default && pIdx->onError==OE_Replace ){ bReplace = 1; } break; @@ -132358,7 +132773,7 @@ SQLITE_PRIVATE void sqlite3Update( #endif /* Jump to labelBreak to abandon further processing of this UPDATE */ - labelContinue = labelBreak = sqlite3VdbeMakeLabel(v); + labelContinue = labelBreak = sqlite3VdbeMakeLabel(pParse); /* Not an UPSERT. Normal processing. Begin by ** initialize the count of updated rows */ @@ -132493,13 +132908,13 @@ SQLITE_PRIVATE void sqlite3Update( VdbeCoverage(v); } if( eOnePass!=ONEPASS_SINGLE ){ - labelContinue = sqlite3VdbeMakeLabel(v); + labelContinue = sqlite3VdbeMakeLabel(pParse); } sqlite3VdbeAddOp2(v, OP_IsNull, pPk ? regKey : regOldRowid, labelBreak); VdbeCoverageIf(v, pPk==0); VdbeCoverageIf(v, pPk!=0); }else if( pPk ){ - labelContinue = sqlite3VdbeMakeLabel(v); + labelContinue = sqlite3VdbeMakeLabel(pParse); sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v); addrTop = sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey); sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey, 0); @@ -133267,16 +133682,16 @@ static int execSqlF(sqlite3 *db, char **pzErrMsg, const char *zSql, ...){ ** transient would cause the database file to appear to be deleted ** following reboot. */ -SQLITE_PRIVATE void sqlite3Vacuum(Parse *pParse, Token *pNm){ +SQLITE_PRIVATE void sqlite3Vacuum(Parse *pParse, Token *pNm, Expr *pInto){ Vdbe *v = sqlite3GetVdbe(pParse); int iDb = 0; - if( v==0 ) return; + if( v==0 ) goto build_vacuum_end; if( pNm ){ #ifndef SQLITE_BUG_COMPATIBLE_20160819 /* Default behavior: Report an error if the argument to VACUUM is ** not recognized */ iDb = sqlite3TwoPartName(pParse, pNm, pNm, &pNm); - if( iDb<0 ) return; + if( iDb<0 ) goto build_vacuum_end; #else /* When SQLITE_BUG_COMPATIBLE_20160819 is defined, unrecognized arguments ** to VACUUM are silently ignored. This is a back-out of a bug fix that @@ -133288,21 +133703,33 @@ SQLITE_PRIVATE void sqlite3Vacuum(Parse *pParse, Token *pNm){ #endif } if( iDb!=1 ){ - sqlite3VdbeAddOp1(v, OP_Vacuum, iDb); + int iIntoReg = 0; + if( pInto && sqlite3ResolveSelfReference(pParse,0,0,pInto,0)==0 ){ + iIntoReg = ++pParse->nMem; + sqlite3ExprCode(pParse, pInto, iIntoReg); + } + sqlite3VdbeAddOp2(v, OP_Vacuum, iDb, iIntoReg); sqlite3VdbeUsesBtree(v, iDb); } +build_vacuum_end: + sqlite3ExprDelete(pParse->db, pInto); return; } /* ** This routine implements the OP_Vacuum opcode of the VDBE. */ -SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){ +SQLITE_PRIVATE int sqlite3RunVacuum( + char **pzErrMsg, /* Write error message here */ + sqlite3 *db, /* Database connection */ + int iDb, /* Which attached DB to vacuum */ + sqlite3_value *pOut /* Write results here, if not NULL */ +){ int rc = SQLITE_OK; /* Return code from service routines */ Btree *pMain; /* The database being vacuumed */ Btree *pTemp; /* The temporary database we vacuum into */ - u16 saved_mDbFlags; /* Saved value of db->mDbFlags */ - u32 saved_flags; /* Saved value of db->flags */ + u32 saved_mDbFlags; /* Saved value of db->mDbFlags */ + u64 saved_flags; /* Saved value of db->flags */ int saved_nChange; /* Saved value of db->nChange */ int saved_nTotalChange; /* Saved value of db->nTotalChange */ u8 saved_mTrace; /* Saved trace settings */ @@ -133311,6 +133738,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){ int nRes; /* Bytes of reserved space at the end of each page */ int nDb; /* Number of attached databases */ const char *zDbMain; /* Schema name of database to vacuum */ + const char *zOut; /* Name of output file */ if( !db->autoCommit ){ sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction"); @@ -133320,6 +133748,15 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){ sqlite3SetString(pzErrMsg, db,"cannot VACUUM - SQL statements in progress"); return SQLITE_ERROR; } + if( pOut ){ + if( sqlite3_value_type(pOut)!=SQLITE_TEXT ){ + sqlite3SetString(pzErrMsg, db, "non-text filename"); + return SQLITE_ERROR; + } + zOut = (const char*)sqlite3_value_text(pOut); + }else{ + zOut = ""; + } /* Save the current value of the database flags so that it can be ** restored before returning. Then set the writable-schema flag, and @@ -133331,7 +133768,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){ saved_mTrace = db->mTrace; db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks; db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum; - db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder + db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder | SQLITE_Defensive | SQLITE_CountRows); db->mTrace = 0; @@ -133354,19 +133791,21 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){ ** to write the journal header file. */ nDb = db->nDb; - rc = execSql(db, pzErrMsg, "ATTACH''AS vacuum_db"); + rc = execSqlF(db, pzErrMsg, "ATTACH %Q AS vacuum_db", zOut); if( rc!=SQLITE_OK ) goto end_of_vacuum; assert( (db->nDb-1)==nDb ); pDb = &db->aDb[nDb]; assert( strcmp(pDb->zDbSName,"vacuum_db")==0 ); pTemp = pDb->pBt; - - /* The call to execSql() to attach the temp database has left the file - ** locked (as there was more than one active statement when the transaction - ** to read the schema was concluded. Unlock it here so that this doesn't - ** cause problems for the call to BtreeSetPageSize() below. */ - sqlite3BtreeCommit(pTemp); - + if( pOut ){ + sqlite3_file *id = sqlite3PagerFile(sqlite3BtreePager(pTemp)); + i64 sz = 0; + if( id->pMethods!=0 && (sqlite3OsFileSize(id, &sz)!=SQLITE_OK || sz>0) ){ + rc = SQLITE_ERROR; + sqlite3SetString(pzErrMsg, db, "output file already exists"); + goto end_of_vacuum; + } + } nRes = sqlite3BtreeGetOptimalReserve(pMain); /* A VACUUM cannot change the pagesize of an encrypted database. */ @@ -133390,7 +133829,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){ */ rc = execSql(db, pzErrMsg, "BEGIN"); if( rc!=SQLITE_OK ) goto end_of_vacuum; - rc = sqlite3BtreeBeginTrans(pMain, 2, 0); + rc = sqlite3BtreeBeginTrans(pMain, pOut==0 ? 2 : 0, 0); if( rc!=SQLITE_OK ) goto end_of_vacuum; /* Do not attempt to change the page size for a WAL database */ @@ -133485,7 +133924,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){ }; assert( 1==sqlite3BtreeIsInTrans(pTemp) ); - assert( 1==sqlite3BtreeIsInTrans(pMain) ); + assert( pOut!=0 || 1==sqlite3BtreeIsInTrans(pMain) ); /* Copy Btree meta values */ for(i=0; iflags */ @@ -134547,6 +134992,7 @@ SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){ const sqlite3_module *pMod = pVTab->pMod->pModule; if( pVTab->pVtab && pMod->iVersion>=2 ){ int (*xMethod)(sqlite3_vtab *, int); + sqlite3VtabLock(pVTab); switch( op ){ case SAVEPOINT_BEGIN: xMethod = pMod->xSavepoint; @@ -134562,6 +135008,7 @@ SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){ if( xMethod && pVTab->iSavepoint>iSavepoint ){ rc = xMethod(pVTab->pVtab, iSavepoint); } + sqlite3VtabUnlock(pVTab); } } } @@ -135323,8 +135770,11 @@ SQLITE_PRIVATE void sqlite3WhereAddScanStatus( # define sqlite3WhereAddScanStatus(a, b, c, d) ((void)d) #endif SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + Parse *pParse, /* Parsing context */ + Vdbe *v, /* Prepared statement under construction */ WhereInfo *pWInfo, /* Complete information about the WHERE clause */ int iLevel, /* Which level of pWInfo->a[] should be coded */ + WhereLevel *pLevel, /* The current level pointer */ Bitmask notReady /* Which tables are currently available */ ); @@ -135594,6 +136044,7 @@ SQLITE_PRIVATE int sqlite3WhereExplainOneScan( } #endif zMsg = sqlite3StrAccumFinish(&str); + sqlite3ExplainBreakpoint("",zMsg); ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v), pParse->addrExplain, 0, zMsg,P4_DYNAMIC); } @@ -135919,16 +136370,17 @@ static int codeEqualityTerm( if( pLoop->aLTerm[i]->pExpr==pX ) nEq++; } + iTab = 0; if( (pX->flags & EP_xIsSelect)==0 || pX->x.pSelect->pEList->nExpr==1 ){ - eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0); + eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0, &iTab); }else{ sqlite3 *db = pParse->db; pX = removeUnindexableInClauseTerms(pParse, iEq, pLoop, pX); if( !db->mallocFailed ){ aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*nEq); - eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap); - pTerm->pExpr->iTable = pX->iTable; + eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap, &iTab); + pTerm->pExpr->iTable = iTab; } sqlite3ExprDelete(db, pX); pX = pTerm->pExpr; @@ -135938,7 +136390,6 @@ static int codeEqualityTerm( testcase( bRev ); bRev = !bRev; } - iTab = pX->iTable; sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iTab, 0); VdbeCoverageIf(v, bRev); VdbeCoverageIf(v, !bRev); @@ -135946,7 +136397,7 @@ static int codeEqualityTerm( pLoop->wsFlags |= WHERE_IN_ABLE; if( pLevel->u.in.nIn==0 ){ - pLevel->addrNxt = sqlite3VdbeMakeLabel(v); + pLevel->addrNxt = sqlite3VdbeMakeLabel(pParse); } i = pLevel->u.in.nIn; @@ -135962,7 +136413,6 @@ static int codeEqualityTerm( if( pLoop->aLTerm[i]->pExpr==pX ){ int iOut = iReg + i - iEq; if( eType==IN_INDEX_ROWID ){ - testcase( nEq>1 ); /* Happens with a UNIQUE index on ROWID */ pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iOut); }else{ int iCol = aiMap ? aiMap[iMap++] : 0; @@ -136457,7 +136907,9 @@ static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){ #ifndef SQLITE_OMIT_SUBQUERY if( (p->flags & EP_xIsSelect) ){ Vdbe *v = pParse->pVdbe; - int iSelect = sqlite3CodeSubselect(pParse, p, 0, 0); + int iSelect; + assert( p->op==TK_SELECT ); + iSelect = sqlite3CodeSubselect(pParse, p); sqlite3VdbeAddOp3(v, OP_Copy, iSelect, iReg, nReg-1); }else #endif @@ -136543,22 +136995,21 @@ static void whereIndexExprTrans( ** implementation described by pWInfo. */ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( + Parse *pParse, /* Parsing context */ + Vdbe *v, /* Prepared statement under construction */ WhereInfo *pWInfo, /* Complete information about the WHERE clause */ int iLevel, /* Which level of pWInfo->a[] should be coded */ + WhereLevel *pLevel, /* The current level pointer */ Bitmask notReady /* Which tables are currently available */ ){ int j, k; /* Loop counters */ int iCur; /* The VDBE cursor for the table */ int addrNxt; /* Where to jump to continue with the next IN case */ - int omitTable; /* True if we use the index only */ int bRev; /* True if we need to scan in reverse order */ - WhereLevel *pLevel; /* The where level to be coded */ WhereLoop *pLoop; /* The WhereLoop object being coded */ WhereClause *pWC; /* Decomposition of the entire WHERE clause */ WhereTerm *pTerm; /* A WHERE clause term */ - Parse *pParse; /* Parsing context */ sqlite3 *db; /* Database connection */ - Vdbe *v; /* The prepared stmt under constructions */ struct SrcList_item *pTabItem; /* FROM clause term being coded */ int addrBrk; /* Jump here to break out of the loop */ int addrHalt; /* addrBrk for the outermost loop */ @@ -136568,18 +137019,13 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( Index *pIdx = 0; /* Index used by loop (if any) */ int iLoop; /* Iteration of constraint generator loop */ - pParse = pWInfo->pParse; - v = pParse->pVdbe; pWC = &pWInfo->sWC; db = pParse->db; - pLevel = &pWInfo->a[iLevel]; pLoop = pLevel->pWLoop; pTabItem = &pWInfo->pTabList->a[pLevel->iFrom]; iCur = pTabItem->iCursor; pLevel->notReady = notReady & ~sqlite3WhereGetMask(&pWInfo->sMaskSet, iCur); bRev = (pWInfo->revMask>>iLevel)&1; - omitTable = (pLoop->wsFlags & WHERE_IDX_ONLY)!=0 - && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0; VdbeModuleComment((v, "Begin WHERE-loop%d: %s",iLevel,pTabItem->pTab->zName)); /* Create labels for the "break" and "continue" instructions @@ -136592,8 +137038,8 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( ** there are no IN operators in the constraints, the "addrNxt" label ** is the same as "addrBrk". */ - addrBrk = pLevel->addrBrk = pLevel->addrNxt = sqlite3VdbeMakeLabel(v); - addrCont = pLevel->addrCont = sqlite3VdbeMakeLabel(v); + addrBrk = pLevel->addrBrk = pLevel->addrNxt = sqlite3VdbeMakeLabel(pParse); + addrCont = pLevel->addrCont = sqlite3VdbeMakeLabel(pParse); /* If this is the right table of a LEFT OUTER JOIN, allocate and ** initialize a memory cell that records if this table matches any @@ -136720,7 +137166,6 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( pTerm = pLoop->aLTerm[0]; assert( pTerm!=0 ); assert( pTerm->pExpr!=0 ); - assert( omitTable==0 ); testcase( pTerm->wtFlags & TERM_VIRTUAL ); iReleaseReg = ++pParse->nMem; iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, bRev, iReleaseReg); @@ -136729,6 +137174,9 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( sqlite3VdbeAddOp3(v, OP_SeekRowid, iCur, addrNxt, iRowidReg); VdbeCoverage(v); pLevel->op = OP_Noop; + if( (pTerm->prereqAll & pLevel->notReady)==0 ){ + pTerm->wtFlags |= TERM_CODED; + } }else if( (pLoop->wsFlags & WHERE_IPK)!=0 && (pLoop->wsFlags & WHERE_COLUMN_RANGE)!=0 ){ @@ -136739,7 +137187,6 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( int memEndValue = 0; WhereTerm *pStart, *pEnd; - assert( omitTable==0 ); j = 0; pStart = pEnd = 0; if( pLoop->wsFlags & WHERE_BTM_LIMIT ) pStart = pLoop->aLTerm[j++]; @@ -136903,6 +137350,8 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( char *zEndAff = 0; /* Affinity for end of range constraint */ u8 bSeekPastNull = 0; /* True to seek past initial nulls */ u8 bStopAtNull = 0; /* Add condition to terminate at NULLs */ + int omitTable; /* True if we use the index only */ + pIdx = pLoop->u.btree.pIndex; iIdxCur = pLevel->iIdxCur; @@ -137104,6 +137553,8 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( } /* Seek the table cursor, if required */ + omitTable = (pLoop->wsFlags & WHERE_IDX_ONLY)!=0 + && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0; if( omitTable ){ /* pIdx is a covering index. No need to access the main table. */ }else if( HasRowid(pIdx->pTable) ){ @@ -137138,8 +137589,13 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( ** the cursor. In this case it is important to do the full evaluation, ** as the result of the expression may not be NULL, even if all table ** column values are. https://www.sqlite.org/src/info/7fa8049685b50b5a + ** + ** Also, do not do this when processing one index an a multi-index + ** OR clause, since the transformation will become invalid once we + ** move forward to the next index. + ** https://sqlite.org/src/info/4e8e4857d32d401f */ - if( pLevel->iLeftJoin==0 ){ + if( pLevel->iLeftJoin==0 && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ){ whereIndexExprTrans(pIdx, iCur, iIdxCur, pWInfo); } @@ -137214,7 +137670,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */ int regRowset = 0; /* Register for RowSet object */ int regRowid = 0; /* Register holding rowid */ - int iLoopBody = sqlite3VdbeMakeLabel(v); /* Start of loop body */ + int iLoopBody = sqlite3VdbeMakeLabel(pParse);/* Start of loop body */ int iRetInit; /* Address of regReturn init */ int untestedTerms = 0; /* Some terms not completely tested */ int ii; /* Loop counter */ @@ -137330,6 +137786,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( pOrExpr = pAndExpr; } /* Loop through table entries that match term pOrTerm. */ + ExplainQueryPlan((pParse, 1, "INDEX %d", ii+1)); WHERETRACE(0xffff, ("Subplan for OR-clause:\n")); pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0, wctrlFlags, iCovCur); @@ -137433,6 +137890,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart( /* Finish the loop through table entries that match term pOrTerm. */ sqlite3WhereEnd(pSubWInfo); + ExplainQueryPlanPop(pParse); } } } @@ -138394,6 +138852,7 @@ static void exprAnalyzeOrTerm( ** and column is found but leave okToChngToIN false if not found. */ for(j=0; j<2 && !okToChngToIN; j++){ + Expr *pLeft = 0; pOrTerm = pOrWc->a; for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){ assert( pOrTerm->eOperator & WO_EQ ); @@ -138417,6 +138876,7 @@ static void exprAnalyzeOrTerm( } iColumn = pOrTerm->u.leftColumn; iCursor = pOrTerm->leftCursor; + pLeft = pOrTerm->pExpr->pLeft; break; } if( i<0 ){ @@ -138436,7 +138896,9 @@ static void exprAnalyzeOrTerm( assert( pOrTerm->eOperator & WO_EQ ); if( pOrTerm->leftCursor!=iCursor ){ pOrTerm->wtFlags &= ~TERM_OR_OK; - }else if( pOrTerm->u.leftColumn!=iColumn ){ + }else if( pOrTerm->u.leftColumn!=iColumn || (iColumn==XN_EXPR + && sqlite3ExprCompare(pParse, pOrTerm->pExpr->pLeft, pLeft, -1) + )){ okToChngToIN = 0; }else{ int affLeft, affRight; @@ -139524,6 +139986,17 @@ static WhereTerm *whereScanNext(WhereScan *pScan){ return 0; } +/* +** This is whereScanInit() for the case of an index on an expression. +** It is factored out into a separate tail-recursion subroutine so that +** the normal whereScanInit() routine, which is a high-runner, does not +** need to push registers onto the stack as part of its prologue. +*/ +static SQLITE_NOINLINE WhereTerm *whereScanInitIndexExpr(WhereScan *pScan){ + pScan->idxaff = sqlite3ExprAffinity(pScan->pIdxExpr); + return whereScanNext(pScan); +} + /* ** Initialize a WHERE clause scanner object. Return a pointer to the ** first match. Return NULL if there are no matches. @@ -139556,12 +140029,19 @@ static WhereTerm *whereScanInit( pScan->pIdxExpr = 0; pScan->idxaff = 0; pScan->zCollName = 0; + pScan->opMask = opMask; + pScan->k = 0; + pScan->aiCur[0] = iCur; + pScan->nEquiv = 1; + pScan->iEquiv = 1; if( pIdx ){ int j = iColumn; iColumn = pIdx->aiColumn[j]; if( iColumn==XN_EXPR ){ pScan->pIdxExpr = pIdx->aColExpr->a[j].pExpr; pScan->zCollName = pIdx->azColl[j]; + pScan->aiColumn[0] = XN_EXPR; + return whereScanInitIndexExpr(pScan); }else if( iColumn==pIdx->pTable->iPKey ){ iColumn = XN_ROWID; }else if( iColumn>=0 ){ @@ -139571,12 +140051,7 @@ static WhereTerm *whereScanInit( }else if( iColumn==XN_EXPR ){ return 0; } - pScan->opMask = opMask; - pScan->k = 0; - pScan->aiCur[0] = iCur; pScan->aiColumn[0] = iColumn; - pScan->nEquiv = 1; - pScan->iEquiv = 1; return whereScanNext(pScan); } @@ -140051,7 +140526,7 @@ static void constructAutomaticIndex( addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v); } if( pPartial ){ - iContinue = sqlite3VdbeMakeLabel(v); + iContinue = sqlite3VdbeMakeLabel(pParse); sqlite3ExprIfFalse(pParse, pPartial, iContinue, SQLITE_JUMPIFNULL); pLoop->wsFlags |= WHERE_PARTIALIDX; } @@ -140068,6 +140543,7 @@ static void constructAutomaticIndex( translateColumnToCopy(pParse, addrTop, pLevel->iTabCur, pTabItem->regResult, 1); sqlite3VdbeGoto(v, addrTop); + pTabItem->fg.viaCoroutine = 0; }else{ sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v); } @@ -141423,7 +141899,7 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){ rc = whereLoopXfer(db, p, pTemplate); if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){ Index *pIndex = p->u.btree.pIndex; - if( pIndex && pIndex->tnum==0 ){ + if( pIndex && pIndex->idxType==SQLITE_IDXTYPE_IPK ){ p->u.btree.pIndex = 0; } } @@ -141590,8 +142066,8 @@ static int whereRangeVectorLen( ** terms only. If it is modified, this value is restored before this ** function returns. ** -** If pProbe->tnum==0, that means pIndex is a fake index used for the -** INTEGER PRIMARY KEY. +** If pProbe->idxType==SQLITE_IDXTYPE_IPK, that means pIndex is +** a fake index used for the INTEGER PRIMARY KEY. */ static int whereLoopAddBtreeIndex( WhereLoopBuilder *pBuilder, /* The WhereLoop factory */ @@ -142091,6 +142567,7 @@ static int whereLoopAddBtree( sPk.onError = OE_Replace; sPk.pTable = pTab; sPk.szIdxRow = pTab->szTabRow; + sPk.idxType = SQLITE_IDXTYPE_IPK; aiRowEstPk[0] = pTab->nRowLogEst; aiRowEstPk[1] = 0; pFirst = pSrc->pTab->pIndex; @@ -142181,7 +142658,7 @@ static int whereLoopAddBtree( b = indexMightHelpWithOrderBy(pBuilder, pProbe, pSrc->iCursor); /* The ONEPASS_DESIRED flags never occurs together with ORDER BY */ assert( (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || b==0 ); - if( pProbe->tnum<=0 ){ + if( pProbe->idxType==SQLITE_IDXTYPE_IPK ){ /* Integer primary key index */ pNew->wsFlags = WHERE_IPK; @@ -143857,7 +144334,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( pWInfo->pResultSet = pResultSet; pWInfo->aiCurOnePass[0] = pWInfo->aiCurOnePass[1] = -1; pWInfo->nLevel = nTabList; - pWInfo->iBreak = pWInfo->iContinue = sqlite3VdbeMakeLabel(v); + pWInfo->iBreak = pWInfo->iContinue = sqlite3VdbeMakeLabel(pParse); pWInfo->wctrlFlags = wctrlFlags; pWInfo->iLimit = iAuxArg; pWInfo->savedNQueryLoop = pParse->nQueryLoop; @@ -144131,9 +144608,10 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 ){ int wsFlags = pWInfo->a[0].pWLoop->wsFlags; int bOnerow = (wsFlags & WHERE_ONEROW)!=0; + assert( !(wsFlags & WHERE_VIRTUALTABLE) || IsVirtual(pTabList->a[0].pTab) ); if( bOnerow || ( 0!=(wctrlFlags & WHERE_ONEPASS_MULTIROW) - && 0==(wsFlags & WHERE_VIRTUALTABLE) + && !IsVirtual(pTabList->a[0].pTab) && (0==(wsFlags & WHERE_MULTI_OR) || (wctrlFlags & WHERE_DUPLICATES_OK)) )){ pWInfo->eOnePass = bOnerow ? ONEPASS_SINGLE : ONEPASS_MULTI; @@ -144288,7 +144766,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin( pParse, pTabList, pLevel, wctrlFlags ); pLevel->addrBody = sqlite3VdbeCurrentAddr(v); - notReady = sqlite3WhereCodeOneLoopStart(pWInfo, ii, notReady); + notReady = sqlite3WhereCodeOneLoopStart(pParse,v,pWInfo,ii,pLevel,notReady); pWInfo->iContinue = pLevel->addrCont; if( (wsFlags&WHERE_MULTI_OR)==0 && (wctrlFlags&WHERE_OR_SUBCLAUSE)==0 ){ sqlite3WhereAddScanStatus(v, pTabList, pLevel, addrExplain); @@ -144473,6 +144951,29 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){ continue; } +#ifdef SQLITE_ENABLE_EARLY_CURSOR_CLOSE + /* Close all of the cursors that were opened by sqlite3WhereBegin. + ** Except, do not close cursors that will be reused by the OR optimization + ** (WHERE_OR_SUBCLAUSE). And do not close the OP_OpenWrite cursors + ** created for the ONEPASS optimization. + */ + if( (pTab->tabFlags & TF_Ephemeral)==0 + && pTab->pSelect==0 + && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0 + ){ + int ws = pLoop->wsFlags; + if( pWInfo->eOnePass==ONEPASS_OFF && (ws & WHERE_IDX_ONLY)==0 ){ + sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor); + } + if( (ws & WHERE_INDEXED)!=0 + && (ws & (WHERE_IPK|WHERE_AUTO_INDEX))==0 + && pLevel->iIdxCur!=pWInfo->aiCurOnePass[1] + ){ + sqlite3VdbeAddOp1(v, OP_Close, pLevel->iIdxCur); + } + } +#endif + /* If this scan uses an index, make VDBE code substitutions to read data ** from the index instead of from the table where possible. In some cases ** this optimization prevents the table from ever being read, which can @@ -145372,8 +145873,7 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){ pSub = sqlite3SelectNew( pParse, pSublist, pSrc, pWhere, pGroupBy, pHaving, pSort, 0, 0 ); - p->pSrc = sqlite3SrcListAppend(db, 0, 0, 0); - assert( p->pSrc || db->mallocFailed ); + p->pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0); if( p->pSrc ){ p->pSrc->a[0].pSelect = pSub; sqlite3SrcListAssignCursors(pParse, p->pSrc); @@ -145430,6 +145930,7 @@ SQLITE_PRIVATE void sqlite3WindowListDelete(sqlite3 *db, Window *p){ */ static Expr *sqlite3WindowOffsetExpr(Parse *pParse, Expr *pExpr){ if( 0==sqlite3ExprIsConstant(pExpr) ){ + if( IN_RENAME_OBJECT ) sqlite3RenameExprUnmap(pParse, pExpr); sqlite3ExprDelete(pParse->db, pExpr); pExpr = sqlite3ExprAlloc(pParse->db, TK_NULL, 0, 0); } @@ -145624,6 +146125,7 @@ static void windowCheckIntValue(Parse *pParse, int reg, int eCond){ VdbeCoverageNeverNullIf(v, eCond==0); VdbeCoverageNeverNullIf(v, eCond==1); VdbeCoverageNeverNullIf(v, eCond==2); + sqlite3MayAbort(pParse); sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_ERROR, OE_Abort); sqlite3VdbeAppendP4(v, (void*)azErr[eCond], P4_STATIC); sqlite3ReleaseTempReg(pParse, regZero); @@ -145879,7 +146381,7 @@ static void windowReturnOneRow( || pFunc->zName==first_valueName ){ int csr = pWin->csrApp; - int lbl = sqlite3VdbeMakeLabel(v); + int lbl = sqlite3VdbeMakeLabel(pParse); int tmpReg = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult); @@ -145902,7 +146404,7 @@ static void windowReturnOneRow( int nArg = pWin->pOwner->x.pList->nExpr; int iEph = pMWin->iEphCsr; int csr = pWin->csrApp; - int lbl = sqlite3VdbeMakeLabel(v); + int lbl = sqlite3VdbeMakeLabel(pParse); int tmpReg = sqlite3GetTempReg(pParse); if( nArg<3 ){ @@ -146163,8 +146665,8 @@ static void windowCodeRowExprStep( /* Allocate register and label for the "flush_partition" sub-routine. */ regFlushPart = ++pParse->nMem; - lblFlushPart = sqlite3VdbeMakeLabel(v); - lblFlushDone = sqlite3VdbeMakeLabel(v); + lblFlushPart = sqlite3VdbeMakeLabel(pParse); + lblFlushDone = sqlite3VdbeMakeLabel(pParse); regStart = ++pParse->nMem; regEnd = ++pParse->nMem; @@ -146274,7 +146776,7 @@ static void windowCodeRowExprStep( || pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_FOLLOWING ){ - int lblSkipInverse = sqlite3VdbeMakeLabel(v);; + int lblSkipInverse = sqlite3VdbeMakeLabel(pParse);; if( pMWin->eStart==TK_PRECEDING ){ sqlite3VdbeAddOp3(v, OP_IfPos, regStart, lblSkipInverse, 1); VdbeCoverage(v); @@ -146439,13 +146941,13 @@ static void windowCodeCacheStep( || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED) ); - lblEmpty = sqlite3VdbeMakeLabel(v); + lblEmpty = sqlite3VdbeMakeLabel(pParse); regNewPeer = pParse->nMem+1; pParse->nMem += nPeer; /* Allocate register and label for the "flush_partition" sub-routine. */ regFlushPart = ++pParse->nMem; - lblFlushPart = sqlite3VdbeMakeLabel(v); + lblFlushPart = sqlite3VdbeMakeLabel(pParse); csrLead = pParse->nTab++; regCtr = ++pParse->nMem; @@ -146682,6 +147184,7 @@ SQLITE_PRIVATE Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p){ if( pNew ){ pNew->zName = sqlite3DbStrDup(db, p->zName); pNew->pFilter = sqlite3ExprDup(db, p->pFilter, 0); + pNew->pFunc = p->pFunc; pNew->pPartition = sqlite3ExprListDup(db, p->pPartition, 0); pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, 0); pNew->eType = p->eType; @@ -146939,8 +147442,7 @@ static void disableLookaside(Parse *pParse){ memcpy(p->u.zToken, t.z, t.n); p->u.zToken[t.n] = 0; if( sqlite3Isquote(p->u.zToken[0]) ){ - if( p->u.zToken[0]=='"' ) p->flags |= EP_DblQuoted; - sqlite3Dequote(p->u.zToken); + sqlite3DequoteExpr(p); } #if SQLITE_MAX_EXPR_DEPTH>0 p->nHeight = 1; @@ -147049,27 +147551,27 @@ static void disableLookaside(Parse *pParse){ #endif /************* Begin control #defines *****************************************/ #define YYCODETYPE unsigned short int -#define YYNOCODE 277 +#define YYNOCODE 278 #define YYACTIONTYPE unsigned short int #define YYWILDCARD 91 #define sqlite3ParserTOKENTYPE Token typedef union { int yyinit; sqlite3ParserTOKENTYPE yy0; - Expr* yy18; - struct TrigEvent yy34; - IdList* yy48; - int yy70; - struct {int value; int mask;} yy111; - struct FrameBound yy119; - SrcList* yy135; - TriggerStep* yy207; - Window* yy327; - Upsert* yy340; - const char* yy392; - ExprList* yy420; - With* yy449; - Select* yy489; + ExprList* yy42; + int yy96; + TriggerStep* yy119; + Window* yy147; + SrcList* yy167; + Upsert* yy266; + struct FrameBound yy317; + IdList* yy336; + struct TrigEvent yy350; + struct {int value; int mask;} yy367; + Select* yy423; + const char* yy464; + Expr* yy490; + With* yy499; } YYMINORTYPE; #ifndef YYSTACKDEPTH #define YYSTACKDEPTH 100 @@ -147085,17 +147587,17 @@ typedef union { #define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse; #define sqlite3ParserCTX_STORE yypParser->pParse=pParse; #define YYFALLBACK 1 -#define YYNSTATE 521 -#define YYNRULE 367 +#define YYNSTATE 524 +#define YYNRULE 369 #define YYNTOKEN 155 -#define YY_MAX_SHIFT 520 -#define YY_MIN_SHIFTREDUCE 756 -#define YY_MAX_SHIFTREDUCE 1122 -#define YY_ERROR_ACTION 1123 -#define YY_ACCEPT_ACTION 1124 -#define YY_NO_ACTION 1125 -#define YY_MIN_REDUCE 1126 -#define YY_MAX_REDUCE 1492 +#define YY_MAX_SHIFT 523 +#define YY_MIN_SHIFTREDUCE 760 +#define YY_MAX_SHIFTREDUCE 1128 +#define YY_ERROR_ACTION 1129 +#define YY_ACCEPT_ACTION 1130 +#define YY_NO_ACTION 1131 +#define YY_MIN_REDUCE 1132 +#define YY_MAX_REDUCE 1500 /************* End control #defines *******************************************/ #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) @@ -147164,566 +147666,567 @@ typedef union { *********** Begin parsing tables **********************************************/ #define YY_ACTTAB_COUNT (2009) static const YYACTIONTYPE yy_action[] = { - /* 0 */ 368, 105, 102, 197, 105, 102, 197, 515, 1124, 1, - /* 10 */ 1, 520, 2, 1128, 515, 1192, 1171, 1456, 275, 370, - /* 20 */ 127, 1389, 1197, 1197, 1192, 1166, 178, 1205, 64, 64, - /* 30 */ 477, 887, 322, 428, 348, 37, 37, 808, 362, 888, - /* 40 */ 509, 509, 509, 112, 113, 103, 1100, 1100, 953, 956, - /* 50 */ 946, 946, 110, 110, 111, 111, 111, 111, 365, 252, - /* 60 */ 252, 515, 252, 252, 497, 515, 309, 515, 459, 515, - /* 70 */ 1079, 491, 512, 478, 6, 512, 809, 134, 498, 228, - /* 80 */ 194, 428, 37, 37, 515, 208, 64, 64, 64, 64, - /* 90 */ 13, 13, 109, 109, 109, 109, 108, 108, 107, 107, - /* 100 */ 107, 106, 401, 258, 381, 13, 13, 398, 397, 428, - /* 110 */ 252, 252, 370, 476, 405, 1104, 1079, 1080, 1081, 386, - /* 120 */ 1106, 390, 497, 512, 497, 1423, 1419, 304, 1105, 307, - /* 130 */ 1256, 496, 370, 499, 16, 16, 112, 113, 103, 1100, - /* 140 */ 1100, 953, 956, 946, 946, 110, 110, 111, 111, 111, - /* 150 */ 111, 262, 1107, 495, 1107, 401, 112, 113, 103, 1100, - /* 160 */ 1100, 953, 956, 946, 946, 110, 110, 111, 111, 111, - /* 170 */ 111, 129, 1425, 343, 1420, 339, 1059, 492, 1057, 263, - /* 180 */ 73, 105, 102, 197, 994, 109, 109, 109, 109, 108, - /* 190 */ 108, 107, 107, 107, 106, 401, 370, 111, 111, 111, - /* 200 */ 111, 104, 492, 89, 1432, 109, 109, 109, 109, 108, - /* 210 */ 108, 107, 107, 107, 106, 401, 111, 111, 111, 111, - /* 220 */ 112, 113, 103, 1100, 1100, 953, 956, 946, 946, 110, - /* 230 */ 110, 111, 111, 111, 111, 109, 109, 109, 109, 108, - /* 240 */ 108, 107, 107, 107, 106, 401, 114, 108, 108, 107, - /* 250 */ 107, 107, 106, 401, 109, 109, 109, 109, 108, 108, - /* 260 */ 107, 107, 107, 106, 401, 152, 399, 399, 399, 109, - /* 270 */ 109, 109, 109, 108, 108, 107, 107, 107, 106, 401, - /* 280 */ 178, 493, 1412, 434, 1037, 1486, 1079, 515, 1486, 370, - /* 290 */ 421, 297, 357, 412, 74, 1079, 109, 109, 109, 109, - /* 300 */ 108, 108, 107, 107, 107, 106, 401, 1413, 37, 37, - /* 310 */ 1431, 274, 506, 112, 113, 103, 1100, 1100, 953, 956, - /* 320 */ 946, 946, 110, 110, 111, 111, 111, 111, 1436, 520, - /* 330 */ 2, 1128, 1079, 1080, 1081, 430, 275, 1079, 127, 366, - /* 340 */ 933, 1079, 1080, 1081, 220, 1205, 913, 458, 455, 454, - /* 350 */ 392, 167, 515, 1035, 152, 445, 924, 453, 152, 874, - /* 360 */ 923, 289, 109, 109, 109, 109, 108, 108, 107, 107, - /* 370 */ 107, 106, 401, 13, 13, 261, 853, 252, 252, 227, - /* 380 */ 106, 401, 370, 1079, 1080, 1081, 311, 388, 1079, 296, - /* 390 */ 512, 923, 923, 925, 231, 323, 1255, 1388, 1423, 490, - /* 400 */ 274, 506, 12, 208, 274, 506, 112, 113, 103, 1100, - /* 410 */ 1100, 953, 956, 946, 946, 110, 110, 111, 111, 111, - /* 420 */ 111, 1440, 286, 1128, 288, 1079, 1097, 247, 275, 1098, - /* 430 */ 127, 387, 405, 389, 1079, 1080, 1081, 1205, 159, 238, - /* 440 */ 255, 321, 461, 316, 460, 225, 790, 105, 102, 197, - /* 450 */ 513, 314, 842, 842, 445, 109, 109, 109, 109, 108, - /* 460 */ 108, 107, 107, 107, 106, 401, 515, 514, 515, 252, - /* 470 */ 252, 1079, 1080, 1081, 435, 370, 1098, 933, 1460, 794, - /* 480 */ 274, 506, 512, 105, 102, 197, 336, 63, 63, 64, - /* 490 */ 64, 27, 790, 924, 287, 208, 1354, 923, 515, 112, - /* 500 */ 113, 103, 1100, 1100, 953, 956, 946, 946, 110, 110, - /* 510 */ 111, 111, 111, 111, 107, 107, 107, 106, 401, 49, - /* 520 */ 49, 515, 28, 1079, 405, 497, 421, 297, 923, 923, - /* 530 */ 925, 186, 468, 1079, 467, 999, 999, 442, 515, 1079, - /* 540 */ 334, 515, 45, 45, 1083, 342, 173, 168, 109, 109, - /* 550 */ 109, 109, 108, 108, 107, 107, 107, 106, 401, 13, - /* 560 */ 13, 205, 13, 13, 252, 252, 1195, 1195, 370, 1079, - /* 570 */ 1080, 1081, 787, 265, 5, 359, 494, 512, 469, 1079, - /* 580 */ 1080, 1081, 398, 397, 1079, 1079, 1080, 1081, 3, 282, - /* 590 */ 1079, 1083, 112, 113, 103, 1100, 1100, 953, 956, 946, - /* 600 */ 946, 110, 110, 111, 111, 111, 111, 252, 252, 1015, - /* 610 */ 220, 1079, 873, 458, 455, 454, 943, 943, 954, 957, - /* 620 */ 512, 252, 252, 453, 1016, 1079, 445, 1107, 1209, 1107, - /* 630 */ 1079, 1080, 1081, 515, 512, 426, 1079, 1080, 1081, 1017, - /* 640 */ 512, 109, 109, 109, 109, 108, 108, 107, 107, 107, - /* 650 */ 106, 401, 1052, 515, 50, 50, 515, 1079, 1080, 1081, - /* 660 */ 828, 370, 1051, 379, 411, 1064, 1358, 207, 408, 773, - /* 670 */ 829, 1079, 1080, 1081, 64, 64, 322, 64, 64, 1302, - /* 680 */ 947, 411, 410, 1358, 1360, 112, 113, 103, 1100, 1100, - /* 690 */ 953, 956, 946, 946, 110, 110, 111, 111, 111, 111, - /* 700 */ 294, 482, 515, 1037, 1487, 515, 434, 1487, 354, 1120, - /* 710 */ 483, 996, 913, 485, 466, 996, 132, 178, 33, 450, - /* 720 */ 1203, 136, 406, 64, 64, 479, 64, 64, 419, 369, - /* 730 */ 283, 1146, 252, 252, 109, 109, 109, 109, 108, 108, - /* 740 */ 107, 107, 107, 106, 401, 512, 224, 440, 411, 266, - /* 750 */ 1358, 266, 252, 252, 370, 296, 416, 284, 934, 396, - /* 760 */ 976, 470, 400, 252, 252, 512, 9, 473, 231, 500, - /* 770 */ 354, 1036, 1035, 1488, 355, 374, 512, 1121, 112, 113, - /* 780 */ 103, 1100, 1100, 953, 956, 946, 946, 110, 110, 111, - /* 790 */ 111, 111, 111, 252, 252, 1015, 515, 1347, 295, 252, - /* 800 */ 252, 252, 252, 1098, 375, 249, 512, 445, 872, 322, - /* 810 */ 1016, 480, 512, 195, 512, 434, 273, 15, 15, 515, - /* 820 */ 314, 515, 95, 515, 93, 1017, 367, 109, 109, 109, - /* 830 */ 109, 108, 108, 107, 107, 107, 106, 401, 515, 1121, - /* 840 */ 39, 39, 51, 51, 52, 52, 503, 370, 515, 1204, - /* 850 */ 1098, 918, 439, 341, 133, 436, 223, 222, 221, 53, - /* 860 */ 53, 322, 1400, 761, 762, 763, 515, 370, 88, 54, - /* 870 */ 54, 112, 113, 103, 1100, 1100, 953, 956, 946, 946, - /* 880 */ 110, 110, 111, 111, 111, 111, 407, 55, 55, 196, - /* 890 */ 515, 112, 113, 103, 1100, 1100, 953, 956, 946, 946, - /* 900 */ 110, 110, 111, 111, 111, 111, 135, 264, 1149, 376, - /* 910 */ 515, 40, 40, 515, 872, 515, 993, 515, 993, 116, - /* 920 */ 109, 109, 109, 109, 108, 108, 107, 107, 107, 106, - /* 930 */ 401, 41, 41, 515, 43, 43, 44, 44, 56, 56, - /* 940 */ 109, 109, 109, 109, 108, 108, 107, 107, 107, 106, - /* 950 */ 401, 515, 379, 515, 57, 57, 515, 799, 515, 379, - /* 960 */ 515, 445, 200, 515, 323, 515, 1397, 515, 1459, 515, - /* 970 */ 1287, 817, 58, 58, 14, 14, 515, 59, 59, 118, - /* 980 */ 118, 60, 60, 515, 46, 46, 61, 61, 62, 62, - /* 990 */ 47, 47, 515, 190, 189, 91, 515, 140, 140, 515, - /* 1000 */ 394, 515, 277, 1200, 141, 141, 515, 1115, 515, 992, - /* 1010 */ 515, 992, 515, 69, 69, 370, 278, 48, 48, 259, - /* 1020 */ 65, 65, 119, 119, 246, 246, 260, 66, 66, 120, - /* 1030 */ 120, 121, 121, 117, 117, 370, 515, 512, 383, 112, - /* 1040 */ 113, 103, 1100, 1100, 953, 956, 946, 946, 110, 110, - /* 1050 */ 111, 111, 111, 111, 515, 872, 515, 139, 139, 112, - /* 1060 */ 113, 103, 1100, 1100, 953, 956, 946, 946, 110, 110, - /* 1070 */ 111, 111, 111, 111, 1287, 138, 138, 125, 125, 515, - /* 1080 */ 12, 515, 281, 1287, 515, 445, 131, 1287, 109, 109, - /* 1090 */ 109, 109, 108, 108, 107, 107, 107, 106, 401, 515, - /* 1100 */ 124, 124, 122, 122, 515, 123, 123, 515, 109, 109, - /* 1110 */ 109, 109, 108, 108, 107, 107, 107, 106, 401, 515, - /* 1120 */ 68, 68, 463, 783, 515, 70, 70, 302, 67, 67, - /* 1130 */ 1032, 253, 253, 356, 1287, 191, 196, 1433, 465, 1301, - /* 1140 */ 38, 38, 384, 94, 512, 42, 42, 177, 848, 274, - /* 1150 */ 506, 385, 420, 847, 1356, 441, 508, 376, 377, 153, - /* 1160 */ 423, 872, 432, 370, 224, 251, 194, 887, 182, 293, - /* 1170 */ 783, 848, 88, 254, 466, 888, 847, 915, 807, 806, - /* 1180 */ 230, 1241, 910, 370, 17, 413, 797, 112, 113, 103, - /* 1190 */ 1100, 1100, 953, 956, 946, 946, 110, 110, 111, 111, - /* 1200 */ 111, 111, 395, 814, 815, 1175, 983, 112, 101, 103, - /* 1210 */ 1100, 1100, 953, 956, 946, 946, 110, 110, 111, 111, - /* 1220 */ 111, 111, 375, 422, 427, 429, 298, 230, 230, 88, - /* 1230 */ 1240, 451, 312, 797, 226, 88, 109, 109, 109, 109, - /* 1240 */ 108, 108, 107, 107, 107, 106, 401, 86, 433, 979, - /* 1250 */ 927, 881, 226, 983, 230, 415, 109, 109, 109, 109, - /* 1260 */ 108, 108, 107, 107, 107, 106, 401, 320, 845, 781, - /* 1270 */ 846, 100, 130, 100, 1403, 290, 370, 319, 1377, 1376, - /* 1280 */ 437, 1449, 299, 1237, 303, 306, 308, 310, 1188, 1174, - /* 1290 */ 1173, 1172, 315, 324, 325, 1228, 370, 927, 1249, 271, - /* 1300 */ 1286, 113, 103, 1100, 1100, 953, 956, 946, 946, 110, - /* 1310 */ 110, 111, 111, 111, 111, 1224, 1235, 502, 501, 1292, - /* 1320 */ 1221, 1155, 103, 1100, 1100, 953, 956, 946, 946, 110, - /* 1330 */ 110, 111, 111, 111, 111, 1148, 1137, 1136, 1138, 1443, - /* 1340 */ 446, 244, 184, 98, 507, 188, 4, 353, 327, 109, - /* 1350 */ 109, 109, 109, 108, 108, 107, 107, 107, 106, 401, - /* 1360 */ 510, 329, 331, 199, 414, 456, 292, 285, 318, 109, - /* 1370 */ 109, 109, 109, 108, 108, 107, 107, 107, 106, 401, - /* 1380 */ 11, 1271, 1279, 402, 361, 192, 1171, 1351, 431, 505, - /* 1390 */ 346, 1350, 333, 98, 507, 504, 4, 187, 1446, 1115, - /* 1400 */ 233, 1396, 155, 1394, 1112, 152, 72, 75, 378, 425, - /* 1410 */ 510, 165, 149, 157, 933, 1276, 86, 30, 1268, 417, - /* 1420 */ 96, 96, 8, 160, 161, 162, 163, 97, 418, 402, - /* 1430 */ 517, 516, 449, 402, 923, 210, 358, 424, 1282, 438, - /* 1440 */ 169, 214, 360, 1345, 80, 504, 31, 444, 1365, 301, - /* 1450 */ 245, 274, 506, 216, 174, 305, 488, 447, 217, 462, - /* 1460 */ 1139, 487, 218, 363, 933, 923, 923, 925, 926, 24, - /* 1470 */ 96, 96, 1191, 1190, 1189, 391, 1182, 97, 1163, 402, - /* 1480 */ 517, 516, 799, 364, 923, 1162, 317, 1161, 98, 507, - /* 1490 */ 1181, 4, 1458, 472, 393, 269, 270, 475, 481, 1232, - /* 1500 */ 85, 1233, 326, 328, 232, 510, 495, 1231, 330, 98, - /* 1510 */ 507, 1230, 4, 486, 335, 923, 923, 925, 926, 24, - /* 1520 */ 1435, 1068, 404, 181, 336, 256, 510, 115, 402, 332, - /* 1530 */ 352, 352, 351, 241, 349, 1214, 1414, 770, 338, 10, - /* 1540 */ 504, 340, 272, 92, 1331, 1213, 87, 183, 484, 402, - /* 1550 */ 201, 488, 280, 239, 344, 345, 489, 1145, 29, 933, - /* 1560 */ 279, 504, 1074, 518, 240, 96, 96, 242, 243, 519, - /* 1570 */ 1134, 1129, 97, 154, 402, 517, 516, 372, 373, 923, - /* 1580 */ 933, 142, 143, 128, 1381, 267, 96, 96, 852, 757, - /* 1590 */ 203, 144, 403, 97, 1382, 402, 517, 516, 204, 1380, - /* 1600 */ 923, 146, 1379, 1159, 1158, 71, 1156, 276, 202, 185, - /* 1610 */ 923, 923, 925, 926, 24, 198, 257, 126, 991, 989, - /* 1620 */ 907, 98, 507, 156, 4, 145, 158, 206, 831, 209, - /* 1630 */ 291, 923, 923, 925, 926, 24, 1005, 911, 510, 164, - /* 1640 */ 147, 380, 371, 382, 166, 76, 77, 274, 506, 148, - /* 1650 */ 78, 79, 1008, 211, 212, 1004, 137, 213, 18, 300, - /* 1660 */ 230, 402, 997, 1109, 443, 215, 32, 170, 171, 772, - /* 1670 */ 409, 448, 319, 504, 219, 172, 452, 81, 19, 457, - /* 1680 */ 313, 20, 82, 268, 488, 150, 810, 179, 83, 487, - /* 1690 */ 464, 151, 933, 180, 959, 84, 1040, 34, 96, 96, - /* 1700 */ 471, 1041, 35, 474, 193, 97, 248, 402, 517, 516, - /* 1710 */ 1068, 404, 923, 250, 256, 880, 229, 175, 875, 352, - /* 1720 */ 352, 351, 241, 349, 100, 21, 770, 22, 1054, 1056, - /* 1730 */ 7, 98, 507, 1045, 4, 337, 1058, 23, 974, 201, - /* 1740 */ 176, 280, 88, 923, 923, 925, 926, 24, 510, 279, - /* 1750 */ 960, 958, 962, 1014, 963, 1013, 235, 234, 25, 36, - /* 1760 */ 99, 90, 507, 928, 4, 511, 350, 782, 26, 841, - /* 1770 */ 236, 402, 347, 1069, 237, 1125, 1125, 1451, 510, 203, - /* 1780 */ 1450, 1125, 1125, 504, 1125, 1125, 1125, 204, 1125, 1125, - /* 1790 */ 146, 1125, 1125, 1125, 1125, 1125, 1125, 202, 1125, 1125, - /* 1800 */ 1125, 402, 933, 1125, 1125, 1125, 1125, 1125, 96, 96, - /* 1810 */ 1125, 1125, 1125, 504, 1125, 97, 1125, 402, 517, 516, - /* 1820 */ 1125, 1125, 923, 1125, 1125, 1125, 1125, 1125, 1125, 1125, - /* 1830 */ 1125, 371, 933, 1125, 1125, 1125, 274, 506, 96, 96, - /* 1840 */ 1125, 1125, 1125, 1125, 1125, 97, 1125, 402, 517, 516, - /* 1850 */ 1125, 1125, 923, 923, 923, 925, 926, 24, 1125, 409, - /* 1860 */ 1125, 1125, 1125, 256, 1125, 1125, 1125, 1125, 352, 352, - /* 1870 */ 351, 241, 349, 1125, 1125, 770, 1125, 1125, 1125, 1125, - /* 1880 */ 1125, 1125, 1125, 923, 923, 925, 926, 24, 201, 1125, - /* 1890 */ 280, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 279, 1125, - /* 1900 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, - /* 1910 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, - /* 1920 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 203, 1125, - /* 1930 */ 1125, 1125, 1125, 1125, 1125, 1125, 204, 1125, 1125, 146, - /* 1940 */ 1125, 1125, 1125, 1125, 1125, 1125, 202, 1125, 1125, 1125, - /* 1950 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, - /* 1960 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, - /* 1970 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, - /* 1980 */ 371, 1125, 1125, 1125, 1125, 274, 506, 1125, 1125, 1125, - /* 1990 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, - /* 2000 */ 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 409, + /* 0 */ 377, 518, 371, 107, 104, 200, 1293, 518, 1130, 1, + /* 10 */ 1, 523, 2, 1134, 518, 1203, 1203, 1262, 277, 373, + /* 20 */ 129, 495, 37, 37, 1397, 1201, 1201, 1211, 65, 65, + /* 30 */ 480, 891, 107, 104, 200, 37, 37, 1043, 1494, 892, + /* 40 */ 346, 1494, 342, 114, 115, 105, 1106, 1106, 957, 960, + /* 50 */ 950, 950, 112, 112, 113, 113, 113, 113, 285, 254, + /* 60 */ 254, 518, 254, 254, 500, 518, 495, 518, 107, 104, + /* 70 */ 200, 1085, 515, 481, 386, 515, 1464, 442, 501, 230, + /* 80 */ 197, 439, 37, 37, 1172, 210, 65, 65, 65, 65, + /* 90 */ 254, 254, 111, 111, 111, 111, 110, 110, 109, 109, + /* 100 */ 109, 108, 404, 515, 404, 155, 1041, 431, 401, 400, + /* 110 */ 254, 254, 373, 1431, 1427, 408, 1110, 1085, 1086, 1087, + /* 120 */ 284, 1112, 500, 515, 500, 368, 1433, 1421, 1428, 1111, + /* 130 */ 1261, 499, 373, 502, 108, 404, 114, 115, 105, 1106, + /* 140 */ 1106, 957, 960, 950, 950, 112, 112, 113, 113, 113, + /* 150 */ 113, 276, 509, 1113, 369, 1113, 114, 115, 105, 1106, + /* 160 */ 1106, 957, 960, 950, 950, 112, 112, 113, 113, 113, + /* 170 */ 113, 496, 1420, 1431, 493, 1468, 1065, 260, 1063, 433, + /* 180 */ 74, 107, 104, 200, 498, 111, 111, 111, 111, 110, + /* 190 */ 110, 109, 109, 109, 108, 404, 373, 113, 113, 113, + /* 200 */ 113, 106, 131, 91, 1361, 111, 111, 111, 111, 110, + /* 210 */ 110, 109, 109, 109, 108, 404, 113, 113, 113, 113, + /* 220 */ 114, 115, 105, 1106, 1106, 957, 960, 950, 950, 112, + /* 230 */ 112, 113, 113, 113, 113, 111, 111, 111, 111, 110, + /* 240 */ 110, 109, 109, 109, 108, 404, 116, 110, 110, 109, + /* 250 */ 109, 109, 108, 404, 111, 111, 111, 111, 110, 110, + /* 260 */ 109, 109, 109, 108, 404, 917, 512, 512, 512, 111, + /* 270 */ 111, 111, 111, 110, 110, 109, 109, 109, 108, 404, + /* 280 */ 517, 1198, 1177, 181, 109, 109, 109, 108, 404, 373, + /* 290 */ 1198, 402, 402, 402, 75, 360, 111, 111, 111, 111, + /* 300 */ 110, 110, 109, 109, 109, 108, 404, 382, 299, 419, + /* 310 */ 287, 170, 518, 114, 115, 105, 1106, 1106, 957, 960, + /* 320 */ 950, 950, 112, 112, 113, 113, 113, 113, 1444, 523, + /* 330 */ 2, 1134, 518, 13, 13, 337, 277, 1085, 129, 226, + /* 340 */ 937, 1058, 1000, 471, 917, 1211, 453, 384, 1085, 395, + /* 350 */ 162, 1057, 155, 45, 45, 416, 928, 401, 400, 479, + /* 360 */ 927, 12, 111, 111, 111, 111, 110, 110, 109, 109, + /* 370 */ 109, 108, 404, 226, 286, 254, 254, 254, 254, 518, + /* 380 */ 16, 16, 373, 1085, 1086, 1087, 314, 299, 515, 472, + /* 390 */ 515, 927, 927, 929, 1085, 1086, 1087, 378, 276, 509, + /* 400 */ 65, 65, 1113, 210, 1113, 1085, 114, 115, 105, 1106, + /* 410 */ 1106, 957, 960, 950, 950, 112, 112, 113, 113, 113, + /* 420 */ 113, 1448, 222, 1134, 1089, 461, 458, 457, 277, 180, + /* 430 */ 129, 378, 392, 408, 423, 456, 500, 1211, 240, 257, + /* 440 */ 324, 464, 319, 463, 227, 470, 12, 317, 424, 300, + /* 450 */ 317, 1085, 1086, 1087, 485, 111, 111, 111, 111, 110, + /* 460 */ 110, 109, 109, 109, 108, 404, 181, 118, 1085, 254, + /* 470 */ 254, 1089, 518, 90, 351, 373, 518, 1181, 365, 798, + /* 480 */ 1440, 339, 515, 248, 248, 77, 325, 133, 1085, 249, + /* 490 */ 424, 300, 794, 49, 49, 210, 515, 65, 65, 114, + /* 500 */ 115, 105, 1106, 1106, 957, 960, 950, 950, 112, 112, + /* 510 */ 113, 113, 113, 113, 1085, 1086, 1087, 222, 1085, 438, + /* 520 */ 461, 458, 457, 937, 787, 408, 171, 857, 362, 1021, + /* 530 */ 456, 136, 198, 486, 1085, 1086, 1087, 448, 794, 928, + /* 540 */ 5, 193, 192, 927, 1022, 107, 104, 200, 111, 111, + /* 550 */ 111, 111, 110, 110, 109, 109, 109, 108, 404, 1023, + /* 560 */ 254, 254, 803, 1085, 1085, 1086, 1087, 437, 373, 1085, + /* 570 */ 344, 787, 791, 515, 927, 927, 929, 1085, 1408, 1396, + /* 580 */ 832, 1085, 176, 3, 852, 1085, 518, 1439, 429, 851, + /* 590 */ 833, 518, 114, 115, 105, 1106, 1106, 957, 960, 950, + /* 600 */ 950, 112, 112, 113, 113, 113, 113, 13, 13, 1085, + /* 610 */ 1086, 1087, 13, 13, 518, 1085, 1086, 1087, 1496, 358, + /* 620 */ 1085, 389, 1234, 1085, 1086, 1087, 391, 1085, 1086, 1087, + /* 630 */ 448, 1085, 1086, 1087, 518, 65, 65, 947, 947, 958, + /* 640 */ 961, 111, 111, 111, 111, 110, 110, 109, 109, 109, + /* 650 */ 108, 404, 518, 382, 878, 13, 13, 518, 877, 518, + /* 660 */ 263, 373, 518, 431, 448, 1070, 1085, 1086, 1087, 267, + /* 670 */ 448, 488, 1360, 64, 64, 431, 812, 155, 50, 50, + /* 680 */ 65, 65, 518, 65, 65, 114, 115, 105, 1106, 1106, + /* 690 */ 957, 960, 950, 950, 112, 112, 113, 113, 113, 113, + /* 700 */ 518, 951, 382, 13, 13, 415, 411, 462, 414, 1085, + /* 710 */ 1366, 777, 1210, 292, 297, 813, 399, 497, 181, 403, + /* 720 */ 261, 15, 15, 276, 509, 414, 413, 1366, 1368, 410, + /* 730 */ 372, 345, 1209, 264, 111, 111, 111, 111, 110, 110, + /* 740 */ 109, 109, 109, 108, 404, 265, 254, 254, 229, 1405, + /* 750 */ 268, 1215, 268, 1103, 373, 1085, 1086, 1087, 938, 515, + /* 760 */ 393, 409, 876, 515, 254, 254, 1152, 482, 473, 262, + /* 770 */ 422, 476, 325, 503, 289, 518, 291, 515, 114, 115, + /* 780 */ 105, 1106, 1106, 957, 960, 950, 950, 112, 112, 113, + /* 790 */ 113, 113, 113, 414, 1021, 1366, 39, 39, 254, 254, + /* 800 */ 254, 254, 980, 254, 254, 254, 254, 255, 255, 1022, + /* 810 */ 279, 515, 516, 515, 846, 846, 515, 138, 515, 518, + /* 820 */ 515, 1043, 1495, 251, 1023, 1495, 876, 111, 111, 111, + /* 830 */ 111, 110, 110, 109, 109, 109, 108, 404, 518, 1353, + /* 840 */ 51, 51, 518, 199, 518, 506, 290, 373, 518, 276, + /* 850 */ 509, 922, 9, 483, 233, 1005, 1005, 445, 189, 52, + /* 860 */ 52, 325, 280, 53, 53, 54, 54, 373, 876, 55, + /* 870 */ 55, 114, 115, 105, 1106, 1106, 957, 960, 950, 950, + /* 880 */ 112, 112, 113, 113, 113, 113, 97, 518, 95, 1104, + /* 890 */ 1041, 114, 115, 105, 1106, 1106, 957, 960, 950, 950, + /* 900 */ 112, 112, 113, 113, 113, 113, 135, 199, 56, 56, + /* 910 */ 765, 766, 767, 225, 224, 223, 518, 283, 437, 233, + /* 920 */ 111, 111, 111, 111, 110, 110, 109, 109, 109, 108, + /* 930 */ 404, 1002, 876, 326, 518, 1002, 1104, 40, 40, 518, + /* 940 */ 111, 111, 111, 111, 110, 110, 109, 109, 109, 108, + /* 950 */ 404, 518, 448, 518, 1104, 41, 41, 518, 17, 518, + /* 960 */ 43, 43, 1155, 379, 518, 448, 518, 443, 518, 390, + /* 970 */ 518, 194, 44, 44, 57, 57, 1247, 518, 58, 58, + /* 980 */ 59, 59, 518, 466, 326, 14, 14, 60, 60, 120, + /* 990 */ 120, 61, 61, 449, 1206, 93, 518, 425, 46, 46, + /* 1000 */ 518, 1104, 518, 62, 62, 518, 437, 305, 518, 852, + /* 1010 */ 518, 298, 518, 1246, 851, 373, 518, 63, 63, 1293, + /* 1020 */ 397, 47, 47, 142, 142, 1467, 143, 143, 821, 70, + /* 1030 */ 70, 48, 48, 66, 66, 373, 518, 121, 121, 114, + /* 1040 */ 115, 105, 1106, 1106, 957, 960, 950, 950, 112, 112, + /* 1050 */ 113, 113, 113, 113, 518, 418, 518, 67, 67, 114, + /* 1060 */ 115, 105, 1106, 1106, 957, 960, 950, 950, 112, 112, + /* 1070 */ 113, 113, 113, 113, 312, 122, 122, 123, 123, 1293, + /* 1080 */ 518, 357, 1126, 88, 518, 435, 325, 387, 111, 111, + /* 1090 */ 111, 111, 110, 110, 109, 109, 109, 108, 404, 266, + /* 1100 */ 518, 119, 119, 518, 1293, 141, 141, 518, 111, 111, + /* 1110 */ 111, 111, 110, 110, 109, 109, 109, 108, 404, 518, + /* 1120 */ 801, 140, 140, 518, 127, 127, 511, 379, 126, 126, + /* 1130 */ 518, 137, 518, 1308, 518, 307, 518, 310, 518, 203, + /* 1140 */ 124, 124, 1307, 96, 125, 125, 207, 388, 1441, 468, + /* 1150 */ 1127, 69, 69, 71, 71, 68, 68, 38, 38, 42, + /* 1160 */ 42, 357, 1042, 373, 1293, 276, 509, 801, 185, 469, + /* 1170 */ 494, 436, 444, 6, 380, 156, 253, 197, 469, 134, + /* 1180 */ 426, 33, 1038, 373, 1121, 359, 1411, 114, 115, 105, + /* 1190 */ 1106, 1106, 957, 960, 950, 950, 112, 112, 113, 113, + /* 1200 */ 113, 113, 914, 296, 27, 293, 90, 114, 103, 105, + /* 1210 */ 1106, 1106, 957, 960, 950, 950, 112, 112, 113, 113, + /* 1220 */ 113, 113, 919, 275, 430, 232, 891, 232, 432, 256, + /* 1230 */ 1127, 232, 398, 370, 892, 28, 111, 111, 111, 111, + /* 1240 */ 110, 110, 109, 109, 109, 108, 404, 301, 454, 1385, + /* 1250 */ 90, 228, 209, 987, 811, 810, 111, 111, 111, 111, + /* 1260 */ 110, 110, 109, 109, 109, 108, 404, 315, 818, 819, + /* 1270 */ 90, 323, 983, 931, 885, 228, 373, 232, 999, 849, + /* 1280 */ 999, 322, 102, 998, 1384, 998, 785, 850, 440, 132, + /* 1290 */ 102, 302, 1243, 306, 309, 311, 373, 313, 1194, 1180, + /* 1300 */ 987, 115, 105, 1106, 1106, 957, 960, 950, 950, 112, + /* 1310 */ 112, 113, 113, 113, 113, 1178, 1179, 318, 327, 328, + /* 1320 */ 931, 1255, 105, 1106, 1106, 957, 960, 950, 950, 112, + /* 1330 */ 112, 113, 113, 113, 113, 1292, 1230, 1457, 273, 1241, + /* 1340 */ 504, 505, 1298, 100, 510, 246, 4, 1161, 1154, 111, + /* 1350 */ 111, 111, 111, 110, 110, 109, 109, 109, 108, 404, + /* 1360 */ 513, 1143, 187, 1142, 202, 1144, 1451, 356, 1227, 111, + /* 1370 */ 111, 111, 111, 110, 110, 109, 109, 109, 108, 404, + /* 1380 */ 11, 1277, 330, 405, 332, 334, 191, 1285, 364, 195, + /* 1390 */ 295, 417, 288, 100, 510, 507, 4, 434, 459, 321, + /* 1400 */ 1177, 349, 1357, 1356, 336, 155, 190, 1454, 1121, 158, + /* 1410 */ 513, 508, 235, 1404, 937, 1402, 1118, 381, 77, 428, + /* 1420 */ 98, 98, 8, 1282, 168, 30, 152, 99, 160, 405, + /* 1430 */ 520, 519, 88, 405, 927, 1362, 1274, 420, 163, 73, + /* 1440 */ 164, 76, 165, 166, 421, 507, 452, 212, 361, 363, + /* 1450 */ 427, 276, 509, 31, 1288, 172, 491, 441, 216, 1351, + /* 1460 */ 82, 490, 447, 1373, 937, 927, 927, 929, 930, 24, + /* 1470 */ 98, 98, 304, 247, 218, 177, 308, 99, 219, 405, + /* 1480 */ 520, 519, 450, 1145, 927, 220, 366, 1197, 100, 510, + /* 1490 */ 465, 4, 1188, 1196, 1195, 394, 803, 1169, 1187, 367, + /* 1500 */ 1168, 396, 484, 320, 1167, 513, 1466, 87, 475, 100, + /* 1510 */ 510, 271, 4, 272, 478, 927, 927, 929, 930, 24, + /* 1520 */ 1443, 1074, 407, 1238, 1239, 258, 513, 329, 405, 331, + /* 1530 */ 355, 355, 354, 243, 352, 234, 489, 774, 498, 184, + /* 1540 */ 507, 338, 1422, 339, 117, 1220, 10, 341, 333, 405, + /* 1550 */ 204, 491, 282, 1219, 1237, 1236, 492, 335, 343, 937, + /* 1560 */ 281, 507, 94, 1337, 186, 98, 98, 347, 89, 487, + /* 1570 */ 348, 241, 99, 29, 405, 520, 519, 274, 1151, 927, + /* 1580 */ 937, 521, 1080, 245, 242, 244, 98, 98, 856, 522, + /* 1590 */ 206, 1140, 1135, 99, 144, 405, 520, 519, 147, 375, + /* 1600 */ 927, 149, 376, 157, 1389, 1390, 1388, 1387, 205, 145, + /* 1610 */ 927, 927, 929, 930, 24, 146, 130, 761, 1165, 1164, + /* 1620 */ 72, 100, 510, 1162, 4, 269, 406, 188, 278, 201, + /* 1630 */ 259, 927, 927, 929, 930, 24, 128, 911, 513, 997, + /* 1640 */ 995, 159, 374, 208, 148, 161, 835, 276, 509, 211, + /* 1650 */ 294, 1011, 915, 167, 150, 383, 169, 78, 385, 79, + /* 1660 */ 80, 405, 81, 151, 1014, 213, 214, 1010, 139, 18, + /* 1670 */ 412, 215, 303, 507, 232, 1115, 1003, 446, 173, 217, + /* 1680 */ 174, 32, 776, 451, 491, 322, 221, 175, 814, 490, + /* 1690 */ 83, 455, 937, 19, 460, 316, 20, 84, 98, 98, + /* 1700 */ 270, 182, 85, 467, 153, 99, 154, 405, 520, 519, + /* 1710 */ 1074, 407, 927, 183, 258, 963, 1046, 86, 34, 355, + /* 1720 */ 355, 354, 243, 352, 474, 1047, 774, 35, 477, 196, + /* 1730 */ 250, 100, 510, 252, 4, 884, 178, 231, 1060, 204, + /* 1740 */ 21, 282, 102, 927, 927, 929, 930, 24, 513, 281, + /* 1750 */ 879, 22, 1064, 1062, 1051, 7, 340, 23, 978, 179, + /* 1760 */ 90, 92, 510, 964, 4, 236, 962, 966, 1020, 1019, + /* 1770 */ 237, 405, 967, 25, 36, 514, 932, 786, 513, 206, + /* 1780 */ 101, 26, 845, 507, 238, 239, 1459, 147, 350, 1458, + /* 1790 */ 149, 353, 1075, 1131, 1131, 1131, 1131, 205, 1131, 1131, + /* 1800 */ 1131, 405, 937, 1131, 1131, 1131, 1131, 1131, 98, 98, + /* 1810 */ 1131, 1131, 1131, 507, 1131, 99, 1131, 405, 520, 519, + /* 1820 */ 1131, 1131, 927, 1131, 1131, 1131, 1131, 1131, 1131, 1131, + /* 1830 */ 1131, 374, 937, 1131, 1131, 1131, 276, 509, 98, 98, + /* 1840 */ 1131, 1131, 1131, 1131, 1131, 99, 1131, 405, 520, 519, + /* 1850 */ 1131, 1131, 927, 927, 927, 929, 930, 24, 1131, 412, + /* 1860 */ 1131, 1131, 1131, 258, 1131, 1131, 1131, 1131, 355, 355, + /* 1870 */ 354, 243, 352, 1131, 1131, 774, 1131, 1131, 1131, 1131, + /* 1880 */ 1131, 1131, 1131, 927, 927, 929, 930, 24, 204, 1131, + /* 1890 */ 282, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 281, 1131, + /* 1900 */ 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, + /* 1910 */ 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, + /* 1920 */ 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 206, 1131, + /* 1930 */ 1131, 1131, 1131, 1131, 1131, 1131, 147, 1131, 1131, 149, + /* 1940 */ 1131, 1131, 1131, 1131, 1131, 1131, 205, 1131, 1131, 1131, + /* 1950 */ 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, + /* 1960 */ 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, + /* 1970 */ 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, + /* 1980 */ 374, 1131, 1131, 1131, 1131, 276, 509, 1131, 1131, 1131, + /* 1990 */ 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, + /* 2000 */ 1131, 1131, 1131, 1131, 1131, 1131, 1131, 1131, 412, }; static const YYCODETYPE yy_lookahead[] = { - /* 0 */ 184, 238, 239, 240, 238, 239, 240, 163, 155, 156, - /* 10 */ 157, 158, 159, 160, 163, 191, 192, 183, 165, 19, - /* 20 */ 167, 258, 202, 203, 200, 191, 163, 174, 184, 185, - /* 30 */ 174, 31, 163, 163, 171, 184, 185, 35, 175, 39, - /* 40 */ 179, 180, 181, 43, 44, 45, 46, 47, 48, 49, - /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 184, 206, - /* 60 */ 207, 163, 206, 207, 220, 163, 16, 163, 66, 163, - /* 70 */ 59, 270, 219, 229, 273, 219, 74, 208, 174, 223, - /* 80 */ 224, 163, 184, 185, 163, 232, 184, 185, 184, 185, - /* 90 */ 184, 185, 92, 93, 94, 95, 96, 97, 98, 99, - /* 100 */ 100, 101, 102, 233, 198, 184, 185, 96, 97, 163, - /* 110 */ 206, 207, 19, 163, 261, 104, 105, 106, 107, 198, - /* 120 */ 109, 119, 220, 219, 220, 274, 275, 77, 117, 79, - /* 130 */ 187, 229, 19, 229, 184, 185, 43, 44, 45, 46, + /* 0 */ 168, 163, 184, 238, 239, 240, 163, 163, 155, 156, + /* 10 */ 157, 158, 159, 160, 163, 202, 203, 187, 165, 19, + /* 20 */ 167, 163, 184, 185, 259, 202, 203, 174, 184, 185, + /* 30 */ 174, 31, 238, 239, 240, 184, 185, 22, 23, 39, + /* 40 */ 216, 26, 218, 43, 44, 45, 46, 47, 48, 49, + /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 174, 206, + /* 60 */ 207, 163, 206, 207, 220, 163, 163, 163, 238, 239, + /* 70 */ 240, 59, 219, 229, 231, 219, 183, 245, 174, 223, + /* 80 */ 224, 249, 184, 185, 191, 232, 184, 185, 184, 185, + /* 90 */ 206, 207, 92, 93, 94, 95, 96, 97, 98, 99, + /* 100 */ 100, 101, 102, 219, 102, 81, 91, 163, 96, 97, + /* 110 */ 206, 207, 19, 275, 276, 262, 104, 105, 106, 107, + /* 120 */ 163, 109, 220, 219, 220, 184, 275, 269, 277, 117, + /* 130 */ 187, 229, 19, 229, 101, 102, 43, 44, 45, 46, /* 140 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 150 */ 57, 233, 141, 134, 143, 102, 43, 44, 45, 46, + /* 150 */ 57, 127, 128, 141, 184, 143, 43, 44, 45, 46, /* 160 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 170 */ 57, 152, 274, 216, 276, 218, 83, 163, 85, 233, - /* 180 */ 67, 238, 239, 240, 11, 92, 93, 94, 95, 96, + /* 170 */ 57, 268, 269, 275, 276, 197, 83, 233, 85, 163, + /* 180 */ 67, 238, 239, 240, 134, 92, 93, 94, 95, 96, /* 190 */ 97, 98, 99, 100, 101, 102, 19, 54, 55, 56, - /* 200 */ 57, 58, 163, 26, 163, 92, 93, 94, 95, 96, + /* 200 */ 57, 58, 152, 26, 247, 92, 93, 94, 95, 96, /* 210 */ 97, 98, 99, 100, 101, 102, 54, 55, 56, 57, /* 220 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, /* 230 */ 53, 54, 55, 56, 57, 92, 93, 94, 95, 96, /* 240 */ 97, 98, 99, 100, 101, 102, 69, 96, 97, 98, /* 250 */ 99, 100, 101, 102, 92, 93, 94, 95, 96, 97, - /* 260 */ 98, 99, 100, 101, 102, 81, 179, 180, 181, 92, + /* 260 */ 98, 99, 100, 101, 102, 73, 179, 180, 181, 92, /* 270 */ 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, - /* 280 */ 163, 267, 268, 163, 22, 23, 59, 163, 26, 19, - /* 290 */ 117, 118, 175, 109, 24, 59, 92, 93, 94, 95, - /* 300 */ 96, 97, 98, 99, 100, 101, 102, 268, 184, 185, - /* 310 */ 269, 127, 128, 43, 44, 45, 46, 47, 48, 49, + /* 280 */ 163, 191, 192, 163, 98, 99, 100, 101, 102, 19, + /* 290 */ 200, 179, 180, 181, 24, 175, 92, 93, 94, 95, + /* 300 */ 96, 97, 98, 99, 100, 101, 102, 163, 116, 117, + /* 310 */ 118, 22, 163, 43, 44, 45, 46, 47, 48, 49, /* 320 */ 50, 51, 52, 53, 54, 55, 56, 57, 157, 158, - /* 330 */ 159, 160, 105, 106, 107, 163, 165, 59, 167, 184, - /* 340 */ 90, 105, 106, 107, 108, 174, 73, 111, 112, 113, - /* 350 */ 19, 22, 163, 91, 81, 163, 106, 121, 81, 132, - /* 360 */ 110, 16, 92, 93, 94, 95, 96, 97, 98, 99, - /* 370 */ 100, 101, 102, 184, 185, 255, 98, 206, 207, 26, - /* 380 */ 101, 102, 19, 105, 106, 107, 23, 198, 59, 116, - /* 390 */ 219, 141, 142, 143, 24, 163, 187, 205, 274, 275, - /* 400 */ 127, 128, 182, 232, 127, 128, 43, 44, 45, 46, + /* 330 */ 159, 160, 163, 184, 185, 163, 165, 59, 167, 46, + /* 340 */ 90, 76, 11, 174, 73, 174, 19, 198, 59, 19, + /* 350 */ 72, 86, 81, 184, 185, 234, 106, 96, 97, 163, + /* 360 */ 110, 182, 92, 93, 94, 95, 96, 97, 98, 99, + /* 370 */ 100, 101, 102, 46, 230, 206, 207, 206, 207, 163, + /* 380 */ 184, 185, 19, 105, 106, 107, 23, 116, 219, 220, + /* 390 */ 219, 141, 142, 143, 105, 106, 107, 104, 127, 128, + /* 400 */ 184, 185, 141, 232, 143, 59, 43, 44, 45, 46, /* 410 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - /* 420 */ 57, 158, 77, 160, 79, 59, 26, 182, 165, 59, - /* 430 */ 167, 199, 261, 102, 105, 106, 107, 174, 72, 108, - /* 440 */ 109, 110, 111, 112, 113, 114, 59, 238, 239, 240, - /* 450 */ 123, 120, 125, 126, 163, 92, 93, 94, 95, 96, - /* 460 */ 97, 98, 99, 100, 101, 102, 163, 163, 163, 206, - /* 470 */ 207, 105, 106, 107, 254, 19, 106, 90, 197, 23, - /* 480 */ 127, 128, 219, 238, 239, 240, 22, 184, 185, 184, - /* 490 */ 185, 22, 105, 106, 149, 232, 205, 110, 163, 43, + /* 420 */ 57, 158, 108, 160, 59, 111, 112, 113, 165, 250, + /* 430 */ 167, 104, 102, 262, 255, 121, 220, 174, 108, 109, + /* 440 */ 110, 111, 112, 113, 114, 229, 182, 120, 117, 118, + /* 450 */ 120, 105, 106, 107, 163, 92, 93, 94, 95, 96, + /* 460 */ 97, 98, 99, 100, 101, 102, 163, 22, 59, 206, + /* 470 */ 207, 106, 163, 26, 171, 19, 163, 193, 175, 23, + /* 480 */ 163, 22, 219, 206, 207, 139, 163, 22, 59, 182, + /* 490 */ 117, 118, 59, 184, 185, 232, 219, 184, 185, 43, /* 500 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - /* 510 */ 54, 55, 56, 57, 98, 99, 100, 101, 102, 184, - /* 520 */ 185, 163, 53, 59, 261, 220, 117, 118, 141, 142, - /* 530 */ 143, 131, 174, 59, 229, 116, 117, 118, 163, 59, - /* 540 */ 163, 163, 184, 185, 59, 242, 72, 22, 92, 93, - /* 550 */ 94, 95, 96, 97, 98, 99, 100, 101, 102, 184, - /* 560 */ 185, 24, 184, 185, 206, 207, 202, 203, 19, 105, - /* 570 */ 106, 107, 23, 198, 22, 174, 198, 219, 220, 105, - /* 580 */ 106, 107, 96, 97, 59, 105, 106, 107, 22, 174, - /* 590 */ 59, 106, 43, 44, 45, 46, 47, 48, 49, 50, - /* 600 */ 51, 52, 53, 54, 55, 56, 57, 206, 207, 12, - /* 610 */ 108, 59, 132, 111, 112, 113, 46, 47, 48, 49, - /* 620 */ 219, 206, 207, 121, 27, 59, 163, 141, 207, 143, - /* 630 */ 105, 106, 107, 163, 219, 234, 105, 106, 107, 42, - /* 640 */ 219, 92, 93, 94, 95, 96, 97, 98, 99, 100, - /* 650 */ 101, 102, 76, 163, 184, 185, 163, 105, 106, 107, - /* 660 */ 63, 19, 86, 163, 163, 23, 163, 130, 205, 21, - /* 670 */ 73, 105, 106, 107, 184, 185, 163, 184, 185, 237, - /* 680 */ 110, 180, 181, 180, 181, 43, 44, 45, 46, 47, + /* 510 */ 54, 55, 56, 57, 105, 106, 107, 108, 59, 255, + /* 520 */ 111, 112, 113, 90, 59, 262, 22, 98, 174, 12, + /* 530 */ 121, 208, 163, 220, 105, 106, 107, 163, 105, 106, + /* 540 */ 22, 96, 97, 110, 27, 238, 239, 240, 92, 93, + /* 550 */ 94, 95, 96, 97, 98, 99, 100, 101, 102, 42, + /* 560 */ 206, 207, 115, 59, 105, 106, 107, 163, 19, 59, + /* 570 */ 163, 106, 23, 219, 141, 142, 143, 59, 163, 205, + /* 580 */ 63, 59, 72, 22, 124, 59, 163, 270, 234, 129, + /* 590 */ 73, 163, 43, 44, 45, 46, 47, 48, 49, 50, + /* 600 */ 51, 52, 53, 54, 55, 56, 57, 184, 185, 105, + /* 610 */ 106, 107, 184, 185, 163, 105, 106, 107, 265, 266, + /* 620 */ 59, 198, 225, 105, 106, 107, 198, 105, 106, 107, + /* 630 */ 163, 105, 106, 107, 163, 184, 185, 46, 47, 48, + /* 640 */ 49, 92, 93, 94, 95, 96, 97, 98, 99, 100, + /* 650 */ 101, 102, 163, 163, 132, 184, 185, 163, 132, 163, + /* 660 */ 256, 19, 163, 163, 163, 23, 105, 106, 107, 198, + /* 670 */ 163, 220, 205, 184, 185, 163, 35, 81, 184, 185, + /* 680 */ 184, 185, 163, 184, 185, 43, 44, 45, 46, 47, /* 690 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - /* 700 */ 174, 163, 163, 22, 23, 163, 163, 26, 22, 23, - /* 710 */ 220, 29, 73, 220, 272, 33, 22, 163, 24, 19, - /* 720 */ 174, 208, 259, 184, 185, 19, 184, 185, 80, 175, - /* 730 */ 230, 174, 206, 207, 92, 93, 94, 95, 96, 97, - /* 740 */ 98, 99, 100, 101, 102, 219, 46, 65, 247, 195, - /* 750 */ 247, 197, 206, 207, 19, 116, 117, 118, 23, 220, - /* 760 */ 112, 174, 220, 206, 207, 219, 22, 174, 24, 174, - /* 770 */ 22, 23, 91, 264, 265, 168, 219, 91, 43, 44, + /* 700 */ 163, 110, 163, 184, 185, 109, 205, 66, 163, 59, + /* 710 */ 163, 21, 205, 16, 174, 74, 220, 198, 163, 220, + /* 720 */ 230, 184, 185, 127, 128, 180, 181, 180, 181, 163, + /* 730 */ 175, 242, 174, 233, 92, 93, 94, 95, 96, 97, + /* 740 */ 98, 99, 100, 101, 102, 233, 206, 207, 26, 163, + /* 750 */ 195, 207, 197, 26, 19, 105, 106, 107, 23, 219, + /* 760 */ 119, 260, 26, 219, 206, 207, 174, 19, 174, 230, + /* 770 */ 80, 174, 163, 174, 77, 163, 79, 219, 43, 44, /* 780 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, - /* 790 */ 55, 56, 57, 206, 207, 12, 163, 149, 255, 206, - /* 800 */ 207, 206, 207, 59, 104, 23, 219, 163, 26, 163, - /* 810 */ 27, 105, 219, 163, 219, 163, 211, 184, 185, 163, - /* 820 */ 120, 163, 146, 163, 148, 42, 221, 92, 93, 94, - /* 830 */ 95, 96, 97, 98, 99, 100, 101, 102, 163, 91, - /* 840 */ 184, 185, 184, 185, 184, 185, 63, 19, 163, 205, - /* 850 */ 106, 23, 245, 163, 208, 248, 116, 117, 118, 184, - /* 860 */ 185, 163, 163, 7, 8, 9, 163, 19, 26, 184, + /* 790 */ 55, 56, 57, 248, 12, 248, 184, 185, 206, 207, + /* 800 */ 206, 207, 112, 206, 207, 206, 207, 206, 207, 27, + /* 810 */ 163, 219, 123, 219, 125, 126, 219, 208, 219, 163, + /* 820 */ 219, 22, 23, 23, 42, 26, 26, 92, 93, 94, + /* 830 */ 95, 96, 97, 98, 99, 100, 101, 102, 163, 149, + /* 840 */ 184, 185, 163, 107, 163, 63, 149, 19, 163, 127, + /* 850 */ 128, 23, 22, 105, 24, 116, 117, 118, 131, 184, + /* 860 */ 185, 163, 163, 184, 185, 184, 185, 19, 132, 184, /* 870 */ 185, 43, 44, 45, 46, 47, 48, 49, 50, 51, - /* 880 */ 52, 53, 54, 55, 56, 57, 163, 184, 185, 107, - /* 890 */ 163, 43, 44, 45, 46, 47, 48, 49, 50, 51, - /* 900 */ 52, 53, 54, 55, 56, 57, 208, 255, 177, 178, - /* 910 */ 163, 184, 185, 163, 132, 163, 141, 163, 143, 22, + /* 880 */ 52, 53, 54, 55, 56, 57, 146, 163, 148, 59, + /* 890 */ 91, 43, 44, 45, 46, 47, 48, 49, 50, 51, + /* 900 */ 52, 53, 54, 55, 56, 57, 208, 107, 184, 185, + /* 910 */ 7, 8, 9, 116, 117, 118, 163, 163, 163, 24, /* 920 */ 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, - /* 930 */ 102, 184, 185, 163, 184, 185, 184, 185, 184, 185, + /* 930 */ 102, 29, 132, 163, 163, 33, 106, 184, 185, 163, /* 940 */ 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, - /* 950 */ 102, 163, 163, 163, 184, 185, 163, 115, 163, 163, - /* 960 */ 163, 163, 15, 163, 163, 163, 163, 163, 23, 163, - /* 970 */ 163, 26, 184, 185, 184, 185, 163, 184, 185, 184, - /* 980 */ 185, 184, 185, 163, 184, 185, 184, 185, 184, 185, - /* 990 */ 184, 185, 163, 96, 97, 147, 163, 184, 185, 163, - /* 1000 */ 199, 163, 163, 205, 184, 185, 163, 60, 163, 141, - /* 1010 */ 163, 143, 163, 184, 185, 19, 163, 184, 185, 230, - /* 1020 */ 184, 185, 184, 185, 206, 207, 230, 184, 185, 184, - /* 1030 */ 185, 184, 185, 184, 185, 19, 163, 219, 231, 43, + /* 950 */ 102, 163, 163, 163, 59, 184, 185, 163, 22, 163, + /* 960 */ 184, 185, 177, 178, 163, 163, 163, 65, 163, 199, + /* 970 */ 163, 26, 184, 185, 184, 185, 163, 163, 184, 185, + /* 980 */ 184, 185, 163, 98, 163, 184, 185, 184, 185, 184, + /* 990 */ 185, 184, 185, 252, 205, 147, 163, 61, 184, 185, + /* 1000 */ 163, 106, 163, 184, 185, 163, 163, 205, 163, 124, + /* 1010 */ 163, 256, 163, 163, 129, 19, 163, 184, 185, 163, + /* 1020 */ 199, 184, 185, 184, 185, 23, 184, 185, 26, 184, + /* 1030 */ 185, 184, 185, 184, 185, 19, 163, 184, 185, 43, /* 1040 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - /* 1050 */ 54, 55, 56, 57, 163, 26, 163, 184, 185, 43, + /* 1050 */ 54, 55, 56, 57, 163, 163, 163, 184, 185, 43, /* 1060 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - /* 1070 */ 54, 55, 56, 57, 163, 184, 185, 184, 185, 163, - /* 1080 */ 182, 163, 163, 163, 163, 163, 22, 163, 92, 93, - /* 1090 */ 94, 95, 96, 97, 98, 99, 100, 101, 102, 163, - /* 1100 */ 184, 185, 184, 185, 163, 184, 185, 163, 92, 93, + /* 1070 */ 54, 55, 56, 57, 16, 184, 185, 184, 185, 163, + /* 1080 */ 163, 22, 23, 138, 163, 19, 163, 231, 92, 93, + /* 1090 */ 94, 95, 96, 97, 98, 99, 100, 101, 102, 256, + /* 1100 */ 163, 184, 185, 163, 163, 184, 185, 163, 92, 93, /* 1110 */ 94, 95, 96, 97, 98, 99, 100, 101, 102, 163, - /* 1120 */ 184, 185, 98, 59, 163, 184, 185, 205, 184, 185, - /* 1130 */ 23, 206, 207, 26, 163, 26, 107, 153, 154, 237, - /* 1140 */ 184, 185, 231, 147, 219, 184, 185, 249, 124, 127, - /* 1150 */ 128, 231, 254, 129, 163, 231, 177, 178, 262, 263, - /* 1160 */ 118, 132, 19, 19, 46, 223, 224, 31, 24, 23, - /* 1170 */ 106, 124, 26, 22, 272, 39, 129, 23, 109, 110, - /* 1180 */ 26, 163, 140, 19, 22, 234, 59, 43, 44, 45, + /* 1120 */ 59, 184, 185, 163, 184, 185, 177, 178, 184, 185, + /* 1130 */ 163, 208, 163, 237, 163, 77, 163, 79, 163, 15, + /* 1140 */ 184, 185, 237, 147, 184, 185, 24, 231, 153, 154, + /* 1150 */ 91, 184, 185, 184, 185, 184, 185, 184, 185, 184, + /* 1160 */ 185, 22, 23, 19, 163, 127, 128, 106, 24, 273, + /* 1170 */ 271, 105, 231, 274, 263, 264, 223, 224, 273, 22, + /* 1180 */ 118, 24, 23, 19, 60, 26, 163, 43, 44, 45, /* 1190 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, - /* 1200 */ 56, 57, 231, 7, 8, 193, 59, 43, 44, 45, + /* 1200 */ 56, 57, 140, 23, 22, 163, 26, 43, 44, 45, /* 1210 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, - /* 1220 */ 56, 57, 104, 61, 23, 23, 23, 26, 26, 26, - /* 1230 */ 163, 23, 23, 106, 26, 26, 92, 93, 94, 95, - /* 1240 */ 96, 97, 98, 99, 100, 101, 102, 138, 105, 23, - /* 1250 */ 59, 23, 26, 106, 26, 163, 92, 93, 94, 95, - /* 1260 */ 96, 97, 98, 99, 100, 101, 102, 110, 23, 23, - /* 1270 */ 23, 26, 26, 26, 163, 163, 19, 120, 163, 163, - /* 1280 */ 163, 130, 163, 163, 163, 163, 163, 163, 163, 193, - /* 1290 */ 193, 163, 163, 163, 163, 225, 19, 106, 163, 222, - /* 1300 */ 163, 44, 45, 46, 47, 48, 49, 50, 51, 52, - /* 1310 */ 53, 54, 55, 56, 57, 163, 163, 203, 163, 163, - /* 1320 */ 222, 163, 45, 46, 47, 48, 49, 50, 51, 52, - /* 1330 */ 53, 54, 55, 56, 57, 163, 163, 163, 163, 163, - /* 1340 */ 251, 250, 209, 19, 20, 182, 22, 161, 222, 92, + /* 1220 */ 56, 57, 23, 211, 23, 26, 31, 26, 23, 22, + /* 1230 */ 91, 26, 231, 221, 39, 53, 92, 93, 94, 95, + /* 1240 */ 96, 97, 98, 99, 100, 101, 102, 23, 23, 163, + /* 1250 */ 26, 26, 130, 59, 109, 110, 92, 93, 94, 95, + /* 1260 */ 96, 97, 98, 99, 100, 101, 102, 23, 7, 8, + /* 1270 */ 26, 110, 23, 59, 23, 26, 19, 26, 141, 23, + /* 1280 */ 143, 120, 26, 141, 163, 143, 23, 23, 163, 26, + /* 1290 */ 26, 163, 163, 163, 163, 163, 19, 163, 163, 193, + /* 1300 */ 106, 44, 45, 46, 47, 48, 49, 50, 51, 52, + /* 1310 */ 53, 54, 55, 56, 57, 163, 193, 163, 163, 163, + /* 1320 */ 106, 163, 45, 46, 47, 48, 49, 50, 51, 52, + /* 1330 */ 53, 54, 55, 56, 57, 163, 163, 130, 222, 163, + /* 1340 */ 163, 203, 163, 19, 20, 251, 22, 163, 163, 92, /* 1350 */ 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, - /* 1360 */ 36, 222, 222, 260, 226, 188, 256, 226, 187, 92, + /* 1360 */ 36, 163, 209, 163, 261, 163, 163, 161, 222, 92, /* 1370 */ 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, - /* 1380 */ 210, 213, 213, 59, 213, 196, 192, 187, 256, 244, - /* 1390 */ 212, 187, 226, 19, 20, 71, 22, 210, 166, 60, - /* 1400 */ 130, 170, 260, 170, 38, 81, 257, 257, 170, 104, - /* 1410 */ 36, 22, 43, 201, 90, 236, 138, 235, 213, 18, - /* 1420 */ 96, 97, 48, 204, 204, 204, 204, 103, 170, 105, - /* 1430 */ 106, 107, 18, 59, 110, 169, 213, 213, 201, 170, - /* 1440 */ 201, 169, 236, 213, 146, 71, 235, 62, 253, 252, - /* 1450 */ 170, 127, 128, 169, 22, 170, 82, 189, 169, 104, - /* 1460 */ 170, 87, 169, 189, 90, 141, 142, 143, 144, 145, - /* 1470 */ 96, 97, 186, 186, 186, 64, 194, 103, 186, 105, - /* 1480 */ 106, 107, 115, 189, 110, 188, 186, 186, 19, 20, - /* 1490 */ 194, 22, 186, 189, 102, 246, 246, 189, 133, 228, - /* 1500 */ 104, 228, 227, 227, 170, 36, 134, 228, 227, 19, - /* 1510 */ 20, 228, 22, 84, 271, 141, 142, 143, 144, 145, - /* 1520 */ 0, 1, 2, 216, 22, 5, 36, 137, 59, 227, - /* 1530 */ 10, 11, 12, 13, 14, 217, 269, 17, 216, 22, - /* 1540 */ 71, 170, 243, 146, 241, 217, 136, 215, 135, 59, - /* 1550 */ 30, 82, 32, 25, 214, 213, 87, 173, 26, 90, - /* 1560 */ 40, 71, 13, 172, 164, 96, 97, 164, 6, 162, - /* 1570 */ 162, 162, 103, 263, 105, 106, 107, 266, 266, 110, - /* 1580 */ 90, 176, 176, 190, 182, 190, 96, 97, 98, 4, - /* 1590 */ 70, 176, 3, 103, 182, 105, 106, 107, 78, 182, - /* 1600 */ 110, 81, 182, 182, 182, 182, 182, 151, 88, 22, - /* 1610 */ 141, 142, 143, 144, 145, 15, 89, 16, 23, 23, - /* 1620 */ 128, 19, 20, 139, 22, 119, 131, 24, 20, 133, - /* 1630 */ 16, 141, 142, 143, 144, 145, 1, 140, 36, 131, - /* 1640 */ 119, 61, 122, 37, 139, 53, 53, 127, 128, 119, - /* 1650 */ 53, 53, 105, 34, 130, 1, 5, 104, 22, 149, - /* 1660 */ 26, 59, 68, 75, 41, 130, 24, 68, 104, 20, - /* 1670 */ 150, 19, 120, 71, 114, 22, 67, 22, 22, 67, - /* 1680 */ 23, 22, 22, 67, 82, 37, 28, 23, 138, 87, - /* 1690 */ 22, 153, 90, 23, 23, 26, 23, 22, 96, 97, - /* 1700 */ 24, 23, 22, 24, 130, 103, 23, 105, 106, 107, - /* 1710 */ 1, 2, 110, 23, 5, 105, 34, 22, 132, 10, - /* 1720 */ 11, 12, 13, 14, 26, 34, 17, 34, 85, 83, - /* 1730 */ 44, 19, 20, 23, 22, 24, 75, 34, 23, 30, - /* 1740 */ 26, 32, 26, 141, 142, 143, 144, 145, 36, 40, - /* 1750 */ 23, 23, 23, 23, 11, 23, 22, 26, 22, 22, - /* 1760 */ 22, 19, 20, 23, 22, 26, 15, 23, 22, 124, - /* 1770 */ 130, 59, 23, 1, 130, 277, 277, 130, 36, 70, - /* 1780 */ 130, 277, 277, 71, 277, 277, 277, 78, 277, 277, - /* 1790 */ 81, 277, 277, 277, 277, 277, 277, 88, 277, 277, - /* 1800 */ 277, 59, 90, 277, 277, 277, 277, 277, 96, 97, - /* 1810 */ 277, 277, 277, 71, 277, 103, 277, 105, 106, 107, - /* 1820 */ 277, 277, 110, 277, 277, 277, 277, 277, 277, 277, - /* 1830 */ 277, 122, 90, 277, 277, 277, 127, 128, 96, 97, - /* 1840 */ 277, 277, 277, 277, 277, 103, 277, 105, 106, 107, - /* 1850 */ 277, 277, 110, 141, 142, 143, 144, 145, 277, 150, - /* 1860 */ 277, 277, 277, 5, 277, 277, 277, 277, 10, 11, - /* 1870 */ 12, 13, 14, 277, 277, 17, 277, 277, 277, 277, - /* 1880 */ 277, 277, 277, 141, 142, 143, 144, 145, 30, 277, - /* 1890 */ 32, 277, 277, 277, 277, 277, 277, 277, 40, 277, - /* 1900 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, - /* 1910 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, - /* 1920 */ 277, 277, 277, 277, 277, 277, 277, 277, 70, 277, - /* 1930 */ 277, 277, 277, 277, 277, 277, 78, 277, 277, 81, - /* 1940 */ 277, 277, 277, 277, 277, 277, 88, 277, 277, 277, - /* 1950 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, - /* 1960 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, - /* 1970 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, - /* 1980 */ 122, 277, 277, 277, 277, 127, 128, 277, 277, 277, - /* 1990 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, - /* 2000 */ 277, 277, 277, 277, 277, 277, 277, 277, 150, 277, - /* 2010 */ 277, 277, 277, 277, 277, 277, 277, 277, 277, + /* 1380 */ 210, 213, 222, 59, 222, 222, 182, 213, 213, 196, + /* 1390 */ 257, 226, 226, 19, 20, 71, 22, 257, 188, 187, + /* 1400 */ 192, 212, 187, 187, 226, 81, 210, 166, 60, 261, + /* 1410 */ 36, 244, 130, 170, 90, 170, 38, 170, 139, 104, + /* 1420 */ 96, 97, 48, 236, 22, 235, 43, 103, 201, 105, + /* 1430 */ 106, 107, 138, 59, 110, 247, 213, 18, 204, 258, + /* 1440 */ 204, 258, 204, 204, 170, 71, 18, 169, 213, 236, + /* 1450 */ 213, 127, 128, 235, 201, 201, 82, 170, 169, 213, + /* 1460 */ 146, 87, 62, 254, 90, 141, 142, 143, 144, 145, + /* 1470 */ 96, 97, 253, 170, 169, 22, 170, 103, 169, 105, + /* 1480 */ 106, 107, 189, 170, 110, 169, 189, 186, 19, 20, + /* 1490 */ 104, 22, 194, 186, 186, 64, 115, 186, 194, 189, + /* 1500 */ 188, 102, 133, 186, 186, 36, 186, 104, 189, 19, + /* 1510 */ 20, 246, 22, 246, 189, 141, 142, 143, 144, 145, + /* 1520 */ 0, 1, 2, 228, 228, 5, 36, 227, 59, 227, + /* 1530 */ 10, 11, 12, 13, 14, 170, 84, 17, 134, 216, + /* 1540 */ 71, 272, 270, 22, 137, 217, 22, 216, 227, 59, + /* 1550 */ 30, 82, 32, 217, 228, 228, 87, 227, 170, 90, + /* 1560 */ 40, 71, 146, 241, 215, 96, 97, 214, 136, 135, + /* 1570 */ 213, 25, 103, 26, 105, 106, 107, 243, 173, 110, + /* 1580 */ 90, 172, 13, 6, 164, 164, 96, 97, 98, 162, + /* 1590 */ 70, 162, 162, 103, 176, 105, 106, 107, 78, 267, + /* 1600 */ 110, 81, 267, 264, 182, 182, 182, 182, 88, 176, + /* 1610 */ 141, 142, 143, 144, 145, 176, 190, 4, 182, 182, + /* 1620 */ 182, 19, 20, 182, 22, 190, 3, 22, 151, 15, + /* 1630 */ 89, 141, 142, 143, 144, 145, 16, 128, 36, 23, + /* 1640 */ 23, 139, 122, 24, 119, 131, 20, 127, 128, 133, + /* 1650 */ 16, 1, 140, 131, 119, 61, 139, 53, 37, 53, + /* 1660 */ 53, 59, 53, 119, 105, 34, 130, 1, 5, 22, + /* 1670 */ 150, 104, 149, 71, 26, 75, 68, 41, 68, 130, + /* 1680 */ 104, 24, 20, 19, 82, 120, 114, 22, 28, 87, + /* 1690 */ 22, 67, 90, 22, 67, 23, 22, 22, 96, 97, + /* 1700 */ 67, 23, 138, 22, 37, 103, 153, 105, 106, 107, + /* 1710 */ 1, 2, 110, 23, 5, 23, 23, 26, 22, 10, + /* 1720 */ 11, 12, 13, 14, 24, 23, 17, 22, 24, 130, + /* 1730 */ 23, 19, 20, 23, 22, 105, 22, 34, 85, 30, + /* 1740 */ 34, 32, 26, 141, 142, 143, 144, 145, 36, 40, + /* 1750 */ 132, 34, 75, 83, 23, 44, 24, 34, 23, 26, + /* 1760 */ 26, 19, 20, 23, 22, 26, 23, 23, 23, 23, + /* 1770 */ 22, 59, 11, 22, 22, 26, 23, 23, 36, 70, + /* 1780 */ 22, 22, 124, 71, 130, 130, 130, 78, 23, 130, + /* 1790 */ 81, 15, 1, 278, 278, 278, 278, 88, 278, 278, + /* 1800 */ 278, 59, 90, 278, 278, 278, 278, 278, 96, 97, + /* 1810 */ 278, 278, 278, 71, 278, 103, 278, 105, 106, 107, + /* 1820 */ 278, 278, 110, 278, 278, 278, 278, 278, 278, 278, + /* 1830 */ 278, 122, 90, 278, 278, 278, 127, 128, 96, 97, + /* 1840 */ 278, 278, 278, 278, 278, 103, 278, 105, 106, 107, + /* 1850 */ 278, 278, 110, 141, 142, 143, 144, 145, 278, 150, + /* 1860 */ 278, 278, 278, 5, 278, 278, 278, 278, 10, 11, + /* 1870 */ 12, 13, 14, 278, 278, 17, 278, 278, 278, 278, + /* 1880 */ 278, 278, 278, 141, 142, 143, 144, 145, 30, 278, + /* 1890 */ 32, 278, 278, 278, 278, 278, 278, 278, 40, 278, + /* 1900 */ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + /* 1910 */ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + /* 1920 */ 278, 278, 278, 278, 278, 278, 278, 278, 70, 278, + /* 1930 */ 278, 278, 278, 278, 278, 278, 78, 278, 278, 81, + /* 1940 */ 278, 278, 278, 278, 278, 278, 88, 278, 278, 278, + /* 1950 */ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + /* 1960 */ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + /* 1970 */ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + /* 1980 */ 122, 278, 278, 278, 278, 127, 128, 278, 278, 278, + /* 1990 */ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + /* 2000 */ 278, 278, 278, 278, 278, 278, 278, 278, 150, 278, + /* 2010 */ 278, 278, 278, 278, 278, 278, 278, 278, 278, }; -#define YY_SHIFT_COUNT (520) +#define YY_SHIFT_COUNT (523) #define YY_SHIFT_MIN (0) #define YY_SHIFT_MAX (1858) static const unsigned short int yy_shift_ofst[] = { - /* 0 */ 1709, 1520, 1858, 1324, 1324, 277, 1374, 1469, 1602, 1712, - /* 10 */ 1712, 1712, 273, 0, 0, 113, 1016, 1712, 1712, 1712, - /* 20 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 11, 11, 236, - /* 30 */ 184, 277, 277, 277, 277, 277, 277, 93, 177, 270, + /* 0 */ 1709, 1520, 1858, 1324, 1324, 24, 1374, 1469, 1602, 1712, + /* 10 */ 1712, 1712, 271, 0, 0, 113, 1016, 1712, 1712, 1712, + /* 20 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 12, 12, 409, + /* 30 */ 596, 24, 24, 24, 24, 24, 24, 93, 177, 270, /* 40 */ 363, 456, 549, 642, 735, 828, 848, 996, 1144, 1016, /* 50 */ 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, - /* 60 */ 1016, 1016, 1016, 1016, 1016, 1016, 1164, 1016, 1257, 1277, - /* 70 */ 1277, 1490, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, + /* 60 */ 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1164, 1016, 1257, + /* 70 */ 1277, 1277, 1490, 1712, 1712, 1712, 1712, 1712, 1712, 1712, /* 80 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, /* 90 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, - /* 100 */ 1712, 1712, 1712, 1742, 1712, 1712, 1712, 1712, 1712, 1712, - /* 110 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 143, 162, 162, - /* 120 */ 162, 162, 162, 204, 151, 416, 531, 648, 700, 531, - /* 130 */ 486, 486, 531, 353, 353, 353, 353, 409, 279, 53, - /* 140 */ 2009, 2009, 331, 331, 331, 329, 366, 329, 329, 597, - /* 150 */ 597, 464, 474, 262, 681, 531, 531, 531, 531, 531, - /* 160 */ 531, 531, 531, 531, 531, 531, 531, 531, 531, 531, - /* 170 */ 531, 531, 531, 531, 531, 531, 531, 173, 485, 984, - /* 180 */ 984, 576, 485, 19, 1022, 2009, 2009, 2009, 387, 250, - /* 190 */ 250, 525, 502, 278, 552, 227, 480, 566, 531, 531, - /* 200 */ 531, 531, 531, 531, 531, 531, 531, 531, 639, 531, - /* 210 */ 531, 531, 531, 531, 531, 531, 531, 531, 531, 531, - /* 220 */ 531, 2, 2, 2, 531, 531, 531, 531, 782, 531, - /* 230 */ 531, 531, 744, 531, 531, 783, 531, 531, 531, 531, - /* 240 */ 531, 531, 531, 531, 419, 682, 327, 370, 370, 370, - /* 250 */ 370, 1029, 327, 327, 1024, 897, 856, 947, 1109, 706, - /* 260 */ 706, 1143, 1109, 1109, 1143, 842, 945, 1118, 1136, 1136, - /* 270 */ 1136, 706, 676, 400, 1047, 694, 1339, 1270, 1270, 1366, - /* 280 */ 1366, 1270, 1305, 1389, 1369, 1278, 1401, 1401, 1401, 1401, - /* 290 */ 1270, 1414, 1278, 1278, 1305, 1389, 1369, 1369, 1278, 1270, - /* 300 */ 1414, 1298, 1385, 1270, 1414, 1432, 1270, 1414, 1270, 1414, - /* 310 */ 1432, 1355, 1355, 1355, 1411, 1432, 1355, 1367, 1355, 1411, - /* 320 */ 1355, 1355, 1432, 1392, 1392, 1432, 1365, 1396, 1365, 1396, - /* 330 */ 1365, 1396, 1365, 1396, 1270, 1372, 1429, 1502, 1390, 1372, - /* 340 */ 1517, 1270, 1397, 1390, 1410, 1413, 1278, 1528, 1532, 1549, - /* 350 */ 1549, 1562, 1562, 1562, 2009, 2009, 2009, 2009, 2009, 2009, + /* 100 */ 1712, 1712, 1712, 1712, 1712, 1742, 1712, 1712, 1712, 1712, + /* 110 */ 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 143, + /* 120 */ 162, 162, 162, 162, 162, 204, 151, 186, 650, 690, + /* 130 */ 327, 650, 261, 261, 650, 722, 722, 722, 722, 373, + /* 140 */ 33, 2, 2009, 2009, 330, 330, 330, 346, 289, 278, + /* 150 */ 289, 289, 517, 517, 459, 510, 15, 799, 650, 650, + /* 160 */ 650, 650, 650, 650, 650, 650, 650, 650, 650, 650, + /* 170 */ 650, 650, 650, 650, 650, 650, 650, 650, 650, 650, + /* 180 */ 331, 365, 995, 995, 265, 365, 50, 1038, 2009, 2009, + /* 190 */ 2009, 433, 250, 250, 504, 314, 429, 518, 522, 526, + /* 200 */ 561, 650, 650, 650, 650, 650, 650, 650, 650, 650, + /* 210 */ 192, 650, 650, 650, 650, 650, 650, 650, 650, 650, + /* 220 */ 650, 650, 650, 641, 641, 641, 650, 650, 650, 650, + /* 230 */ 800, 650, 650, 650, 830, 650, 650, 782, 650, 650, + /* 240 */ 650, 650, 650, 650, 650, 650, 739, 902, 689, 895, + /* 250 */ 895, 895, 895, 736, 689, 689, 885, 445, 903, 1124, + /* 260 */ 945, 748, 748, 1066, 945, 945, 1066, 447, 1002, 293, + /* 270 */ 1195, 1195, 1195, 748, 740, 727, 460, 1157, 1348, 1282, + /* 280 */ 1282, 1378, 1378, 1282, 1279, 1315, 1402, 1383, 1294, 1419, + /* 290 */ 1419, 1419, 1419, 1282, 1428, 1294, 1294, 1315, 1402, 1383, + /* 300 */ 1383, 1294, 1282, 1428, 1314, 1400, 1282, 1428, 1453, 1282, + /* 310 */ 1428, 1282, 1428, 1453, 1386, 1386, 1386, 1431, 1453, 1386, + /* 320 */ 1381, 1386, 1431, 1386, 1386, 1453, 1399, 1399, 1453, 1369, + /* 330 */ 1403, 1369, 1403, 1369, 1403, 1369, 1403, 1282, 1404, 1452, + /* 340 */ 1521, 1407, 1404, 1524, 1282, 1416, 1407, 1432, 1434, 1294, + /* 350 */ 1546, 1547, 1569, 1569, 1577, 1577, 1577, 2009, 2009, 2009, /* 360 */ 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, - /* 370 */ 570, 345, 686, 748, 50, 740, 1064, 1107, 469, 537, - /* 380 */ 1042, 1146, 1162, 1154, 1201, 1202, 1203, 1208, 1209, 1127, - /* 390 */ 1069, 1196, 1157, 1147, 1226, 1228, 1245, 775, 868, 1246, - /* 400 */ 1247, 1191, 1151, 1585, 1589, 1587, 1456, 1600, 1527, 1601, - /* 410 */ 1595, 1596, 1492, 1484, 1506, 1603, 1495, 1608, 1496, 1614, - /* 420 */ 1635, 1508, 1497, 1521, 1580, 1606, 1505, 1592, 1593, 1597, - /* 430 */ 1598, 1530, 1547, 1619, 1524, 1654, 1651, 1636, 1553, 1510, - /* 440 */ 1594, 1634, 1599, 1588, 1623, 1535, 1564, 1642, 1649, 1652, - /* 450 */ 1552, 1560, 1653, 1609, 1655, 1656, 1657, 1659, 1612, 1658, - /* 460 */ 1660, 1616, 1648, 1664, 1550, 1668, 1538, 1670, 1671, 1669, - /* 470 */ 1673, 1675, 1676, 1678, 1680, 1679, 1574, 1683, 1690, 1610, - /* 480 */ 1682, 1695, 1586, 1698, 1691, 1698, 1693, 1643, 1661, 1646, - /* 490 */ 1686, 1710, 1711, 1714, 1716, 1703, 1715, 1698, 1727, 1728, - /* 500 */ 1729, 1730, 1731, 1732, 1734, 1743, 1736, 1737, 1740, 1744, - /* 510 */ 1738, 1746, 1739, 1645, 1640, 1644, 1647, 1650, 1749, 1751, - /* 520 */ 1772, + /* 370 */ 2009, 2009, 2009, 591, 697, 1059, 1139, 1058, 797, 465, + /* 380 */ 1159, 1182, 1122, 1062, 1180, 936, 1199, 1201, 1205, 1224, + /* 390 */ 1225, 1244, 1061, 1145, 1261, 1161, 1194, 1249, 1251, 1256, + /* 400 */ 1137, 1142, 1263, 1264, 1214, 1207, 1613, 1623, 1605, 1477, + /* 410 */ 1614, 1541, 1620, 1616, 1617, 1509, 1502, 1525, 1619, 1514, + /* 420 */ 1626, 1516, 1634, 1650, 1522, 1512, 1535, 1594, 1621, 1517, + /* 430 */ 1604, 1606, 1607, 1609, 1544, 1559, 1631, 1536, 1666, 1663, + /* 440 */ 1647, 1567, 1523, 1608, 1648, 1610, 1600, 1636, 1549, 1576, + /* 450 */ 1657, 1662, 1664, 1565, 1572, 1665, 1624, 1668, 1671, 1672, + /* 460 */ 1674, 1627, 1660, 1675, 1633, 1667, 1678, 1564, 1681, 1553, + /* 470 */ 1690, 1692, 1691, 1693, 1696, 1700, 1702, 1705, 1704, 1599, + /* 480 */ 1707, 1710, 1630, 1703, 1714, 1618, 1716, 1706, 1716, 1717, + /* 490 */ 1653, 1677, 1670, 1711, 1731, 1732, 1733, 1734, 1723, 1735, + /* 500 */ 1716, 1740, 1743, 1744, 1745, 1739, 1746, 1748, 1761, 1751, + /* 510 */ 1752, 1753, 1754, 1758, 1759, 1749, 1658, 1654, 1655, 1656, + /* 520 */ 1659, 1765, 1776, 1791, }; -#define YY_REDUCE_COUNT (369) -#define YY_REDUCE_MIN (-237) -#define YY_REDUCE_MAX (1424) +#define YY_REDUCE_COUNT (372) +#define YY_REDUCE_MIN (-235) +#define YY_REDUCE_MAX (1441) static const short yy_reduce_ofst[] = { - /* 0 */ -147, 171, 263, -96, 358, -144, -149, -102, 124, -156, - /* 10 */ -98, 305, 401, -57, 209, -237, 245, -94, -79, 189, - /* 20 */ 375, 490, 493, 378, 303, 539, 542, 501, 503, 554, - /* 30 */ 415, 526, 546, 557, 587, 593, 595, -234, -234, -234, - /* 40 */ -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, - /* 50 */ -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, - /* 60 */ -234, -234, -234, -234, -234, -234, -234, -234, -234, -234, - /* 70 */ -234, -50, 335, 470, 633, 656, 658, 660, 675, 685, - /* 80 */ 703, 727, 747, 750, 752, 754, 770, 788, 790, 793, - /* 90 */ 795, 797, 800, 802, 804, 806, 813, 820, 829, 833, - /* 100 */ 836, 838, 843, 845, 847, 849, 873, 891, 893, 916, - /* 110 */ 918, 921, 936, 941, 944, 956, 961, -234, -234, -234, - /* 120 */ -234, -234, -234, -234, -234, -234, 463, 607, -176, 14, - /* 130 */ -139, 87, -137, 818, 925, 818, 925, 898, -234, -234, - /* 140 */ -234, -234, -166, -166, -166, -130, -131, -82, -54, -180, - /* 150 */ 364, 41, 513, 509, 509, 117, 500, 789, 796, 646, - /* 160 */ 192, 291, 644, 798, 120, 807, 543, 911, 920, 652, - /* 170 */ 924, 922, 232, 698, 801, 971, 39, 220, 731, 442, - /* 180 */ 902, -199, 979, -43, 421, 896, 942, 605, -184, -126, - /* 190 */ 155, 172, 281, 304, 377, 538, 650, 690, 699, 723, - /* 200 */ 803, 839, 853, 919, 991, 1018, 1067, 1092, 951, 1111, - /* 210 */ 1112, 1115, 1116, 1117, 1119, 1120, 1121, 1122, 1123, 1124, - /* 220 */ 1125, 1012, 1096, 1097, 1128, 1129, 1130, 1131, 1070, 1135, - /* 230 */ 1137, 1152, 1077, 1153, 1155, 1114, 1156, 304, 1158, 1172, - /* 240 */ 1173, 1174, 1175, 1176, 1089, 1091, 1133, 1098, 1126, 1139, - /* 250 */ 1140, 1070, 1133, 1133, 1170, 1163, 1186, 1103, 1168, 1138, - /* 260 */ 1141, 1110, 1169, 1171, 1132, 1177, 1189, 1194, 1181, 1200, - /* 270 */ 1204, 1166, 1145, 1178, 1187, 1232, 1142, 1231, 1233, 1149, - /* 280 */ 1150, 1238, 1179, 1182, 1212, 1205, 1219, 1220, 1221, 1222, - /* 290 */ 1258, 1266, 1223, 1224, 1206, 1211, 1237, 1239, 1230, 1269, - /* 300 */ 1272, 1195, 1197, 1280, 1284, 1268, 1285, 1289, 1290, 1293, - /* 310 */ 1274, 1286, 1287, 1288, 1282, 1294, 1292, 1297, 1300, 1296, - /* 320 */ 1301, 1306, 1304, 1249, 1250, 1308, 1271, 1275, 1273, 1276, - /* 330 */ 1279, 1281, 1283, 1302, 1334, 1307, 1243, 1267, 1318, 1322, - /* 340 */ 1303, 1371, 1299, 1328, 1332, 1340, 1342, 1384, 1391, 1400, - /* 350 */ 1403, 1407, 1408, 1409, 1311, 1312, 1310, 1405, 1402, 1412, - /* 360 */ 1417, 1420, 1406, 1393, 1395, 1421, 1422, 1423, 1424, 1415, + /* 0 */ -147, 171, 263, -96, 169, -144, -162, -149, -102, -156, + /* 10 */ -98, 216, 354, -170, -57, -235, 307, 149, 423, 428, + /* 20 */ 471, 313, 451, 519, 489, 496, 499, 545, 547, 555, + /* 30 */ -116, 540, 558, 592, 594, 597, 599, -206, -206, -206, + /* 40 */ -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, + /* 50 */ -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, + /* 60 */ -206, -206, -206, -206, -206, -206, -206, -206, -206, -206, + /* 70 */ -206, -206, 196, 309, 494, 537, 612, 656, 675, 679, + /* 80 */ 681, 685, 724, 753, 771, 776, 788, 790, 794, 796, + /* 90 */ 801, 803, 805, 807, 814, 819, 833, 837, 839, 842, + /* 100 */ 845, 847, 849, 853, 873, 891, 893, 917, 921, 937, + /* 110 */ 940, 944, 956, 960, 967, 969, 971, 973, 975, -206, + /* 120 */ -206, -206, -206, -206, -206, -206, -206, -206, 501, -168, + /* 130 */ 90, -97, 87, 112, 303, 277, 601, 277, 601, 179, + /* 140 */ -206, -206, -206, -206, -107, -107, -107, -43, -56, 323, + /* 150 */ 500, 512, -187, -177, 317, 609, 353, 353, 120, 144, + /* 160 */ 490, 539, 698, 374, 467, 507, 789, 404, -157, 755, + /* 170 */ 856, 916, 843, 941, 802, 770, 923, 821, 1001, -142, + /* 180 */ 264, 785, 896, 905, 899, 949, -176, 544, 911, 953, + /* 190 */ 1012, -182, -59, -30, 16, -22, 117, 172, 291, 369, + /* 200 */ 407, 415, 566, 586, 647, 699, 754, 813, 850, 892, + /* 210 */ 121, 1023, 1042, 1086, 1121, 1125, 1128, 1129, 1130, 1131, + /* 220 */ 1132, 1134, 1135, 284, 1106, 1123, 1152, 1154, 1155, 1156, + /* 230 */ 397, 1158, 1172, 1173, 1116, 1176, 1177, 1138, 1179, 117, + /* 240 */ 1184, 1185, 1198, 1200, 1202, 1203, 741, 1094, 1153, 1146, + /* 250 */ 1160, 1162, 1163, 397, 1153, 1153, 1170, 1204, 1206, 1103, + /* 260 */ 1168, 1165, 1166, 1133, 1174, 1175, 1140, 1210, 1193, 1208, + /* 270 */ 1212, 1215, 1216, 1178, 1167, 1189, 1196, 1241, 1148, 1243, + /* 280 */ 1245, 1181, 1183, 1247, 1188, 1187, 1190, 1227, 1223, 1234, + /* 290 */ 1236, 1238, 1239, 1274, 1278, 1235, 1237, 1213, 1218, 1253, + /* 300 */ 1254, 1246, 1287, 1289, 1209, 1219, 1303, 1305, 1293, 1306, + /* 310 */ 1309, 1313, 1316, 1297, 1301, 1307, 1308, 1298, 1310, 1311, + /* 320 */ 1312, 1317, 1304, 1318, 1320, 1319, 1265, 1267, 1325, 1295, + /* 330 */ 1300, 1296, 1302, 1326, 1321, 1327, 1330, 1365, 1323, 1269, + /* 340 */ 1272, 1328, 1331, 1322, 1388, 1334, 1336, 1349, 1353, 1357, + /* 350 */ 1405, 1409, 1420, 1421, 1427, 1429, 1430, 1332, 1335, 1339, + /* 360 */ 1418, 1422, 1423, 1424, 1425, 1433, 1426, 1435, 1436, 1437, + /* 370 */ 1438, 1441, 1439, }; static const YYACTIONTYPE yy_default[] = { - /* 0 */ 1492, 1492, 1492, 1340, 1123, 1229, 1123, 1123, 1123, 1340, - /* 10 */ 1340, 1340, 1123, 1259, 1259, 1391, 1154, 1123, 1123, 1123, - /* 20 */ 1123, 1123, 1123, 1123, 1339, 1123, 1123, 1123, 1123, 1123, - /* 30 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1265, 1123, - /* 40 */ 1123, 1123, 1123, 1123, 1341, 1342, 1123, 1123, 1123, 1390, - /* 50 */ 1392, 1275, 1274, 1273, 1272, 1373, 1246, 1270, 1263, 1267, - /* 60 */ 1335, 1336, 1334, 1338, 1342, 1341, 1123, 1266, 1306, 1320, - /* 70 */ 1305, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 80 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 90 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 100 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 110 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1314, 1319, 1325, - /* 120 */ 1318, 1315, 1308, 1307, 1309, 1310, 1123, 1144, 1193, 1123, - /* 130 */ 1123, 1123, 1123, 1409, 1408, 1123, 1123, 1154, 1311, 1312, - /* 140 */ 1322, 1321, 1398, 1448, 1447, 1123, 1123, 1123, 1123, 1123, - /* 150 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 160 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 170 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1154, 1150, 1300, - /* 180 */ 1299, 1418, 1150, 1253, 1123, 1404, 1229, 1220, 1123, 1123, - /* 190 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 200 */ 1123, 1395, 1393, 1123, 1355, 1123, 1123, 1123, 1123, 1123, - /* 210 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 220 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 230 */ 1123, 1123, 1225, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 240 */ 1123, 1123, 1123, 1442, 1123, 1368, 1207, 1225, 1225, 1225, - /* 250 */ 1225, 1227, 1208, 1206, 1219, 1154, 1130, 1484, 1269, 1248, - /* 260 */ 1248, 1481, 1269, 1269, 1481, 1168, 1462, 1165, 1259, 1259, - /* 270 */ 1259, 1248, 1337, 1226, 1219, 1123, 1484, 1234, 1234, 1483, - /* 280 */ 1483, 1234, 1278, 1284, 1196, 1269, 1202, 1202, 1202, 1202, - /* 290 */ 1234, 1141, 1269, 1269, 1278, 1284, 1196, 1196, 1269, 1234, - /* 300 */ 1141, 1372, 1478, 1234, 1141, 1348, 1234, 1141, 1234, 1141, - /* 310 */ 1348, 1194, 1194, 1194, 1183, 1348, 1194, 1168, 1194, 1183, - /* 320 */ 1194, 1194, 1348, 1352, 1352, 1348, 1252, 1247, 1252, 1247, - /* 330 */ 1252, 1247, 1252, 1247, 1234, 1253, 1417, 1123, 1264, 1253, - /* 340 */ 1343, 1234, 1123, 1264, 1262, 1260, 1269, 1147, 1186, 1445, - /* 350 */ 1445, 1441, 1441, 1441, 1489, 1489, 1404, 1457, 1154, 1154, - /* 360 */ 1154, 1154, 1457, 1170, 1170, 1154, 1154, 1154, 1154, 1457, - /* 370 */ 1123, 1123, 1123, 1123, 1123, 1123, 1452, 1123, 1357, 1238, - /* 380 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 390 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 400 */ 1123, 1123, 1289, 1123, 1126, 1401, 1123, 1123, 1399, 1123, - /* 410 */ 1123, 1123, 1123, 1123, 1123, 1239, 1123, 1123, 1123, 1123, - /* 420 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 430 */ 1123, 1123, 1123, 1123, 1480, 1123, 1123, 1123, 1123, 1123, - /* 440 */ 1123, 1371, 1370, 1123, 1123, 1236, 1123, 1123, 1123, 1123, - /* 450 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 460 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 470 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 480 */ 1123, 1123, 1123, 1261, 1123, 1416, 1123, 1123, 1123, 1123, - /* 490 */ 1123, 1123, 1123, 1430, 1254, 1123, 1123, 1471, 1123, 1123, - /* 500 */ 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, 1123, - /* 510 */ 1123, 1123, 1466, 1210, 1291, 1123, 1290, 1294, 1123, 1135, - /* 520 */ 1123, + /* 0 */ 1500, 1500, 1500, 1346, 1129, 1235, 1129, 1129, 1129, 1346, + /* 10 */ 1346, 1346, 1129, 1265, 1265, 1399, 1160, 1129, 1129, 1129, + /* 20 */ 1129, 1129, 1129, 1129, 1345, 1129, 1129, 1129, 1129, 1129, + /* 30 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1271, 1129, + /* 40 */ 1129, 1129, 1129, 1129, 1347, 1348, 1129, 1129, 1129, 1398, + /* 50 */ 1400, 1363, 1281, 1280, 1279, 1278, 1381, 1252, 1276, 1269, + /* 60 */ 1273, 1341, 1342, 1340, 1344, 1348, 1347, 1129, 1272, 1312, + /* 70 */ 1326, 1311, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, + /* 80 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, + /* 90 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, + /* 100 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, + /* 110 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1320, + /* 120 */ 1325, 1331, 1324, 1321, 1314, 1313, 1315, 1316, 1129, 1150, + /* 130 */ 1199, 1129, 1129, 1129, 1129, 1417, 1416, 1129, 1129, 1160, + /* 140 */ 1317, 1318, 1328, 1327, 1406, 1456, 1455, 1364, 1129, 1129, + /* 150 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, + /* 160 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, + /* 170 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, + /* 180 */ 1160, 1156, 1306, 1305, 1426, 1156, 1259, 1129, 1412, 1235, + /* 190 */ 1226, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, + /* 200 */ 1129, 1129, 1129, 1129, 1403, 1401, 1129, 1129, 1129, 1129, + /* 210 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, + /* 220 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, + /* 230 */ 1129, 1129, 1129, 1129, 1231, 1129, 1129, 1129, 1129, 1129, + /* 240 */ 1129, 1129, 1129, 1129, 1129, 1450, 1129, 1376, 1213, 1231, + /* 250 */ 1231, 1231, 1231, 1233, 1214, 1212, 1225, 1160, 1136, 1492, + /* 260 */ 1275, 1254, 1254, 1489, 1275, 1275, 1489, 1174, 1470, 1171, + /* 270 */ 1265, 1265, 1265, 1254, 1343, 1232, 1225, 1129, 1492, 1240, + /* 280 */ 1240, 1491, 1491, 1240, 1364, 1284, 1290, 1202, 1275, 1208, + /* 290 */ 1208, 1208, 1208, 1240, 1147, 1275, 1275, 1284, 1290, 1202, + /* 300 */ 1202, 1275, 1240, 1147, 1380, 1486, 1240, 1147, 1354, 1240, + /* 310 */ 1147, 1240, 1147, 1354, 1200, 1200, 1200, 1189, 1354, 1200, + /* 320 */ 1174, 1200, 1189, 1200, 1200, 1354, 1358, 1358, 1354, 1258, + /* 330 */ 1253, 1258, 1253, 1258, 1253, 1258, 1253, 1240, 1259, 1425, + /* 340 */ 1129, 1270, 1259, 1349, 1240, 1129, 1270, 1268, 1266, 1275, + /* 350 */ 1153, 1192, 1453, 1453, 1449, 1449, 1449, 1497, 1497, 1412, + /* 360 */ 1465, 1160, 1160, 1160, 1160, 1465, 1176, 1176, 1160, 1160, + /* 370 */ 1160, 1160, 1465, 1129, 1129, 1129, 1129, 1129, 1129, 1460, + /* 380 */ 1129, 1365, 1244, 1129, 1129, 1129, 1129, 1129, 1129, 1129, + /* 390 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, + /* 400 */ 1129, 1129, 1129, 1129, 1129, 1295, 1129, 1132, 1409, 1129, + /* 410 */ 1129, 1407, 1129, 1129, 1129, 1129, 1129, 1129, 1245, 1129, + /* 420 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, + /* 430 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1488, 1129, 1129, + /* 440 */ 1129, 1129, 1129, 1129, 1379, 1378, 1129, 1129, 1242, 1129, + /* 450 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, + /* 460 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, + /* 470 */ 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, + /* 480 */ 1129, 1129, 1129, 1129, 1129, 1129, 1267, 1129, 1424, 1129, + /* 490 */ 1129, 1129, 1129, 1129, 1129, 1129, 1438, 1260, 1129, 1129, + /* 500 */ 1479, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, 1129, + /* 510 */ 1129, 1129, 1129, 1129, 1129, 1474, 1216, 1297, 1129, 1296, + /* 520 */ 1300, 1129, 1141, 1129, }; /********** End of lemon-generated parsing tables *****************************/ @@ -148168,36 +148671,37 @@ static const char *const yyTokenName[] = { /* 244 */ "case_else", /* 245 */ "uniqueflag", /* 246 */ "collate", - /* 247 */ "nmnum", - /* 248 */ "trigger_decl", - /* 249 */ "trigger_cmd_list", - /* 250 */ "trigger_time", - /* 251 */ "trigger_event", - /* 252 */ "foreach_clause", - /* 253 */ "when_clause", - /* 254 */ "trigger_cmd", - /* 255 */ "trnm", - /* 256 */ "tridxby", - /* 257 */ "database_kw_opt", - /* 258 */ "key_opt", - /* 259 */ "add_column_fullname", - /* 260 */ "kwcolumn_opt", - /* 261 */ "create_vtab", - /* 262 */ "vtabarglist", - /* 263 */ "vtabarg", - /* 264 */ "vtabargtoken", - /* 265 */ "lp", - /* 266 */ "anylist", - /* 267 */ "windowdefn_list", - /* 268 */ "windowdefn", - /* 269 */ "window", - /* 270 */ "frame_opt", - /* 271 */ "part_opt", - /* 272 */ "filter_opt", - /* 273 */ "range_or_rows", - /* 274 */ "frame_bound", - /* 275 */ "frame_bound_s", - /* 276 */ "frame_bound_e", + /* 247 */ "vinto", + /* 248 */ "nmnum", + /* 249 */ "trigger_decl", + /* 250 */ "trigger_cmd_list", + /* 251 */ "trigger_time", + /* 252 */ "trigger_event", + /* 253 */ "foreach_clause", + /* 254 */ "when_clause", + /* 255 */ "trigger_cmd", + /* 256 */ "trnm", + /* 257 */ "tridxby", + /* 258 */ "database_kw_opt", + /* 259 */ "key_opt", + /* 260 */ "add_column_fullname", + /* 261 */ "kwcolumn_opt", + /* 262 */ "create_vtab", + /* 263 */ "vtabarglist", + /* 264 */ "vtabarg", + /* 265 */ "vtabargtoken", + /* 266 */ "lp", + /* 267 */ "anylist", + /* 268 */ "windowdefn_list", + /* 269 */ "windowdefn", + /* 270 */ "window", + /* 271 */ "frame_opt", + /* 272 */ "part_opt", + /* 273 */ "filter_opt", + /* 274 */ "range_or_rows", + /* 275 */ "frame_bound", + /* 276 */ "frame_bound_s", + /* 277 */ "frame_bound_e", }; #endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */ @@ -148434,144 +148938,146 @@ static const char *const yyRuleName[] = { /* 226 */ "collate ::=", /* 227 */ "collate ::= COLLATE ID|STRING", /* 228 */ "cmd ::= DROP INDEX ifexists fullname", - /* 229 */ "cmd ::= VACUUM", - /* 230 */ "cmd ::= VACUUM nm", - /* 231 */ "cmd ::= PRAGMA nm dbnm", - /* 232 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", - /* 233 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", - /* 234 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", - /* 235 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", - /* 236 */ "plus_num ::= PLUS INTEGER|FLOAT", - /* 237 */ "minus_num ::= MINUS INTEGER|FLOAT", - /* 238 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", - /* 239 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", - /* 240 */ "trigger_time ::= BEFORE|AFTER", - /* 241 */ "trigger_time ::= INSTEAD OF", - /* 242 */ "trigger_time ::=", - /* 243 */ "trigger_event ::= DELETE|INSERT", - /* 244 */ "trigger_event ::= UPDATE", - /* 245 */ "trigger_event ::= UPDATE OF idlist", - /* 246 */ "when_clause ::=", - /* 247 */ "when_clause ::= WHEN expr", - /* 248 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", - /* 249 */ "trigger_cmd_list ::= trigger_cmd SEMI", - /* 250 */ "trnm ::= nm DOT nm", - /* 251 */ "tridxby ::= INDEXED BY nm", - /* 252 */ "tridxby ::= NOT INDEXED", - /* 253 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt", - /* 254 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", - /* 255 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", - /* 256 */ "trigger_cmd ::= scanpt select scanpt", - /* 257 */ "expr ::= RAISE LP IGNORE RP", - /* 258 */ "expr ::= RAISE LP raisetype COMMA nm RP", - /* 259 */ "raisetype ::= ROLLBACK", - /* 260 */ "raisetype ::= ABORT", - /* 261 */ "raisetype ::= FAIL", - /* 262 */ "cmd ::= DROP TRIGGER ifexists fullname", - /* 263 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", - /* 264 */ "cmd ::= DETACH database_kw_opt expr", - /* 265 */ "key_opt ::=", - /* 266 */ "key_opt ::= KEY expr", - /* 267 */ "cmd ::= REINDEX", - /* 268 */ "cmd ::= REINDEX nm dbnm", - /* 269 */ "cmd ::= ANALYZE", - /* 270 */ "cmd ::= ANALYZE nm dbnm", - /* 271 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", - /* 272 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", - /* 273 */ "add_column_fullname ::= fullname", - /* 274 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", - /* 275 */ "cmd ::= create_vtab", - /* 276 */ "cmd ::= create_vtab LP vtabarglist RP", - /* 277 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", - /* 278 */ "vtabarg ::=", - /* 279 */ "vtabargtoken ::= ANY", - /* 280 */ "vtabargtoken ::= lp anylist RP", - /* 281 */ "lp ::= LP", - /* 282 */ "with ::= WITH wqlist", - /* 283 */ "with ::= WITH RECURSIVE wqlist", - /* 284 */ "wqlist ::= nm eidlist_opt AS LP select RP", - /* 285 */ "wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP", - /* 286 */ "windowdefn_list ::= windowdefn", - /* 287 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", - /* 288 */ "windowdefn ::= nm AS window", - /* 289 */ "window ::= LP part_opt orderby_opt frame_opt RP", - /* 290 */ "part_opt ::= PARTITION BY nexprlist", - /* 291 */ "part_opt ::=", - /* 292 */ "frame_opt ::=", - /* 293 */ "frame_opt ::= range_or_rows frame_bound_s", - /* 294 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e", - /* 295 */ "range_or_rows ::= RANGE", - /* 296 */ "range_or_rows ::= ROWS", - /* 297 */ "frame_bound_s ::= frame_bound", - /* 298 */ "frame_bound_s ::= UNBOUNDED PRECEDING", - /* 299 */ "frame_bound_e ::= frame_bound", - /* 300 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", - /* 301 */ "frame_bound ::= expr PRECEDING", - /* 302 */ "frame_bound ::= CURRENT ROW", - /* 303 */ "frame_bound ::= expr FOLLOWING", - /* 304 */ "window_clause ::= WINDOW windowdefn_list", - /* 305 */ "over_clause ::= filter_opt OVER window", - /* 306 */ "over_clause ::= filter_opt OVER nm", - /* 307 */ "filter_opt ::=", - /* 308 */ "filter_opt ::= FILTER LP WHERE expr RP", - /* 309 */ "input ::= cmdlist", - /* 310 */ "cmdlist ::= cmdlist ecmd", - /* 311 */ "cmdlist ::= ecmd", - /* 312 */ "ecmd ::= SEMI", - /* 313 */ "ecmd ::= cmdx SEMI", - /* 314 */ "ecmd ::= explain cmdx", - /* 315 */ "trans_opt ::=", - /* 316 */ "trans_opt ::= TRANSACTION", - /* 317 */ "trans_opt ::= TRANSACTION nm", - /* 318 */ "savepoint_opt ::= SAVEPOINT", - /* 319 */ "savepoint_opt ::=", - /* 320 */ "cmd ::= create_table create_table_args", - /* 321 */ "columnlist ::= columnlist COMMA columnname carglist", - /* 322 */ "columnlist ::= columnname carglist", - /* 323 */ "nm ::= ID|INDEXED", - /* 324 */ "nm ::= STRING", - /* 325 */ "nm ::= JOIN_KW", - /* 326 */ "typetoken ::= typename", - /* 327 */ "typename ::= ID|STRING", - /* 328 */ "signed ::= plus_num", - /* 329 */ "signed ::= minus_num", - /* 330 */ "carglist ::= carglist ccons", - /* 331 */ "carglist ::=", - /* 332 */ "ccons ::= NULL onconf", - /* 333 */ "conslist_opt ::= COMMA conslist", - /* 334 */ "conslist ::= conslist tconscomma tcons", - /* 335 */ "conslist ::= tcons", - /* 336 */ "tconscomma ::=", - /* 337 */ "defer_subclause_opt ::= defer_subclause", - /* 338 */ "resolvetype ::= raisetype", - /* 339 */ "selectnowith ::= oneselect", - /* 340 */ "oneselect ::= values", - /* 341 */ "sclp ::= selcollist COMMA", - /* 342 */ "as ::= ID|STRING", - /* 343 */ "expr ::= term", - /* 344 */ "likeop ::= LIKE_KW|MATCH", - /* 345 */ "exprlist ::= nexprlist", - /* 346 */ "nmnum ::= plus_num", - /* 347 */ "nmnum ::= nm", - /* 348 */ "nmnum ::= ON", - /* 349 */ "nmnum ::= DELETE", - /* 350 */ "nmnum ::= DEFAULT", - /* 351 */ "plus_num ::= INTEGER|FLOAT", - /* 352 */ "foreach_clause ::=", - /* 353 */ "foreach_clause ::= FOR EACH ROW", - /* 354 */ "trnm ::= nm", - /* 355 */ "tridxby ::=", - /* 356 */ "database_kw_opt ::= DATABASE", - /* 357 */ "database_kw_opt ::=", - /* 358 */ "kwcolumn_opt ::=", - /* 359 */ "kwcolumn_opt ::= COLUMNKW", - /* 360 */ "vtabarglist ::= vtabarg", - /* 361 */ "vtabarglist ::= vtabarglist COMMA vtabarg", - /* 362 */ "vtabarg ::= vtabarg vtabargtoken", - /* 363 */ "anylist ::=", - /* 364 */ "anylist ::= anylist LP anylist RP", - /* 365 */ "anylist ::= anylist ANY", - /* 366 */ "with ::=", + /* 229 */ "cmd ::= VACUUM vinto", + /* 230 */ "cmd ::= VACUUM nm vinto", + /* 231 */ "vinto ::= INTO expr", + /* 232 */ "vinto ::=", + /* 233 */ "cmd ::= PRAGMA nm dbnm", + /* 234 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", + /* 235 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", + /* 236 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", + /* 237 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", + /* 238 */ "plus_num ::= PLUS INTEGER|FLOAT", + /* 239 */ "minus_num ::= MINUS INTEGER|FLOAT", + /* 240 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", + /* 241 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", + /* 242 */ "trigger_time ::= BEFORE|AFTER", + /* 243 */ "trigger_time ::= INSTEAD OF", + /* 244 */ "trigger_time ::=", + /* 245 */ "trigger_event ::= DELETE|INSERT", + /* 246 */ "trigger_event ::= UPDATE", + /* 247 */ "trigger_event ::= UPDATE OF idlist", + /* 248 */ "when_clause ::=", + /* 249 */ "when_clause ::= WHEN expr", + /* 250 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", + /* 251 */ "trigger_cmd_list ::= trigger_cmd SEMI", + /* 252 */ "trnm ::= nm DOT nm", + /* 253 */ "tridxby ::= INDEXED BY nm", + /* 254 */ "tridxby ::= NOT INDEXED", + /* 255 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt", + /* 256 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt", + /* 257 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt", + /* 258 */ "trigger_cmd ::= scanpt select scanpt", + /* 259 */ "expr ::= RAISE LP IGNORE RP", + /* 260 */ "expr ::= RAISE LP raisetype COMMA nm RP", + /* 261 */ "raisetype ::= ROLLBACK", + /* 262 */ "raisetype ::= ABORT", + /* 263 */ "raisetype ::= FAIL", + /* 264 */ "cmd ::= DROP TRIGGER ifexists fullname", + /* 265 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", + /* 266 */ "cmd ::= DETACH database_kw_opt expr", + /* 267 */ "key_opt ::=", + /* 268 */ "key_opt ::= KEY expr", + /* 269 */ "cmd ::= REINDEX", + /* 270 */ "cmd ::= REINDEX nm dbnm", + /* 271 */ "cmd ::= ANALYZE", + /* 272 */ "cmd ::= ANALYZE nm dbnm", + /* 273 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", + /* 274 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist", + /* 275 */ "add_column_fullname ::= fullname", + /* 276 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm", + /* 277 */ "cmd ::= create_vtab", + /* 278 */ "cmd ::= create_vtab LP vtabarglist RP", + /* 279 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm", + /* 280 */ "vtabarg ::=", + /* 281 */ "vtabargtoken ::= ANY", + /* 282 */ "vtabargtoken ::= lp anylist RP", + /* 283 */ "lp ::= LP", + /* 284 */ "with ::= WITH wqlist", + /* 285 */ "with ::= WITH RECURSIVE wqlist", + /* 286 */ "wqlist ::= nm eidlist_opt AS LP select RP", + /* 287 */ "wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP", + /* 288 */ "windowdefn_list ::= windowdefn", + /* 289 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn", + /* 290 */ "windowdefn ::= nm AS window", + /* 291 */ "window ::= LP part_opt orderby_opt frame_opt RP", + /* 292 */ "part_opt ::= PARTITION BY nexprlist", + /* 293 */ "part_opt ::=", + /* 294 */ "frame_opt ::=", + /* 295 */ "frame_opt ::= range_or_rows frame_bound_s", + /* 296 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e", + /* 297 */ "range_or_rows ::= RANGE", + /* 298 */ "range_or_rows ::= ROWS", + /* 299 */ "frame_bound_s ::= frame_bound", + /* 300 */ "frame_bound_s ::= UNBOUNDED PRECEDING", + /* 301 */ "frame_bound_e ::= frame_bound", + /* 302 */ "frame_bound_e ::= UNBOUNDED FOLLOWING", + /* 303 */ "frame_bound ::= expr PRECEDING", + /* 304 */ "frame_bound ::= CURRENT ROW", + /* 305 */ "frame_bound ::= expr FOLLOWING", + /* 306 */ "window_clause ::= WINDOW windowdefn_list", + /* 307 */ "over_clause ::= filter_opt OVER window", + /* 308 */ "over_clause ::= filter_opt OVER nm", + /* 309 */ "filter_opt ::=", + /* 310 */ "filter_opt ::= FILTER LP WHERE expr RP", + /* 311 */ "input ::= cmdlist", + /* 312 */ "cmdlist ::= cmdlist ecmd", + /* 313 */ "cmdlist ::= ecmd", + /* 314 */ "ecmd ::= SEMI", + /* 315 */ "ecmd ::= cmdx SEMI", + /* 316 */ "ecmd ::= explain cmdx", + /* 317 */ "trans_opt ::=", + /* 318 */ "trans_opt ::= TRANSACTION", + /* 319 */ "trans_opt ::= TRANSACTION nm", + /* 320 */ "savepoint_opt ::= SAVEPOINT", + /* 321 */ "savepoint_opt ::=", + /* 322 */ "cmd ::= create_table create_table_args", + /* 323 */ "columnlist ::= columnlist COMMA columnname carglist", + /* 324 */ "columnlist ::= columnname carglist", + /* 325 */ "nm ::= ID|INDEXED", + /* 326 */ "nm ::= STRING", + /* 327 */ "nm ::= JOIN_KW", + /* 328 */ "typetoken ::= typename", + /* 329 */ "typename ::= ID|STRING", + /* 330 */ "signed ::= plus_num", + /* 331 */ "signed ::= minus_num", + /* 332 */ "carglist ::= carglist ccons", + /* 333 */ "carglist ::=", + /* 334 */ "ccons ::= NULL onconf", + /* 335 */ "conslist_opt ::= COMMA conslist", + /* 336 */ "conslist ::= conslist tconscomma tcons", + /* 337 */ "conslist ::= tcons", + /* 338 */ "tconscomma ::=", + /* 339 */ "defer_subclause_opt ::= defer_subclause", + /* 340 */ "resolvetype ::= raisetype", + /* 341 */ "selectnowith ::= oneselect", + /* 342 */ "oneselect ::= values", + /* 343 */ "sclp ::= selcollist COMMA", + /* 344 */ "as ::= ID|STRING", + /* 345 */ "expr ::= term", + /* 346 */ "likeop ::= LIKE_KW|MATCH", + /* 347 */ "exprlist ::= nexprlist", + /* 348 */ "nmnum ::= plus_num", + /* 349 */ "nmnum ::= nm", + /* 350 */ "nmnum ::= ON", + /* 351 */ "nmnum ::= DELETE", + /* 352 */ "nmnum ::= DEFAULT", + /* 353 */ "plus_num ::= INTEGER|FLOAT", + /* 354 */ "foreach_clause ::=", + /* 355 */ "foreach_clause ::= FOR EACH ROW", + /* 356 */ "trnm ::= nm", + /* 357 */ "tridxby ::=", + /* 358 */ "database_kw_opt ::= DATABASE", + /* 359 */ "database_kw_opt ::=", + /* 360 */ "kwcolumn_opt ::=", + /* 361 */ "kwcolumn_opt ::= COLUMNKW", + /* 362 */ "vtabarglist ::= vtabarg", + /* 363 */ "vtabarglist ::= vtabarglist COMMA vtabarg", + /* 364 */ "vtabarg ::= vtabarg vtabargtoken", + /* 365 */ "anylist ::=", + /* 366 */ "anylist ::= anylist LP anylist RP", + /* 367 */ "anylist ::= anylist ANY", + /* 368 */ "with ::=", }; #endif /* NDEBUG */ @@ -148702,7 +149208,7 @@ static void yy_destructor( case 207: /* oneselect */ case 219: /* values */ { -sqlite3SelectDelete(pParse->db, (yypminor->yy489)); +sqlite3SelectDelete(pParse->db, (yypminor->yy423)); } break; case 184: /* term */ @@ -148712,11 +149218,12 @@ sqlite3SelectDelete(pParse->db, (yypminor->yy489)); case 227: /* on_opt */ case 242: /* case_operand */ case 244: /* case_else */ - case 253: /* when_clause */ - case 258: /* key_opt */ - case 272: /* filter_opt */ + case 247: /* vinto */ + case 254: /* when_clause */ + case 259: /* key_opt */ + case 273: /* filter_opt */ { -sqlite3ExprDelete(pParse->db, (yypminor->yy18)); +sqlite3ExprDelete(pParse->db, (yypminor->yy490)); } break; case 189: /* eidlist_opt */ @@ -148731,9 +149238,9 @@ sqlite3ExprDelete(pParse->db, (yypminor->yy18)); case 233: /* setlist */ case 241: /* paren_exprlist */ case 243: /* case_exprlist */ - case 271: /* part_opt */ + case 272: /* part_opt */ { -sqlite3ExprListDelete(pParse->db, (yypminor->yy420)); +sqlite3ExprListDelete(pParse->db, (yypminor->yy42)); } break; case 205: /* fullname */ @@ -148742,51 +149249,51 @@ sqlite3ExprListDelete(pParse->db, (yypminor->yy420)); case 224: /* stl_prefix */ case 230: /* xfullname */ { -sqlite3SrcListDelete(pParse->db, (yypminor->yy135)); +sqlite3SrcListDelete(pParse->db, (yypminor->yy167)); } break; case 208: /* wqlist */ { -sqlite3WithDelete(pParse->db, (yypminor->yy449)); +sqlite3WithDelete(pParse->db, (yypminor->yy499)); } break; case 218: /* window_clause */ - case 267: /* windowdefn_list */ + case 268: /* windowdefn_list */ { -sqlite3WindowListDelete(pParse->db, (yypminor->yy327)); +sqlite3WindowListDelete(pParse->db, (yypminor->yy147)); } break; case 228: /* using_opt */ case 231: /* idlist */ case 235: /* idlist_opt */ { -sqlite3IdListDelete(pParse->db, (yypminor->yy48)); +sqlite3IdListDelete(pParse->db, (yypminor->yy336)); } break; case 237: /* over_clause */ - case 268: /* windowdefn */ - case 269: /* window */ - case 270: /* frame_opt */ + case 269: /* windowdefn */ + case 270: /* window */ + case 271: /* frame_opt */ { -sqlite3WindowDelete(pParse->db, (yypminor->yy327)); +sqlite3WindowDelete(pParse->db, (yypminor->yy147)); } break; - case 249: /* trigger_cmd_list */ - case 254: /* trigger_cmd */ + case 250: /* trigger_cmd_list */ + case 255: /* trigger_cmd */ { -sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy207)); +sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy119)); } break; - case 251: /* trigger_event */ + case 252: /* trigger_event */ { -sqlite3IdListDelete(pParse->db, (yypminor->yy34).b); +sqlite3IdListDelete(pParse->db, (yypminor->yy350).b); } break; - case 274: /* frame_bound */ - case 275: /* frame_bound_s */ - case 276: /* frame_bound_e */ + case 275: /* frame_bound */ + case 276: /* frame_bound_s */ + case 277: /* frame_bound_e */ { -sqlite3ExprDelete(pParse->db, (yypminor->yy119).pExpr); +sqlite3ExprDelete(pParse->db, (yypminor->yy317).pExpr); } break; /********* End destructor definitions *****************************************/ @@ -149078,380 +149585,752 @@ static void yy_shift( yyTraceShift(yypParser, yyNewState, "Shift"); } -/* The following table contains information about every rule that -** is used during the reduce. -*/ -static const struct { - YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ - signed char nrhs; /* Negative of the number of RHS symbols in the rule */ -} yyRuleInfo[] = { - { 159, -1 }, /* (0) explain ::= EXPLAIN */ - { 159, -3 }, /* (1) explain ::= EXPLAIN QUERY PLAN */ - { 158, -1 }, /* (2) cmdx ::= cmd */ - { 160, -3 }, /* (3) cmd ::= BEGIN transtype trans_opt */ - { 161, 0 }, /* (4) transtype ::= */ - { 161, -1 }, /* (5) transtype ::= DEFERRED */ - { 161, -1 }, /* (6) transtype ::= IMMEDIATE */ - { 161, -1 }, /* (7) transtype ::= EXCLUSIVE */ - { 160, -2 }, /* (8) cmd ::= COMMIT|END trans_opt */ - { 160, -2 }, /* (9) cmd ::= ROLLBACK trans_opt */ - { 160, -2 }, /* (10) cmd ::= SAVEPOINT nm */ - { 160, -3 }, /* (11) cmd ::= RELEASE savepoint_opt nm */ - { 160, -5 }, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ - { 165, -6 }, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */ - { 167, -1 }, /* (14) createkw ::= CREATE */ - { 169, 0 }, /* (15) ifnotexists ::= */ - { 169, -3 }, /* (16) ifnotexists ::= IF NOT EXISTS */ - { 168, -1 }, /* (17) temp ::= TEMP */ - { 168, 0 }, /* (18) temp ::= */ - { 166, -5 }, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_options */ - { 166, -2 }, /* (20) create_table_args ::= AS select */ - { 173, 0 }, /* (21) table_options ::= */ - { 173, -2 }, /* (22) table_options ::= WITHOUT nm */ - { 175, -2 }, /* (23) columnname ::= nm typetoken */ - { 177, 0 }, /* (24) typetoken ::= */ - { 177, -4 }, /* (25) typetoken ::= typename LP signed RP */ - { 177, -6 }, /* (26) typetoken ::= typename LP signed COMMA signed RP */ - { 178, -2 }, /* (27) typename ::= typename ID|STRING */ - { 182, 0 }, /* (28) scanpt ::= */ - { 183, -2 }, /* (29) ccons ::= CONSTRAINT nm */ - { 183, -4 }, /* (30) ccons ::= DEFAULT scanpt term scanpt */ - { 183, -4 }, /* (31) ccons ::= DEFAULT LP expr RP */ - { 183, -4 }, /* (32) ccons ::= DEFAULT PLUS term scanpt */ - { 183, -4 }, /* (33) ccons ::= DEFAULT MINUS term scanpt */ - { 183, -3 }, /* (34) ccons ::= DEFAULT scanpt ID|INDEXED */ - { 183, -3 }, /* (35) ccons ::= NOT NULL onconf */ - { 183, -5 }, /* (36) ccons ::= PRIMARY KEY sortorder onconf autoinc */ - { 183, -2 }, /* (37) ccons ::= UNIQUE onconf */ - { 183, -4 }, /* (38) ccons ::= CHECK LP expr RP */ - { 183, -4 }, /* (39) ccons ::= REFERENCES nm eidlist_opt refargs */ - { 183, -1 }, /* (40) ccons ::= defer_subclause */ - { 183, -2 }, /* (41) ccons ::= COLLATE ID|STRING */ - { 188, 0 }, /* (42) autoinc ::= */ - { 188, -1 }, /* (43) autoinc ::= AUTOINCR */ - { 190, 0 }, /* (44) refargs ::= */ - { 190, -2 }, /* (45) refargs ::= refargs refarg */ - { 192, -2 }, /* (46) refarg ::= MATCH nm */ - { 192, -3 }, /* (47) refarg ::= ON INSERT refact */ - { 192, -3 }, /* (48) refarg ::= ON DELETE refact */ - { 192, -3 }, /* (49) refarg ::= ON UPDATE refact */ - { 193, -2 }, /* (50) refact ::= SET NULL */ - { 193, -2 }, /* (51) refact ::= SET DEFAULT */ - { 193, -1 }, /* (52) refact ::= CASCADE */ - { 193, -1 }, /* (53) refact ::= RESTRICT */ - { 193, -2 }, /* (54) refact ::= NO ACTION */ - { 191, -3 }, /* (55) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ - { 191, -2 }, /* (56) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ - { 194, 0 }, /* (57) init_deferred_pred_opt ::= */ - { 194, -2 }, /* (58) init_deferred_pred_opt ::= INITIALLY DEFERRED */ - { 194, -2 }, /* (59) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ - { 172, 0 }, /* (60) conslist_opt ::= */ - { 196, -1 }, /* (61) tconscomma ::= COMMA */ - { 197, -2 }, /* (62) tcons ::= CONSTRAINT nm */ - { 197, -7 }, /* (63) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ - { 197, -5 }, /* (64) tcons ::= UNIQUE LP sortlist RP onconf */ - { 197, -5 }, /* (65) tcons ::= CHECK LP expr RP onconf */ - { 197, -10 }, /* (66) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ - { 200, 0 }, /* (67) defer_subclause_opt ::= */ - { 186, 0 }, /* (68) onconf ::= */ - { 186, -3 }, /* (69) onconf ::= ON CONFLICT resolvetype */ - { 201, 0 }, /* (70) orconf ::= */ - { 201, -2 }, /* (71) orconf ::= OR resolvetype */ - { 202, -1 }, /* (72) resolvetype ::= IGNORE */ - { 202, -1 }, /* (73) resolvetype ::= REPLACE */ - { 160, -4 }, /* (74) cmd ::= DROP TABLE ifexists fullname */ - { 204, -2 }, /* (75) ifexists ::= IF EXISTS */ - { 204, 0 }, /* (76) ifexists ::= */ - { 160, -9 }, /* (77) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ - { 160, -4 }, /* (78) cmd ::= DROP VIEW ifexists fullname */ - { 160, -1 }, /* (79) cmd ::= select */ - { 174, -3 }, /* (80) select ::= WITH wqlist selectnowith */ - { 174, -4 }, /* (81) select ::= WITH RECURSIVE wqlist selectnowith */ - { 174, -1 }, /* (82) select ::= selectnowith */ - { 206, -3 }, /* (83) selectnowith ::= selectnowith multiselect_op oneselect */ - { 209, -1 }, /* (84) multiselect_op ::= UNION */ - { 209, -2 }, /* (85) multiselect_op ::= UNION ALL */ - { 209, -1 }, /* (86) multiselect_op ::= EXCEPT|INTERSECT */ - { 207, -9 }, /* (87) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ - { 207, -10 }, /* (88) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ - { 219, -4 }, /* (89) values ::= VALUES LP nexprlist RP */ - { 219, -5 }, /* (90) values ::= values COMMA LP nexprlist RP */ - { 210, -1 }, /* (91) distinct ::= DISTINCT */ - { 210, -1 }, /* (92) distinct ::= ALL */ - { 210, 0 }, /* (93) distinct ::= */ - { 221, 0 }, /* (94) sclp ::= */ - { 211, -5 }, /* (95) selcollist ::= sclp scanpt expr scanpt as */ - { 211, -3 }, /* (96) selcollist ::= sclp scanpt STAR */ - { 211, -5 }, /* (97) selcollist ::= sclp scanpt nm DOT STAR */ - { 222, -2 }, /* (98) as ::= AS nm */ - { 222, 0 }, /* (99) as ::= */ - { 212, 0 }, /* (100) from ::= */ - { 212, -2 }, /* (101) from ::= FROM seltablist */ - { 224, -2 }, /* (102) stl_prefix ::= seltablist joinop */ - { 224, 0 }, /* (103) stl_prefix ::= */ - { 223, -7 }, /* (104) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ - { 223, -9 }, /* (105) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ - { 223, -7 }, /* (106) seltablist ::= stl_prefix LP select RP as on_opt using_opt */ - { 223, -7 }, /* (107) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ - { 170, 0 }, /* (108) dbnm ::= */ - { 170, -2 }, /* (109) dbnm ::= DOT nm */ - { 205, -1 }, /* (110) fullname ::= nm */ - { 205, -3 }, /* (111) fullname ::= nm DOT nm */ - { 230, -1 }, /* (112) xfullname ::= nm */ - { 230, -3 }, /* (113) xfullname ::= nm DOT nm */ - { 230, -5 }, /* (114) xfullname ::= nm DOT nm AS nm */ - { 230, -3 }, /* (115) xfullname ::= nm AS nm */ - { 225, -1 }, /* (116) joinop ::= COMMA|JOIN */ - { 225, -2 }, /* (117) joinop ::= JOIN_KW JOIN */ - { 225, -3 }, /* (118) joinop ::= JOIN_KW nm JOIN */ - { 225, -4 }, /* (119) joinop ::= JOIN_KW nm nm JOIN */ - { 227, -2 }, /* (120) on_opt ::= ON expr */ - { 227, 0 }, /* (121) on_opt ::= */ - { 226, 0 }, /* (122) indexed_opt ::= */ - { 226, -3 }, /* (123) indexed_opt ::= INDEXED BY nm */ - { 226, -2 }, /* (124) indexed_opt ::= NOT INDEXED */ - { 228, -4 }, /* (125) using_opt ::= USING LP idlist RP */ - { 228, 0 }, /* (126) using_opt ::= */ - { 216, 0 }, /* (127) orderby_opt ::= */ - { 216, -3 }, /* (128) orderby_opt ::= ORDER BY sortlist */ - { 198, -4 }, /* (129) sortlist ::= sortlist COMMA expr sortorder */ - { 198, -2 }, /* (130) sortlist ::= expr sortorder */ - { 187, -1 }, /* (131) sortorder ::= ASC */ - { 187, -1 }, /* (132) sortorder ::= DESC */ - { 187, 0 }, /* (133) sortorder ::= */ - { 214, 0 }, /* (134) groupby_opt ::= */ - { 214, -3 }, /* (135) groupby_opt ::= GROUP BY nexprlist */ - { 215, 0 }, /* (136) having_opt ::= */ - { 215, -2 }, /* (137) having_opt ::= HAVING expr */ - { 217, 0 }, /* (138) limit_opt ::= */ - { 217, -2 }, /* (139) limit_opt ::= LIMIT expr */ - { 217, -4 }, /* (140) limit_opt ::= LIMIT expr OFFSET expr */ - { 217, -4 }, /* (141) limit_opt ::= LIMIT expr COMMA expr */ - { 160, -6 }, /* (142) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */ - { 213, 0 }, /* (143) where_opt ::= */ - { 213, -2 }, /* (144) where_opt ::= WHERE expr */ - { 160, -8 }, /* (145) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */ - { 233, -5 }, /* (146) setlist ::= setlist COMMA nm EQ expr */ - { 233, -7 }, /* (147) setlist ::= setlist COMMA LP idlist RP EQ expr */ - { 233, -3 }, /* (148) setlist ::= nm EQ expr */ - { 233, -5 }, /* (149) setlist ::= LP idlist RP EQ expr */ - { 160, -7 }, /* (150) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ - { 160, -7 }, /* (151) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */ - { 236, 0 }, /* (152) upsert ::= */ - { 236, -11 }, /* (153) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */ - { 236, -8 }, /* (154) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */ - { 236, -4 }, /* (155) upsert ::= ON CONFLICT DO NOTHING */ - { 234, -2 }, /* (156) insert_cmd ::= INSERT orconf */ - { 234, -1 }, /* (157) insert_cmd ::= REPLACE */ - { 235, 0 }, /* (158) idlist_opt ::= */ - { 235, -3 }, /* (159) idlist_opt ::= LP idlist RP */ - { 231, -3 }, /* (160) idlist ::= idlist COMMA nm */ - { 231, -1 }, /* (161) idlist ::= nm */ - { 185, -3 }, /* (162) expr ::= LP expr RP */ - { 185, -1 }, /* (163) expr ::= ID|INDEXED */ - { 185, -1 }, /* (164) expr ::= JOIN_KW */ - { 185, -3 }, /* (165) expr ::= nm DOT nm */ - { 185, -5 }, /* (166) expr ::= nm DOT nm DOT nm */ - { 184, -1 }, /* (167) term ::= NULL|FLOAT|BLOB */ - { 184, -1 }, /* (168) term ::= STRING */ - { 184, -1 }, /* (169) term ::= INTEGER */ - { 185, -1 }, /* (170) expr ::= VARIABLE */ - { 185, -3 }, /* (171) expr ::= expr COLLATE ID|STRING */ - { 185, -6 }, /* (172) expr ::= CAST LP expr AS typetoken RP */ - { 185, -5 }, /* (173) expr ::= ID|INDEXED LP distinct exprlist RP */ - { 185, -4 }, /* (174) expr ::= ID|INDEXED LP STAR RP */ - { 185, -6 }, /* (175) expr ::= ID|INDEXED LP distinct exprlist RP over_clause */ - { 185, -5 }, /* (176) expr ::= ID|INDEXED LP STAR RP over_clause */ - { 184, -1 }, /* (177) term ::= CTIME_KW */ - { 185, -5 }, /* (178) expr ::= LP nexprlist COMMA expr RP */ - { 185, -3 }, /* (179) expr ::= expr AND expr */ - { 185, -3 }, /* (180) expr ::= expr OR expr */ - { 185, -3 }, /* (181) expr ::= expr LT|GT|GE|LE expr */ - { 185, -3 }, /* (182) expr ::= expr EQ|NE expr */ - { 185, -3 }, /* (183) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ - { 185, -3 }, /* (184) expr ::= expr PLUS|MINUS expr */ - { 185, -3 }, /* (185) expr ::= expr STAR|SLASH|REM expr */ - { 185, -3 }, /* (186) expr ::= expr CONCAT expr */ - { 238, -2 }, /* (187) likeop ::= NOT LIKE_KW|MATCH */ - { 185, -3 }, /* (188) expr ::= expr likeop expr */ - { 185, -5 }, /* (189) expr ::= expr likeop expr ESCAPE expr */ - { 185, -2 }, /* (190) expr ::= expr ISNULL|NOTNULL */ - { 185, -3 }, /* (191) expr ::= expr NOT NULL */ - { 185, -3 }, /* (192) expr ::= expr IS expr */ - { 185, -4 }, /* (193) expr ::= expr IS NOT expr */ - { 185, -2 }, /* (194) expr ::= NOT expr */ - { 185, -2 }, /* (195) expr ::= BITNOT expr */ - { 185, -2 }, /* (196) expr ::= PLUS|MINUS expr */ - { 239, -1 }, /* (197) between_op ::= BETWEEN */ - { 239, -2 }, /* (198) between_op ::= NOT BETWEEN */ - { 185, -5 }, /* (199) expr ::= expr between_op expr AND expr */ - { 240, -1 }, /* (200) in_op ::= IN */ - { 240, -2 }, /* (201) in_op ::= NOT IN */ - { 185, -5 }, /* (202) expr ::= expr in_op LP exprlist RP */ - { 185, -3 }, /* (203) expr ::= LP select RP */ - { 185, -5 }, /* (204) expr ::= expr in_op LP select RP */ - { 185, -5 }, /* (205) expr ::= expr in_op nm dbnm paren_exprlist */ - { 185, -4 }, /* (206) expr ::= EXISTS LP select RP */ - { 185, -5 }, /* (207) expr ::= CASE case_operand case_exprlist case_else END */ - { 243, -5 }, /* (208) case_exprlist ::= case_exprlist WHEN expr THEN expr */ - { 243, -4 }, /* (209) case_exprlist ::= WHEN expr THEN expr */ - { 244, -2 }, /* (210) case_else ::= ELSE expr */ - { 244, 0 }, /* (211) case_else ::= */ - { 242, -1 }, /* (212) case_operand ::= expr */ - { 242, 0 }, /* (213) case_operand ::= */ - { 229, 0 }, /* (214) exprlist ::= */ - { 220, -3 }, /* (215) nexprlist ::= nexprlist COMMA expr */ - { 220, -1 }, /* (216) nexprlist ::= expr */ - { 241, 0 }, /* (217) paren_exprlist ::= */ - { 241, -3 }, /* (218) paren_exprlist ::= LP exprlist RP */ - { 160, -12 }, /* (219) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ - { 245, -1 }, /* (220) uniqueflag ::= UNIQUE */ - { 245, 0 }, /* (221) uniqueflag ::= */ - { 189, 0 }, /* (222) eidlist_opt ::= */ - { 189, -3 }, /* (223) eidlist_opt ::= LP eidlist RP */ - { 199, -5 }, /* (224) eidlist ::= eidlist COMMA nm collate sortorder */ - { 199, -3 }, /* (225) eidlist ::= nm collate sortorder */ - { 246, 0 }, /* (226) collate ::= */ - { 246, -2 }, /* (227) collate ::= COLLATE ID|STRING */ - { 160, -4 }, /* (228) cmd ::= DROP INDEX ifexists fullname */ - { 160, -1 }, /* (229) cmd ::= VACUUM */ - { 160, -2 }, /* (230) cmd ::= VACUUM nm */ - { 160, -3 }, /* (231) cmd ::= PRAGMA nm dbnm */ - { 160, -5 }, /* (232) cmd ::= PRAGMA nm dbnm EQ nmnum */ - { 160, -6 }, /* (233) cmd ::= PRAGMA nm dbnm LP nmnum RP */ - { 160, -5 }, /* (234) cmd ::= PRAGMA nm dbnm EQ minus_num */ - { 160, -6 }, /* (235) cmd ::= PRAGMA nm dbnm LP minus_num RP */ - { 180, -2 }, /* (236) plus_num ::= PLUS INTEGER|FLOAT */ - { 181, -2 }, /* (237) minus_num ::= MINUS INTEGER|FLOAT */ - { 160, -5 }, /* (238) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ - { 248, -11 }, /* (239) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ - { 250, -1 }, /* (240) trigger_time ::= BEFORE|AFTER */ - { 250, -2 }, /* (241) trigger_time ::= INSTEAD OF */ - { 250, 0 }, /* (242) trigger_time ::= */ - { 251, -1 }, /* (243) trigger_event ::= DELETE|INSERT */ - { 251, -1 }, /* (244) trigger_event ::= UPDATE */ - { 251, -3 }, /* (245) trigger_event ::= UPDATE OF idlist */ - { 253, 0 }, /* (246) when_clause ::= */ - { 253, -2 }, /* (247) when_clause ::= WHEN expr */ - { 249, -3 }, /* (248) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ - { 249, -2 }, /* (249) trigger_cmd_list ::= trigger_cmd SEMI */ - { 255, -3 }, /* (250) trnm ::= nm DOT nm */ - { 256, -3 }, /* (251) tridxby ::= INDEXED BY nm */ - { 256, -2 }, /* (252) tridxby ::= NOT INDEXED */ - { 254, -8 }, /* (253) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */ - { 254, -8 }, /* (254) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ - { 254, -6 }, /* (255) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ - { 254, -3 }, /* (256) trigger_cmd ::= scanpt select scanpt */ - { 185, -4 }, /* (257) expr ::= RAISE LP IGNORE RP */ - { 185, -6 }, /* (258) expr ::= RAISE LP raisetype COMMA nm RP */ - { 203, -1 }, /* (259) raisetype ::= ROLLBACK */ - { 203, -1 }, /* (260) raisetype ::= ABORT */ - { 203, -1 }, /* (261) raisetype ::= FAIL */ - { 160, -4 }, /* (262) cmd ::= DROP TRIGGER ifexists fullname */ - { 160, -6 }, /* (263) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ - { 160, -3 }, /* (264) cmd ::= DETACH database_kw_opt expr */ - { 258, 0 }, /* (265) key_opt ::= */ - { 258, -2 }, /* (266) key_opt ::= KEY expr */ - { 160, -1 }, /* (267) cmd ::= REINDEX */ - { 160, -3 }, /* (268) cmd ::= REINDEX nm dbnm */ - { 160, -1 }, /* (269) cmd ::= ANALYZE */ - { 160, -3 }, /* (270) cmd ::= ANALYZE nm dbnm */ - { 160, -6 }, /* (271) cmd ::= ALTER TABLE fullname RENAME TO nm */ - { 160, -7 }, /* (272) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ - { 259, -1 }, /* (273) add_column_fullname ::= fullname */ - { 160, -8 }, /* (274) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ - { 160, -1 }, /* (275) cmd ::= create_vtab */ - { 160, -4 }, /* (276) cmd ::= create_vtab LP vtabarglist RP */ - { 261, -8 }, /* (277) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ - { 263, 0 }, /* (278) vtabarg ::= */ - { 264, -1 }, /* (279) vtabargtoken ::= ANY */ - { 264, -3 }, /* (280) vtabargtoken ::= lp anylist RP */ - { 265, -1 }, /* (281) lp ::= LP */ - { 232, -2 }, /* (282) with ::= WITH wqlist */ - { 232, -3 }, /* (283) with ::= WITH RECURSIVE wqlist */ - { 208, -6 }, /* (284) wqlist ::= nm eidlist_opt AS LP select RP */ - { 208, -8 }, /* (285) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ - { 267, -1 }, /* (286) windowdefn_list ::= windowdefn */ - { 267, -3 }, /* (287) windowdefn_list ::= windowdefn_list COMMA windowdefn */ - { 268, -3 }, /* (288) windowdefn ::= nm AS window */ - { 269, -5 }, /* (289) window ::= LP part_opt orderby_opt frame_opt RP */ - { 271, -3 }, /* (290) part_opt ::= PARTITION BY nexprlist */ - { 271, 0 }, /* (291) part_opt ::= */ - { 270, 0 }, /* (292) frame_opt ::= */ - { 270, -2 }, /* (293) frame_opt ::= range_or_rows frame_bound_s */ - { 270, -5 }, /* (294) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e */ - { 273, -1 }, /* (295) range_or_rows ::= RANGE */ - { 273, -1 }, /* (296) range_or_rows ::= ROWS */ - { 275, -1 }, /* (297) frame_bound_s ::= frame_bound */ - { 275, -2 }, /* (298) frame_bound_s ::= UNBOUNDED PRECEDING */ - { 276, -1 }, /* (299) frame_bound_e ::= frame_bound */ - { 276, -2 }, /* (300) frame_bound_e ::= UNBOUNDED FOLLOWING */ - { 274, -2 }, /* (301) frame_bound ::= expr PRECEDING */ - { 274, -2 }, /* (302) frame_bound ::= CURRENT ROW */ - { 274, -2 }, /* (303) frame_bound ::= expr FOLLOWING */ - { 218, -2 }, /* (304) window_clause ::= WINDOW windowdefn_list */ - { 237, -3 }, /* (305) over_clause ::= filter_opt OVER window */ - { 237, -3 }, /* (306) over_clause ::= filter_opt OVER nm */ - { 272, 0 }, /* (307) filter_opt ::= */ - { 272, -5 }, /* (308) filter_opt ::= FILTER LP WHERE expr RP */ - { 155, -1 }, /* (309) input ::= cmdlist */ - { 156, -2 }, /* (310) cmdlist ::= cmdlist ecmd */ - { 156, -1 }, /* (311) cmdlist ::= ecmd */ - { 157, -1 }, /* (312) ecmd ::= SEMI */ - { 157, -2 }, /* (313) ecmd ::= cmdx SEMI */ - { 157, -2 }, /* (314) ecmd ::= explain cmdx */ - { 162, 0 }, /* (315) trans_opt ::= */ - { 162, -1 }, /* (316) trans_opt ::= TRANSACTION */ - { 162, -2 }, /* (317) trans_opt ::= TRANSACTION nm */ - { 164, -1 }, /* (318) savepoint_opt ::= SAVEPOINT */ - { 164, 0 }, /* (319) savepoint_opt ::= */ - { 160, -2 }, /* (320) cmd ::= create_table create_table_args */ - { 171, -4 }, /* (321) columnlist ::= columnlist COMMA columnname carglist */ - { 171, -2 }, /* (322) columnlist ::= columnname carglist */ - { 163, -1 }, /* (323) nm ::= ID|INDEXED */ - { 163, -1 }, /* (324) nm ::= STRING */ - { 163, -1 }, /* (325) nm ::= JOIN_KW */ - { 177, -1 }, /* (326) typetoken ::= typename */ - { 178, -1 }, /* (327) typename ::= ID|STRING */ - { 179, -1 }, /* (328) signed ::= plus_num */ - { 179, -1 }, /* (329) signed ::= minus_num */ - { 176, -2 }, /* (330) carglist ::= carglist ccons */ - { 176, 0 }, /* (331) carglist ::= */ - { 183, -2 }, /* (332) ccons ::= NULL onconf */ - { 172, -2 }, /* (333) conslist_opt ::= COMMA conslist */ - { 195, -3 }, /* (334) conslist ::= conslist tconscomma tcons */ - { 195, -1 }, /* (335) conslist ::= tcons */ - { 196, 0 }, /* (336) tconscomma ::= */ - { 200, -1 }, /* (337) defer_subclause_opt ::= defer_subclause */ - { 202, -1 }, /* (338) resolvetype ::= raisetype */ - { 206, -1 }, /* (339) selectnowith ::= oneselect */ - { 207, -1 }, /* (340) oneselect ::= values */ - { 221, -2 }, /* (341) sclp ::= selcollist COMMA */ - { 222, -1 }, /* (342) as ::= ID|STRING */ - { 185, -1 }, /* (343) expr ::= term */ - { 238, -1 }, /* (344) likeop ::= LIKE_KW|MATCH */ - { 229, -1 }, /* (345) exprlist ::= nexprlist */ - { 247, -1 }, /* (346) nmnum ::= plus_num */ - { 247, -1 }, /* (347) nmnum ::= nm */ - { 247, -1 }, /* (348) nmnum ::= ON */ - { 247, -1 }, /* (349) nmnum ::= DELETE */ - { 247, -1 }, /* (350) nmnum ::= DEFAULT */ - { 180, -1 }, /* (351) plus_num ::= INTEGER|FLOAT */ - { 252, 0 }, /* (352) foreach_clause ::= */ - { 252, -3 }, /* (353) foreach_clause ::= FOR EACH ROW */ - { 255, -1 }, /* (354) trnm ::= nm */ - { 256, 0 }, /* (355) tridxby ::= */ - { 257, -1 }, /* (356) database_kw_opt ::= DATABASE */ - { 257, 0 }, /* (357) database_kw_opt ::= */ - { 260, 0 }, /* (358) kwcolumn_opt ::= */ - { 260, -1 }, /* (359) kwcolumn_opt ::= COLUMNKW */ - { 262, -1 }, /* (360) vtabarglist ::= vtabarg */ - { 262, -3 }, /* (361) vtabarglist ::= vtabarglist COMMA vtabarg */ - { 263, -2 }, /* (362) vtabarg ::= vtabarg vtabargtoken */ - { 266, 0 }, /* (363) anylist ::= */ - { 266, -4 }, /* (364) anylist ::= anylist LP anylist RP */ - { 266, -2 }, /* (365) anylist ::= anylist ANY */ - { 232, 0 }, /* (366) with ::= */ +/* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side +** of that rule */ +static const YYCODETYPE yyRuleInfoLhs[] = { + 159, /* (0) explain ::= EXPLAIN */ + 159, /* (1) explain ::= EXPLAIN QUERY PLAN */ + 158, /* (2) cmdx ::= cmd */ + 160, /* (3) cmd ::= BEGIN transtype trans_opt */ + 161, /* (4) transtype ::= */ + 161, /* (5) transtype ::= DEFERRED */ + 161, /* (6) transtype ::= IMMEDIATE */ + 161, /* (7) transtype ::= EXCLUSIVE */ + 160, /* (8) cmd ::= COMMIT|END trans_opt */ + 160, /* (9) cmd ::= ROLLBACK trans_opt */ + 160, /* (10) cmd ::= SAVEPOINT nm */ + 160, /* (11) cmd ::= RELEASE savepoint_opt nm */ + 160, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ + 165, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */ + 167, /* (14) createkw ::= CREATE */ + 169, /* (15) ifnotexists ::= */ + 169, /* (16) ifnotexists ::= IF NOT EXISTS */ + 168, /* (17) temp ::= TEMP */ + 168, /* (18) temp ::= */ + 166, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_options */ + 166, /* (20) create_table_args ::= AS select */ + 173, /* (21) table_options ::= */ + 173, /* (22) table_options ::= WITHOUT nm */ + 175, /* (23) columnname ::= nm typetoken */ + 177, /* (24) typetoken ::= */ + 177, /* (25) typetoken ::= typename LP signed RP */ + 177, /* (26) typetoken ::= typename LP signed COMMA signed RP */ + 178, /* (27) typename ::= typename ID|STRING */ + 182, /* (28) scanpt ::= */ + 183, /* (29) ccons ::= CONSTRAINT nm */ + 183, /* (30) ccons ::= DEFAULT scanpt term scanpt */ + 183, /* (31) ccons ::= DEFAULT LP expr RP */ + 183, /* (32) ccons ::= DEFAULT PLUS term scanpt */ + 183, /* (33) ccons ::= DEFAULT MINUS term scanpt */ + 183, /* (34) ccons ::= DEFAULT scanpt ID|INDEXED */ + 183, /* (35) ccons ::= NOT NULL onconf */ + 183, /* (36) ccons ::= PRIMARY KEY sortorder onconf autoinc */ + 183, /* (37) ccons ::= UNIQUE onconf */ + 183, /* (38) ccons ::= CHECK LP expr RP */ + 183, /* (39) ccons ::= REFERENCES nm eidlist_opt refargs */ + 183, /* (40) ccons ::= defer_subclause */ + 183, /* (41) ccons ::= COLLATE ID|STRING */ + 188, /* (42) autoinc ::= */ + 188, /* (43) autoinc ::= AUTOINCR */ + 190, /* (44) refargs ::= */ + 190, /* (45) refargs ::= refargs refarg */ + 192, /* (46) refarg ::= MATCH nm */ + 192, /* (47) refarg ::= ON INSERT refact */ + 192, /* (48) refarg ::= ON DELETE refact */ + 192, /* (49) refarg ::= ON UPDATE refact */ + 193, /* (50) refact ::= SET NULL */ + 193, /* (51) refact ::= SET DEFAULT */ + 193, /* (52) refact ::= CASCADE */ + 193, /* (53) refact ::= RESTRICT */ + 193, /* (54) refact ::= NO ACTION */ + 191, /* (55) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ + 191, /* (56) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ + 194, /* (57) init_deferred_pred_opt ::= */ + 194, /* (58) init_deferred_pred_opt ::= INITIALLY DEFERRED */ + 194, /* (59) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ + 172, /* (60) conslist_opt ::= */ + 196, /* (61) tconscomma ::= COMMA */ + 197, /* (62) tcons ::= CONSTRAINT nm */ + 197, /* (63) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ + 197, /* (64) tcons ::= UNIQUE LP sortlist RP onconf */ + 197, /* (65) tcons ::= CHECK LP expr RP onconf */ + 197, /* (66) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ + 200, /* (67) defer_subclause_opt ::= */ + 186, /* (68) onconf ::= */ + 186, /* (69) onconf ::= ON CONFLICT resolvetype */ + 201, /* (70) orconf ::= */ + 201, /* (71) orconf ::= OR resolvetype */ + 202, /* (72) resolvetype ::= IGNORE */ + 202, /* (73) resolvetype ::= REPLACE */ + 160, /* (74) cmd ::= DROP TABLE ifexists fullname */ + 204, /* (75) ifexists ::= IF EXISTS */ + 204, /* (76) ifexists ::= */ + 160, /* (77) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ + 160, /* (78) cmd ::= DROP VIEW ifexists fullname */ + 160, /* (79) cmd ::= select */ + 174, /* (80) select ::= WITH wqlist selectnowith */ + 174, /* (81) select ::= WITH RECURSIVE wqlist selectnowith */ + 174, /* (82) select ::= selectnowith */ + 206, /* (83) selectnowith ::= selectnowith multiselect_op oneselect */ + 209, /* (84) multiselect_op ::= UNION */ + 209, /* (85) multiselect_op ::= UNION ALL */ + 209, /* (86) multiselect_op ::= EXCEPT|INTERSECT */ + 207, /* (87) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ + 207, /* (88) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ + 219, /* (89) values ::= VALUES LP nexprlist RP */ + 219, /* (90) values ::= values COMMA LP nexprlist RP */ + 210, /* (91) distinct ::= DISTINCT */ + 210, /* (92) distinct ::= ALL */ + 210, /* (93) distinct ::= */ + 221, /* (94) sclp ::= */ + 211, /* (95) selcollist ::= sclp scanpt expr scanpt as */ + 211, /* (96) selcollist ::= sclp scanpt STAR */ + 211, /* (97) selcollist ::= sclp scanpt nm DOT STAR */ + 222, /* (98) as ::= AS nm */ + 222, /* (99) as ::= */ + 212, /* (100) from ::= */ + 212, /* (101) from ::= FROM seltablist */ + 224, /* (102) stl_prefix ::= seltablist joinop */ + 224, /* (103) stl_prefix ::= */ + 223, /* (104) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ + 223, /* (105) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ + 223, /* (106) seltablist ::= stl_prefix LP select RP as on_opt using_opt */ + 223, /* (107) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ + 170, /* (108) dbnm ::= */ + 170, /* (109) dbnm ::= DOT nm */ + 205, /* (110) fullname ::= nm */ + 205, /* (111) fullname ::= nm DOT nm */ + 230, /* (112) xfullname ::= nm */ + 230, /* (113) xfullname ::= nm DOT nm */ + 230, /* (114) xfullname ::= nm DOT nm AS nm */ + 230, /* (115) xfullname ::= nm AS nm */ + 225, /* (116) joinop ::= COMMA|JOIN */ + 225, /* (117) joinop ::= JOIN_KW JOIN */ + 225, /* (118) joinop ::= JOIN_KW nm JOIN */ + 225, /* (119) joinop ::= JOIN_KW nm nm JOIN */ + 227, /* (120) on_opt ::= ON expr */ + 227, /* (121) on_opt ::= */ + 226, /* (122) indexed_opt ::= */ + 226, /* (123) indexed_opt ::= INDEXED BY nm */ + 226, /* (124) indexed_opt ::= NOT INDEXED */ + 228, /* (125) using_opt ::= USING LP idlist RP */ + 228, /* (126) using_opt ::= */ + 216, /* (127) orderby_opt ::= */ + 216, /* (128) orderby_opt ::= ORDER BY sortlist */ + 198, /* (129) sortlist ::= sortlist COMMA expr sortorder */ + 198, /* (130) sortlist ::= expr sortorder */ + 187, /* (131) sortorder ::= ASC */ + 187, /* (132) sortorder ::= DESC */ + 187, /* (133) sortorder ::= */ + 214, /* (134) groupby_opt ::= */ + 214, /* (135) groupby_opt ::= GROUP BY nexprlist */ + 215, /* (136) having_opt ::= */ + 215, /* (137) having_opt ::= HAVING expr */ + 217, /* (138) limit_opt ::= */ + 217, /* (139) limit_opt ::= LIMIT expr */ + 217, /* (140) limit_opt ::= LIMIT expr OFFSET expr */ + 217, /* (141) limit_opt ::= LIMIT expr COMMA expr */ + 160, /* (142) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */ + 213, /* (143) where_opt ::= */ + 213, /* (144) where_opt ::= WHERE expr */ + 160, /* (145) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */ + 233, /* (146) setlist ::= setlist COMMA nm EQ expr */ + 233, /* (147) setlist ::= setlist COMMA LP idlist RP EQ expr */ + 233, /* (148) setlist ::= nm EQ expr */ + 233, /* (149) setlist ::= LP idlist RP EQ expr */ + 160, /* (150) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ + 160, /* (151) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */ + 236, /* (152) upsert ::= */ + 236, /* (153) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */ + 236, /* (154) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */ + 236, /* (155) upsert ::= ON CONFLICT DO NOTHING */ + 234, /* (156) insert_cmd ::= INSERT orconf */ + 234, /* (157) insert_cmd ::= REPLACE */ + 235, /* (158) idlist_opt ::= */ + 235, /* (159) idlist_opt ::= LP idlist RP */ + 231, /* (160) idlist ::= idlist COMMA nm */ + 231, /* (161) idlist ::= nm */ + 185, /* (162) expr ::= LP expr RP */ + 185, /* (163) expr ::= ID|INDEXED */ + 185, /* (164) expr ::= JOIN_KW */ + 185, /* (165) expr ::= nm DOT nm */ + 185, /* (166) expr ::= nm DOT nm DOT nm */ + 184, /* (167) term ::= NULL|FLOAT|BLOB */ + 184, /* (168) term ::= STRING */ + 184, /* (169) term ::= INTEGER */ + 185, /* (170) expr ::= VARIABLE */ + 185, /* (171) expr ::= expr COLLATE ID|STRING */ + 185, /* (172) expr ::= CAST LP expr AS typetoken RP */ + 185, /* (173) expr ::= ID|INDEXED LP distinct exprlist RP */ + 185, /* (174) expr ::= ID|INDEXED LP STAR RP */ + 185, /* (175) expr ::= ID|INDEXED LP distinct exprlist RP over_clause */ + 185, /* (176) expr ::= ID|INDEXED LP STAR RP over_clause */ + 184, /* (177) term ::= CTIME_KW */ + 185, /* (178) expr ::= LP nexprlist COMMA expr RP */ + 185, /* (179) expr ::= expr AND expr */ + 185, /* (180) expr ::= expr OR expr */ + 185, /* (181) expr ::= expr LT|GT|GE|LE expr */ + 185, /* (182) expr ::= expr EQ|NE expr */ + 185, /* (183) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ + 185, /* (184) expr ::= expr PLUS|MINUS expr */ + 185, /* (185) expr ::= expr STAR|SLASH|REM expr */ + 185, /* (186) expr ::= expr CONCAT expr */ + 238, /* (187) likeop ::= NOT LIKE_KW|MATCH */ + 185, /* (188) expr ::= expr likeop expr */ + 185, /* (189) expr ::= expr likeop expr ESCAPE expr */ + 185, /* (190) expr ::= expr ISNULL|NOTNULL */ + 185, /* (191) expr ::= expr NOT NULL */ + 185, /* (192) expr ::= expr IS expr */ + 185, /* (193) expr ::= expr IS NOT expr */ + 185, /* (194) expr ::= NOT expr */ + 185, /* (195) expr ::= BITNOT expr */ + 185, /* (196) expr ::= PLUS|MINUS expr */ + 239, /* (197) between_op ::= BETWEEN */ + 239, /* (198) between_op ::= NOT BETWEEN */ + 185, /* (199) expr ::= expr between_op expr AND expr */ + 240, /* (200) in_op ::= IN */ + 240, /* (201) in_op ::= NOT IN */ + 185, /* (202) expr ::= expr in_op LP exprlist RP */ + 185, /* (203) expr ::= LP select RP */ + 185, /* (204) expr ::= expr in_op LP select RP */ + 185, /* (205) expr ::= expr in_op nm dbnm paren_exprlist */ + 185, /* (206) expr ::= EXISTS LP select RP */ + 185, /* (207) expr ::= CASE case_operand case_exprlist case_else END */ + 243, /* (208) case_exprlist ::= case_exprlist WHEN expr THEN expr */ + 243, /* (209) case_exprlist ::= WHEN expr THEN expr */ + 244, /* (210) case_else ::= ELSE expr */ + 244, /* (211) case_else ::= */ + 242, /* (212) case_operand ::= expr */ + 242, /* (213) case_operand ::= */ + 229, /* (214) exprlist ::= */ + 220, /* (215) nexprlist ::= nexprlist COMMA expr */ + 220, /* (216) nexprlist ::= expr */ + 241, /* (217) paren_exprlist ::= */ + 241, /* (218) paren_exprlist ::= LP exprlist RP */ + 160, /* (219) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + 245, /* (220) uniqueflag ::= UNIQUE */ + 245, /* (221) uniqueflag ::= */ + 189, /* (222) eidlist_opt ::= */ + 189, /* (223) eidlist_opt ::= LP eidlist RP */ + 199, /* (224) eidlist ::= eidlist COMMA nm collate sortorder */ + 199, /* (225) eidlist ::= nm collate sortorder */ + 246, /* (226) collate ::= */ + 246, /* (227) collate ::= COLLATE ID|STRING */ + 160, /* (228) cmd ::= DROP INDEX ifexists fullname */ + 160, /* (229) cmd ::= VACUUM vinto */ + 160, /* (230) cmd ::= VACUUM nm vinto */ + 247, /* (231) vinto ::= INTO expr */ + 247, /* (232) vinto ::= */ + 160, /* (233) cmd ::= PRAGMA nm dbnm */ + 160, /* (234) cmd ::= PRAGMA nm dbnm EQ nmnum */ + 160, /* (235) cmd ::= PRAGMA nm dbnm LP nmnum RP */ + 160, /* (236) cmd ::= PRAGMA nm dbnm EQ minus_num */ + 160, /* (237) cmd ::= PRAGMA nm dbnm LP minus_num RP */ + 180, /* (238) plus_num ::= PLUS INTEGER|FLOAT */ + 181, /* (239) minus_num ::= MINUS INTEGER|FLOAT */ + 160, /* (240) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + 249, /* (241) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + 251, /* (242) trigger_time ::= BEFORE|AFTER */ + 251, /* (243) trigger_time ::= INSTEAD OF */ + 251, /* (244) trigger_time ::= */ + 252, /* (245) trigger_event ::= DELETE|INSERT */ + 252, /* (246) trigger_event ::= UPDATE */ + 252, /* (247) trigger_event ::= UPDATE OF idlist */ + 254, /* (248) when_clause ::= */ + 254, /* (249) when_clause ::= WHEN expr */ + 250, /* (250) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + 250, /* (251) trigger_cmd_list ::= trigger_cmd SEMI */ + 256, /* (252) trnm ::= nm DOT nm */ + 257, /* (253) tridxby ::= INDEXED BY nm */ + 257, /* (254) tridxby ::= NOT INDEXED */ + 255, /* (255) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */ + 255, /* (256) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + 255, /* (257) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ + 255, /* (258) trigger_cmd ::= scanpt select scanpt */ + 185, /* (259) expr ::= RAISE LP IGNORE RP */ + 185, /* (260) expr ::= RAISE LP raisetype COMMA nm RP */ + 203, /* (261) raisetype ::= ROLLBACK */ + 203, /* (262) raisetype ::= ABORT */ + 203, /* (263) raisetype ::= FAIL */ + 160, /* (264) cmd ::= DROP TRIGGER ifexists fullname */ + 160, /* (265) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + 160, /* (266) cmd ::= DETACH database_kw_opt expr */ + 259, /* (267) key_opt ::= */ + 259, /* (268) key_opt ::= KEY expr */ + 160, /* (269) cmd ::= REINDEX */ + 160, /* (270) cmd ::= REINDEX nm dbnm */ + 160, /* (271) cmd ::= ANALYZE */ + 160, /* (272) cmd ::= ANALYZE nm dbnm */ + 160, /* (273) cmd ::= ALTER TABLE fullname RENAME TO nm */ + 160, /* (274) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + 260, /* (275) add_column_fullname ::= fullname */ + 160, /* (276) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + 160, /* (277) cmd ::= create_vtab */ + 160, /* (278) cmd ::= create_vtab LP vtabarglist RP */ + 262, /* (279) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + 264, /* (280) vtabarg ::= */ + 265, /* (281) vtabargtoken ::= ANY */ + 265, /* (282) vtabargtoken ::= lp anylist RP */ + 266, /* (283) lp ::= LP */ + 232, /* (284) with ::= WITH wqlist */ + 232, /* (285) with ::= WITH RECURSIVE wqlist */ + 208, /* (286) wqlist ::= nm eidlist_opt AS LP select RP */ + 208, /* (287) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ + 268, /* (288) windowdefn_list ::= windowdefn */ + 268, /* (289) windowdefn_list ::= windowdefn_list COMMA windowdefn */ + 269, /* (290) windowdefn ::= nm AS window */ + 270, /* (291) window ::= LP part_opt orderby_opt frame_opt RP */ + 272, /* (292) part_opt ::= PARTITION BY nexprlist */ + 272, /* (293) part_opt ::= */ + 271, /* (294) frame_opt ::= */ + 271, /* (295) frame_opt ::= range_or_rows frame_bound_s */ + 271, /* (296) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e */ + 274, /* (297) range_or_rows ::= RANGE */ + 274, /* (298) range_or_rows ::= ROWS */ + 276, /* (299) frame_bound_s ::= frame_bound */ + 276, /* (300) frame_bound_s ::= UNBOUNDED PRECEDING */ + 277, /* (301) frame_bound_e ::= frame_bound */ + 277, /* (302) frame_bound_e ::= UNBOUNDED FOLLOWING */ + 275, /* (303) frame_bound ::= expr PRECEDING */ + 275, /* (304) frame_bound ::= CURRENT ROW */ + 275, /* (305) frame_bound ::= expr FOLLOWING */ + 218, /* (306) window_clause ::= WINDOW windowdefn_list */ + 237, /* (307) over_clause ::= filter_opt OVER window */ + 237, /* (308) over_clause ::= filter_opt OVER nm */ + 273, /* (309) filter_opt ::= */ + 273, /* (310) filter_opt ::= FILTER LP WHERE expr RP */ + 155, /* (311) input ::= cmdlist */ + 156, /* (312) cmdlist ::= cmdlist ecmd */ + 156, /* (313) cmdlist ::= ecmd */ + 157, /* (314) ecmd ::= SEMI */ + 157, /* (315) ecmd ::= cmdx SEMI */ + 157, /* (316) ecmd ::= explain cmdx */ + 162, /* (317) trans_opt ::= */ + 162, /* (318) trans_opt ::= TRANSACTION */ + 162, /* (319) trans_opt ::= TRANSACTION nm */ + 164, /* (320) savepoint_opt ::= SAVEPOINT */ + 164, /* (321) savepoint_opt ::= */ + 160, /* (322) cmd ::= create_table create_table_args */ + 171, /* (323) columnlist ::= columnlist COMMA columnname carglist */ + 171, /* (324) columnlist ::= columnname carglist */ + 163, /* (325) nm ::= ID|INDEXED */ + 163, /* (326) nm ::= STRING */ + 163, /* (327) nm ::= JOIN_KW */ + 177, /* (328) typetoken ::= typename */ + 178, /* (329) typename ::= ID|STRING */ + 179, /* (330) signed ::= plus_num */ + 179, /* (331) signed ::= minus_num */ + 176, /* (332) carglist ::= carglist ccons */ + 176, /* (333) carglist ::= */ + 183, /* (334) ccons ::= NULL onconf */ + 172, /* (335) conslist_opt ::= COMMA conslist */ + 195, /* (336) conslist ::= conslist tconscomma tcons */ + 195, /* (337) conslist ::= tcons */ + 196, /* (338) tconscomma ::= */ + 200, /* (339) defer_subclause_opt ::= defer_subclause */ + 202, /* (340) resolvetype ::= raisetype */ + 206, /* (341) selectnowith ::= oneselect */ + 207, /* (342) oneselect ::= values */ + 221, /* (343) sclp ::= selcollist COMMA */ + 222, /* (344) as ::= ID|STRING */ + 185, /* (345) expr ::= term */ + 238, /* (346) likeop ::= LIKE_KW|MATCH */ + 229, /* (347) exprlist ::= nexprlist */ + 248, /* (348) nmnum ::= plus_num */ + 248, /* (349) nmnum ::= nm */ + 248, /* (350) nmnum ::= ON */ + 248, /* (351) nmnum ::= DELETE */ + 248, /* (352) nmnum ::= DEFAULT */ + 180, /* (353) plus_num ::= INTEGER|FLOAT */ + 253, /* (354) foreach_clause ::= */ + 253, /* (355) foreach_clause ::= FOR EACH ROW */ + 256, /* (356) trnm ::= nm */ + 257, /* (357) tridxby ::= */ + 258, /* (358) database_kw_opt ::= DATABASE */ + 258, /* (359) database_kw_opt ::= */ + 261, /* (360) kwcolumn_opt ::= */ + 261, /* (361) kwcolumn_opt ::= COLUMNKW */ + 263, /* (362) vtabarglist ::= vtabarg */ + 263, /* (363) vtabarglist ::= vtabarglist COMMA vtabarg */ + 264, /* (364) vtabarg ::= vtabarg vtabargtoken */ + 267, /* (365) anylist ::= */ + 267, /* (366) anylist ::= anylist LP anylist RP */ + 267, /* (367) anylist ::= anylist ANY */ + 232, /* (368) with ::= */ +}; + +/* For rule J, yyRuleInfoNRhs[J] contains the negative of the number +** of symbols on the right-hand side of that rule. */ +static const signed char yyRuleInfoNRhs[] = { + -1, /* (0) explain ::= EXPLAIN */ + -3, /* (1) explain ::= EXPLAIN QUERY PLAN */ + -1, /* (2) cmdx ::= cmd */ + -3, /* (3) cmd ::= BEGIN transtype trans_opt */ + 0, /* (4) transtype ::= */ + -1, /* (5) transtype ::= DEFERRED */ + -1, /* (6) transtype ::= IMMEDIATE */ + -1, /* (7) transtype ::= EXCLUSIVE */ + -2, /* (8) cmd ::= COMMIT|END trans_opt */ + -2, /* (9) cmd ::= ROLLBACK trans_opt */ + -2, /* (10) cmd ::= SAVEPOINT nm */ + -3, /* (11) cmd ::= RELEASE savepoint_opt nm */ + -5, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */ + -6, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */ + -1, /* (14) createkw ::= CREATE */ + 0, /* (15) ifnotexists ::= */ + -3, /* (16) ifnotexists ::= IF NOT EXISTS */ + -1, /* (17) temp ::= TEMP */ + 0, /* (18) temp ::= */ + -5, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_options */ + -2, /* (20) create_table_args ::= AS select */ + 0, /* (21) table_options ::= */ + -2, /* (22) table_options ::= WITHOUT nm */ + -2, /* (23) columnname ::= nm typetoken */ + 0, /* (24) typetoken ::= */ + -4, /* (25) typetoken ::= typename LP signed RP */ + -6, /* (26) typetoken ::= typename LP signed COMMA signed RP */ + -2, /* (27) typename ::= typename ID|STRING */ + 0, /* (28) scanpt ::= */ + -2, /* (29) ccons ::= CONSTRAINT nm */ + -4, /* (30) ccons ::= DEFAULT scanpt term scanpt */ + -4, /* (31) ccons ::= DEFAULT LP expr RP */ + -4, /* (32) ccons ::= DEFAULT PLUS term scanpt */ + -4, /* (33) ccons ::= DEFAULT MINUS term scanpt */ + -3, /* (34) ccons ::= DEFAULT scanpt ID|INDEXED */ + -3, /* (35) ccons ::= NOT NULL onconf */ + -5, /* (36) ccons ::= PRIMARY KEY sortorder onconf autoinc */ + -2, /* (37) ccons ::= UNIQUE onconf */ + -4, /* (38) ccons ::= CHECK LP expr RP */ + -4, /* (39) ccons ::= REFERENCES nm eidlist_opt refargs */ + -1, /* (40) ccons ::= defer_subclause */ + -2, /* (41) ccons ::= COLLATE ID|STRING */ + 0, /* (42) autoinc ::= */ + -1, /* (43) autoinc ::= AUTOINCR */ + 0, /* (44) refargs ::= */ + -2, /* (45) refargs ::= refargs refarg */ + -2, /* (46) refarg ::= MATCH nm */ + -3, /* (47) refarg ::= ON INSERT refact */ + -3, /* (48) refarg ::= ON DELETE refact */ + -3, /* (49) refarg ::= ON UPDATE refact */ + -2, /* (50) refact ::= SET NULL */ + -2, /* (51) refact ::= SET DEFAULT */ + -1, /* (52) refact ::= CASCADE */ + -1, /* (53) refact ::= RESTRICT */ + -2, /* (54) refact ::= NO ACTION */ + -3, /* (55) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ + -2, /* (56) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ + 0, /* (57) init_deferred_pred_opt ::= */ + -2, /* (58) init_deferred_pred_opt ::= INITIALLY DEFERRED */ + -2, /* (59) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ + 0, /* (60) conslist_opt ::= */ + -1, /* (61) tconscomma ::= COMMA */ + -2, /* (62) tcons ::= CONSTRAINT nm */ + -7, /* (63) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ + -5, /* (64) tcons ::= UNIQUE LP sortlist RP onconf */ + -5, /* (65) tcons ::= CHECK LP expr RP onconf */ + -10, /* (66) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ + 0, /* (67) defer_subclause_opt ::= */ + 0, /* (68) onconf ::= */ + -3, /* (69) onconf ::= ON CONFLICT resolvetype */ + 0, /* (70) orconf ::= */ + -2, /* (71) orconf ::= OR resolvetype */ + -1, /* (72) resolvetype ::= IGNORE */ + -1, /* (73) resolvetype ::= REPLACE */ + -4, /* (74) cmd ::= DROP TABLE ifexists fullname */ + -2, /* (75) ifexists ::= IF EXISTS */ + 0, /* (76) ifexists ::= */ + -9, /* (77) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ + -4, /* (78) cmd ::= DROP VIEW ifexists fullname */ + -1, /* (79) cmd ::= select */ + -3, /* (80) select ::= WITH wqlist selectnowith */ + -4, /* (81) select ::= WITH RECURSIVE wqlist selectnowith */ + -1, /* (82) select ::= selectnowith */ + -3, /* (83) selectnowith ::= selectnowith multiselect_op oneselect */ + -1, /* (84) multiselect_op ::= UNION */ + -2, /* (85) multiselect_op ::= UNION ALL */ + -1, /* (86) multiselect_op ::= EXCEPT|INTERSECT */ + -9, /* (87) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ + -10, /* (88) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ + -4, /* (89) values ::= VALUES LP nexprlist RP */ + -5, /* (90) values ::= values COMMA LP nexprlist RP */ + -1, /* (91) distinct ::= DISTINCT */ + -1, /* (92) distinct ::= ALL */ + 0, /* (93) distinct ::= */ + 0, /* (94) sclp ::= */ + -5, /* (95) selcollist ::= sclp scanpt expr scanpt as */ + -3, /* (96) selcollist ::= sclp scanpt STAR */ + -5, /* (97) selcollist ::= sclp scanpt nm DOT STAR */ + -2, /* (98) as ::= AS nm */ + 0, /* (99) as ::= */ + 0, /* (100) from ::= */ + -2, /* (101) from ::= FROM seltablist */ + -2, /* (102) stl_prefix ::= seltablist joinop */ + 0, /* (103) stl_prefix ::= */ + -7, /* (104) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ + -9, /* (105) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ + -7, /* (106) seltablist ::= stl_prefix LP select RP as on_opt using_opt */ + -7, /* (107) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ + 0, /* (108) dbnm ::= */ + -2, /* (109) dbnm ::= DOT nm */ + -1, /* (110) fullname ::= nm */ + -3, /* (111) fullname ::= nm DOT nm */ + -1, /* (112) xfullname ::= nm */ + -3, /* (113) xfullname ::= nm DOT nm */ + -5, /* (114) xfullname ::= nm DOT nm AS nm */ + -3, /* (115) xfullname ::= nm AS nm */ + -1, /* (116) joinop ::= COMMA|JOIN */ + -2, /* (117) joinop ::= JOIN_KW JOIN */ + -3, /* (118) joinop ::= JOIN_KW nm JOIN */ + -4, /* (119) joinop ::= JOIN_KW nm nm JOIN */ + -2, /* (120) on_opt ::= ON expr */ + 0, /* (121) on_opt ::= */ + 0, /* (122) indexed_opt ::= */ + -3, /* (123) indexed_opt ::= INDEXED BY nm */ + -2, /* (124) indexed_opt ::= NOT INDEXED */ + -4, /* (125) using_opt ::= USING LP idlist RP */ + 0, /* (126) using_opt ::= */ + 0, /* (127) orderby_opt ::= */ + -3, /* (128) orderby_opt ::= ORDER BY sortlist */ + -4, /* (129) sortlist ::= sortlist COMMA expr sortorder */ + -2, /* (130) sortlist ::= expr sortorder */ + -1, /* (131) sortorder ::= ASC */ + -1, /* (132) sortorder ::= DESC */ + 0, /* (133) sortorder ::= */ + 0, /* (134) groupby_opt ::= */ + -3, /* (135) groupby_opt ::= GROUP BY nexprlist */ + 0, /* (136) having_opt ::= */ + -2, /* (137) having_opt ::= HAVING expr */ + 0, /* (138) limit_opt ::= */ + -2, /* (139) limit_opt ::= LIMIT expr */ + -4, /* (140) limit_opt ::= LIMIT expr OFFSET expr */ + -4, /* (141) limit_opt ::= LIMIT expr COMMA expr */ + -6, /* (142) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */ + 0, /* (143) where_opt ::= */ + -2, /* (144) where_opt ::= WHERE expr */ + -8, /* (145) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */ + -5, /* (146) setlist ::= setlist COMMA nm EQ expr */ + -7, /* (147) setlist ::= setlist COMMA LP idlist RP EQ expr */ + -3, /* (148) setlist ::= nm EQ expr */ + -5, /* (149) setlist ::= LP idlist RP EQ expr */ + -7, /* (150) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ + -7, /* (151) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */ + 0, /* (152) upsert ::= */ + -11, /* (153) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */ + -8, /* (154) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */ + -4, /* (155) upsert ::= ON CONFLICT DO NOTHING */ + -2, /* (156) insert_cmd ::= INSERT orconf */ + -1, /* (157) insert_cmd ::= REPLACE */ + 0, /* (158) idlist_opt ::= */ + -3, /* (159) idlist_opt ::= LP idlist RP */ + -3, /* (160) idlist ::= idlist COMMA nm */ + -1, /* (161) idlist ::= nm */ + -3, /* (162) expr ::= LP expr RP */ + -1, /* (163) expr ::= ID|INDEXED */ + -1, /* (164) expr ::= JOIN_KW */ + -3, /* (165) expr ::= nm DOT nm */ + -5, /* (166) expr ::= nm DOT nm DOT nm */ + -1, /* (167) term ::= NULL|FLOAT|BLOB */ + -1, /* (168) term ::= STRING */ + -1, /* (169) term ::= INTEGER */ + -1, /* (170) expr ::= VARIABLE */ + -3, /* (171) expr ::= expr COLLATE ID|STRING */ + -6, /* (172) expr ::= CAST LP expr AS typetoken RP */ + -5, /* (173) expr ::= ID|INDEXED LP distinct exprlist RP */ + -4, /* (174) expr ::= ID|INDEXED LP STAR RP */ + -6, /* (175) expr ::= ID|INDEXED LP distinct exprlist RP over_clause */ + -5, /* (176) expr ::= ID|INDEXED LP STAR RP over_clause */ + -1, /* (177) term ::= CTIME_KW */ + -5, /* (178) expr ::= LP nexprlist COMMA expr RP */ + -3, /* (179) expr ::= expr AND expr */ + -3, /* (180) expr ::= expr OR expr */ + -3, /* (181) expr ::= expr LT|GT|GE|LE expr */ + -3, /* (182) expr ::= expr EQ|NE expr */ + -3, /* (183) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ + -3, /* (184) expr ::= expr PLUS|MINUS expr */ + -3, /* (185) expr ::= expr STAR|SLASH|REM expr */ + -3, /* (186) expr ::= expr CONCAT expr */ + -2, /* (187) likeop ::= NOT LIKE_KW|MATCH */ + -3, /* (188) expr ::= expr likeop expr */ + -5, /* (189) expr ::= expr likeop expr ESCAPE expr */ + -2, /* (190) expr ::= expr ISNULL|NOTNULL */ + -3, /* (191) expr ::= expr NOT NULL */ + -3, /* (192) expr ::= expr IS expr */ + -4, /* (193) expr ::= expr IS NOT expr */ + -2, /* (194) expr ::= NOT expr */ + -2, /* (195) expr ::= BITNOT expr */ + -2, /* (196) expr ::= PLUS|MINUS expr */ + -1, /* (197) between_op ::= BETWEEN */ + -2, /* (198) between_op ::= NOT BETWEEN */ + -5, /* (199) expr ::= expr between_op expr AND expr */ + -1, /* (200) in_op ::= IN */ + -2, /* (201) in_op ::= NOT IN */ + -5, /* (202) expr ::= expr in_op LP exprlist RP */ + -3, /* (203) expr ::= LP select RP */ + -5, /* (204) expr ::= expr in_op LP select RP */ + -5, /* (205) expr ::= expr in_op nm dbnm paren_exprlist */ + -4, /* (206) expr ::= EXISTS LP select RP */ + -5, /* (207) expr ::= CASE case_operand case_exprlist case_else END */ + -5, /* (208) case_exprlist ::= case_exprlist WHEN expr THEN expr */ + -4, /* (209) case_exprlist ::= WHEN expr THEN expr */ + -2, /* (210) case_else ::= ELSE expr */ + 0, /* (211) case_else ::= */ + -1, /* (212) case_operand ::= expr */ + 0, /* (213) case_operand ::= */ + 0, /* (214) exprlist ::= */ + -3, /* (215) nexprlist ::= nexprlist COMMA expr */ + -1, /* (216) nexprlist ::= expr */ + 0, /* (217) paren_exprlist ::= */ + -3, /* (218) paren_exprlist ::= LP exprlist RP */ + -12, /* (219) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ + -1, /* (220) uniqueflag ::= UNIQUE */ + 0, /* (221) uniqueflag ::= */ + 0, /* (222) eidlist_opt ::= */ + -3, /* (223) eidlist_opt ::= LP eidlist RP */ + -5, /* (224) eidlist ::= eidlist COMMA nm collate sortorder */ + -3, /* (225) eidlist ::= nm collate sortorder */ + 0, /* (226) collate ::= */ + -2, /* (227) collate ::= COLLATE ID|STRING */ + -4, /* (228) cmd ::= DROP INDEX ifexists fullname */ + -2, /* (229) cmd ::= VACUUM vinto */ + -3, /* (230) cmd ::= VACUUM nm vinto */ + -2, /* (231) vinto ::= INTO expr */ + 0, /* (232) vinto ::= */ + -3, /* (233) cmd ::= PRAGMA nm dbnm */ + -5, /* (234) cmd ::= PRAGMA nm dbnm EQ nmnum */ + -6, /* (235) cmd ::= PRAGMA nm dbnm LP nmnum RP */ + -5, /* (236) cmd ::= PRAGMA nm dbnm EQ minus_num */ + -6, /* (237) cmd ::= PRAGMA nm dbnm LP minus_num RP */ + -2, /* (238) plus_num ::= PLUS INTEGER|FLOAT */ + -2, /* (239) minus_num ::= MINUS INTEGER|FLOAT */ + -5, /* (240) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + -11, /* (241) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + -1, /* (242) trigger_time ::= BEFORE|AFTER */ + -2, /* (243) trigger_time ::= INSTEAD OF */ + 0, /* (244) trigger_time ::= */ + -1, /* (245) trigger_event ::= DELETE|INSERT */ + -1, /* (246) trigger_event ::= UPDATE */ + -3, /* (247) trigger_event ::= UPDATE OF idlist */ + 0, /* (248) when_clause ::= */ + -2, /* (249) when_clause ::= WHEN expr */ + -3, /* (250) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + -2, /* (251) trigger_cmd_list ::= trigger_cmd SEMI */ + -3, /* (252) trnm ::= nm DOT nm */ + -3, /* (253) tridxby ::= INDEXED BY nm */ + -2, /* (254) tridxby ::= NOT INDEXED */ + -8, /* (255) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */ + -8, /* (256) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + -6, /* (257) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ + -3, /* (258) trigger_cmd ::= scanpt select scanpt */ + -4, /* (259) expr ::= RAISE LP IGNORE RP */ + -6, /* (260) expr ::= RAISE LP raisetype COMMA nm RP */ + -1, /* (261) raisetype ::= ROLLBACK */ + -1, /* (262) raisetype ::= ABORT */ + -1, /* (263) raisetype ::= FAIL */ + -4, /* (264) cmd ::= DROP TRIGGER ifexists fullname */ + -6, /* (265) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + -3, /* (266) cmd ::= DETACH database_kw_opt expr */ + 0, /* (267) key_opt ::= */ + -2, /* (268) key_opt ::= KEY expr */ + -1, /* (269) cmd ::= REINDEX */ + -3, /* (270) cmd ::= REINDEX nm dbnm */ + -1, /* (271) cmd ::= ANALYZE */ + -3, /* (272) cmd ::= ANALYZE nm dbnm */ + -6, /* (273) cmd ::= ALTER TABLE fullname RENAME TO nm */ + -7, /* (274) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + -1, /* (275) add_column_fullname ::= fullname */ + -8, /* (276) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + -1, /* (277) cmd ::= create_vtab */ + -4, /* (278) cmd ::= create_vtab LP vtabarglist RP */ + -8, /* (279) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + 0, /* (280) vtabarg ::= */ + -1, /* (281) vtabargtoken ::= ANY */ + -3, /* (282) vtabargtoken ::= lp anylist RP */ + -1, /* (283) lp ::= LP */ + -2, /* (284) with ::= WITH wqlist */ + -3, /* (285) with ::= WITH RECURSIVE wqlist */ + -6, /* (286) wqlist ::= nm eidlist_opt AS LP select RP */ + -8, /* (287) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ + -1, /* (288) windowdefn_list ::= windowdefn */ + -3, /* (289) windowdefn_list ::= windowdefn_list COMMA windowdefn */ + -3, /* (290) windowdefn ::= nm AS window */ + -5, /* (291) window ::= LP part_opt orderby_opt frame_opt RP */ + -3, /* (292) part_opt ::= PARTITION BY nexprlist */ + 0, /* (293) part_opt ::= */ + 0, /* (294) frame_opt ::= */ + -2, /* (295) frame_opt ::= range_or_rows frame_bound_s */ + -5, /* (296) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e */ + -1, /* (297) range_or_rows ::= RANGE */ + -1, /* (298) range_or_rows ::= ROWS */ + -1, /* (299) frame_bound_s ::= frame_bound */ + -2, /* (300) frame_bound_s ::= UNBOUNDED PRECEDING */ + -1, /* (301) frame_bound_e ::= frame_bound */ + -2, /* (302) frame_bound_e ::= UNBOUNDED FOLLOWING */ + -2, /* (303) frame_bound ::= expr PRECEDING */ + -2, /* (304) frame_bound ::= CURRENT ROW */ + -2, /* (305) frame_bound ::= expr FOLLOWING */ + -2, /* (306) window_clause ::= WINDOW windowdefn_list */ + -3, /* (307) over_clause ::= filter_opt OVER window */ + -3, /* (308) over_clause ::= filter_opt OVER nm */ + 0, /* (309) filter_opt ::= */ + -5, /* (310) filter_opt ::= FILTER LP WHERE expr RP */ + -1, /* (311) input ::= cmdlist */ + -2, /* (312) cmdlist ::= cmdlist ecmd */ + -1, /* (313) cmdlist ::= ecmd */ + -1, /* (314) ecmd ::= SEMI */ + -2, /* (315) ecmd ::= cmdx SEMI */ + -2, /* (316) ecmd ::= explain cmdx */ + 0, /* (317) trans_opt ::= */ + -1, /* (318) trans_opt ::= TRANSACTION */ + -2, /* (319) trans_opt ::= TRANSACTION nm */ + -1, /* (320) savepoint_opt ::= SAVEPOINT */ + 0, /* (321) savepoint_opt ::= */ + -2, /* (322) cmd ::= create_table create_table_args */ + -4, /* (323) columnlist ::= columnlist COMMA columnname carglist */ + -2, /* (324) columnlist ::= columnname carglist */ + -1, /* (325) nm ::= ID|INDEXED */ + -1, /* (326) nm ::= STRING */ + -1, /* (327) nm ::= JOIN_KW */ + -1, /* (328) typetoken ::= typename */ + -1, /* (329) typename ::= ID|STRING */ + -1, /* (330) signed ::= plus_num */ + -1, /* (331) signed ::= minus_num */ + -2, /* (332) carglist ::= carglist ccons */ + 0, /* (333) carglist ::= */ + -2, /* (334) ccons ::= NULL onconf */ + -2, /* (335) conslist_opt ::= COMMA conslist */ + -3, /* (336) conslist ::= conslist tconscomma tcons */ + -1, /* (337) conslist ::= tcons */ + 0, /* (338) tconscomma ::= */ + -1, /* (339) defer_subclause_opt ::= defer_subclause */ + -1, /* (340) resolvetype ::= raisetype */ + -1, /* (341) selectnowith ::= oneselect */ + -1, /* (342) oneselect ::= values */ + -2, /* (343) sclp ::= selcollist COMMA */ + -1, /* (344) as ::= ID|STRING */ + -1, /* (345) expr ::= term */ + -1, /* (346) likeop ::= LIKE_KW|MATCH */ + -1, /* (347) exprlist ::= nexprlist */ + -1, /* (348) nmnum ::= plus_num */ + -1, /* (349) nmnum ::= nm */ + -1, /* (350) nmnum ::= ON */ + -1, /* (351) nmnum ::= DELETE */ + -1, /* (352) nmnum ::= DEFAULT */ + -1, /* (353) plus_num ::= INTEGER|FLOAT */ + 0, /* (354) foreach_clause ::= */ + -3, /* (355) foreach_clause ::= FOR EACH ROW */ + -1, /* (356) trnm ::= nm */ + 0, /* (357) tridxby ::= */ + -1, /* (358) database_kw_opt ::= DATABASE */ + 0, /* (359) database_kw_opt ::= */ + 0, /* (360) kwcolumn_opt ::= */ + -1, /* (361) kwcolumn_opt ::= COLUMNKW */ + -1, /* (362) vtabarglist ::= vtabarg */ + -3, /* (363) vtabarglist ::= vtabarglist COMMA vtabarg */ + -2, /* (364) vtabarg ::= vtabarg vtabargtoken */ + 0, /* (365) anylist ::= */ + -4, /* (366) anylist ::= anylist LP anylist RP */ + -2, /* (367) anylist ::= anylist ANY */ + 0, /* (368) with ::= */ }; static void yy_accept(yyParser*); /* Forward Declaration */ @@ -149483,7 +150362,7 @@ static YYACTIONTYPE yy_reduce( yymsp = yypParser->yytos; #ifndef NDEBUG if( yyTraceFILE && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){ - yysize = yyRuleInfo[yyruleno].nrhs; + yysize = yyRuleInfoNRhs[yyruleno]; if( yysize ){ fprintf(yyTraceFILE, "%sReduce %d [%s], go to state %d.\n", yyTracePrompt, @@ -149498,7 +150377,7 @@ static YYACTIONTYPE yy_reduce( /* Check that the stack is large enough to grow by a single entry ** if the RHS of the rule is empty. This ensures that there is room ** enough on the stack to push the LHS value */ - if( yyRuleInfo[yyruleno].nrhs==0 ){ + if( yyRuleInfoNRhs[yyruleno]==0 ){ #ifdef YYTRACKMAXSTACKDEPTH if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){ yypParser->yyhwm++; @@ -149548,15 +150427,15 @@ static YYACTIONTYPE yy_reduce( { sqlite3FinishCoding(pParse); } break; case 3: /* cmd ::= BEGIN transtype trans_opt */ -{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy70);} +{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy96);} break; case 4: /* transtype ::= */ -{yymsp[1].minor.yy70 = TK_DEFERRED;} +{yymsp[1].minor.yy96 = TK_DEFERRED;} break; case 5: /* transtype ::= DEFERRED */ case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6); case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7); -{yymsp[0].minor.yy70 = yymsp[0].major; /*A-overwrites-X*/} +{yymsp[0].minor.yy96 = yymsp[0].major; /*A-overwrites-X*/} break; case 8: /* cmd ::= COMMIT|END trans_opt */ case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9); @@ -149579,7 +150458,7 @@ static YYACTIONTYPE yy_reduce( break; case 13: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */ { - sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy70,0,0,yymsp[-2].minor.yy70); + sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy96,0,0,yymsp[-2].minor.yy96); } break; case 14: /* createkw ::= CREATE */ @@ -149594,32 +150473,32 @@ static YYACTIONTYPE yy_reduce( case 76: /* ifexists ::= */ yytestcase(yyruleno==76); case 93: /* distinct ::= */ yytestcase(yyruleno==93); case 226: /* collate ::= */ yytestcase(yyruleno==226); -{yymsp[1].minor.yy70 = 0;} +{yymsp[1].minor.yy96 = 0;} break; case 16: /* ifnotexists ::= IF NOT EXISTS */ -{yymsp[-2].minor.yy70 = 1;} +{yymsp[-2].minor.yy96 = 1;} break; case 17: /* temp ::= TEMP */ case 43: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==43); -{yymsp[0].minor.yy70 = 1;} +{yymsp[0].minor.yy96 = 1;} break; case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_options */ { - sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy70,0); + sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy96,0); } break; case 20: /* create_table_args ::= AS select */ { - sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy489); - sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy489); + sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy423); + sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy423); } break; case 22: /* table_options ::= WITHOUT nm */ { if( yymsp[0].minor.yy0.n==5 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"rowid",5)==0 ){ - yymsp[-1].minor.yy70 = TF_WithoutRowid | TF_NoVisibleRowid; + yymsp[-1].minor.yy96 = TF_WithoutRowid | TF_NoVisibleRowid; }else{ - yymsp[-1].minor.yy70 = 0; + yymsp[-1].minor.yy96 = 0; sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z); } } @@ -149648,7 +150527,7 @@ static YYACTIONTYPE yy_reduce( case 28: /* scanpt ::= */ { assert( yyLookahead!=YYNOCODE ); - yymsp[1].minor.yy392 = yyLookaheadToken.z; + yymsp[1].minor.yy464 = yyLookaheadToken.z; } break; case 29: /* ccons ::= CONSTRAINT nm */ @@ -149656,18 +150535,18 @@ static YYACTIONTYPE yy_reduce( {pParse->constraintName = yymsp[0].minor.yy0;} break; case 30: /* ccons ::= DEFAULT scanpt term scanpt */ -{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy18,yymsp[-2].minor.yy392,yymsp[0].minor.yy392);} +{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy490,yymsp[-2].minor.yy464,yymsp[0].minor.yy464);} break; case 31: /* ccons ::= DEFAULT LP expr RP */ -{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy18,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);} +{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy490,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);} break; case 32: /* ccons ::= DEFAULT PLUS term scanpt */ -{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy18,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy392);} +{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy490,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy464);} break; case 33: /* ccons ::= DEFAULT MINUS term scanpt */ { - Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[-1].minor.yy18, 0); - sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy392); + Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[-1].minor.yy490, 0); + sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy464); } break; case 34: /* ccons ::= DEFAULT scanpt ID|INDEXED */ @@ -149681,170 +150560,170 @@ static YYACTIONTYPE yy_reduce( } break; case 35: /* ccons ::= NOT NULL onconf */ -{sqlite3AddNotNull(pParse, yymsp[0].minor.yy70);} +{sqlite3AddNotNull(pParse, yymsp[0].minor.yy96);} break; case 36: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */ -{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy70,yymsp[0].minor.yy70,yymsp[-2].minor.yy70);} +{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy96,yymsp[0].minor.yy96,yymsp[-2].minor.yy96);} break; case 37: /* ccons ::= UNIQUE onconf */ -{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy70,0,0,0,0, +{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy96,0,0,0,0, SQLITE_IDXTYPE_UNIQUE);} break; case 38: /* ccons ::= CHECK LP expr RP */ -{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy18);} +{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy490);} break; case 39: /* ccons ::= REFERENCES nm eidlist_opt refargs */ -{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy420,yymsp[0].minor.yy70);} +{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy42,yymsp[0].minor.yy96);} break; case 40: /* ccons ::= defer_subclause */ -{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy70);} +{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy96);} break; case 41: /* ccons ::= COLLATE ID|STRING */ {sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);} break; case 44: /* refargs ::= */ -{ yymsp[1].minor.yy70 = OE_None*0x0101; /* EV: R-19803-45884 */} +{ yymsp[1].minor.yy96 = OE_None*0x0101; /* EV: R-19803-45884 */} break; case 45: /* refargs ::= refargs refarg */ -{ yymsp[-1].minor.yy70 = (yymsp[-1].minor.yy70 & ~yymsp[0].minor.yy111.mask) | yymsp[0].minor.yy111.value; } +{ yymsp[-1].minor.yy96 = (yymsp[-1].minor.yy96 & ~yymsp[0].minor.yy367.mask) | yymsp[0].minor.yy367.value; } break; case 46: /* refarg ::= MATCH nm */ -{ yymsp[-1].minor.yy111.value = 0; yymsp[-1].minor.yy111.mask = 0x000000; } +{ yymsp[-1].minor.yy367.value = 0; yymsp[-1].minor.yy367.mask = 0x000000; } break; case 47: /* refarg ::= ON INSERT refact */ -{ yymsp[-2].minor.yy111.value = 0; yymsp[-2].minor.yy111.mask = 0x000000; } +{ yymsp[-2].minor.yy367.value = 0; yymsp[-2].minor.yy367.mask = 0x000000; } break; case 48: /* refarg ::= ON DELETE refact */ -{ yymsp[-2].minor.yy111.value = yymsp[0].minor.yy70; yymsp[-2].minor.yy111.mask = 0x0000ff; } +{ yymsp[-2].minor.yy367.value = yymsp[0].minor.yy96; yymsp[-2].minor.yy367.mask = 0x0000ff; } break; case 49: /* refarg ::= ON UPDATE refact */ -{ yymsp[-2].minor.yy111.value = yymsp[0].minor.yy70<<8; yymsp[-2].minor.yy111.mask = 0x00ff00; } +{ yymsp[-2].minor.yy367.value = yymsp[0].minor.yy96<<8; yymsp[-2].minor.yy367.mask = 0x00ff00; } break; case 50: /* refact ::= SET NULL */ -{ yymsp[-1].minor.yy70 = OE_SetNull; /* EV: R-33326-45252 */} +{ yymsp[-1].minor.yy96 = OE_SetNull; /* EV: R-33326-45252 */} break; case 51: /* refact ::= SET DEFAULT */ -{ yymsp[-1].minor.yy70 = OE_SetDflt; /* EV: R-33326-45252 */} +{ yymsp[-1].minor.yy96 = OE_SetDflt; /* EV: R-33326-45252 */} break; case 52: /* refact ::= CASCADE */ -{ yymsp[0].minor.yy70 = OE_Cascade; /* EV: R-33326-45252 */} +{ yymsp[0].minor.yy96 = OE_Cascade; /* EV: R-33326-45252 */} break; case 53: /* refact ::= RESTRICT */ -{ yymsp[0].minor.yy70 = OE_Restrict; /* EV: R-33326-45252 */} +{ yymsp[0].minor.yy96 = OE_Restrict; /* EV: R-33326-45252 */} break; case 54: /* refact ::= NO ACTION */ -{ yymsp[-1].minor.yy70 = OE_None; /* EV: R-33326-45252 */} +{ yymsp[-1].minor.yy96 = OE_None; /* EV: R-33326-45252 */} break; case 55: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ -{yymsp[-2].minor.yy70 = 0;} +{yymsp[-2].minor.yy96 = 0;} break; case 56: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ case 71: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==71); case 156: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==156); -{yymsp[-1].minor.yy70 = yymsp[0].minor.yy70;} +{yymsp[-1].minor.yy96 = yymsp[0].minor.yy96;} break; case 58: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ case 75: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==75); case 198: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==198); case 201: /* in_op ::= NOT IN */ yytestcase(yyruleno==201); case 227: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==227); -{yymsp[-1].minor.yy70 = 1;} +{yymsp[-1].minor.yy96 = 1;} break; case 59: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ -{yymsp[-1].minor.yy70 = 0;} +{yymsp[-1].minor.yy96 = 0;} break; case 61: /* tconscomma ::= COMMA */ {pParse->constraintName.n = 0;} break; case 63: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */ -{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy420,yymsp[0].minor.yy70,yymsp[-2].minor.yy70,0);} +{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy42,yymsp[0].minor.yy96,yymsp[-2].minor.yy96,0);} break; case 64: /* tcons ::= UNIQUE LP sortlist RP onconf */ -{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy420,yymsp[0].minor.yy70,0,0,0,0, +{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy42,yymsp[0].minor.yy96,0,0,0,0, SQLITE_IDXTYPE_UNIQUE);} break; case 65: /* tcons ::= CHECK LP expr RP onconf */ -{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy18);} +{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy490);} break; case 66: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */ { - sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy420, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy420, yymsp[-1].minor.yy70); - sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy70); + sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy42, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy42, yymsp[-1].minor.yy96); + sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy96); } break; case 68: /* onconf ::= */ case 70: /* orconf ::= */ yytestcase(yyruleno==70); -{yymsp[1].minor.yy70 = OE_Default;} +{yymsp[1].minor.yy96 = OE_Default;} break; case 69: /* onconf ::= ON CONFLICT resolvetype */ -{yymsp[-2].minor.yy70 = yymsp[0].minor.yy70;} +{yymsp[-2].minor.yy96 = yymsp[0].minor.yy96;} break; case 72: /* resolvetype ::= IGNORE */ -{yymsp[0].minor.yy70 = OE_Ignore;} +{yymsp[0].minor.yy96 = OE_Ignore;} break; case 73: /* resolvetype ::= REPLACE */ case 157: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==157); -{yymsp[0].minor.yy70 = OE_Replace;} +{yymsp[0].minor.yy96 = OE_Replace;} break; case 74: /* cmd ::= DROP TABLE ifexists fullname */ { - sqlite3DropTable(pParse, yymsp[0].minor.yy135, 0, yymsp[-1].minor.yy70); + sqlite3DropTable(pParse, yymsp[0].minor.yy167, 0, yymsp[-1].minor.yy96); } break; case 77: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */ { - sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy420, yymsp[0].minor.yy489, yymsp[-7].minor.yy70, yymsp[-5].minor.yy70); + sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy42, yymsp[0].minor.yy423, yymsp[-7].minor.yy96, yymsp[-5].minor.yy96); } break; case 78: /* cmd ::= DROP VIEW ifexists fullname */ { - sqlite3DropTable(pParse, yymsp[0].minor.yy135, 1, yymsp[-1].minor.yy70); + sqlite3DropTable(pParse, yymsp[0].minor.yy167, 1, yymsp[-1].minor.yy96); } break; case 79: /* cmd ::= select */ { SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0}; - sqlite3Select(pParse, yymsp[0].minor.yy489, &dest); - sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy489); + sqlite3Select(pParse, yymsp[0].minor.yy423, &dest); + sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy423); } break; case 80: /* select ::= WITH wqlist selectnowith */ { - Select *p = yymsp[0].minor.yy489; + Select *p = yymsp[0].minor.yy423; if( p ){ - p->pWith = yymsp[-1].minor.yy449; + p->pWith = yymsp[-1].minor.yy499; parserDoubleLinkSelect(pParse, p); }else{ - sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy449); + sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy499); } - yymsp[-2].minor.yy489 = p; + yymsp[-2].minor.yy423 = p; } break; case 81: /* select ::= WITH RECURSIVE wqlist selectnowith */ { - Select *p = yymsp[0].minor.yy489; + Select *p = yymsp[0].minor.yy423; if( p ){ - p->pWith = yymsp[-1].minor.yy449; + p->pWith = yymsp[-1].minor.yy499; parserDoubleLinkSelect(pParse, p); }else{ - sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy449); + sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy499); } - yymsp[-3].minor.yy489 = p; + yymsp[-3].minor.yy423 = p; } break; case 82: /* select ::= selectnowith */ { - Select *p = yymsp[0].minor.yy489; + Select *p = yymsp[0].minor.yy423; if( p ){ parserDoubleLinkSelect(pParse, p); } - yymsp[0].minor.yy489 = p; /*A-overwrites-X*/ + yymsp[0].minor.yy423 = p; /*A-overwrites-X*/ } break; case 83: /* selectnowith ::= selectnowith multiselect_op oneselect */ { - Select *pRhs = yymsp[0].minor.yy489; - Select *pLhs = yymsp[-2].minor.yy489; + Select *pRhs = yymsp[0].minor.yy423; + Select *pLhs = yymsp[-2].minor.yy423; if( pRhs && pRhs->pPrior ){ SrcList *pFrom; Token x; @@ -149854,63 +150733,63 @@ static YYACTIONTYPE yy_reduce( pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0); } if( pRhs ){ - pRhs->op = (u8)yymsp[-1].minor.yy70; + pRhs->op = (u8)yymsp[-1].minor.yy96; pRhs->pPrior = pLhs; if( ALWAYS(pLhs) ) pLhs->selFlags &= ~SF_MultiValue; pRhs->selFlags &= ~SF_MultiValue; - if( yymsp[-1].minor.yy70!=TK_ALL ) pParse->hasCompound = 1; + if( yymsp[-1].minor.yy96!=TK_ALL ) pParse->hasCompound = 1; }else{ sqlite3SelectDelete(pParse->db, pLhs); } - yymsp[-2].minor.yy489 = pRhs; + yymsp[-2].minor.yy423 = pRhs; } break; case 84: /* multiselect_op ::= UNION */ case 86: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==86); -{yymsp[0].minor.yy70 = yymsp[0].major; /*A-overwrites-OP*/} +{yymsp[0].minor.yy96 = yymsp[0].major; /*A-overwrites-OP*/} break; case 85: /* multiselect_op ::= UNION ALL */ -{yymsp[-1].minor.yy70 = TK_ALL;} +{yymsp[-1].minor.yy96 = TK_ALL;} break; case 87: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ { - yymsp[-8].minor.yy489 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy420,yymsp[-5].minor.yy135,yymsp[-4].minor.yy18,yymsp[-3].minor.yy420,yymsp[-2].minor.yy18,yymsp[-1].minor.yy420,yymsp[-7].minor.yy70,yymsp[0].minor.yy18); + yymsp[-8].minor.yy423 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy42,yymsp[-5].minor.yy167,yymsp[-4].minor.yy490,yymsp[-3].minor.yy42,yymsp[-2].minor.yy490,yymsp[-1].minor.yy42,yymsp[-7].minor.yy96,yymsp[0].minor.yy490); } break; case 88: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */ { - yymsp[-9].minor.yy489 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy420,yymsp[-6].minor.yy135,yymsp[-5].minor.yy18,yymsp[-4].minor.yy420,yymsp[-3].minor.yy18,yymsp[-1].minor.yy420,yymsp[-8].minor.yy70,yymsp[0].minor.yy18); - if( yymsp[-9].minor.yy489 ){ - yymsp[-9].minor.yy489->pWinDefn = yymsp[-2].minor.yy327; + yymsp[-9].minor.yy423 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy42,yymsp[-6].minor.yy167,yymsp[-5].minor.yy490,yymsp[-4].minor.yy42,yymsp[-3].minor.yy490,yymsp[-1].minor.yy42,yymsp[-8].minor.yy96,yymsp[0].minor.yy490); + if( yymsp[-9].minor.yy423 ){ + yymsp[-9].minor.yy423->pWinDefn = yymsp[-2].minor.yy147; }else{ - sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy327); + sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy147); } } break; case 89: /* values ::= VALUES LP nexprlist RP */ { - yymsp[-3].minor.yy489 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy420,0,0,0,0,0,SF_Values,0); + yymsp[-3].minor.yy423 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy42,0,0,0,0,0,SF_Values,0); } break; case 90: /* values ::= values COMMA LP nexprlist RP */ { - Select *pRight, *pLeft = yymsp[-4].minor.yy489; - pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy420,0,0,0,0,0,SF_Values|SF_MultiValue,0); + Select *pRight, *pLeft = yymsp[-4].minor.yy423; + pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy42,0,0,0,0,0,SF_Values|SF_MultiValue,0); if( ALWAYS(pLeft) ) pLeft->selFlags &= ~SF_MultiValue; if( pRight ){ pRight->op = TK_ALL; pRight->pPrior = pLeft; - yymsp[-4].minor.yy489 = pRight; + yymsp[-4].minor.yy423 = pRight; }else{ - yymsp[-4].minor.yy489 = pLeft; + yymsp[-4].minor.yy423 = pLeft; } } break; case 91: /* distinct ::= DISTINCT */ -{yymsp[0].minor.yy70 = SF_Distinct;} +{yymsp[0].minor.yy96 = SF_Distinct;} break; case 92: /* distinct ::= ALL */ -{yymsp[0].minor.yy70 = SF_All;} +{yymsp[0].minor.yy96 = SF_All;} break; case 94: /* sclp ::= */ case 127: /* orderby_opt ::= */ yytestcase(yyruleno==127); @@ -149918,19 +150797,19 @@ static YYACTIONTYPE yy_reduce( case 214: /* exprlist ::= */ yytestcase(yyruleno==214); case 217: /* paren_exprlist ::= */ yytestcase(yyruleno==217); case 222: /* eidlist_opt ::= */ yytestcase(yyruleno==222); -{yymsp[1].minor.yy420 = 0;} +{yymsp[1].minor.yy42 = 0;} break; case 95: /* selcollist ::= sclp scanpt expr scanpt as */ { - yymsp[-4].minor.yy420 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy420, yymsp[-2].minor.yy18); - if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy420, &yymsp[0].minor.yy0, 1); - sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy420,yymsp[-3].minor.yy392,yymsp[-1].minor.yy392); + yymsp[-4].minor.yy42 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy42, yymsp[-2].minor.yy490); + if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy42, &yymsp[0].minor.yy0, 1); + sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy42,yymsp[-3].minor.yy464,yymsp[-1].minor.yy464); } break; case 96: /* selcollist ::= sclp scanpt STAR */ { Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0); - yymsp[-2].minor.yy420 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy420, p); + yymsp[-2].minor.yy42 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy42, p); } break; case 97: /* selcollist ::= sclp scanpt nm DOT STAR */ @@ -149938,70 +150817,76 @@ static YYACTIONTYPE yy_reduce( Expr *pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0); Expr *pLeft = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1); Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight); - yymsp[-4].minor.yy420 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy420, pDot); + yymsp[-4].minor.yy42 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy42, pDot); } break; case 98: /* as ::= AS nm */ case 109: /* dbnm ::= DOT nm */ yytestcase(yyruleno==109); - case 236: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==236); - case 237: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==237); + case 238: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==238); + case 239: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==239); {yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;} break; case 100: /* from ::= */ -{yymsp[1].minor.yy135 = sqlite3DbMallocZero(pParse->db, sizeof(*yymsp[1].minor.yy135));} +{yymsp[1].minor.yy167 = sqlite3DbMallocZero(pParse->db, sizeof(*yymsp[1].minor.yy167));} break; case 101: /* from ::= FROM seltablist */ { - yymsp[-1].minor.yy135 = yymsp[0].minor.yy135; - sqlite3SrcListShiftJoinType(yymsp[-1].minor.yy135); + yymsp[-1].minor.yy167 = yymsp[0].minor.yy167; + sqlite3SrcListShiftJoinType(yymsp[-1].minor.yy167); } break; case 102: /* stl_prefix ::= seltablist joinop */ { - if( ALWAYS(yymsp[-1].minor.yy135 && yymsp[-1].minor.yy135->nSrc>0) ) yymsp[-1].minor.yy135->a[yymsp[-1].minor.yy135->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy70; + if( ALWAYS(yymsp[-1].minor.yy167 && yymsp[-1].minor.yy167->nSrc>0) ) yymsp[-1].minor.yy167->a[yymsp[-1].minor.yy167->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy96; } break; case 103: /* stl_prefix ::= */ -{yymsp[1].minor.yy135 = 0;} +{yymsp[1].minor.yy167 = 0;} break; case 104: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ { - yymsp[-6].minor.yy135 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy135,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy18,yymsp[0].minor.yy48); - sqlite3SrcListIndexedBy(pParse, yymsp[-6].minor.yy135, &yymsp[-2].minor.yy0); + yymsp[-6].minor.yy167 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy167,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy490,yymsp[0].minor.yy336); + sqlite3SrcListIndexedBy(pParse, yymsp[-6].minor.yy167, &yymsp[-2].minor.yy0); } break; case 105: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */ { - yymsp[-8].minor.yy135 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-8].minor.yy135,&yymsp[-7].minor.yy0,&yymsp[-6].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy18,yymsp[0].minor.yy48); - sqlite3SrcListFuncArgs(pParse, yymsp[-8].minor.yy135, yymsp[-4].minor.yy420); + yymsp[-8].minor.yy167 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-8].minor.yy167,&yymsp[-7].minor.yy0,&yymsp[-6].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy490,yymsp[0].minor.yy336); + sqlite3SrcListFuncArgs(pParse, yymsp[-8].minor.yy167, yymsp[-4].minor.yy42); } break; case 106: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */ { - yymsp[-6].minor.yy135 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy135,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy489,yymsp[-1].minor.yy18,yymsp[0].minor.yy48); + yymsp[-6].minor.yy167 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy167,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy423,yymsp[-1].minor.yy490,yymsp[0].minor.yy336); } break; case 107: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ { - if( yymsp[-6].minor.yy135==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy18==0 && yymsp[0].minor.yy48==0 ){ - yymsp[-6].minor.yy135 = yymsp[-4].minor.yy135; - }else if( yymsp[-4].minor.yy135->nSrc==1 ){ - yymsp[-6].minor.yy135 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy135,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy18,yymsp[0].minor.yy48); - if( yymsp[-6].minor.yy135 ){ - struct SrcList_item *pNew = &yymsp[-6].minor.yy135->a[yymsp[-6].minor.yy135->nSrc-1]; - struct SrcList_item *pOld = yymsp[-4].minor.yy135->a; + if( yymsp[-6].minor.yy167==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy490==0 && yymsp[0].minor.yy336==0 ){ + yymsp[-6].minor.yy167 = yymsp[-4].minor.yy167; + }else if( yymsp[-4].minor.yy167->nSrc==1 ){ + yymsp[-6].minor.yy167 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy167,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy490,yymsp[0].minor.yy336); + if( yymsp[-6].minor.yy167 ){ + struct SrcList_item *pNew = &yymsp[-6].minor.yy167->a[yymsp[-6].minor.yy167->nSrc-1]; + struct SrcList_item *pOld = yymsp[-4].minor.yy167->a; pNew->zName = pOld->zName; pNew->zDatabase = pOld->zDatabase; pNew->pSelect = pOld->pSelect; + if( pOld->fg.isTabFunc ){ + pNew->u1.pFuncArg = pOld->u1.pFuncArg; + pOld->u1.pFuncArg = 0; + pOld->fg.isTabFunc = 0; + pNew->fg.isTabFunc = 1; + } pOld->zName = pOld->zDatabase = 0; pOld->pSelect = 0; } - sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy135); + sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy167); }else{ Select *pSubquery; - sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy135); - pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy135,0,0,0,0,SF_NestedFrom,0); - yymsp[-6].minor.yy135 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy135,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy18,yymsp[0].minor.yy48); + sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy167); + pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy167,0,0,0,0,SF_NestedFrom,0); + yymsp[-6].minor.yy167 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy167,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy490,yymsp[0].minor.yy336); } } break; @@ -150011,53 +150896,54 @@ static YYACTIONTYPE yy_reduce( break; case 110: /* fullname ::= nm */ { - yylhsminor.yy135 = sqlite3SrcListAppend(pParse->db,0,&yymsp[0].minor.yy0,0); - if( IN_RENAME_OBJECT && yylhsminor.yy135 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy135->a[0].zName, &yymsp[0].minor.yy0); + yylhsminor.yy167 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); + if( IN_RENAME_OBJECT && yylhsminor.yy167 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy167->a[0].zName, &yymsp[0].minor.yy0); } - yymsp[0].minor.yy135 = yylhsminor.yy135; + yymsp[0].minor.yy167 = yylhsminor.yy167; break; case 111: /* fullname ::= nm DOT nm */ { - yylhsminor.yy135 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); - if( IN_RENAME_OBJECT && yylhsminor.yy135 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy135->a[0].zName, &yymsp[0].minor.yy0); + yylhsminor.yy167 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); + if( IN_RENAME_OBJECT && yylhsminor.yy167 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy167->a[0].zName, &yymsp[0].minor.yy0); } - yymsp[-2].minor.yy135 = yylhsminor.yy135; + yymsp[-2].minor.yy167 = yylhsminor.yy167; break; case 112: /* xfullname ::= nm */ -{yymsp[0].minor.yy135 = sqlite3SrcListAppend(pParse->db,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/} +{yymsp[0].minor.yy167 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/} break; case 113: /* xfullname ::= nm DOT nm */ -{yymsp[-2].minor.yy135 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/} +{yymsp[-2].minor.yy167 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/} break; case 114: /* xfullname ::= nm DOT nm AS nm */ { - yymsp[-4].minor.yy135 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/ - if( yymsp[-4].minor.yy135 ) yymsp[-4].minor.yy135->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); + yymsp[-4].minor.yy167 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/ + if( yymsp[-4].minor.yy167 ) yymsp[-4].minor.yy167->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); } break; case 115: /* xfullname ::= nm AS nm */ { - yymsp[-2].minor.yy135 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/ - if( yymsp[-2].minor.yy135 ) yymsp[-2].minor.yy135->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); + yymsp[-2].minor.yy167 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/ + if( yymsp[-2].minor.yy167 ) yymsp[-2].minor.yy167->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0); } break; case 116: /* joinop ::= COMMA|JOIN */ -{ yymsp[0].minor.yy70 = JT_INNER; } +{ yymsp[0].minor.yy96 = JT_INNER; } break; case 117: /* joinop ::= JOIN_KW JOIN */ -{yymsp[-1].minor.yy70 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/} +{yymsp[-1].minor.yy96 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/} break; case 118: /* joinop ::= JOIN_KW nm JOIN */ -{yymsp[-2].minor.yy70 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/} +{yymsp[-2].minor.yy96 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/} break; case 119: /* joinop ::= JOIN_KW nm nm JOIN */ -{yymsp[-3].minor.yy70 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/} +{yymsp[-3].minor.yy96 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/} break; case 120: /* on_opt ::= ON expr */ case 137: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==137); case 144: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==144); case 210: /* case_else ::= ELSE expr */ yytestcase(yyruleno==210); -{yymsp[-1].minor.yy18 = yymsp[0].minor.yy18;} + case 231: /* vinto ::= INTO expr */ yytestcase(yyruleno==231); +{yymsp[-1].minor.yy490 = yymsp[0].minor.yy490;} break; case 121: /* on_opt ::= */ case 136: /* having_opt ::= */ yytestcase(yyruleno==136); @@ -150065,7 +150951,8 @@ static YYACTIONTYPE yy_reduce( case 143: /* where_opt ::= */ yytestcase(yyruleno==143); case 211: /* case_else ::= */ yytestcase(yyruleno==211); case 213: /* case_operand ::= */ yytestcase(yyruleno==213); -{yymsp[1].minor.yy18 = 0;} + case 232: /* vinto ::= */ yytestcase(yyruleno==232); +{yymsp[1].minor.yy490 = 0;} break; case 123: /* indexed_opt ::= INDEXED BY nm */ {yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;} @@ -150074,119 +150961,119 @@ static YYACTIONTYPE yy_reduce( {yymsp[-1].minor.yy0.z=0; yymsp[-1].minor.yy0.n=1;} break; case 125: /* using_opt ::= USING LP idlist RP */ -{yymsp[-3].minor.yy48 = yymsp[-1].minor.yy48;} +{yymsp[-3].minor.yy336 = yymsp[-1].minor.yy336;} break; case 126: /* using_opt ::= */ case 158: /* idlist_opt ::= */ yytestcase(yyruleno==158); -{yymsp[1].minor.yy48 = 0;} +{yymsp[1].minor.yy336 = 0;} break; case 128: /* orderby_opt ::= ORDER BY sortlist */ case 135: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==135); -{yymsp[-2].minor.yy420 = yymsp[0].minor.yy420;} +{yymsp[-2].minor.yy42 = yymsp[0].minor.yy42;} break; case 129: /* sortlist ::= sortlist COMMA expr sortorder */ { - yymsp[-3].minor.yy420 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy420,yymsp[-1].minor.yy18); - sqlite3ExprListSetSortOrder(yymsp[-3].minor.yy420,yymsp[0].minor.yy70); + yymsp[-3].minor.yy42 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy42,yymsp[-1].minor.yy490); + sqlite3ExprListSetSortOrder(yymsp[-3].minor.yy42,yymsp[0].minor.yy96); } break; case 130: /* sortlist ::= expr sortorder */ { - yymsp[-1].minor.yy420 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy18); /*A-overwrites-Y*/ - sqlite3ExprListSetSortOrder(yymsp[-1].minor.yy420,yymsp[0].minor.yy70); + yymsp[-1].minor.yy42 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy490); /*A-overwrites-Y*/ + sqlite3ExprListSetSortOrder(yymsp[-1].minor.yy42,yymsp[0].minor.yy96); } break; case 131: /* sortorder ::= ASC */ -{yymsp[0].minor.yy70 = SQLITE_SO_ASC;} +{yymsp[0].minor.yy96 = SQLITE_SO_ASC;} break; case 132: /* sortorder ::= DESC */ -{yymsp[0].minor.yy70 = SQLITE_SO_DESC;} +{yymsp[0].minor.yy96 = SQLITE_SO_DESC;} break; case 133: /* sortorder ::= */ -{yymsp[1].minor.yy70 = SQLITE_SO_UNDEFINED;} +{yymsp[1].minor.yy96 = SQLITE_SO_UNDEFINED;} break; case 139: /* limit_opt ::= LIMIT expr */ -{yymsp[-1].minor.yy18 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy18,0);} +{yymsp[-1].minor.yy490 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy490,0);} break; case 140: /* limit_opt ::= LIMIT expr OFFSET expr */ -{yymsp[-3].minor.yy18 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy18,yymsp[0].minor.yy18);} +{yymsp[-3].minor.yy490 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy490,yymsp[0].minor.yy490);} break; case 141: /* limit_opt ::= LIMIT expr COMMA expr */ -{yymsp[-3].minor.yy18 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy18,yymsp[-2].minor.yy18);} +{yymsp[-3].minor.yy490 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy490,yymsp[-2].minor.yy490);} break; case 142: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt */ { - sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy135, &yymsp[-1].minor.yy0); - sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy135,yymsp[0].minor.yy18,0,0); + sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy167, &yymsp[-1].minor.yy0); + sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy167,yymsp[0].minor.yy490,0,0); } break; case 145: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */ { - sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy135, &yymsp[-3].minor.yy0); - sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy420,"set list"); - sqlite3Update(pParse,yymsp[-4].minor.yy135,yymsp[-1].minor.yy420,yymsp[0].minor.yy18,yymsp[-5].minor.yy70,0,0,0); + sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy167, &yymsp[-3].minor.yy0); + sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy42,"set list"); + sqlite3Update(pParse,yymsp[-4].minor.yy167,yymsp[-1].minor.yy42,yymsp[0].minor.yy490,yymsp[-5].minor.yy96,0,0,0); } break; case 146: /* setlist ::= setlist COMMA nm EQ expr */ { - yymsp[-4].minor.yy420 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy420, yymsp[0].minor.yy18); - sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy420, &yymsp[-2].minor.yy0, 1); + yymsp[-4].minor.yy42 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy42, yymsp[0].minor.yy490); + sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy42, &yymsp[-2].minor.yy0, 1); } break; case 147: /* setlist ::= setlist COMMA LP idlist RP EQ expr */ { - yymsp[-6].minor.yy420 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy420, yymsp[-3].minor.yy48, yymsp[0].minor.yy18); + yymsp[-6].minor.yy42 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy42, yymsp[-3].minor.yy336, yymsp[0].minor.yy490); } break; case 148: /* setlist ::= nm EQ expr */ { - yylhsminor.yy420 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy18); - sqlite3ExprListSetName(pParse, yylhsminor.yy420, &yymsp[-2].minor.yy0, 1); + yylhsminor.yy42 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy490); + sqlite3ExprListSetName(pParse, yylhsminor.yy42, &yymsp[-2].minor.yy0, 1); } - yymsp[-2].minor.yy420 = yylhsminor.yy420; + yymsp[-2].minor.yy42 = yylhsminor.yy42; break; case 149: /* setlist ::= LP idlist RP EQ expr */ { - yymsp[-4].minor.yy420 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy48, yymsp[0].minor.yy18); + yymsp[-4].minor.yy42 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy336, yymsp[0].minor.yy490); } break; case 150: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */ { - sqlite3Insert(pParse, yymsp[-3].minor.yy135, yymsp[-1].minor.yy489, yymsp[-2].minor.yy48, yymsp[-5].minor.yy70, yymsp[0].minor.yy340); + sqlite3Insert(pParse, yymsp[-3].minor.yy167, yymsp[-1].minor.yy423, yymsp[-2].minor.yy336, yymsp[-5].minor.yy96, yymsp[0].minor.yy266); } break; case 151: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */ { - sqlite3Insert(pParse, yymsp[-3].minor.yy135, 0, yymsp[-2].minor.yy48, yymsp[-5].minor.yy70, 0); + sqlite3Insert(pParse, yymsp[-3].minor.yy167, 0, yymsp[-2].minor.yy336, yymsp[-5].minor.yy96, 0); } break; case 152: /* upsert ::= */ -{ yymsp[1].minor.yy340 = 0; } +{ yymsp[1].minor.yy266 = 0; } break; case 153: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */ -{ yymsp[-10].minor.yy340 = sqlite3UpsertNew(pParse->db,yymsp[-7].minor.yy420,yymsp[-5].minor.yy18,yymsp[-1].minor.yy420,yymsp[0].minor.yy18);} +{ yymsp[-10].minor.yy266 = sqlite3UpsertNew(pParse->db,yymsp[-7].minor.yy42,yymsp[-5].minor.yy490,yymsp[-1].minor.yy42,yymsp[0].minor.yy490);} break; case 154: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */ -{ yymsp[-7].minor.yy340 = sqlite3UpsertNew(pParse->db,yymsp[-4].minor.yy420,yymsp[-2].minor.yy18,0,0); } +{ yymsp[-7].minor.yy266 = sqlite3UpsertNew(pParse->db,yymsp[-4].minor.yy42,yymsp[-2].minor.yy490,0,0); } break; case 155: /* upsert ::= ON CONFLICT DO NOTHING */ -{ yymsp[-3].minor.yy340 = sqlite3UpsertNew(pParse->db,0,0,0,0); } +{ yymsp[-3].minor.yy266 = sqlite3UpsertNew(pParse->db,0,0,0,0); } break; case 159: /* idlist_opt ::= LP idlist RP */ -{yymsp[-2].minor.yy48 = yymsp[-1].minor.yy48;} +{yymsp[-2].minor.yy336 = yymsp[-1].minor.yy336;} break; case 160: /* idlist ::= idlist COMMA nm */ -{yymsp[-2].minor.yy48 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy48,&yymsp[0].minor.yy0);} +{yymsp[-2].minor.yy336 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy336,&yymsp[0].minor.yy0);} break; case 161: /* idlist ::= nm */ -{yymsp[0].minor.yy48 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/} +{yymsp[0].minor.yy336 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/} break; case 162: /* expr ::= LP expr RP */ -{yymsp[-2].minor.yy18 = yymsp[-1].minor.yy18;} +{yymsp[-2].minor.yy490 = yymsp[-1].minor.yy490;} break; case 163: /* expr ::= ID|INDEXED */ case 164: /* expr ::= JOIN_KW */ yytestcase(yyruleno==164); -{yymsp[0].minor.yy18=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/} +{yymsp[0].minor.yy490=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/} break; case 165: /* expr ::= nm DOT nm */ { @@ -150196,9 +151083,9 @@ static YYACTIONTYPE yy_reduce( sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[0].minor.yy0); sqlite3RenameTokenMap(pParse, (void*)temp1, &yymsp[-2].minor.yy0); } - yylhsminor.yy18 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2); + yylhsminor.yy490 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2); } - yymsp[-2].minor.yy18 = yylhsminor.yy18; + yymsp[-2].minor.yy490 = yylhsminor.yy490; break; case 166: /* expr ::= nm DOT nm DOT nm */ { @@ -150210,26 +151097,26 @@ static YYACTIONTYPE yy_reduce( sqlite3RenameTokenMap(pParse, (void*)temp3, &yymsp[0].minor.yy0); sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[-2].minor.yy0); } - yylhsminor.yy18 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4); + yylhsminor.yy490 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4); } - yymsp[-4].minor.yy18 = yylhsminor.yy18; + yymsp[-4].minor.yy490 = yylhsminor.yy490; break; case 167: /* term ::= NULL|FLOAT|BLOB */ case 168: /* term ::= STRING */ yytestcase(yyruleno==168); -{yymsp[0].minor.yy18=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/} +{yymsp[0].minor.yy490=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/} break; case 169: /* term ::= INTEGER */ { - yylhsminor.yy18 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1); + yylhsminor.yy490 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1); } - yymsp[0].minor.yy18 = yylhsminor.yy18; + yymsp[0].minor.yy490 = yylhsminor.yy490; break; case 170: /* expr ::= VARIABLE */ { if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){ u32 n = yymsp[0].minor.yy0.n; - yymsp[0].minor.yy18 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0); - sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy18, n); + yymsp[0].minor.yy490 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0); + sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy490, n); }else{ /* When doing a nested parse, one can include terms in an expression ** that look like this: #1 #2 ... These terms refer to registers @@ -150238,63 +151125,63 @@ static YYACTIONTYPE yy_reduce( assert( t.n>=2 ); if( pParse->nested==0 ){ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t); - yymsp[0].minor.yy18 = 0; + yymsp[0].minor.yy490 = 0; }else{ - yymsp[0].minor.yy18 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0); - if( yymsp[0].minor.yy18 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy18->iTable); + yymsp[0].minor.yy490 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0); + if( yymsp[0].minor.yy490 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy490->iTable); } } } break; case 171: /* expr ::= expr COLLATE ID|STRING */ { - yymsp[-2].minor.yy18 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy18, &yymsp[0].minor.yy0, 1); + yymsp[-2].minor.yy490 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy490, &yymsp[0].minor.yy0, 1); } break; case 172: /* expr ::= CAST LP expr AS typetoken RP */ { - yymsp[-5].minor.yy18 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1); - sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy18, yymsp[-3].minor.yy18, 0); + yymsp[-5].minor.yy490 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1); + sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy490, yymsp[-3].minor.yy490, 0); } break; case 173: /* expr ::= ID|INDEXED LP distinct exprlist RP */ { - yylhsminor.yy18 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy420, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy70); + yylhsminor.yy490 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy42, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy96); } - yymsp[-4].minor.yy18 = yylhsminor.yy18; + yymsp[-4].minor.yy490 = yylhsminor.yy490; break; case 174: /* expr ::= ID|INDEXED LP STAR RP */ { - yylhsminor.yy18 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); + yylhsminor.yy490 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0); } - yymsp[-3].minor.yy18 = yylhsminor.yy18; + yymsp[-3].minor.yy490 = yylhsminor.yy490; break; case 175: /* expr ::= ID|INDEXED LP distinct exprlist RP over_clause */ { - yylhsminor.yy18 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy420, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy70); - sqlite3WindowAttach(pParse, yylhsminor.yy18, yymsp[0].minor.yy327); + yylhsminor.yy490 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy42, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy96); + sqlite3WindowAttach(pParse, yylhsminor.yy490, yymsp[0].minor.yy147); } - yymsp[-5].minor.yy18 = yylhsminor.yy18; + yymsp[-5].minor.yy490 = yylhsminor.yy490; break; case 176: /* expr ::= ID|INDEXED LP STAR RP over_clause */ { - yylhsminor.yy18 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0); - sqlite3WindowAttach(pParse, yylhsminor.yy18, yymsp[0].minor.yy327); + yylhsminor.yy490 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0); + sqlite3WindowAttach(pParse, yylhsminor.yy490, yymsp[0].minor.yy147); } - yymsp[-4].minor.yy18 = yylhsminor.yy18; + yymsp[-4].minor.yy490 = yylhsminor.yy490; break; case 177: /* term ::= CTIME_KW */ { - yylhsminor.yy18 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); + yylhsminor.yy490 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0); } - yymsp[0].minor.yy18 = yylhsminor.yy18; + yymsp[0].minor.yy490 = yylhsminor.yy490; break; case 178: /* expr ::= LP nexprlist COMMA expr RP */ { - ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy420, yymsp[-1].minor.yy18); - yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); - if( yymsp[-4].minor.yy18 ){ - yymsp[-4].minor.yy18->x.pList = pList; + ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy42, yymsp[-1].minor.yy490); + yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0); + if( yymsp[-4].minor.yy490 ){ + yymsp[-4].minor.yy490->x.pList = pList; }else{ sqlite3ExprListDelete(pParse->db, pList); } @@ -150308,7 +151195,7 @@ static YYACTIONTYPE yy_reduce( case 184: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==184); case 185: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==185); case 186: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==186); -{yymsp[-2].minor.yy18=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy18,yymsp[0].minor.yy18);} +{yymsp[-2].minor.yy490=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy490,yymsp[0].minor.yy490);} break; case 187: /* likeop ::= NOT LIKE_KW|MATCH */ {yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/} @@ -150318,11 +151205,11 @@ static YYACTIONTYPE yy_reduce( ExprList *pList; int bNot = yymsp[-1].minor.yy0.n & 0x80000000; yymsp[-1].minor.yy0.n &= 0x7fffffff; - pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy18); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy18); - yymsp[-2].minor.yy18 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); - if( bNot ) yymsp[-2].minor.yy18 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy18, 0); - if( yymsp[-2].minor.yy18 ) yymsp[-2].minor.yy18->flags |= EP_InfixFunc; + pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy490); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy490); + yymsp[-2].minor.yy490 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0); + if( bNot ) yymsp[-2].minor.yy490 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy490, 0); + if( yymsp[-2].minor.yy490 ) yymsp[-2].minor.yy490->flags |= EP_InfixFunc; } break; case 189: /* expr ::= expr likeop expr ESCAPE expr */ @@ -150330,62 +151217,62 @@ static YYACTIONTYPE yy_reduce( ExprList *pList; int bNot = yymsp[-3].minor.yy0.n & 0x80000000; yymsp[-3].minor.yy0.n &= 0x7fffffff; - pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy18); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy18); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy18); - yymsp[-4].minor.yy18 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0); - if( bNot ) yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy18, 0); - if( yymsp[-4].minor.yy18 ) yymsp[-4].minor.yy18->flags |= EP_InfixFunc; + pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy490); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy490); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy490); + yymsp[-4].minor.yy490 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0); + if( bNot ) yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy490, 0); + if( yymsp[-4].minor.yy490 ) yymsp[-4].minor.yy490->flags |= EP_InfixFunc; } break; case 190: /* expr ::= expr ISNULL|NOTNULL */ -{yymsp[-1].minor.yy18 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy18,0);} +{yymsp[-1].minor.yy490 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy490,0);} break; case 191: /* expr ::= expr NOT NULL */ -{yymsp[-2].minor.yy18 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy18,0);} +{yymsp[-2].minor.yy490 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy490,0);} break; case 192: /* expr ::= expr IS expr */ { - yymsp[-2].minor.yy18 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy18,yymsp[0].minor.yy18); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy18, yymsp[-2].minor.yy18, TK_ISNULL); + yymsp[-2].minor.yy490 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy490,yymsp[0].minor.yy490); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy490, yymsp[-2].minor.yy490, TK_ISNULL); } break; case 193: /* expr ::= expr IS NOT expr */ { - yymsp[-3].minor.yy18 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy18,yymsp[0].minor.yy18); - binaryToUnaryIfNull(pParse, yymsp[0].minor.yy18, yymsp[-3].minor.yy18, TK_NOTNULL); + yymsp[-3].minor.yy490 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy490,yymsp[0].minor.yy490); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy490, yymsp[-3].minor.yy490, TK_NOTNULL); } break; case 194: /* expr ::= NOT expr */ case 195: /* expr ::= BITNOT expr */ yytestcase(yyruleno==195); -{yymsp[-1].minor.yy18 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy18, 0);/*A-overwrites-B*/} +{yymsp[-1].minor.yy490 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy490, 0);/*A-overwrites-B*/} break; case 196: /* expr ::= PLUS|MINUS expr */ { - yymsp[-1].minor.yy18 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy18, 0); + yymsp[-1].minor.yy490 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy490, 0); /*A-overwrites-B*/ } break; case 197: /* between_op ::= BETWEEN */ case 200: /* in_op ::= IN */ yytestcase(yyruleno==200); -{yymsp[0].minor.yy70 = 0;} +{yymsp[0].minor.yy96 = 0;} break; case 199: /* expr ::= expr between_op expr AND expr */ { - ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy18); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy18); - yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy18, 0); - if( yymsp[-4].minor.yy18 ){ - yymsp[-4].minor.yy18->x.pList = pList; + ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy490); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy490); + yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy490, 0); + if( yymsp[-4].minor.yy490 ){ + yymsp[-4].minor.yy490->x.pList = pList; }else{ sqlite3ExprListDelete(pParse->db, pList); } - if( yymsp[-3].minor.yy70 ) yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy18, 0); + if( yymsp[-3].minor.yy96 ) yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy490, 0); } break; case 202: /* expr ::= expr in_op LP exprlist RP */ { - if( yymsp[-1].minor.yy420==0 ){ + if( yymsp[-1].minor.yy42==0 ){ /* Expressions of the form ** ** expr1 IN () @@ -150394,9 +151281,11 @@ static YYACTIONTYPE yy_reduce( ** simplify to constants 0 (false) and 1 (true), respectively, ** regardless of the value of expr1. */ - sqlite3ExprDelete(pParse->db, yymsp[-4].minor.yy18); - yymsp[-4].minor.yy18 = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[yymsp[-3].minor.yy70],1); - }else if( yymsp[-1].minor.yy420->nExpr==1 ){ + if( IN_RENAME_OBJECT==0 ){ + sqlite3ExprDelete(pParse->db, yymsp[-4].minor.yy490); + yymsp[-4].minor.yy490 = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[yymsp[-3].minor.yy96],1); + } + }else if( yymsp[-1].minor.yy42->nExpr==1 ){ /* Expressions of the form: ** ** expr1 IN (?1) @@ -150413,199 +151302,199 @@ static YYACTIONTYPE yy_reduce( ** affinity or the collating sequence to use for comparison. Otherwise, ** the semantics would be subtly different from IN or NOT IN. */ - Expr *pRHS = yymsp[-1].minor.yy420->a[0].pExpr; - yymsp[-1].minor.yy420->a[0].pExpr = 0; - sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy420); + Expr *pRHS = yymsp[-1].minor.yy42->a[0].pExpr; + yymsp[-1].minor.yy42->a[0].pExpr = 0; + sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy42); /* pRHS cannot be NULL because a malloc error would have been detected ** before now and control would have never reached this point */ if( ALWAYS(pRHS) ){ pRHS->flags &= ~EP_Collate; pRHS->flags |= EP_Generic; } - yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, yymsp[-3].minor.yy70 ? TK_NE : TK_EQ, yymsp[-4].minor.yy18, pRHS); + yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, yymsp[-3].minor.yy96 ? TK_NE : TK_EQ, yymsp[-4].minor.yy490, pRHS); }else{ - yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy18, 0); - if( yymsp[-4].minor.yy18 ){ - yymsp[-4].minor.yy18->x.pList = yymsp[-1].minor.yy420; - sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy18); + yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy490, 0); + if( yymsp[-4].minor.yy490 ){ + yymsp[-4].minor.yy490->x.pList = yymsp[-1].minor.yy42; + sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy490); }else{ - sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy420); + sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy42); } - if( yymsp[-3].minor.yy70 ) yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy18, 0); + if( yymsp[-3].minor.yy96 ) yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy490, 0); } } break; case 203: /* expr ::= LP select RP */ { - yymsp[-2].minor.yy18 = sqlite3PExpr(pParse, TK_SELECT, 0, 0); - sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy18, yymsp[-1].minor.yy489); + yymsp[-2].minor.yy490 = sqlite3PExpr(pParse, TK_SELECT, 0, 0); + sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy490, yymsp[-1].minor.yy423); } break; case 204: /* expr ::= expr in_op LP select RP */ { - yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy18, 0); - sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy18, yymsp[-1].minor.yy489); - if( yymsp[-3].minor.yy70 ) yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy18, 0); + yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy490, 0); + sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy490, yymsp[-1].minor.yy423); + if( yymsp[-3].minor.yy96 ) yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy490, 0); } break; case 205: /* expr ::= expr in_op nm dbnm paren_exprlist */ { - SrcList *pSrc = sqlite3SrcListAppend(pParse->db, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0); + SrcList *pSrc = sqlite3SrcListAppend(pParse, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0); Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0); - if( yymsp[0].minor.yy420 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy420); - yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy18, 0); - sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy18, pSelect); - if( yymsp[-3].minor.yy70 ) yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy18, 0); + if( yymsp[0].minor.yy42 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy42); + yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy490, 0); + sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy490, pSelect); + if( yymsp[-3].minor.yy96 ) yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy490, 0); } break; case 206: /* expr ::= EXISTS LP select RP */ { Expr *p; - p = yymsp[-3].minor.yy18 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0); - sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy489); + p = yymsp[-3].minor.yy490 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0); + sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy423); } break; case 207: /* expr ::= CASE case_operand case_exprlist case_else END */ { - yymsp[-4].minor.yy18 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy18, 0); - if( yymsp[-4].minor.yy18 ){ - yymsp[-4].minor.yy18->x.pList = yymsp[-1].minor.yy18 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy420,yymsp[-1].minor.yy18) : yymsp[-2].minor.yy420; - sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy18); + yymsp[-4].minor.yy490 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy490, 0); + if( yymsp[-4].minor.yy490 ){ + yymsp[-4].minor.yy490->x.pList = yymsp[-1].minor.yy490 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy42,yymsp[-1].minor.yy490) : yymsp[-2].minor.yy42; + sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy490); }else{ - sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy420); - sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy18); + sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy42); + sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy490); } } break; case 208: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ { - yymsp[-4].minor.yy420 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy420, yymsp[-2].minor.yy18); - yymsp[-4].minor.yy420 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy420, yymsp[0].minor.yy18); + yymsp[-4].minor.yy42 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy42, yymsp[-2].minor.yy490); + yymsp[-4].minor.yy42 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy42, yymsp[0].minor.yy490); } break; case 209: /* case_exprlist ::= WHEN expr THEN expr */ { - yymsp[-3].minor.yy420 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy18); - yymsp[-3].minor.yy420 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy420, yymsp[0].minor.yy18); + yymsp[-3].minor.yy42 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy490); + yymsp[-3].minor.yy42 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy42, yymsp[0].minor.yy490); } break; case 212: /* case_operand ::= expr */ -{yymsp[0].minor.yy18 = yymsp[0].minor.yy18; /*A-overwrites-X*/} +{yymsp[0].minor.yy490 = yymsp[0].minor.yy490; /*A-overwrites-X*/} break; case 215: /* nexprlist ::= nexprlist COMMA expr */ -{yymsp[-2].minor.yy420 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy420,yymsp[0].minor.yy18);} +{yymsp[-2].minor.yy42 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy42,yymsp[0].minor.yy490);} break; case 216: /* nexprlist ::= expr */ -{yymsp[0].minor.yy420 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy18); /*A-overwrites-Y*/} +{yymsp[0].minor.yy42 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy490); /*A-overwrites-Y*/} break; case 218: /* paren_exprlist ::= LP exprlist RP */ case 223: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==223); -{yymsp[-2].minor.yy420 = yymsp[-1].minor.yy420;} +{yymsp[-2].minor.yy42 = yymsp[-1].minor.yy42;} break; case 219: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */ { sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, - sqlite3SrcListAppend(pParse->db,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy420, yymsp[-10].minor.yy70, - &yymsp[-11].minor.yy0, yymsp[0].minor.yy18, SQLITE_SO_ASC, yymsp[-8].minor.yy70, SQLITE_IDXTYPE_APPDEF); + sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy42, yymsp[-10].minor.yy96, + &yymsp[-11].minor.yy0, yymsp[0].minor.yy490, SQLITE_SO_ASC, yymsp[-8].minor.yy96, SQLITE_IDXTYPE_APPDEF); if( IN_RENAME_OBJECT && pParse->pNewIndex ){ sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0); } } break; case 220: /* uniqueflag ::= UNIQUE */ - case 260: /* raisetype ::= ABORT */ yytestcase(yyruleno==260); -{yymsp[0].minor.yy70 = OE_Abort;} + case 262: /* raisetype ::= ABORT */ yytestcase(yyruleno==262); +{yymsp[0].minor.yy96 = OE_Abort;} break; case 221: /* uniqueflag ::= */ -{yymsp[1].minor.yy70 = OE_None;} +{yymsp[1].minor.yy96 = OE_None;} break; case 224: /* eidlist ::= eidlist COMMA nm collate sortorder */ { - yymsp[-4].minor.yy420 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy420, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy70, yymsp[0].minor.yy70); + yymsp[-4].minor.yy42 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy42, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy96, yymsp[0].minor.yy96); } break; case 225: /* eidlist ::= nm collate sortorder */ { - yymsp[-2].minor.yy420 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy70, yymsp[0].minor.yy70); /*A-overwrites-Y*/ + yymsp[-2].minor.yy42 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy96, yymsp[0].minor.yy96); /*A-overwrites-Y*/ } break; case 228: /* cmd ::= DROP INDEX ifexists fullname */ -{sqlite3DropIndex(pParse, yymsp[0].minor.yy135, yymsp[-1].minor.yy70);} +{sqlite3DropIndex(pParse, yymsp[0].minor.yy167, yymsp[-1].minor.yy96);} break; - case 229: /* cmd ::= VACUUM */ -{sqlite3Vacuum(pParse,0);} + case 229: /* cmd ::= VACUUM vinto */ +{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy490);} break; - case 230: /* cmd ::= VACUUM nm */ -{sqlite3Vacuum(pParse,&yymsp[0].minor.yy0);} + case 230: /* cmd ::= VACUUM nm vinto */ +{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy490);} break; - case 231: /* cmd ::= PRAGMA nm dbnm */ + case 233: /* cmd ::= PRAGMA nm dbnm */ {sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);} break; - case 232: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ + case 234: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);} break; - case 233: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ + case 235: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ {sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);} break; - case 234: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ + case 236: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);} break; - case 235: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ + case 237: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ {sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);} break; - case 238: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + case 240: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ { Token all; all.z = yymsp[-3].minor.yy0.z; all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n; - sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy207, &all); + sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy119, &all); } break; - case 239: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + case 241: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ { - sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy70, yymsp[-4].minor.yy34.a, yymsp[-4].minor.yy34.b, yymsp[-2].minor.yy135, yymsp[0].minor.yy18, yymsp[-10].minor.yy70, yymsp[-8].minor.yy70); + sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy96, yymsp[-4].minor.yy350.a, yymsp[-4].minor.yy350.b, yymsp[-2].minor.yy167, yymsp[0].minor.yy490, yymsp[-10].minor.yy96, yymsp[-8].minor.yy96); yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/ } break; - case 240: /* trigger_time ::= BEFORE|AFTER */ -{ yymsp[0].minor.yy70 = yymsp[0].major; /*A-overwrites-X*/ } + case 242: /* trigger_time ::= BEFORE|AFTER */ +{ yymsp[0].minor.yy96 = yymsp[0].major; /*A-overwrites-X*/ } break; - case 241: /* trigger_time ::= INSTEAD OF */ -{ yymsp[-1].minor.yy70 = TK_INSTEAD;} + case 243: /* trigger_time ::= INSTEAD OF */ +{ yymsp[-1].minor.yy96 = TK_INSTEAD;} break; - case 242: /* trigger_time ::= */ -{ yymsp[1].minor.yy70 = TK_BEFORE; } + case 244: /* trigger_time ::= */ +{ yymsp[1].minor.yy96 = TK_BEFORE; } break; - case 243: /* trigger_event ::= DELETE|INSERT */ - case 244: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==244); -{yymsp[0].minor.yy34.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy34.b = 0;} + case 245: /* trigger_event ::= DELETE|INSERT */ + case 246: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==246); +{yymsp[0].minor.yy350.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy350.b = 0;} break; - case 245: /* trigger_event ::= UPDATE OF idlist */ -{yymsp[-2].minor.yy34.a = TK_UPDATE; yymsp[-2].minor.yy34.b = yymsp[0].minor.yy48;} + case 247: /* trigger_event ::= UPDATE OF idlist */ +{yymsp[-2].minor.yy350.a = TK_UPDATE; yymsp[-2].minor.yy350.b = yymsp[0].minor.yy336;} break; - case 246: /* when_clause ::= */ - case 265: /* key_opt ::= */ yytestcase(yyruleno==265); - case 307: /* filter_opt ::= */ yytestcase(yyruleno==307); -{ yymsp[1].minor.yy18 = 0; } + case 248: /* when_clause ::= */ + case 267: /* key_opt ::= */ yytestcase(yyruleno==267); + case 309: /* filter_opt ::= */ yytestcase(yyruleno==309); +{ yymsp[1].minor.yy490 = 0; } break; - case 247: /* when_clause ::= WHEN expr */ - case 266: /* key_opt ::= KEY expr */ yytestcase(yyruleno==266); -{ yymsp[-1].minor.yy18 = yymsp[0].minor.yy18; } + case 249: /* when_clause ::= WHEN expr */ + case 268: /* key_opt ::= KEY expr */ yytestcase(yyruleno==268); +{ yymsp[-1].minor.yy490 = yymsp[0].minor.yy490; } break; - case 248: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + case 250: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ { - assert( yymsp[-2].minor.yy207!=0 ); - yymsp[-2].minor.yy207->pLast->pNext = yymsp[-1].minor.yy207; - yymsp[-2].minor.yy207->pLast = yymsp[-1].minor.yy207; + assert( yymsp[-2].minor.yy119!=0 ); + yymsp[-2].minor.yy119->pLast->pNext = yymsp[-1].minor.yy119; + yymsp[-2].minor.yy119->pLast = yymsp[-1].minor.yy119; } break; - case 249: /* trigger_cmd_list ::= trigger_cmd SEMI */ + case 251: /* trigger_cmd_list ::= trigger_cmd SEMI */ { - assert( yymsp[-1].minor.yy207!=0 ); - yymsp[-1].minor.yy207->pLast = yymsp[-1].minor.yy207; + assert( yymsp[-1].minor.yy119!=0 ); + yymsp[-1].minor.yy119->pLast = yymsp[-1].minor.yy119; } break; - case 250: /* trnm ::= nm DOT nm */ + case 252: /* trnm ::= nm DOT nm */ { yymsp[-2].minor.yy0 = yymsp[0].minor.yy0; sqlite3ErrorMsg(pParse, @@ -150613,312 +151502,312 @@ static YYACTIONTYPE yy_reduce( "statements within triggers"); } break; - case 251: /* tridxby ::= INDEXED BY nm */ + case 253: /* tridxby ::= INDEXED BY nm */ { sqlite3ErrorMsg(pParse, "the INDEXED BY clause is not allowed on UPDATE or DELETE statements " "within triggers"); } break; - case 252: /* tridxby ::= NOT INDEXED */ + case 254: /* tridxby ::= NOT INDEXED */ { sqlite3ErrorMsg(pParse, "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements " "within triggers"); } break; - case 253: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */ -{yylhsminor.yy207 = sqlite3TriggerUpdateStep(pParse, &yymsp[-5].minor.yy0, yymsp[-2].minor.yy420, yymsp[-1].minor.yy18, yymsp[-6].minor.yy70, yymsp[-7].minor.yy0.z, yymsp[0].minor.yy392);} - yymsp[-7].minor.yy207 = yylhsminor.yy207; + case 255: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */ +{yylhsminor.yy119 = sqlite3TriggerUpdateStep(pParse, &yymsp[-5].minor.yy0, yymsp[-2].minor.yy42, yymsp[-1].minor.yy490, yymsp[-6].minor.yy96, yymsp[-7].minor.yy0.z, yymsp[0].minor.yy464);} + yymsp[-7].minor.yy119 = yylhsminor.yy119; break; - case 254: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ + case 256: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */ { - yylhsminor.yy207 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy48,yymsp[-2].minor.yy489,yymsp[-6].minor.yy70,yymsp[-1].minor.yy340,yymsp[-7].minor.yy392,yymsp[0].minor.yy392);/*yylhsminor.yy207-overwrites-yymsp[-6].minor.yy70*/ + yylhsminor.yy119 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy336,yymsp[-2].minor.yy423,yymsp[-6].minor.yy96,yymsp[-1].minor.yy266,yymsp[-7].minor.yy464,yymsp[0].minor.yy464);/*yylhsminor.yy119-overwrites-yymsp[-6].minor.yy96*/ } - yymsp[-7].minor.yy207 = yylhsminor.yy207; + yymsp[-7].minor.yy119 = yylhsminor.yy119; break; - case 255: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ -{yylhsminor.yy207 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy18, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy392);} - yymsp[-5].minor.yy207 = yylhsminor.yy207; + case 257: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */ +{yylhsminor.yy119 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy490, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy464);} + yymsp[-5].minor.yy119 = yylhsminor.yy119; break; - case 256: /* trigger_cmd ::= scanpt select scanpt */ -{yylhsminor.yy207 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy489, yymsp[-2].minor.yy392, yymsp[0].minor.yy392); /*yylhsminor.yy207-overwrites-yymsp[-1].minor.yy489*/} - yymsp[-2].minor.yy207 = yylhsminor.yy207; + case 258: /* trigger_cmd ::= scanpt select scanpt */ +{yylhsminor.yy119 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy423, yymsp[-2].minor.yy464, yymsp[0].minor.yy464); /*yylhsminor.yy119-overwrites-yymsp[-1].minor.yy423*/} + yymsp[-2].minor.yy119 = yylhsminor.yy119; break; - case 257: /* expr ::= RAISE LP IGNORE RP */ + case 259: /* expr ::= RAISE LP IGNORE RP */ { - yymsp[-3].minor.yy18 = sqlite3PExpr(pParse, TK_RAISE, 0, 0); - if( yymsp[-3].minor.yy18 ){ - yymsp[-3].minor.yy18->affinity = OE_Ignore; + yymsp[-3].minor.yy490 = sqlite3PExpr(pParse, TK_RAISE, 0, 0); + if( yymsp[-3].minor.yy490 ){ + yymsp[-3].minor.yy490->affinity = OE_Ignore; } } break; - case 258: /* expr ::= RAISE LP raisetype COMMA nm RP */ + case 260: /* expr ::= RAISE LP raisetype COMMA nm RP */ { - yymsp[-5].minor.yy18 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1); - if( yymsp[-5].minor.yy18 ) { - yymsp[-5].minor.yy18->affinity = (char)yymsp[-3].minor.yy70; + yymsp[-5].minor.yy490 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1); + if( yymsp[-5].minor.yy490 ) { + yymsp[-5].minor.yy490->affinity = (char)yymsp[-3].minor.yy96; } } break; - case 259: /* raisetype ::= ROLLBACK */ -{yymsp[0].minor.yy70 = OE_Rollback;} + case 261: /* raisetype ::= ROLLBACK */ +{yymsp[0].minor.yy96 = OE_Rollback;} break; - case 261: /* raisetype ::= FAIL */ -{yymsp[0].minor.yy70 = OE_Fail;} + case 263: /* raisetype ::= FAIL */ +{yymsp[0].minor.yy96 = OE_Fail;} break; - case 262: /* cmd ::= DROP TRIGGER ifexists fullname */ + case 264: /* cmd ::= DROP TRIGGER ifexists fullname */ { - sqlite3DropTrigger(pParse,yymsp[0].minor.yy135,yymsp[-1].minor.yy70); + sqlite3DropTrigger(pParse,yymsp[0].minor.yy167,yymsp[-1].minor.yy96); } break; - case 263: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + case 265: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ { - sqlite3Attach(pParse, yymsp[-3].minor.yy18, yymsp[-1].minor.yy18, yymsp[0].minor.yy18); + sqlite3Attach(pParse, yymsp[-3].minor.yy490, yymsp[-1].minor.yy490, yymsp[0].minor.yy490); } break; - case 264: /* cmd ::= DETACH database_kw_opt expr */ + case 266: /* cmd ::= DETACH database_kw_opt expr */ { - sqlite3Detach(pParse, yymsp[0].minor.yy18); + sqlite3Detach(pParse, yymsp[0].minor.yy490); } break; - case 267: /* cmd ::= REINDEX */ + case 269: /* cmd ::= REINDEX */ {sqlite3Reindex(pParse, 0, 0);} break; - case 268: /* cmd ::= REINDEX nm dbnm */ + case 270: /* cmd ::= REINDEX nm dbnm */ {sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} break; - case 269: /* cmd ::= ANALYZE */ + case 271: /* cmd ::= ANALYZE */ {sqlite3Analyze(pParse, 0, 0);} break; - case 270: /* cmd ::= ANALYZE nm dbnm */ + case 272: /* cmd ::= ANALYZE nm dbnm */ {sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} break; - case 271: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ + case 273: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ { - sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy135,&yymsp[0].minor.yy0); + sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy167,&yymsp[0].minor.yy0); } break; - case 272: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ + case 274: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */ { yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n; sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0); } break; - case 273: /* add_column_fullname ::= fullname */ + case 275: /* add_column_fullname ::= fullname */ { disableLookaside(pParse); - sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy135); + sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy167); } break; - case 274: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ + case 276: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */ { - sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy135, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); + sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy167, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); } break; - case 275: /* cmd ::= create_vtab */ + case 277: /* cmd ::= create_vtab */ {sqlite3VtabFinishParse(pParse,0);} break; - case 276: /* cmd ::= create_vtab LP vtabarglist RP */ + case 278: /* cmd ::= create_vtab LP vtabarglist RP */ {sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);} break; - case 277: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ + case 279: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */ { - sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy70); + sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy96); } break; - case 278: /* vtabarg ::= */ + case 280: /* vtabarg ::= */ {sqlite3VtabArgInit(pParse);} break; - case 279: /* vtabargtoken ::= ANY */ - case 280: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==280); - case 281: /* lp ::= LP */ yytestcase(yyruleno==281); + case 281: /* vtabargtoken ::= ANY */ + case 282: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==282); + case 283: /* lp ::= LP */ yytestcase(yyruleno==283); {sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);} break; - case 282: /* with ::= WITH wqlist */ - case 283: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==283); -{ sqlite3WithPush(pParse, yymsp[0].minor.yy449, 1); } + case 284: /* with ::= WITH wqlist */ + case 285: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==285); +{ sqlite3WithPush(pParse, yymsp[0].minor.yy499, 1); } break; - case 284: /* wqlist ::= nm eidlist_opt AS LP select RP */ + case 286: /* wqlist ::= nm eidlist_opt AS LP select RP */ { - yymsp[-5].minor.yy449 = sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy420, yymsp[-1].minor.yy489); /*A-overwrites-X*/ + yymsp[-5].minor.yy499 = sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy42, yymsp[-1].minor.yy423); /*A-overwrites-X*/ } break; - case 285: /* wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ + case 287: /* wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */ { - yymsp[-7].minor.yy449 = sqlite3WithAdd(pParse, yymsp[-7].minor.yy449, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy420, yymsp[-1].minor.yy489); + yymsp[-7].minor.yy499 = sqlite3WithAdd(pParse, yymsp[-7].minor.yy499, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy42, yymsp[-1].minor.yy423); } break; - case 286: /* windowdefn_list ::= windowdefn */ -{ yylhsminor.yy327 = yymsp[0].minor.yy327; } - yymsp[0].minor.yy327 = yylhsminor.yy327; + case 288: /* windowdefn_list ::= windowdefn */ +{ yylhsminor.yy147 = yymsp[0].minor.yy147; } + yymsp[0].minor.yy147 = yylhsminor.yy147; break; - case 287: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ + case 289: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */ { - assert( yymsp[0].minor.yy327!=0 ); - yymsp[0].minor.yy327->pNextWin = yymsp[-2].minor.yy327; - yylhsminor.yy327 = yymsp[0].minor.yy327; + assert( yymsp[0].minor.yy147!=0 ); + yymsp[0].minor.yy147->pNextWin = yymsp[-2].minor.yy147; + yylhsminor.yy147 = yymsp[0].minor.yy147; } - yymsp[-2].minor.yy327 = yylhsminor.yy327; + yymsp[-2].minor.yy147 = yylhsminor.yy147; break; - case 288: /* windowdefn ::= nm AS window */ + case 290: /* windowdefn ::= nm AS window */ { - if( ALWAYS(yymsp[0].minor.yy327) ){ - yymsp[0].minor.yy327->zName = sqlite3DbStrNDup(pParse->db, yymsp[-2].minor.yy0.z, yymsp[-2].minor.yy0.n); + if( ALWAYS(yymsp[0].minor.yy147) ){ + yymsp[0].minor.yy147->zName = sqlite3DbStrNDup(pParse->db, yymsp[-2].minor.yy0.z, yymsp[-2].minor.yy0.n); } - yylhsminor.yy327 = yymsp[0].minor.yy327; + yylhsminor.yy147 = yymsp[0].minor.yy147; } - yymsp[-2].minor.yy327 = yylhsminor.yy327; + yymsp[-2].minor.yy147 = yylhsminor.yy147; break; - case 289: /* window ::= LP part_opt orderby_opt frame_opt RP */ + case 291: /* window ::= LP part_opt orderby_opt frame_opt RP */ { - yymsp[-4].minor.yy327 = yymsp[-1].minor.yy327; - if( ALWAYS(yymsp[-4].minor.yy327) ){ - yymsp[-4].minor.yy327->pPartition = yymsp[-3].minor.yy420; - yymsp[-4].minor.yy327->pOrderBy = yymsp[-2].minor.yy420; + yymsp[-4].minor.yy147 = yymsp[-1].minor.yy147; + if( ALWAYS(yymsp[-4].minor.yy147) ){ + yymsp[-4].minor.yy147->pPartition = yymsp[-3].minor.yy42; + yymsp[-4].minor.yy147->pOrderBy = yymsp[-2].minor.yy42; } } break; - case 290: /* part_opt ::= PARTITION BY nexprlist */ -{ yymsp[-2].minor.yy420 = yymsp[0].minor.yy420; } + case 292: /* part_opt ::= PARTITION BY nexprlist */ +{ yymsp[-2].minor.yy42 = yymsp[0].minor.yy42; } break; - case 291: /* part_opt ::= */ -{ yymsp[1].minor.yy420 = 0; } + case 293: /* part_opt ::= */ +{ yymsp[1].minor.yy42 = 0; } break; - case 292: /* frame_opt ::= */ + case 294: /* frame_opt ::= */ { - yymsp[1].minor.yy327 = sqlite3WindowAlloc(pParse, TK_RANGE, TK_UNBOUNDED, 0, TK_CURRENT, 0); + yymsp[1].minor.yy147 = sqlite3WindowAlloc(pParse, TK_RANGE, TK_UNBOUNDED, 0, TK_CURRENT, 0); } break; - case 293: /* frame_opt ::= range_or_rows frame_bound_s */ + case 295: /* frame_opt ::= range_or_rows frame_bound_s */ { - yylhsminor.yy327 = sqlite3WindowAlloc(pParse, yymsp[-1].minor.yy70, yymsp[0].minor.yy119.eType, yymsp[0].minor.yy119.pExpr, TK_CURRENT, 0); + yylhsminor.yy147 = sqlite3WindowAlloc(pParse, yymsp[-1].minor.yy96, yymsp[0].minor.yy317.eType, yymsp[0].minor.yy317.pExpr, TK_CURRENT, 0); } - yymsp[-1].minor.yy327 = yylhsminor.yy327; + yymsp[-1].minor.yy147 = yylhsminor.yy147; break; - case 294: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e */ + case 296: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e */ { - yylhsminor.yy327 = sqlite3WindowAlloc(pParse, yymsp[-4].minor.yy70, yymsp[-2].minor.yy119.eType, yymsp[-2].minor.yy119.pExpr, yymsp[0].minor.yy119.eType, yymsp[0].minor.yy119.pExpr); + yylhsminor.yy147 = sqlite3WindowAlloc(pParse, yymsp[-4].minor.yy96, yymsp[-2].minor.yy317.eType, yymsp[-2].minor.yy317.pExpr, yymsp[0].minor.yy317.eType, yymsp[0].minor.yy317.pExpr); } - yymsp[-4].minor.yy327 = yylhsminor.yy327; + yymsp[-4].minor.yy147 = yylhsminor.yy147; break; - case 295: /* range_or_rows ::= RANGE */ -{ yymsp[0].minor.yy70 = TK_RANGE; } + case 297: /* range_or_rows ::= RANGE */ +{ yymsp[0].minor.yy96 = TK_RANGE; } break; - case 296: /* range_or_rows ::= ROWS */ -{ yymsp[0].minor.yy70 = TK_ROWS; } + case 298: /* range_or_rows ::= ROWS */ +{ yymsp[0].minor.yy96 = TK_ROWS; } break; - case 297: /* frame_bound_s ::= frame_bound */ - case 299: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==299); -{ yylhsminor.yy119 = yymsp[0].minor.yy119; } - yymsp[0].minor.yy119 = yylhsminor.yy119; + case 299: /* frame_bound_s ::= frame_bound */ + case 301: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==301); +{ yylhsminor.yy317 = yymsp[0].minor.yy317; } + yymsp[0].minor.yy317 = yylhsminor.yy317; break; - case 298: /* frame_bound_s ::= UNBOUNDED PRECEDING */ - case 300: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==300); -{yymsp[-1].minor.yy119.eType = TK_UNBOUNDED; yymsp[-1].minor.yy119.pExpr = 0;} + case 300: /* frame_bound_s ::= UNBOUNDED PRECEDING */ + case 302: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==302); +{yymsp[-1].minor.yy317.eType = TK_UNBOUNDED; yymsp[-1].minor.yy317.pExpr = 0;} break; - case 301: /* frame_bound ::= expr PRECEDING */ -{ yylhsminor.yy119.eType = TK_PRECEDING; yylhsminor.yy119.pExpr = yymsp[-1].minor.yy18; } - yymsp[-1].minor.yy119 = yylhsminor.yy119; + case 303: /* frame_bound ::= expr PRECEDING */ +{ yylhsminor.yy317.eType = TK_PRECEDING; yylhsminor.yy317.pExpr = yymsp[-1].minor.yy490; } + yymsp[-1].minor.yy317 = yylhsminor.yy317; break; - case 302: /* frame_bound ::= CURRENT ROW */ -{ yymsp[-1].minor.yy119.eType = TK_CURRENT ; yymsp[-1].minor.yy119.pExpr = 0; } + case 304: /* frame_bound ::= CURRENT ROW */ +{ yymsp[-1].minor.yy317.eType = TK_CURRENT ; yymsp[-1].minor.yy317.pExpr = 0; } break; - case 303: /* frame_bound ::= expr FOLLOWING */ -{ yylhsminor.yy119.eType = TK_FOLLOWING; yylhsminor.yy119.pExpr = yymsp[-1].minor.yy18; } - yymsp[-1].minor.yy119 = yylhsminor.yy119; + case 305: /* frame_bound ::= expr FOLLOWING */ +{ yylhsminor.yy317.eType = TK_FOLLOWING; yylhsminor.yy317.pExpr = yymsp[-1].minor.yy490; } + yymsp[-1].minor.yy317 = yylhsminor.yy317; break; - case 304: /* window_clause ::= WINDOW windowdefn_list */ -{ yymsp[-1].minor.yy327 = yymsp[0].minor.yy327; } + case 306: /* window_clause ::= WINDOW windowdefn_list */ +{ yymsp[-1].minor.yy147 = yymsp[0].minor.yy147; } break; - case 305: /* over_clause ::= filter_opt OVER window */ + case 307: /* over_clause ::= filter_opt OVER window */ { - yylhsminor.yy327 = yymsp[0].minor.yy327; - assert( yylhsminor.yy327!=0 ); - yylhsminor.yy327->pFilter = yymsp[-2].minor.yy18; + yylhsminor.yy147 = yymsp[0].minor.yy147; + assert( yylhsminor.yy147!=0 ); + yylhsminor.yy147->pFilter = yymsp[-2].minor.yy490; } - yymsp[-2].minor.yy327 = yylhsminor.yy327; + yymsp[-2].minor.yy147 = yylhsminor.yy147; break; - case 306: /* over_clause ::= filter_opt OVER nm */ + case 308: /* over_clause ::= filter_opt OVER nm */ { - yylhsminor.yy327 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); - if( yylhsminor.yy327 ){ - yylhsminor.yy327->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n); - yylhsminor.yy327->pFilter = yymsp[-2].minor.yy18; + yylhsminor.yy147 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); + if( yylhsminor.yy147 ){ + yylhsminor.yy147->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n); + yylhsminor.yy147->pFilter = yymsp[-2].minor.yy490; }else{ - sqlite3ExprDelete(pParse->db, yymsp[-2].minor.yy18); + sqlite3ExprDelete(pParse->db, yymsp[-2].minor.yy490); } } - yymsp[-2].minor.yy327 = yylhsminor.yy327; + yymsp[-2].minor.yy147 = yylhsminor.yy147; break; - case 308: /* filter_opt ::= FILTER LP WHERE expr RP */ -{ yymsp[-4].minor.yy18 = yymsp[-1].minor.yy18; } + case 310: /* filter_opt ::= FILTER LP WHERE expr RP */ +{ yymsp[-4].minor.yy490 = yymsp[-1].minor.yy490; } break; default: - /* (309) input ::= cmdlist */ yytestcase(yyruleno==309); - /* (310) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==310); - /* (311) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=311); - /* (312) ecmd ::= SEMI */ yytestcase(yyruleno==312); - /* (313) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==313); - /* (314) ecmd ::= explain cmdx */ yytestcase(yyruleno==314); - /* (315) trans_opt ::= */ yytestcase(yyruleno==315); - /* (316) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==316); - /* (317) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==317); - /* (318) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==318); - /* (319) savepoint_opt ::= */ yytestcase(yyruleno==319); - /* (320) cmd ::= create_table create_table_args */ yytestcase(yyruleno==320); - /* (321) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==321); - /* (322) columnlist ::= columnname carglist */ yytestcase(yyruleno==322); - /* (323) nm ::= ID|INDEXED */ yytestcase(yyruleno==323); - /* (324) nm ::= STRING */ yytestcase(yyruleno==324); - /* (325) nm ::= JOIN_KW */ yytestcase(yyruleno==325); - /* (326) typetoken ::= typename */ yytestcase(yyruleno==326); - /* (327) typename ::= ID|STRING */ yytestcase(yyruleno==327); - /* (328) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=328); - /* (329) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=329); - /* (330) carglist ::= carglist ccons */ yytestcase(yyruleno==330); - /* (331) carglist ::= */ yytestcase(yyruleno==331); - /* (332) ccons ::= NULL onconf */ yytestcase(yyruleno==332); - /* (333) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==333); - /* (334) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==334); - /* (335) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=335); - /* (336) tconscomma ::= */ yytestcase(yyruleno==336); - /* (337) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=337); - /* (338) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=338); - /* (339) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=339); - /* (340) oneselect ::= values */ yytestcase(yyruleno==340); - /* (341) sclp ::= selcollist COMMA */ yytestcase(yyruleno==341); - /* (342) as ::= ID|STRING */ yytestcase(yyruleno==342); - /* (343) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=343); - /* (344) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==344); - /* (345) exprlist ::= nexprlist */ yytestcase(yyruleno==345); - /* (346) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=346); - /* (347) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=347); - /* (348) nmnum ::= ON */ yytestcase(yyruleno==348); - /* (349) nmnum ::= DELETE */ yytestcase(yyruleno==349); - /* (350) nmnum ::= DEFAULT */ yytestcase(yyruleno==350); - /* (351) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==351); - /* (352) foreach_clause ::= */ yytestcase(yyruleno==352); - /* (353) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==353); - /* (354) trnm ::= nm */ yytestcase(yyruleno==354); - /* (355) tridxby ::= */ yytestcase(yyruleno==355); - /* (356) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==356); - /* (357) database_kw_opt ::= */ yytestcase(yyruleno==357); - /* (358) kwcolumn_opt ::= */ yytestcase(yyruleno==358); - /* (359) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==359); - /* (360) vtabarglist ::= vtabarg */ yytestcase(yyruleno==360); - /* (361) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==361); - /* (362) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==362); - /* (363) anylist ::= */ yytestcase(yyruleno==363); - /* (364) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==364); - /* (365) anylist ::= anylist ANY */ yytestcase(yyruleno==365); - /* (366) with ::= */ yytestcase(yyruleno==366); + /* (311) input ::= cmdlist */ yytestcase(yyruleno==311); + /* (312) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==312); + /* (313) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=313); + /* (314) ecmd ::= SEMI */ yytestcase(yyruleno==314); + /* (315) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==315); + /* (316) ecmd ::= explain cmdx */ yytestcase(yyruleno==316); + /* (317) trans_opt ::= */ yytestcase(yyruleno==317); + /* (318) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==318); + /* (319) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==319); + /* (320) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==320); + /* (321) savepoint_opt ::= */ yytestcase(yyruleno==321); + /* (322) cmd ::= create_table create_table_args */ yytestcase(yyruleno==322); + /* (323) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==323); + /* (324) columnlist ::= columnname carglist */ yytestcase(yyruleno==324); + /* (325) nm ::= ID|INDEXED */ yytestcase(yyruleno==325); + /* (326) nm ::= STRING */ yytestcase(yyruleno==326); + /* (327) nm ::= JOIN_KW */ yytestcase(yyruleno==327); + /* (328) typetoken ::= typename */ yytestcase(yyruleno==328); + /* (329) typename ::= ID|STRING */ yytestcase(yyruleno==329); + /* (330) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=330); + /* (331) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=331); + /* (332) carglist ::= carglist ccons */ yytestcase(yyruleno==332); + /* (333) carglist ::= */ yytestcase(yyruleno==333); + /* (334) ccons ::= NULL onconf */ yytestcase(yyruleno==334); + /* (335) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==335); + /* (336) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==336); + /* (337) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=337); + /* (338) tconscomma ::= */ yytestcase(yyruleno==338); + /* (339) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=339); + /* (340) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=340); + /* (341) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=341); + /* (342) oneselect ::= values */ yytestcase(yyruleno==342); + /* (343) sclp ::= selcollist COMMA */ yytestcase(yyruleno==343); + /* (344) as ::= ID|STRING */ yytestcase(yyruleno==344); + /* (345) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=345); + /* (346) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==346); + /* (347) exprlist ::= nexprlist */ yytestcase(yyruleno==347); + /* (348) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=348); + /* (349) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=349); + /* (350) nmnum ::= ON */ yytestcase(yyruleno==350); + /* (351) nmnum ::= DELETE */ yytestcase(yyruleno==351); + /* (352) nmnum ::= DEFAULT */ yytestcase(yyruleno==352); + /* (353) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==353); + /* (354) foreach_clause ::= */ yytestcase(yyruleno==354); + /* (355) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==355); + /* (356) trnm ::= nm */ yytestcase(yyruleno==356); + /* (357) tridxby ::= */ yytestcase(yyruleno==357); + /* (358) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==358); + /* (359) database_kw_opt ::= */ yytestcase(yyruleno==359); + /* (360) kwcolumn_opt ::= */ yytestcase(yyruleno==360); + /* (361) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==361); + /* (362) vtabarglist ::= vtabarg */ yytestcase(yyruleno==362); + /* (363) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==363); + /* (364) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==364); + /* (365) anylist ::= */ yytestcase(yyruleno==365); + /* (366) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==366); + /* (367) anylist ::= anylist ANY */ yytestcase(yyruleno==367); + /* (368) with ::= */ yytestcase(yyruleno==368); break; /********** End reduce actions ************************************************/ }; - assert( yyrulenorc = SQLITE_OK; pParse->zTail = zSql; assert( pzErrMsg!=0 ); - /* sqlite3ParserTrace(stdout, "parser: "); */ +#ifdef SQLITE_DEBUG + if( db->flags & SQLITE_ParserTrace ){ + printf("parser: [[[%s]]]\n", zSql); + sqlite3ParserTrace(stdout, "parser: "); + }else{ + sqlite3ParserTrace(0, 0); + } +#endif #ifdef sqlite3Parser_ENGINEALWAYSONSTACK pEngine = &sEngine; sqlite3ParserInit(pEngine, pParse); @@ -152332,6 +153161,141 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr return nErr; } + +#ifdef SQLITE_ENABLE_NORMALIZE +/* +** Insert a single space character into pStr if the current string +** ends with an identifier +*/ +static void addSpaceSeparator(sqlite3_str *pStr){ + if( pStr->nChar && sqlite3IsIdChar(pStr->zText[pStr->nChar-1]) ){ + sqlite3_str_append(pStr, " ", 1); + } +} + +/* +** Compute a normalization of the SQL given by zSql[0..nSql-1]. Return +** the normalization in space obtained from sqlite3DbMalloc(). Or return +** NULL if anything goes wrong or if zSql is NULL. +*/ +SQLITE_PRIVATE char *sqlite3Normalize( + Vdbe *pVdbe, /* VM being reprepared */ + const char *zSql /* The original SQL string */ +){ + sqlite3 *db; /* The database connection */ + int i; /* Next unread byte of zSql[] */ + int n; /* length of current token */ + int tokenType; /* type of current token */ + int prevType = 0; /* Previous non-whitespace token */ + int nParen; /* Number of nested levels of parentheses */ + int iStartIN; /* Start of RHS of IN operator in z[] */ + int nParenAtIN; /* Value of nParent at start of RHS of IN operator */ + int j; /* Bytes of normalized SQL generated so far */ + sqlite3_str *pStr; /* The normalized SQL string under construction */ + + db = sqlite3VdbeDb(pVdbe); + tokenType = -1; + nParen = iStartIN = nParenAtIN = 0; + pStr = sqlite3_str_new(db); + assert( pStr!=0 ); /* sqlite3_str_new() never returns NULL */ + for(i=0; zSql[i] && pStr->accError==0; i+=n){ + if( tokenType!=TK_SPACE ){ + prevType = tokenType; + } + n = sqlite3GetToken((unsigned char*)zSql+i, &tokenType); + if( NEVER(n<=0) ) break; + switch( tokenType ){ + case TK_SPACE: { + break; + } + case TK_NULL: { + if( prevType==TK_IS || prevType==TK_NOT ){ + sqlite3_str_append(pStr, " NULL", 5); + break; + } + /* Fall through */ + } + case TK_STRING: + case TK_INTEGER: + case TK_FLOAT: + case TK_VARIABLE: + case TK_BLOB: { + sqlite3_str_append(pStr, "?", 1); + break; + } + case TK_LP: { + nParen++; + if( prevType==TK_IN ){ + iStartIN = pStr->nChar; + nParenAtIN = nParen; + } + sqlite3_str_append(pStr, "(", 1); + break; + } + case TK_RP: { + if( iStartIN>0 && nParen==nParenAtIN ){ + assert( pStr->nChar>=iStartIN ); + pStr->nChar = iStartIN+1; + sqlite3_str_append(pStr, "?,?,?", 5); + iStartIN = 0; + } + nParen--; + sqlite3_str_append(pStr, ")", 1); + break; + } + case TK_ID: { + iStartIN = 0; + j = pStr->nChar; + if( sqlite3Isquote(zSql[i]) ){ + char *zId = sqlite3DbStrNDup(db, zSql+i, n); + int nId; + int eType = 0; + if( zId==0 ) break; + sqlite3Dequote(zId); + if( zSql[i]=='"' && sqlite3VdbeUsesDoubleQuotedString(pVdbe, zId) ){ + sqlite3_str_append(pStr, "?", 1); + sqlite3DbFree(db, zId); + break; + } + nId = sqlite3Strlen30(zId); + if( sqlite3GetToken((u8*)zId, &eType)==nId && eType==TK_ID ){ + addSpaceSeparator(pStr); + sqlite3_str_append(pStr, zId, nId); + }else{ + sqlite3_str_appendf(pStr, "\"%w\"", zId); + } + sqlite3DbFree(db, zId); + }else{ + addSpaceSeparator(pStr); + sqlite3_str_append(pStr, zSql+i, n); + } + while( jnChar ){ + pStr->zText[j] = sqlite3Tolower(pStr->zText[j]); + j++; + } + break; + } + case TK_SELECT: { + iStartIN = 0; + /* fall through */ + } + default: { + if( sqlite3IsIdChar(zSql[i]) ) addSpaceSeparator(pStr); + j = pStr->nChar; + sqlite3_str_append(pStr, zSql+i, n); + while( jnChar ){ + pStr->zText[j] = sqlite3Toupper(pStr->zText[j]); + j++; + } + break; + } + } + } + if( tokenType!=TK_SEMI ) sqlite3_str_append(pStr, ";", 1); + return sqlite3_str_finish(pStr); +} +#endif /* SQLITE_ENABLE_NORMALIZE */ + /************** End of tokenize.c ********************************************/ /************** Begin file complete.c ****************************************/ /* @@ -153377,6 +154341,13 @@ SQLITE_API int sqlite3_config(int op, ...){ } #endif /* SQLITE_ENABLE_SORTER_REFERENCES */ +#ifdef SQLITE_ENABLE_DESERIALIZE + case SQLITE_CONFIG_MEMDB_MAXSIZE: { + sqlite3GlobalConfig.mxMemdbSize = va_arg(ap, sqlite3_int64); + break; + } +#endif /* SQLITE_ENABLE_DESERIALIZE */ + default: { rc = SQLITE_ERROR; break; @@ -153567,11 +154538,11 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){ if( aFlagOp[i].op==op ){ int onoff = va_arg(ap, int); int *pRes = va_arg(ap, int*); - u32 oldFlags = db->flags; + u64 oldFlags = db->flags; if( onoff>0 ){ db->flags |= aFlagOp[i].mask; }else if( onoff==0 ){ - db->flags &= ~aFlagOp[i].mask; + db->flags &= ~(u64)aFlagOp[i].mask; } if( oldFlags!=db->flags ){ sqlite3ExpirePreparedStatements(db, 0); @@ -154034,7 +155005,7 @@ SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3 *db, int tripCode){ /* Any deferred constraint violations have now been resolved. */ db->nDeferredCons = 0; db->nDeferredImmCons = 0; - db->flags &= ~SQLITE_DeferFKs; + db->flags &= ~(u64)SQLITE_DeferFKs; /* If one has been configured, invoke the rollback-hook callback */ if( db->xRollbackCallback && (inTrans || !db->autoCommit) ){ @@ -154776,6 +155747,8 @@ SQLITE_API void *sqlite3_profile( pOld = db->pProfileArg; db->xProfile = xProfile; db->pProfileArg = pArg; + db->mTrace &= SQLITE_TRACE_NONLEGACY_MASK; + if( db->xProfile ) db->mTrace |= SQLITE_TRACE_XPROFILE; sqlite3_mutex_leave(db->mutex); return pOld; } @@ -155127,7 +156100,7 @@ SQLITE_API const char *sqlite3_errmsg(sqlite3 *db){ z = sqlite3ErrStr(SQLITE_NOMEM_BKPT); }else{ testcase( db->pErr==0 ); - z = (char*)sqlite3_value_text(db->pErr); + z = db->errCode ? (char*)sqlite3_value_text(db->pErr) : 0; assert( !db->mallocFailed ); if( z==0 ){ z = sqlite3ErrStr(db->errCode); @@ -155657,6 +156630,40 @@ SQLITE_PRIVATE int sqlite3ParseUri( return rc; } +#if defined(SQLITE_HAS_CODEC) +/* +** Process URI filename query parameters relevant to the SQLite Encryption +** Extension. Return true if any of the relevant query parameters are +** seen and return false if not. +*/ +SQLITE_PRIVATE int sqlite3CodecQueryParameters( + sqlite3 *db, /* Database connection */ + const char *zDb, /* Which schema is being created/attached */ + const char *zUri /* URI filename */ +){ + const char *zKey; + if( (zKey = sqlite3_uri_parameter(zUri, "hexkey"))!=0 && zKey[0] ){ + u8 iByte; + int i; + char zDecoded[40]; + for(i=0, iByte=0; i=0 ); return 5; @@ -158878,13 +159895,18 @@ static int fts3DestroyMethod(sqlite3_vtab *pVtab){ sqlite3 *db = p->db; /* Database handle */ /* Drop the shadow tables */ - if( p->zContentTbl==0 ){ - fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_content'", zDb, p->zName); - } - fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segments'", zDb,p->zName); - fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segdir'", zDb, p->zName); - fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_docsize'", zDb, p->zName); - fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_stat'", zDb, p->zName); + fts3DbExec(&rc, db, + "DROP TABLE IF EXISTS %Q.'%q_segments';" + "DROP TABLE IF EXISTS %Q.'%q_segdir';" + "DROP TABLE IF EXISTS %Q.'%q_docsize';" + "DROP TABLE IF EXISTS %Q.'%q_stat';" + "%s DROP TABLE IF EXISTS %Q.'%q_content';", + zDb, p->zName, + zDb, p->zName, + zDb, p->zName, + zDb, p->zName, + (p->zContentTbl ? "--" : ""), zDb,p->zName + ); /* If everything has worked, invoke fts3DisconnectMethod() to free the ** memory associated with the Fts3Table structure and return SQLITE_OK. @@ -159116,10 +160138,10 @@ static void fts3Appendf( ** memory. */ static char *fts3QuoteId(char const *zInput){ - int nRet; + sqlite3_int64 nRet; char *zRet; nRet = 2 + (int)strlen(zInput)*2 + 1; - zRet = sqlite3_malloc(nRet); + zRet = sqlite3_malloc64(nRet); if( zRet ){ int i; char *z = zRet; @@ -159300,7 +160322,7 @@ static int fts3PrefixParameter( } } - aIndex = sqlite3_malloc(sizeof(struct Fts3Index) * nIndex); + aIndex = sqlite3_malloc64(sizeof(struct Fts3Index) * nIndex); *apIndex = aIndex; if( !aIndex ){ return SQLITE_NOMEM; @@ -159379,7 +160401,7 @@ static int fts3ContentColumns( if( rc==SQLITE_OK ){ const char **azCol; /* Output array */ - int nStr = 0; /* Size of all column names (incl. 0x00) */ + sqlite3_int64 nStr = 0; /* Size of all column names (incl. 0x00) */ int nCol; /* Number of table columns */ int i; /* Used to iterate through columns */ @@ -159389,11 +160411,11 @@ static int fts3ContentColumns( nCol = sqlite3_column_count(pStmt); for(i=0; i0 ); if( bDescDoclist ){ - aOut = sqlite3_malloc(*pnRight + FTS3_VARINT_MAX); + aOut = sqlite3_malloc64((sqlite3_int64)*pnRight + FTS3_VARINT_MAX); if( aOut==0 ) return SQLITE_NOMEM; }else{ aOut = aRight; @@ -161100,6 +162136,7 @@ static int fts3TermSelectMerge( pTS->anOutput[0] = nDoclist; if( pTS->aaOutput[0] ){ memcpy(pTS->aaOutput[0], aDoclist, nDoclist); + memset(&pTS->aaOutput[0][nDoclist], 0, FTS3_VARINT_MAX); }else{ return SQLITE_NOMEM; } @@ -161151,8 +162188,8 @@ static int fts3SegReaderCursorAppend( ){ if( (pCsr->nSegment%16)==0 ){ Fts3SegReader **apNew; - int nByte = (pCsr->nSegment + 16)*sizeof(Fts3SegReader*); - apNew = (Fts3SegReader **)sqlite3_realloc(pCsr->apSegment, nByte); + sqlite3_int64 nByte = (pCsr->nSegment + 16)*sizeof(Fts3SegReader*); + apNew = (Fts3SegReader **)sqlite3_realloc64(pCsr->apSegment, nByte); if( !apNew ){ sqlite3Fts3SegReaderFree(pNew); return SQLITE_NOMEM; @@ -161216,7 +162253,7 @@ static int fts3SegReaderCursor( /* If zTerm is not NULL, and this segment is not stored entirely on its ** root node, the range of leaves scanned can be reduced. Do this. */ - if( iStartBlock && zTerm ){ + if( iStartBlock && zTerm && zRoot ){ sqlite3_int64 *pi = (isPrefix ? &iLeavesEndBlock : 0); rc = fts3SelectLeaf(p, zTerm, nTerm, zRoot, nRoot, &iStartBlock, pi); if( rc!=SQLITE_OK ) goto finished; @@ -162158,7 +163195,6 @@ static int fts3RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ Fts3Table *p = (Fts3Table*)pVtab; UNUSED_PARAMETER(iSavepoint); assert( p->inTransaction ); - assert( p->mxSavepoint >= iSavepoint ); TESTONLY( p->mxSavepoint = iSavepoint ); sqlite3Fts3PendingTermsClear(p); return SQLITE_OK; @@ -162933,9 +163969,10 @@ static int fts3EvalIncrPhraseNext( if( bEof==0 ){ int nList = 0; int nByte = a[p->nToken-1].nList; - char *aDoclist = sqlite3_malloc(nByte+1); + char *aDoclist = sqlite3_malloc(nByte+FTS3_BUFFER_PADDING); if( !aDoclist ) return SQLITE_NOMEM; memcpy(aDoclist, a[p->nToken-1].pList, nByte+1); + memset(&aDoclist[nByte], 0, FTS3_BUFFER_PADDING); for(i=0; i<(p->nToken-1); i++){ if( a[i].bIgnore==0 ){ @@ -163326,7 +164363,7 @@ static int fts3EvalStart(Fts3Cursor *pCsr){ if( rc==SQLITE_OK && nToken>1 && pTab->bFts4 ){ Fts3TokenAndCost *aTC; Fts3Expr **apOr; - aTC = (Fts3TokenAndCost *)sqlite3_malloc( + aTC = (Fts3TokenAndCost *)sqlite3_malloc64( sizeof(Fts3TokenAndCost) * nToken + sizeof(Fts3Expr *) * nOr * 2 ); @@ -163637,7 +164674,7 @@ static int fts3EvalNearTest(Fts3Expr *pExpr, int *pRc){ && (pExpr->pParent==0 || pExpr->pParent->eType!=FTSQUERY_NEAR) ){ Fts3Expr *p; - int nTmp = 0; /* Bytes of temp space */ + sqlite3_int64 nTmp = 0; /* Bytes of temp space */ char *aTmp; /* Temp space for PoslistNearMerge() */ /* Allocate temporary working space. */ @@ -163646,7 +164683,7 @@ static int fts3EvalNearTest(Fts3Expr *pExpr, int *pRc){ nTmp += p->pRight->pPhrase->doclist.nList; } nTmp += p->pPhrase->doclist.nList; - aTmp = sqlite3_malloc(nTmp*2); + aTmp = sqlite3_malloc64(nTmp*2); if( !aTmp ){ *pRc = SQLITE_NOMEM; res = 0; @@ -163916,15 +164953,14 @@ static void fts3EvalRestart( ** found in Fts3Expr.pPhrase->doclist.pList for each of the phrase ** expression nodes. */ -static void fts3EvalUpdateCounts(Fts3Expr *pExpr){ +static void fts3EvalUpdateCounts(Fts3Expr *pExpr, int nCol){ if( pExpr ){ Fts3Phrase *pPhrase = pExpr->pPhrase; if( pPhrase && pPhrase->doclist.pList ){ int iCol = 0; char *p = pPhrase->doclist.pList; - assert( *p ); - while( 1 ){ + do{ u8 c = 0; int iCnt = 0; while( 0xFE & (*p | c) ){ @@ -163940,11 +164976,11 @@ static void fts3EvalUpdateCounts(Fts3Expr *pExpr){ if( *p==0x00 ) break; p++; p += fts3GetVarint32(p, &iCol); - } + }while( iColpLeft); - fts3EvalUpdateCounts(pExpr->pRight); + fts3EvalUpdateCounts(pExpr->pLeft, nCol); + fts3EvalUpdateCounts(pExpr->pRight, nCol); } } @@ -163988,7 +165024,7 @@ static int fts3EvalGatherStats( for(p=pRoot; p; p=p->pLeft){ Fts3Expr *pE = (p->eType==FTSQUERY_PHRASE?p:p->pRight); assert( pE->aMI==0 ); - pE->aMI = (u32 *)sqlite3_malloc(pTab->nColumn * 3 * sizeof(u32)); + pE->aMI = (u32 *)sqlite3_malloc64(pTab->nColumn * 3 * sizeof(u32)); if( !pE->aMI ) return SQLITE_NOMEM; memset(pE->aMI, 0, pTab->nColumn * 3 * sizeof(u32)); } @@ -164014,7 +165050,7 @@ static int fts3EvalGatherStats( ); if( rc==SQLITE_OK && pCsr->isEof==0 ){ - fts3EvalUpdateCounts(pRoot); + fts3EvalUpdateCounts(pRoot, pTab->nColumn); } } @@ -164364,7 +165400,7 @@ static int fts3auxConnectMethod( char const *zFts3; /* Name of fts3 table */ int nDb; /* Result of strlen(zDb) */ int nFts3; /* Result of strlen(zFts3) */ - int nByte; /* Bytes of space to allocate here */ + sqlite3_int64 nByte; /* Bytes of space to allocate here */ int rc; /* value returned by declare_vtab() */ Fts3auxTable *p; /* Virtual table object to return */ @@ -164396,7 +165432,7 @@ static int fts3auxConnectMethod( if( rc!=SQLITE_OK ) return rc; nByte = sizeof(Fts3auxTable) + sizeof(Fts3Table) + nDb + nFts3 + 2; - p = (Fts3auxTable *)sqlite3_malloc(nByte); + p = (Fts3auxTable *)sqlite3_malloc64(nByte); if( !p ) return SQLITE_NOMEM; memset(p, 0, nByte); @@ -164546,7 +165582,7 @@ static int fts3auxCloseMethod(sqlite3_vtab_cursor *pCursor){ static int fts3auxGrowStatArray(Fts3auxCursor *pCsr, int nSize){ if( nSize>pCsr->nStat ){ struct Fts3auxColstats *aNew; - aNew = (struct Fts3auxColstats *)sqlite3_realloc(pCsr->aStat, + aNew = (struct Fts3auxColstats *)sqlite3_realloc64(pCsr->aStat, sizeof(struct Fts3auxColstats) * nSize ); if( aNew==0 ) return SQLITE_NOMEM; @@ -164714,15 +165750,15 @@ static int fts3auxFilterMethod( assert( (iEq==0 && iGe==-1) || (iEq==-1 && iGe==0) ); if( zStr ){ pCsr->filter.zTerm = sqlite3_mprintf("%s", zStr); - pCsr->filter.nTerm = sqlite3_value_bytes(apVal[0]); if( pCsr->filter.zTerm==0 ) return SQLITE_NOMEM; + pCsr->filter.nTerm = (int)strlen(pCsr->filter.zTerm); } } if( iLe>=0 ){ pCsr->zStop = sqlite3_mprintf("%s", sqlite3_value_text(apVal[iLe])); - pCsr->nStop = sqlite3_value_bytes(apVal[iLe]); if( pCsr->zStop==0 ) return SQLITE_NOMEM; + pCsr->nStop = (int)strlen(pCsr->zStop); } if( iLangid>=0 ){ @@ -164974,8 +166010,8 @@ static int fts3isspace(char c){ ** zero the memory before returning a pointer to it. If unsuccessful, ** return NULL. */ -static void *fts3MallocZero(int nByte){ - void *pRet = sqlite3_malloc(nByte); +static void *fts3MallocZero(sqlite3_int64 nByte){ + void *pRet = sqlite3_malloc64(nByte); if( pRet ) memset(pRet, 0, nByte); return pRet; } @@ -165050,7 +166086,7 @@ static int getNextToken( if( rc==SQLITE_OK ){ const char *zToken; int nToken = 0, iStart = 0, iEnd = 0, iPosition = 0; - int nByte; /* total space to allocate */ + sqlite3_int64 nByte; /* total space to allocate */ rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition); if( rc==SQLITE_OK ){ @@ -165104,8 +166140,8 @@ static int getNextToken( ** Enlarge a memory allocation. If an out-of-memory allocation occurs, ** then free the old allocation. */ -static void *fts3ReallocOrFree(void *pOrig, int nNew){ - void *pRet = sqlite3_realloc(pOrig, nNew); +static void *fts3ReallocOrFree(void *pOrig, sqlite3_int64 nNew){ + void *pRet = sqlite3_realloc64(pOrig, nNew); if( !pRet ){ sqlite3_free(pOrig); } @@ -165349,7 +166385,6 @@ static int getNextNode( int nConsumed = 0; pParse->nNest++; rc = fts3ExprParse(pParse, zInput+1, nInput-1, ppExpr, &nConsumed); - if( rc==SQLITE_OK && !*ppExpr ){ rc = SQLITE_DONE; } *pnConsumed = (int)(zInput - z) + 1 + nConsumed; return rc; }else if( *zInput==')' ){ @@ -165648,7 +166683,7 @@ static int fts3ExprBalance(Fts3Expr **pp, int nMaxDepth){ if( rc==SQLITE_OK ){ if( (eType==FTSQUERY_AND || eType==FTSQUERY_OR) ){ Fts3Expr **apLeaf; - apLeaf = (Fts3Expr **)sqlite3_malloc(sizeof(Fts3Expr *) * nMaxDepth); + apLeaf = (Fts3Expr **)sqlite3_malloc64(sizeof(Fts3Expr *) * nMaxDepth); if( 0==apLeaf ){ rc = SQLITE_NOMEM; }else{ @@ -166068,7 +167103,7 @@ static void fts3ExprTestCommon( zExpr = (const char *)sqlite3_value_text(argv[1]); nExpr = sqlite3_value_bytes(argv[1]); nCol = argc-2; - azCol = (char **)sqlite3_malloc(nCol*sizeof(char *)); + azCol = (char **)sqlite3_malloc64(nCol*sizeof(char *)); if( !azCol ){ sqlite3_result_error_nomem(context); goto exprtest_out; @@ -166182,8 +167217,8 @@ SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3 *db, Fts3Hash *pHash /* ** Malloc and Free functions */ -static void *fts3HashMalloc(int n){ - void *p = sqlite3_malloc(n); +static void *fts3HashMalloc(sqlite3_int64 n){ + void *p = sqlite3_malloc64(n); if( p ){ memset(p, 0, n); } @@ -168076,7 +169111,7 @@ static int fts3tokDequoteArray( nByte += (int)(strlen(argv[i]) + 1); } - *pazDequote = azDequote = sqlite3_malloc(sizeof(char *)*argc + nByte); + *pazDequote = azDequote = sqlite3_malloc64(sizeof(char *)*argc + nByte); if( azDequote==0 ){ rc = SQLITE_NOMEM; }else{ @@ -168808,10 +169843,12 @@ static int fts3SqlStmt( pStmt = p->aStmt[eStmt]; if( !pStmt ){ + int f = SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_NO_VTAB; char *zSql; if( eStmt==SQL_CONTENT_INSERT ){ zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, p->zWriteExprlist); }else if( eStmt==SQL_SELECT_CONTENT_BY_ROWID ){ + f &= ~SQLITE_PREPARE_NO_VTAB; zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist); }else{ zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName); @@ -168819,8 +169856,7 @@ static int fts3SqlStmt( if( !zSql ){ rc = SQLITE_NOMEM; }else{ - rc = sqlite3_prepare_v3(p->db, zSql, -1, SQLITE_PREPARE_PERSISTENT, - &pStmt, NULL); + rc = sqlite3_prepare_v3(p->db, zSql, -1, f, &pStmt, NULL); sqlite3_free(zSql); assert( rc==SQLITE_OK || pStmt==0 ); p->aStmt[eStmt] = pStmt; @@ -168978,7 +170014,7 @@ static sqlite3_int64 getAbsoluteLevel( int iLevel /* Level of segments */ ){ sqlite3_int64 iBase; /* First absolute level for iLangid/iIndex */ - assert( iLangid>=0 ); + assert_fts3_nc( iLangid>=0 ); assert( p->nIndex>0 ); assert( iIndex>=0 && iIndexnIndex ); @@ -169820,7 +170856,7 @@ static int fts3SegReaderNext( ** b-tree node. And that the final byte of the doclist is 0x00. If either ** of these statements is untrue, then the data structure is corrupt. */ - if( (&pReader->aNode[pReader->nNode] - pReader->aDoclist)nDoclist + if( pReader->nDoclist > pReader->nNode-(pReader->aDoclist-pReader->aNode) || (pReader->nPopulate==0 && pReader->aDoclist[pReader->nDoclist-1]) ){ return FTS_CORRUPT_VTAB; @@ -170020,8 +171056,13 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderNew( Fts3SegReader *pReader; /* Newly allocated SegReader object */ int nExtra = 0; /* Bytes to allocate segment root node */ - assert( iStartLeaf<=iEndLeaf ); + assert( zRoot!=0 || nRoot==0 ); +#ifdef CORRUPT_DB + assert( zRoot!=0 || CORRUPT_DB ); +#endif + if( iStartLeaf==0 ){ + if( iEndLeaf!=0 ) return FTS_CORRUPT_VTAB; nExtra = nRoot + FTS3_NODE_PADDING; } @@ -170041,7 +171082,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderNew( pReader->aNode = (char *)&pReader[1]; pReader->rootOnly = 1; pReader->nNode = nRoot; - memcpy(pReader->aNode, zRoot, nRoot); + if( nRoot ) memcpy(pReader->aNode, zRoot, nRoot); memset(&pReader->aNode[nRoot], 0, FTS3_NODE_PADDING); }else{ pReader->iCurrentBlock = iStartLeaf-1; @@ -170661,6 +171702,11 @@ static int fts3SegWriterAdd( nPrefix = fts3PrefixCompress(pWriter->zTerm, pWriter->nTerm, zTerm, nTerm); nSuffix = nTerm-nPrefix; + /* If nSuffix is zero or less, then zTerm/nTerm must be a prefix of + ** pWriter->zTerm/pWriter->nTerm. i.e. must be equal to or less than when + ** compared with BINARY collation. This indicates corruption. */ + if( nSuffix<=0 ) return FTS_CORRUPT_VTAB; + /* Figure out how many bytes are required by this new entry */ nReq = sqlite3Fts3VarintLen(nPrefix) + /* varint containing prefix size */ sqlite3Fts3VarintLen(nSuffix) + /* varint containing suffix size */ @@ -171368,7 +172414,9 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStep( }else{ iDelta = iDocid - iPrev; } - assert( iDelta>0 || (nDoclist==0 && iDelta==iDocid) ); + if( iDelta<=0 && (nDoclist>0 || iDelta!=iDocid) ){ + return FTS_CORRUPT_VTAB; + } assert( nDoclist>0 || iDelta==iDocid ); nByte = sqlite3Fts3VarintLen(iDelta) + (isRequirePos?nList+1:0); @@ -171734,14 +172782,16 @@ static void fts3DecodeIntArray( const char *zBuf, /* The BLOB containing the varints */ int nBuf /* size of the BLOB */ ){ - int i, j; - UNUSED_PARAMETER(nBuf); - for(i=j=0; iiOff += fts3GetVarint32(&p->aNode[p->iOff], &nSuffix); if( nPrefix>p->iOff || nSuffix>p->nNode-p->iOff ){ - return SQLITE_CORRUPT_VTAB; + return FTS_CORRUPT_VTAB; } blobGrowBuffer(&p->term, nPrefix+nSuffix, &rc); if( rc==SQLITE_OK ){ @@ -172157,7 +173207,7 @@ static int nodeReaderNext(NodeReader *p){ if( p->iChild==0 ){ p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &p->nDoclist); if( (p->nNode-p->iOff)nDoclist ){ - return SQLITE_CORRUPT_VTAB; + return FTS_CORRUPT_VTAB; } p->aDoclist = &p->aNode[p->iOff]; p->iOff += p->nDoclist; @@ -174287,7 +175337,7 @@ static void (*fts3MIBufferAlloc(MatchinfoBuffer *p, u32 **paOut))(void*){ aOut = &p->aMatchinfo[p->nElem+2]; xRet = fts3MIBufferFree; }else{ - aOut = (u32*)sqlite3_malloc(p->nElem * sizeof(u32)); + aOut = (u32*)sqlite3_malloc64(p->nElem * sizeof(u32)); if( aOut ){ xRet = sqlite3_free; if( p->bGlobal ) memcpy(aOut, &p->aMatchinfo[1], p->nElem*sizeof(u32)); @@ -174542,7 +175592,8 @@ static void fts3SnippetDetails( int j; u64 mPhrase = (u64)1 << i; u64 mPos = (u64)1 << (iCsr - iStart); - assert( iCsr>=iStart ); + assert( iCsr>=iStart && (iCsr - iStart)<=64 ); + assert( i>=0 && i<=64 ); if( (mCover|mCovered)&mPhrase ){ iScore++; }else{ @@ -174584,11 +175635,14 @@ static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){ int iFirst = 0; pPhrase->pList = pCsr; fts3GetDeltaPosition(&pCsr, &iFirst); - assert( iFirst>=0 ); - pPhrase->pHead = pCsr; - pPhrase->pTail = pCsr; - pPhrase->iHead = iFirst; - pPhrase->iTail = iFirst; + if( iFirst<0 ){ + rc = FTS_CORRUPT_VTAB; + }else{ + pPhrase->pHead = pCsr; + pPhrase->pTail = pCsr; + pPhrase->iHead = iFirst; + pPhrase->iTail = iFirst; + } }else{ assert( rc!=SQLITE_OK || ( pPhrase->pList==0 && pPhrase->pHead==0 && pPhrase->pTail==0 @@ -174625,7 +175679,7 @@ static int fts3BestSnippet( int rc; /* Return Code */ int nList; /* Number of phrases in expression */ SnippetIter sIter; /* Iterates through snippet candidates */ - int nByte; /* Number of bytes of space to allocate */ + sqlite3_int64 nByte; /* Number of bytes of space to allocate */ int iBestScore = -1; /* Best snippet score found so far */ int i; /* Loop counter */ @@ -174643,7 +175697,7 @@ static int fts3BestSnippet( ** the required space using malloc(). */ nByte = sizeof(SnippetPhrase) * nList; - sIter.aPhrase = (SnippetPhrase *)sqlite3_malloc(nByte); + sIter.aPhrase = (SnippetPhrase *)sqlite3_malloc64(nByte); if( !sIter.aPhrase ){ return SQLITE_NOMEM; } @@ -174713,8 +175767,8 @@ static int fts3StringAppend( ** appended data. */ if( pStr->n+nAppend+1>=pStr->nAlloc ){ - int nAlloc = pStr->nAlloc+nAppend+100; - char *zNew = sqlite3_realloc(pStr->z, nAlloc); + sqlite3_int64 nAlloc = pStr->nAlloc+(sqlite3_int64)nAppend+100; + char *zNew = sqlite3_realloc64(pStr->z, nAlloc); if( !zNew ){ return SQLITE_NOMEM; } @@ -174769,6 +175823,7 @@ static int fts3SnippetShift( for(nLeft=0; !(hlmask & ((u64)1 << nLeft)); nLeft++); for(nRight=0; !(hlmask & ((u64)1 << (nSnippet-1-nRight))); nRight++); + assert( (nSnippet-1-nRight)<=63 && (nSnippet-1-nRight)>=0 ); nDesired = (nLeft-nRight)/2; /* Ideally, the start of the snippet should be pushed forward in the @@ -174961,7 +176016,7 @@ static int fts3ColumnlistCount(char **ppCollist){ /* ** This function gathers 'y' or 'b' data for a single phrase. */ -static void fts3ExprLHits( +static int fts3ExprLHits( Fts3Expr *pExpr, /* Phrase expression node */ MatchInfo *p /* Matchinfo context */ ){ @@ -174991,25 +176046,29 @@ static void fts3ExprLHits( if( *pIter!=0x01 ) break; pIter++; pIter += fts3GetVarint32(pIter, &iCol); + if( iCol>=p->nCol ) return FTS_CORRUPT_VTAB; } + return SQLITE_OK; } /* ** Gather the results for matchinfo directives 'y' and 'b'. */ -static void fts3ExprLHitGather( +static int fts3ExprLHitGather( Fts3Expr *pExpr, MatchInfo *p ){ + int rc = SQLITE_OK; assert( (pExpr->pLeft==0)==(pExpr->pRight==0) ); if( pExpr->bEof==0 && pExpr->iDocid==p->pCursor->iPrevId ){ if( pExpr->pLeft ){ - fts3ExprLHitGather(pExpr->pLeft, p); - fts3ExprLHitGather(pExpr->pRight, p); + rc = fts3ExprLHitGather(pExpr->pLeft, p); + if( rc==SQLITE_OK ) rc = fts3ExprLHitGather(pExpr->pRight, p); }else{ - fts3ExprLHits(pExpr, p); + rc = fts3ExprLHits(pExpr, p); } } + return rc; } /* @@ -175226,11 +176285,12 @@ static int fts3MatchinfoLcs(Fts3Cursor *pCsr, MatchInfo *pInfo){ int i; int iCol; int nToken = 0; + int rc = SQLITE_OK; /* Allocate and populate the array of LcsIterator objects. The array ** contains one element for each matchable phrase in the query. **/ - aIter = sqlite3_malloc(sizeof(LcsIterator) * pCsr->nPhrase); + aIter = sqlite3_malloc64(sizeof(LcsIterator) * pCsr->nPhrase); if( !aIter ) return SQLITE_NOMEM; memset(aIter, 0, sizeof(LcsIterator) * pCsr->nPhrase); (void)fts3ExprIterate(pCsr->pExpr, fts3MatchinfoLcsCb, (void*)aIter); @@ -175246,13 +176306,16 @@ static int fts3MatchinfoLcs(Fts3Cursor *pCsr, MatchInfo *pInfo){ int nLive = 0; /* Number of iterators in aIter not at EOF */ for(i=0; inPhrase; i++){ - int rc; LcsIterator *pIt = &aIter[i]; rc = sqlite3Fts3EvalPhrasePoslist(pCsr, pIt->pExpr, iCol, &pIt->pRead); - if( rc!=SQLITE_OK ) return rc; + if( rc!=SQLITE_OK ) goto matchinfo_lcs_out; if( pIt->pRead ){ pIt->iPos = pIt->iPosOffset; - fts3LcsIteratorAdvance(&aIter[i]); + fts3LcsIteratorAdvance(pIt); + if( pIt->pRead==0 ){ + rc = FTS_CORRUPT_VTAB; + goto matchinfo_lcs_out; + } nLive++; } } @@ -175284,8 +176347,9 @@ static int fts3MatchinfoLcs(Fts3Cursor *pCsr, MatchInfo *pInfo){ pInfo->aMatchinfo[iCol] = nLcs; } + matchinfo_lcs_out: sqlite3_free(aIter); - return SQLITE_OK; + return rc; } /* @@ -175381,7 +176445,7 @@ static int fts3MatchinfoValues( case FTS3_MATCHINFO_LHITS: { int nZero = fts3MatchinfoSize(pInfo, zArg[i]) * sizeof(u32); memset(pInfo->aMatchinfo, 0, nZero); - fts3ExprLHitGather(pCsr->pExpr, pInfo); + rc = fts3ExprLHitGather(pCsr->pExpr, pInfo); break; } @@ -175533,6 +176597,10 @@ SQLITE_PRIVATE void sqlite3Fts3Snippet( return; } + /* Limit the snippet length to 64 tokens. */ + if( nToken<-64 ) nToken = -64; + if( nToken>+64 ) nToken = +64; + for(nSnippet=1; 1; nSnippet++){ int iSnip; /* Loop counter 0..nSnippet-1 */ @@ -175675,7 +176743,7 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets( if( rc!=SQLITE_OK ) goto offsets_out; /* Allocate the array of TermOffset iterators. */ - sCtx.aTerm = (TermOffset *)sqlite3_malloc(sizeof(TermOffset)*nToken); + sCtx.aTerm = (TermOffset *)sqlite3_malloc64(sizeof(TermOffset)*nToken); if( 0==sCtx.aTerm ){ rc = SQLITE_NOMEM; goto offsets_out; @@ -175900,7 +176968,7 @@ typedef struct unicode_cursor unicode_cursor; struct unicode_tokenizer { sqlite3_tokenizer base; - int bRemoveDiacritic; + int eRemoveDiacritic; int nException; int *aiException; }; @@ -175973,7 +177041,7 @@ static int unicodeAddExceptions( int *aNew; /* New aiException[] array */ int nNew; /* Number of valid entries in array aNew[] */ - aNew = sqlite3_realloc(p->aiException, (p->nException+nEntry)*sizeof(int)); + aNew = sqlite3_realloc64(p->aiException,(p->nException+nEntry)*sizeof(int)); if( aNew==0 ) return SQLITE_NOMEM; nNew = p->nException; @@ -176045,17 +177113,20 @@ static int unicodeCreate( pNew = (unicode_tokenizer *) sqlite3_malloc(sizeof(unicode_tokenizer)); if( pNew==NULL ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(unicode_tokenizer)); - pNew->bRemoveDiacritic = 1; + pNew->eRemoveDiacritic = 1; for(i=0; rc==SQLITE_OK && ibRemoveDiacritic = 1; + pNew->eRemoveDiacritic = 1; } else if( n==19 && memcmp("remove_diacritics=0", z, 19)==0 ){ - pNew->bRemoveDiacritic = 0; + pNew->eRemoveDiacritic = 0; + } + else if( n==19 && memcmp("remove_diacritics=2", z, 19)==0 ){ + pNew->eRemoveDiacritic = 2; } else if( n>=11 && memcmp("tokenchars=", z, 11)==0 ){ rc = unicodeAddExceptions(pNew, 1, &z[11], n-11); @@ -176159,7 +177230,7 @@ static int unicodeNext( /* Grow the output buffer if required. */ if( (zOut-pCsr->zToken)>=(pCsr->nAlloc-4) ){ - char *zNew = sqlite3_realloc(pCsr->zToken, pCsr->nAlloc+64); + char *zNew = sqlite3_realloc64(pCsr->zToken, pCsr->nAlloc+64); if( !zNew ) return SQLITE_NOMEM; zOut = &zNew[zOut - pCsr->zToken]; pCsr->zToken = zNew; @@ -176168,7 +177239,7 @@ static int unicodeNext( /* Write the folded case of the last character read to the output */ zEnd = z; - iOut = sqlite3FtsUnicodeFold((int)iCode, p->bRemoveDiacritic); + iOut = sqlite3FtsUnicodeFold((int)iCode, p->eRemoveDiacritic); if( iOut ){ WRITE_UTF8(zOut, iOut); } @@ -176213,7 +177284,7 @@ SQLITE_PRIVATE void sqlite3Fts3UnicodeTokenizer(sqlite3_tokenizer_module const * /************** End of fts3_unicode.c ****************************************/ /************** Begin file fts3_unicode2.c ***********************************/ /* -** 2012 May 25 +** 2012-05-25 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -176373,32 +177444,48 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsalnum(int c){ ** E"). The resuls of passing a codepoint that corresponds to an ** uppercase letter are undefined. */ -static int remove_diacritic(int c){ +static int remove_diacritic(int c, int bComplex){ unsigned short aDia[] = { 0, 1797, 1848, 1859, 1891, 1928, 1940, 1995, 2024, 2040, 2060, 2110, 2168, 2206, 2264, 2286, 2344, 2383, 2472, 2488, 2516, 2596, 2668, 2732, 2782, 2842, 2894, 2954, 2984, 3000, 3028, 3336, - 3456, 3696, 3712, 3728, 3744, 3896, 3912, 3928, - 3968, 4008, 4040, 4106, 4138, 4170, 4202, 4234, - 4266, 4296, 4312, 4344, 4408, 4424, 4472, 4504, - 6148, 6198, 6264, 6280, 6360, 6429, 6505, 6529, - 61448, 61468, 61534, 61592, 61642, 61688, 61704, 61726, - 61784, 61800, 61836, 61880, 61914, 61948, 61998, 62122, - 62154, 62200, 62218, 62302, 62364, 62442, 62478, 62536, - 62554, 62584, 62604, 62640, 62648, 62656, 62664, 62730, - 62924, 63050, 63082, 63274, 63390, + 3456, 3696, 3712, 3728, 3744, 3766, 3832, 3896, + 3912, 3928, 3944, 3968, 4008, 4040, 4056, 4106, + 4138, 4170, 4202, 4234, 4266, 4296, 4312, 4344, + 4408, 4424, 4442, 4472, 4488, 4504, 6148, 6198, + 6264, 6280, 6360, 6429, 6505, 6529, 61448, 61468, + 61512, 61534, 61592, 61610, 61642, 61672, 61688, 61704, + 61726, 61784, 61800, 61816, 61836, 61880, 61896, 61914, + 61948, 61998, 62062, 62122, 62154, 62184, 62200, 62218, + 62252, 62302, 62364, 62410, 62442, 62478, 62536, 62554, + 62584, 62604, 62640, 62648, 62656, 62664, 62730, 62766, + 62830, 62890, 62924, 62974, 63032, 63050, 63082, 63118, + 63182, 63242, 63274, 63310, 63368, 63390, }; - char aChar[] = { - '\0', 'a', 'c', 'e', 'i', 'n', 'o', 'u', 'y', 'y', 'a', 'c', - 'd', 'e', 'e', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'o', 'r', - 's', 't', 'u', 'u', 'w', 'y', 'z', 'o', 'u', 'a', 'i', 'o', - 'u', 'g', 'k', 'o', 'j', 'g', 'n', 'a', 'e', 'i', 'o', 'r', - 'u', 's', 't', 'h', 'a', 'e', 'o', 'y', '\0', '\0', '\0', '\0', - '\0', '\0', '\0', '\0', 'a', 'b', 'd', 'd', 'e', 'f', 'g', 'h', - 'h', 'i', 'k', 'l', 'l', 'm', 'n', 'p', 'r', 'r', 's', 't', - 'u', 'v', 'w', 'w', 'x', 'y', 'z', 'h', 't', 'w', 'y', 'a', - 'e', 'i', 'o', 'u', 'y', +#define HIBIT ((unsigned char)0x80) + unsigned char aChar[] = { + '\0', 'a', 'c', 'e', 'i', 'n', + 'o', 'u', 'y', 'y', 'a', 'c', + 'd', 'e', 'e', 'g', 'h', 'i', + 'j', 'k', 'l', 'n', 'o', 'r', + 's', 't', 'u', 'u', 'w', 'y', + 'z', 'o', 'u', 'a', 'i', 'o', + 'u', 'u'|HIBIT, 'a'|HIBIT, 'g', 'k', 'o', + 'o'|HIBIT, 'j', 'g', 'n', 'a'|HIBIT, 'a', + 'e', 'i', 'o', 'r', 'u', 's', + 't', 'h', 'a', 'e', 'o'|HIBIT, 'o', + 'o'|HIBIT, 'y', '\0', '\0', '\0', '\0', + '\0', '\0', '\0', '\0', 'a', 'b', + 'c'|HIBIT, 'd', 'd', 'e'|HIBIT, 'e', 'e'|HIBIT, + 'f', 'g', 'h', 'h', 'i', 'i'|HIBIT, + 'k', 'l', 'l'|HIBIT, 'l', 'm', 'n', + 'o'|HIBIT, 'p', 'r', 'r'|HIBIT, 'r', 's', + 's'|HIBIT, 't', 'u', 'u'|HIBIT, 'v', 'w', + 'w', 'x', 'y', 'z', 'h', 't', + 'w', 'y', 'a', 'a'|HIBIT, 'a'|HIBIT, 'a'|HIBIT, + 'e', 'e'|HIBIT, 'e'|HIBIT, 'i', 'o', 'o'|HIBIT, + 'o'|HIBIT, 'o'|HIBIT, 'u', 'u'|HIBIT, 'u'|HIBIT, 'y', }; unsigned int key = (((unsigned int)c)<<3) | 0x00000007; @@ -176415,7 +177502,8 @@ static int remove_diacritic(int c){ } } assert( key>=aDia[iRes] ); - return ((c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : (int)aChar[iRes]); + if( bComplex==0 && (aChar[iRes] & 0x80) ) return c; + return (c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : ((int)aChar[iRes] & 0x7F); } @@ -176428,8 +177516,8 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int c){ unsigned int mask1 = 0x000361F8; if( c<768 || c>817 ) return 0; return (c < 768+32) ? - (mask0 & (1 << (c-768))) : - (mask1 & (1 << (c-768-32))); + (mask0 & ((unsigned int)1 << (c-768))) : + (mask1 & ((unsigned int)1 << (c-768-32))); } @@ -176442,7 +177530,7 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int c){ ** The results are undefined if the value passed to this function ** is less than zero. */ -SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int bRemoveDiacritic){ +SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int eRemoveDiacritic){ /* Each entry in the following array defines a rule for folding a range ** of codepoints to lower case. The rule applies to a range of nRange ** codepoints starting at codepoint iCode. @@ -176565,7 +177653,9 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int bRemoveDiacritic){ assert( ret>0 ); } - if( bRemoveDiacritic ) ret = remove_diacritic(ret); + if( eRemoveDiacritic ){ + ret = remove_diacritic(ret, eRemoveDiacritic==2); + } } else if( c>=66560 && c<66600 ){ @@ -177272,7 +178362,7 @@ static JSON_NOINLINE int jsonParseAddNodeExpand( assert( pParse->nNode>=pParse->nAlloc ); if( pParse->oom ) return -1; nNew = pParse->nAlloc*2 + 10; - pNew = sqlite3_realloc(pParse->aNode, sizeof(JsonNode)*nNew); + pNew = sqlite3_realloc64(pParse->aNode, sizeof(JsonNode)*nNew); if( pNew==0 ){ pParse->oom = 1; return -1; @@ -177546,7 +178636,7 @@ static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){ static int jsonParseFindParents(JsonParse *pParse){ u32 *aUp; assert( pParse->aUp==0 ); - aUp = pParse->aUp = sqlite3_malloc( sizeof(u32)*pParse->nNode ); + aUp = pParse->aUp = sqlite3_malloc64( sizeof(u32)*pParse->nNode ); if( aUp==0 ){ pParse->oom = 1; return SQLITE_NOMEM; @@ -177608,7 +178698,7 @@ static JsonParse *jsonParseCached( pMatch->iHold = iMaxHold+1; return pMatch; } - p = sqlite3_malloc( sizeof(*p) + nJson + 1 ); + p = sqlite3_malloc64( sizeof(*p) + nJson + 1 ); if( p==0 ){ sqlite3_result_error_nomem(pCtx); return 0; @@ -179253,6 +180343,9 @@ struct Rtree { u8 inWrTrans; /* True if inside write transaction */ u8 nAux; /* # of auxiliary columns in %_rowid */ u8 nAuxNotNull; /* Number of initial not-null aux columns */ +#ifdef SQLITE_DEBUG + u8 bCorrupt; /* Shadow table corruption detected */ +#endif int iDepth; /* Current depth of the r-tree structure */ char *zDb; /* Name of database containing r-tree table */ char *zName; /* Name of r-tree table */ @@ -179312,6 +180405,15 @@ struct Rtree { # define RTREE_ZERO 0.0 #endif +/* +** Set the Rtree.bCorrupt flag +*/ +#ifdef SQLITE_DEBUG +# define RTREE_IS_CORRUPT(X) ((X)->bCorrupt = 1) +#else +# define RTREE_IS_CORRUPT(X) +#endif + /* ** When doing a search of an r-tree, instances of the following structure ** record intermediate results from the tree walk. @@ -179678,8 +180780,8 @@ static void nodeZero(Rtree *pRtree, RtreeNode *p){ ** Given a node number iNode, return the corresponding key to use ** in the Rtree.aHash table. */ -static int nodeHash(i64 iNode){ - return iNode % HASHSIZE; +static unsigned int nodeHash(i64 iNode){ + return ((unsigned)iNode) % HASHSIZE; } /* @@ -179724,7 +180826,7 @@ static void nodeHashDelete(Rtree *pRtree, RtreeNode *pNode){ */ static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent){ RtreeNode *pNode; - pNode = (RtreeNode *)sqlite3_malloc(sizeof(RtreeNode) + pRtree->iNodeSize); + pNode = (RtreeNode *)sqlite3_malloc64(sizeof(RtreeNode) + pRtree->iNodeSize); if( pNode ){ memset(pNode, 0, sizeof(RtreeNode) + pRtree->iNodeSize); pNode->zData = (u8 *)&pNode[1]; @@ -179748,6 +180850,18 @@ static void nodeBlobReset(Rtree *pRtree){ } } +/* +** Check to see if pNode is the same as pParent or any of the parents +** of pParent. +*/ +static int nodeInParentChain(const RtreeNode *pNode, const RtreeNode *pParent){ + do{ + if( pNode==pParent ) return 1; + pParent = pParent->pParent; + }while( pParent ); + return 0; +} + /* ** Obtain a reference to an r-tree node. */ @@ -179766,6 +180880,10 @@ static int nodeAcquire( if( (pNode = nodeHashLookup(pRtree, iNode))!=0 ){ assert( !pParent || !pNode->pParent || pNode->pParent==pParent ); if( pParent && !pNode->pParent ){ + if( nodeInParentChain(pNode, pParent) ){ + RTREE_IS_CORRUPT(pRtree); + return SQLITE_CORRUPT_VTAB; + } pParent->nRef++; pNode->pParent = pParent; } @@ -179796,9 +180914,12 @@ static int nodeAcquire( *ppNode = 0; /* If unable to open an sqlite3_blob on the desired row, that can only ** be because the shadow tables hold erroneous data. */ - if( rc==SQLITE_ERROR ) rc = SQLITE_CORRUPT_VTAB; + if( rc==SQLITE_ERROR ){ + rc = SQLITE_CORRUPT_VTAB; + RTREE_IS_CORRUPT(pRtree); + } }else if( pRtree->iNodeSize==sqlite3_blob_bytes(pRtree->pNodeBlob) ){ - pNode = (RtreeNode *)sqlite3_malloc(sizeof(RtreeNode)+pRtree->iNodeSize); + pNode = (RtreeNode *)sqlite3_malloc64(sizeof(RtreeNode)+pRtree->iNodeSize); if( !pNode ){ rc = SQLITE_NOMEM; }else{ @@ -179811,7 +180932,6 @@ static int nodeAcquire( pNode->pNext = 0; rc = sqlite3_blob_read(pRtree->pNodeBlob, pNode->zData, pRtree->iNodeSize, 0); - nodeReference(pParent); } } @@ -179825,6 +180945,7 @@ static int nodeAcquire( pRtree->iDepth = readInt16(pNode->zData); if( pRtree->iDepth>RTREE_MAX_DEPTH ){ rc = SQLITE_CORRUPT_VTAB; + RTREE_IS_CORRUPT(pRtree); } } @@ -179835,14 +180956,17 @@ static int nodeAcquire( if( pNode && rc==SQLITE_OK ){ if( NCELL(pNode)>((pRtree->iNodeSize-4)/pRtree->nBytesPerCell) ){ rc = SQLITE_CORRUPT_VTAB; + RTREE_IS_CORRUPT(pRtree); } } if( rc==SQLITE_OK ){ if( pNode!=0 ){ + nodeReference(pParent); nodeHashInsert(pRtree, pNode); }else{ rc = SQLITE_CORRUPT_VTAB; + RTREE_IS_CORRUPT(pRtree); } *ppNode = pNode; }else{ @@ -180068,7 +181192,7 @@ static void rtreeRelease(Rtree *pRtree){ pRtree->inWrTrans = 0; assert( pRtree->nCursor==0 ); nodeBlobReset(pRtree); - assert( pRtree->nNodeRef==0 ); + assert( pRtree->nNodeRef==0 || pRtree->bCorrupt ); sqlite3_finalize(pRtree->pWriteNode); sqlite3_finalize(pRtree->pDeleteNode); sqlite3_finalize(pRtree->pReadRowid); @@ -180127,7 +181251,7 @@ static int rtreeOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ Rtree *pRtree = (Rtree *)pVTab; RtreeCursor *pCsr; - pCsr = (RtreeCursor *)sqlite3_malloc(sizeof(RtreeCursor)); + pCsr = (RtreeCursor *)sqlite3_malloc64(sizeof(RtreeCursor)); if( pCsr ){ memset(pCsr, 0, sizeof(RtreeCursor)); pCsr->base.pVtab = pVTab; @@ -180400,6 +181524,7 @@ static int nodeRowidIndex( return SQLITE_OK; } } + RTREE_IS_CORRUPT(pRtree); return SQLITE_CORRUPT_VTAB; } @@ -180493,7 +181618,7 @@ static RtreeSearchPoint *rtreeEnqueue( RtreeSearchPoint *pNew; if( pCur->nPoint>=pCur->nPointAlloc ){ int nNew = pCur->nPointAlloc*2 + 8; - pNew = sqlite3_realloc(pCur->aPoint, nNew*sizeof(pCur->aPoint[0])); + pNew = sqlite3_realloc64(pCur->aPoint, nNew*sizeof(pCur->aPoint[0])); if( pNew==0 ) return 0; pCur->aPoint = pNew; pCur->nPointAlloc = nNew; @@ -180895,7 +182020,7 @@ static int rtreeFilter( */ rc = nodeAcquire(pRtree, 1, 0, &pRoot); if( rc==SQLITE_OK && argc>0 ){ - pCsr->aConstraint = sqlite3_malloc(sizeof(RtreeConstraint)*argc); + pCsr->aConstraint = sqlite3_malloc64(sizeof(RtreeConstraint)*argc); pCsr->nConstraint = argc; if( !pCsr->aConstraint ){ rc = SQLITE_NOMEM; @@ -181040,20 +182165,20 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ ){ u8 op; switch( p->op ){ - case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; break; - case SQLITE_INDEX_CONSTRAINT_GT: op = RTREE_GT; break; - case SQLITE_INDEX_CONSTRAINT_LE: op = RTREE_LE; break; - case SQLITE_INDEX_CONSTRAINT_LT: op = RTREE_LT; break; - case SQLITE_INDEX_CONSTRAINT_GE: op = RTREE_GE; break; - default: - assert( p->op==SQLITE_INDEX_CONSTRAINT_MATCH ); - op = RTREE_MATCH; - break; + case SQLITE_INDEX_CONSTRAINT_EQ: op = RTREE_EQ; break; + case SQLITE_INDEX_CONSTRAINT_GT: op = RTREE_GT; break; + case SQLITE_INDEX_CONSTRAINT_LE: op = RTREE_LE; break; + case SQLITE_INDEX_CONSTRAINT_LT: op = RTREE_LT; break; + case SQLITE_INDEX_CONSTRAINT_GE: op = RTREE_GE; break; + case SQLITE_INDEX_CONSTRAINT_MATCH: op = RTREE_MATCH; break; + default: op = 0; break; + } + if( op ){ + zIdxStr[iIdx++] = op; + zIdxStr[iIdx++] = (char)(p->iColumn - 1 + '0'); + pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2); + pIdxInfo->aConstraintUsage[ii].omit = 1; } - zIdxStr[iIdx++] = op; - zIdxStr[iIdx++] = (char)(p->iColumn - 1 + '0'); - pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2); - pIdxInfo->aConstraintUsage[ii].omit = 1; } } @@ -181089,11 +182214,11 @@ static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){ #endif { switch( pRtree->nDim ){ - case 5: area = p->aCoord[9].i - p->aCoord[8].i; - case 4: area *= p->aCoord[7].i - p->aCoord[6].i; - case 3: area *= p->aCoord[5].i - p->aCoord[4].i; - case 2: area *= p->aCoord[3].i - p->aCoord[2].i; - default: area *= p->aCoord[1].i - p->aCoord[0].i; + case 5: area = (i64)p->aCoord[9].i - (i64)p->aCoord[8].i; + case 4: area *= (i64)p->aCoord[7].i - (i64)p->aCoord[6].i; + case 3: area *= (i64)p->aCoord[5].i - (i64)p->aCoord[4].i; + case 2: area *= (i64)p->aCoord[3].i - (i64)p->aCoord[2].i; + default: area *= (i64)p->aCoord[1].i - (i64)p->aCoord[0].i; } } return area; @@ -181262,12 +182387,14 @@ static int AdjustTree( RtreeCell *pCell /* This cell was just inserted */ ){ RtreeNode *p = pNode; + int cnt = 0; while( p->pParent ){ RtreeNode *pParent = p->pParent; RtreeCell cell; int iCell; - if( nodeParentIndex(pRtree, p, &iCell) ){ + if( (++cnt)>1000 || nodeParentIndex(pRtree, p, &iCell) ){ + RTREE_IS_CORRUPT(pRtree); return SQLITE_CORRUPT_VTAB; } @@ -181464,9 +182591,9 @@ static int splitNodeStartree( int iBestSplit = 0; RtreeDValue fBestMargin = RTREE_ZERO; - int nByte = (pRtree->nDim+1)*(sizeof(int*)+nCell*sizeof(int)); + sqlite3_int64 nByte = (pRtree->nDim+1)*(sizeof(int*)+nCell*sizeof(int)); - aaSorted = (int **)sqlite3_malloc(nByte); + aaSorted = (int **)sqlite3_malloc64(nByte); if( !aaSorted ){ return SQLITE_NOMEM; } @@ -181587,7 +182714,7 @@ static int SplitNode( /* Allocate an array and populate it with a copy of pCell and ** all cells from node pLeft. Then zero the original node. */ - aCell = sqlite3_malloc((sizeof(RtreeCell)+sizeof(int))*(nCell+1)); + aCell = sqlite3_malloc64((sizeof(RtreeCell)+sizeof(int))*(nCell+1)); if( !aCell ){ rc = SQLITE_NOMEM; goto splitnode_out; @@ -181735,7 +182862,10 @@ static int fixLeafParent(Rtree *pRtree, RtreeNode *pLeaf){ } rc = sqlite3_reset(pRtree->pReadParent); if( rc==SQLITE_OK ) rc = rc2; - if( rc==SQLITE_OK && !pChild->pParent ) rc = SQLITE_CORRUPT_VTAB; + if( rc==SQLITE_OK && !pChild->pParent ){ + RTREE_IS_CORRUPT(pRtree); + rc = SQLITE_CORRUPT_VTAB; + } pChild = pChild->pParent; } return rc; @@ -181875,7 +183005,7 @@ static int Reinsert( /* Allocate the buffers used by this operation. The allocation is ** relinquished before this function returns. */ - aCell = (RtreeCell *)sqlite3_malloc(n * ( + aCell = (RtreeCell *)sqlite3_malloc64(n * ( sizeof(RtreeCell) + /* aCell array */ sizeof(int) + /* aOrder array */ sizeof(int) + /* aSpare array */ @@ -182049,8 +183179,12 @@ static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){ rc = findLeafNode(pRtree, iDelete, &pLeaf, 0); } +#ifdef CORRUPT_DB + assert( pLeaf!=0 || rc!=SQLITE_OK || CORRUPT_DB ); +#endif + /* Delete the cell in question from the leaf node. */ - if( rc==SQLITE_OK ){ + if( rc==SQLITE_OK && pLeaf ){ int rc2; rc = nodeRowidIndex(pRtree, pLeaf, iDelete, &iCell); if( rc==SQLITE_OK ){ @@ -182322,7 +183456,7 @@ static int rtreeUpdate( rc = rc2; } } - if( pRtree->nAux ){ + if( rc==SQLITE_OK && pRtree->nAux ){ sqlite3_stmt *pUp = pRtree->pWriteAux; int jj; sqlite3_bind_int64(pUp, 1, *pRowid); @@ -182520,6 +183654,7 @@ static int rtreeSqlInit( }; sqlite3_stmt **appStmt[N_STATEMENT]; int i; + const int f = SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_NO_VTAB; pRtree->db = db; @@ -182576,8 +183711,7 @@ static int rtreeSqlInit( } zSql = sqlite3_mprintf(zFormat, zDb, zPrefix); if( zSql ){ - rc = sqlite3_prepare_v3(db, zSql, -1, SQLITE_PREPARE_PERSISTENT, - appStmt[i], 0); + rc = sqlite3_prepare_v3(db, zSql, -1, f, appStmt[i], 0); }else{ rc = SQLITE_NOMEM; } @@ -182607,8 +183741,7 @@ static int rtreeSqlInit( if( zSql==0 ){ rc = SQLITE_NOMEM; }else{ - rc = sqlite3_prepare_v3(db, zSql, -1, SQLITE_PREPARE_PERSISTENT, - &pRtree->pWriteAux, 0); + rc = sqlite3_prepare_v3(db, zSql, -1, f, &pRtree->pWriteAux, 0); sqlite3_free(zSql); } } @@ -182684,6 +183817,7 @@ static int getNodeSize( *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); }else if( pRtree->iNodeSize<(512-64) ){ rc = SQLITE_CORRUPT_VTAB; + RTREE_IS_CORRUPT(pRtree); *pzErr = sqlite3_mprintf("undersize RTree blobs in \"%q_node\"", pRtree->zName); } @@ -182739,7 +183873,7 @@ static int rtreeInit( /* Allocate the sqlite3_vtab structure */ nDb = (int)strlen(argv[1]); nName = (int)strlen(argv[2]); - pRtree = (Rtree *)sqlite3_malloc(sizeof(Rtree)+nDb+nName+2); + pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName+2); if( !pRtree ){ return SQLITE_NOMEM; } @@ -183007,8 +184141,7 @@ static void rtreeCheckAppendMsg(RtreeCheck *pCheck, const char *zFmt, ...){ static u8 *rtreeCheckGetNode(RtreeCheck *pCheck, i64 iNode, int *pnNode){ u8 *pRet = 0; /* Return value */ - assert( pCheck->rc==SQLITE_OK ); - if( pCheck->pGetNode==0 ){ + if( pCheck->rc==SQLITE_OK && pCheck->pGetNode==0 ){ pCheck->pGetNode = rtreeCheckPrepare(pCheck, "SELECT data FROM %Q.'%q_node' WHERE nodeno=?", pCheck->zDb, pCheck->zTab @@ -183020,7 +184153,7 @@ static u8 *rtreeCheckGetNode(RtreeCheck *pCheck, i64 iNode, int *pnNode){ if( sqlite3_step(pCheck->pGetNode)==SQLITE_ROW ){ int nNode = sqlite3_column_bytes(pCheck->pGetNode, 0); const u8 *pNode = (const u8*)sqlite3_column_blob(pCheck->pGetNode, 0); - pRet = sqlite3_malloc(nNode); + pRet = sqlite3_malloc64(nNode); if( pRet==0 ){ pCheck->rc = SQLITE_NOMEM; }else{ @@ -183499,6 +184632,14 @@ struct GeoPoly { */ #define GEOPOLY_SZ(N) (sizeof(GeoPoly) + sizeof(GeoCoord)*2*((N)-4)) +/* Macros to access coordinates of a GeoPoly. +** We have to use these macros, rather than just say p->a[i] in order +** to silence (incorrect) UBSAN warnings if the array index is too large. +*/ +#define GeoX(P,I) (((GeoCoord*)(P)->a)[(I)*2]) +#define GeoY(P,I) (((GeoCoord*)(P)->a)[(I)*2+1]) + + /* ** State of a parse of a GeoJSON input. */ @@ -183691,8 +184832,9 @@ static GeoPoly *geopolyFuncParam( memcpy(p->hdr, a, nByte); if( a[0] != *(unsigned char*)&x ){ int ii; - for(ii=0; iia[ii]); + for(ii=0; iihdr[0] ^= 1; } @@ -183751,9 +184893,9 @@ static void geopolyJsonFunc( int i; sqlite3_str_append(x, "[", 1); for(i=0; inVertex; i++){ - sqlite3_str_appendf(x, "[%!g,%!g],", p->a[i*2], p->a[i*2+1]); + sqlite3_str_appendf(x, "[%!g,%!g],", GeoX(p,i), GeoY(p,i)); } - sqlite3_str_appendf(x, "[%!g,%!g]]", p->a[0], p->a[1]); + sqlite3_str_appendf(x, "[%!g,%!g]]", GeoX(p,0), GeoY(p,0)); sqlite3_result_text(context, sqlite3_str_finish(x), -1, sqlite3_free); sqlite3_free(p); } @@ -183770,7 +184912,9 @@ static void geopolySvgFunc( int argc, sqlite3_value **argv ){ - GeoPoly *p = geopolyFuncParam(context, argv[0], 0); + GeoPoly *p; + if( argc<1 ) return; + p = geopolyFuncParam(context, argv[0], 0); if( p ){ sqlite3 *db = sqlite3_context_db_handle(context); sqlite3_str *x = sqlite3_str_new(db); @@ -183778,10 +184922,10 @@ static void geopolySvgFunc( char cSep = '\''; sqlite3_str_appendf(x, "a[i*2], p->a[i*2+1]); + sqlite3_str_appendf(x, "%c%g,%g", cSep, GeoX(p,i), GeoY(p,i)); cSep = ' '; } - sqlite3_str_appendf(x, " %g,%g'", p->a[0], p->a[1]); + sqlite3_str_appendf(x, " %g,%g'", GeoX(p,0), GeoY(p,0)); for(i=1; inVertex; ii++){ - x0 = p->a[ii*2]; - y0 = p->a[ii*2+1]; + x0 = GeoX(p,ii); + y0 = GeoY(p,ii); x1 = (GeoCoord)(A*x0 + B*y0 + E); y1 = (GeoCoord)(C*x0 + D*y0 + F); - p->a[ii*2] = x1; - p->a[ii*2+1] = y1; + GeoX(p,ii) = x1; + GeoY(p,ii) = y1; } sqlite3_result_blob(context, p->hdr, 4+8*p->nVertex, SQLITE_TRANSIENT); @@ -183850,12 +184994,12 @@ static double geopolyArea(GeoPoly *p){ double rArea = 0.0; int ii; for(ii=0; iinVertex-1; ii++){ - rArea += (p->a[ii*2] - p->a[ii*2+2]) /* (x0 - x1) */ - * (p->a[ii*2+1] + p->a[ii*2+3]) /* (y0 + y1) */ + rArea += (GeoX(p,ii) - GeoX(p,ii+1)) /* (x0 - x1) */ + * (GeoY(p,ii) + GeoY(p,ii+1)) /* (y0 + y1) */ * 0.5; } - rArea += (p->a[ii*2] - p->a[0]) /* (xN - x0) */ - * (p->a[ii*2+1] + p->a[1]) /* (yN + y0) */ + rArea += (GeoX(p,ii) - GeoX(p,0)) /* (xN - x0) */ + * (GeoY(p,ii) + GeoY(p,0)) /* (yN + y0) */ * 0.5; return rArea; } @@ -183902,13 +185046,13 @@ static void geopolyCcwFunc( if( p ){ if( geopolyArea(p)<0.0 ){ int ii, jj; - for(ii=2, jj=p->nVertex*2 - 2; iia[ii]; - p->a[ii] = p->a[jj]; - p->a[jj] = t; - t = p->a[ii+1]; - p->a[ii+1] = p->a[jj+1]; - p->a[jj+1] = t; + for(ii=1, jj=p->nVertex-1; iihdr, @@ -183968,8 +185112,8 @@ static void geopolyRegularFunc( p->hdr[3] = n&0xff; for(i=0; ia[i*2] = x - r*geopolySine(rAngle-0.5*GEOPOLY_PI); - p->a[i*2+1] = y + r*geopolySine(rAngle); + GeoX(p,i) = x - r*geopolySine(rAngle-0.5*GEOPOLY_PI); + GeoY(p,i) = y + r*geopolySine(rAngle); } sqlite3_result_blob(context, p->hdr, 4+8*n, SQLITE_TRANSIENT); sqlite3_free(p); @@ -184006,13 +185150,13 @@ static GeoPoly *geopolyBBox( } if( p ){ int ii; - mnX = mxX = p->a[0]; - mnY = mxY = p->a[1]; + mnX = mxX = GeoX(p,0); + mnY = mxY = GeoY(p,0); for(ii=1; iinVertex; ii++){ - double r = p->a[ii*2]; + double r = GeoX(p,ii); if( rmxX ) mxX = (float)r; - r = p->a[ii*2+1]; + r = GeoY(p,ii); if( rmxY ) mxY = (float)r; } @@ -184032,14 +185176,14 @@ static GeoPoly *geopolyBBox( pOut->hdr[1] = 0; pOut->hdr[2] = 0; pOut->hdr[3] = 4; - pOut->a[0] = mnX; - pOut->a[1] = mnY; - pOut->a[2] = mxX; - pOut->a[3] = mnY; - pOut->a[4] = mxX; - pOut->a[5] = mxY; - pOut->a[6] = mnX; - pOut->a[7] = mxY; + GeoX(pOut,0) = mnX; + GeoY(pOut,0) = mnY; + GeoX(pOut,1) = mxX; + GeoY(pOut,1) = mnY; + GeoX(pOut,2) = mxX; + GeoY(pOut,2) = mxY; + GeoX(pOut,3) = mnX; + GeoY(pOut,3) = mxY; }else{ sqlite3_free(p); aCoord[0].f = mnX; @@ -184177,14 +185321,14 @@ static void geopolyContainsPointFunc( int ii; if( p1==0 ) return; for(ii=0; iinVertex-1; ii++){ - v = pointBeneathLine(x0,y0,p1->a[ii*2],p1->a[ii*2+1], - p1->a[ii*2+2],p1->a[ii*2+3]); + v = pointBeneathLine(x0,y0,GeoX(p1,ii), GeoY(p1,ii), + GeoX(p1,ii+1),GeoY(p1,ii+1)); if( v==2 ) break; cnt += v; } if( v!=2 ){ - v = pointBeneathLine(x0,y0,p1->a[ii*2],p1->a[ii*2+1], - p1->a[0],p1->a[1]); + v = pointBeneathLine(x0,y0,GeoX(p1,ii), GeoY(p1,ii), + GeoX(p1,0), GeoY(p1,0)); } if( v==2 ){ sqlite3_result_int(context, 1); @@ -184306,10 +185450,10 @@ static void geopolyAddSegments( unsigned int i; GeoCoord *x; for(i=0; i<(unsigned)pPoly->nVertex-1; i++){ - x = pPoly->a + (i*2); + x = &GeoX(pPoly,i); geopolyAddOneSegment(p, x[0], x[1], x[2], x[3], side, i); } - x = pPoly->a + (i*2); + x = &GeoX(pPoly,i); geopolyAddOneSegment(p, x[0], x[1], pPoly->a[0], pPoly->a[1], side, i); } @@ -185254,12 +186398,12 @@ static void rtreeMatchArgFree(void *pArg){ static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){ RtreeGeomCallback *pGeomCtx = (RtreeGeomCallback *)sqlite3_user_data(ctx); RtreeMatchArg *pBlob; - int nBlob; + sqlite3_int64 nBlob; int memErr = 0; nBlob = sizeof(RtreeMatchArg) + (nArg-1)*sizeof(RtreeDValue) + nArg*sizeof(sqlite3_value*); - pBlob = (RtreeMatchArg *)sqlite3_malloc(nBlob); + pBlob = (RtreeMatchArg *)sqlite3_malloc64(nBlob); if( !pBlob ){ sqlite3_result_error_nomem(ctx); }else{ @@ -185970,7 +187114,7 @@ static int icuCreate( if( argc>0 ){ n = strlen(argv[0])+1; } - p = (IcuTokenizer *)sqlite3_malloc(sizeof(IcuTokenizer)+n); + p = (IcuTokenizer *)sqlite3_malloc64(sizeof(IcuTokenizer)+n); if( !p ){ return SQLITE_NOMEM; } @@ -186027,7 +187171,7 @@ static int icuOpen( nInput = strlen(zInput); } nChar = nInput+1; - pCsr = (IcuCursor *)sqlite3_malloc( + pCsr = (IcuCursor *)sqlite3_malloc64( sizeof(IcuCursor) + /* IcuCursor */ ((nChar+3)&~3) * sizeof(UChar) + /* IcuCursor.aChar[] */ (nChar+1) * sizeof(int) /* IcuCursor.aOffset[] */ @@ -186599,7 +187743,11 @@ SQLITE_API sqlite3rbu *sqlite3rbu_open( ** name of the state database is "-vacuum", where ** is the name of the target database file. In this case, on UNIX, if the ** state database is not already present in the file-system, it is created -** with the same permissions as the target db is made. +** with the same permissions as the target db is made. +** +** With an RBU vacuum, it is an SQLITE_MISUSE error if the name of the +** state database ends with "-vactmp". This name is reserved for internal +** use. ** ** This function does not delete the state database after an RBU vacuum ** is completed, even if it created it. However, if the call to @@ -189257,7 +190405,7 @@ static void rbuOpenDatabase(sqlite3rbu *p, int *pbRetry){ if( *zExtra=='\0' ) zExtra = 0; } - zTarget = sqlite3_mprintf("file:%s-vacuum?rbu_memory=1%s%s", + zTarget = sqlite3_mprintf("file:%s-vactmp?rbu_memory=1%s%s", sqlite3_db_filename(p->dbRbu, "main"), (zExtra==0 ? "" : "&"), (zExtra==0 ? "" : zExtra) ); @@ -190523,6 +191671,12 @@ SQLITE_API sqlite3rbu *sqlite3rbu_vacuum( const char *zState ){ if( zTarget==0 ){ return rbuMisuseError(); } + if( zState ){ + int n = strlen(zState); + if( n>=7 && 0==memcmp("-vactmp", &zState[n-7], 7) ){ + return rbuMisuseError(); + } + } /* TODO: Check that both arguments are non-NULL */ return openRbuHandle(0, zTarget, zState); } @@ -190719,7 +191873,10 @@ SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *p){ if( p->eStage==RBU_STAGE_OAL ){ assert( rc!=SQLITE_DONE ); if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbRbu, "COMMIT", 0, 0, 0); - if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbRbu, "BEGIN IMMEDIATE", 0, 0, 0); + if( rc==SQLITE_OK ){ + const char *zBegin = rbuIsVacuum(p) ? "BEGIN" : "BEGIN IMMEDIATE"; + rc = sqlite3_exec(p->dbRbu, zBegin, 0, 0, 0); + } if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbMain, "BEGIN IMMEDIATE", 0, 0,0); } @@ -192250,6 +193407,10 @@ statNextRestart: goto statNextRestart; /* Tail recursion */ } pCsr->iPage++; + if( pCsr->iPage>=ArraySize(pCsr->aPage) ){ + statResetCsr(pCsr); + return SQLITE_CORRUPT_BKPT; + } assert( p==&pCsr->aPage[pCsr->iPage-1] ); if( p->iCell==p->nCell ){ @@ -192321,7 +193482,6 @@ static int statFilter( StatTable *pTab = (StatTable*)(pCursor->pVtab); char *zSql; int rc = SQLITE_OK; - char *zMaster; if( idxNum==1 ){ const char *zDbase = (const char*)sqlite3_value_text(argv[0]); @@ -192337,13 +193497,12 @@ static int statFilter( statResetCsr(pCsr); sqlite3_finalize(pCsr->pStmt); pCsr->pStmt = 0; - zMaster = pCsr->iDb==1 ? "sqlite_temp_master" : "sqlite_master"; zSql = sqlite3_mprintf( "SELECT 'sqlite_master' AS name, 1 AS rootpage, 'table' AS type" " UNION ALL " "SELECT name, rootpage, type" - " FROM \"%w\".%s WHERE rootpage!=0" - " ORDER BY name", pTab->db->aDb[pCsr->iDb].zDbSName, zMaster); + " FROM \"%w\".sqlite_master WHERE rootpage!=0" + " ORDER BY name", pTab->db->aDb[pCsr->iDb].zDbSName); if( zSql==0 ){ return SQLITE_NOMEM_BKPT; }else{ @@ -193231,7 +194390,7 @@ static void sessionPutI64(u8 *aBuf, sqlite3_int64 i){ static int sessionSerializeValue( u8 *aBuf, /* If non-NULL, write serialized value here */ sqlite3_value *pValue, /* Value to serialize */ - int *pnWrite /* IN/OUT: Increment by bytes written */ + sqlite3_int64 *pnWrite /* IN/OUT: Increment by bytes written */ ){ int nByte; /* Size of serialized value in bytes */ @@ -193772,7 +194931,7 @@ static int sessionGrowHash(int bPatchset, SessionTable *pTab){ SessionChange **apNew; int nNew = (pTab->nChange ? pTab->nChange : 128) * 2; - apNew = (SessionChange **)sqlite3_malloc(sizeof(SessionChange *) * nNew); + apNew = (SessionChange **)sqlite3_malloc64(sizeof(SessionChange *) * nNew); if( apNew==0 ){ if( pTab->nChange==0 ){ return SQLITE_ERROR; @@ -193838,7 +194997,7 @@ static int sessionTableInfo( char *zPragma; sqlite3_stmt *pStmt; int rc; - int nByte; + sqlite3_int64 nByte; int nDbCol = 0; int nThis; int i; @@ -193881,7 +195040,7 @@ static int sessionTableInfo( if( rc==SQLITE_OK ){ nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1); - pAlloc = sqlite3_malloc(nByte); + pAlloc = sqlite3_malloc64(nByte); if( pAlloc==0 ){ rc = SQLITE_NOMEM; } @@ -194022,7 +195181,7 @@ static void sessionPreupdateOneChange( int iHash; int bNull = 0; int rc = SQLITE_OK; - SessionStat1Ctx stat1 = {0}; + SessionStat1Ctx stat1 = {{0,0,0,0,0},0}; if( pSession->rc ) return; @@ -194079,7 +195238,7 @@ static void sessionPreupdateOneChange( ** this is an SQLITE_UPDATE or SQLITE_DELETE), or just the PK ** values (if this is an INSERT). */ SessionChange *pChange; /* New change object */ - int nByte; /* Number of bytes to allocate */ + sqlite3_int64 nByte; /* Number of bytes to allocate */ int i; /* Used to iterate through columns */ assert( rc==SQLITE_OK ); @@ -194104,7 +195263,7 @@ static void sessionPreupdateOneChange( } /* Allocate the change object */ - pChange = (SessionChange *)sqlite3_malloc(nByte); + pChange = (SessionChange *)sqlite3_malloc64(nByte); if( !pChange ){ rc = SQLITE_NOMEM; goto error_out; @@ -194548,7 +195707,7 @@ SQLITE_API int sqlite3session_create( *ppSession = 0; /* Allocate and populate the new session object. */ - pNew = (sqlite3_session *)sqlite3_malloc(sizeof(sqlite3_session) + nDb + 1); + pNew = (sqlite3_session *)sqlite3_malloc64(sizeof(sqlite3_session) + nDb + 1); if( !pNew ) return SQLITE_NOMEM; memset(pNew, 0, sizeof(sqlite3_session)); pNew->db = db; @@ -194667,7 +195826,7 @@ SQLITE_API int sqlite3session_attach( if( !pTab ){ /* Allocate new SessionTable object. */ - pTab = (SessionTable *)sqlite3_malloc(sizeof(SessionTable) + nName + 1); + pTab = (SessionTable *)sqlite3_malloc64(sizeof(SessionTable) + nName + 1); if( !pTab ){ rc = SQLITE_NOMEM; }else{ @@ -194727,7 +195886,7 @@ static int sessionBufferGrow(SessionBuffer *p, int nByte, int *pRc){ static void sessionAppendValue(SessionBuffer *p, sqlite3_value *pVal, int *pRc){ int rc = *pRc; if( rc==SQLITE_OK ){ - int nByte = 0; + sqlite3_int64 nByte = 0; rc = sessionSerializeValue(0, pVal, &nByte); sessionBufferGrow(p, nByte, &rc); if( rc==SQLITE_OK ){ @@ -195603,7 +196762,7 @@ static int sessionValueSetStr( ** argument to sqlite3ValueSetStr() and have the copy created ** automatically. But doing so makes it difficult to detect any OOM ** error. Hence the code to create the copy externally. */ - u8 *aCopy = sqlite3_malloc(nData+1); + u8 *aCopy = sqlite3_malloc64((sqlite3_int64)nData+1); if( aCopy==0 ) return SQLITE_NOMEM; memcpy(aCopy, aData, nData); sqlite3ValueSetStr(pVal, nData, (char*)aCopy, enc, sqlite3_free); @@ -196216,7 +197375,7 @@ static int sessionChangesetInvert( int iCol; if( 0==apVal ){ - apVal = (sqlite3_value **)sqlite3_malloc(sizeof(apVal[0])*nCol*2); + apVal = (sqlite3_value **)sqlite3_malloc64(sizeof(apVal[0])*nCol*2); if( 0==apVal ){ rc = SQLITE_NOMEM; goto finished_invert; @@ -197489,7 +198648,7 @@ static int sessionChangeMerge( int rc = SQLITE_OK; if( !pExist ){ - pNew = (SessionChange *)sqlite3_malloc(sizeof(SessionChange) + nRec); + pNew = (SessionChange *)sqlite3_malloc64(sizeof(SessionChange) + nRec); if( !pNew ){ return SQLITE_NOMEM; } @@ -197522,8 +198681,8 @@ static int sessionChangeMerge( if( pExist->op==SQLITE_DELETE && pExist->bIndirect ){ *ppNew = pExist; }else{ - int nByte = nRec + pExist->nRecord + sizeof(SessionChange); - pNew = (SessionChange*)sqlite3_malloc(nByte); + sqlite3_int64 nByte = nRec + pExist->nRecord + sizeof(SessionChange); + pNew = (SessionChange*)sqlite3_malloc64(nByte); if( pNew==0 ){ rc = SQLITE_NOMEM; }else{ @@ -197583,14 +198742,14 @@ static int sessionChangeMerge( assert( pNew==0 ); }else{ u8 *aExist = pExist->aRecord; - int nByte; + sqlite3_int64 nByte; u8 *aCsr; /* Allocate a new SessionChange object. Ensure that the aRecord[] ** buffer of the new object is large enough to hold any record that ** may be generated by combining the input records. */ nByte = sizeof(SessionChange) + pExist->nRecord + nRec; - pNew = (SessionChange *)sqlite3_malloc(nByte); + pNew = (SessionChange *)sqlite3_malloc64(nByte); if( !pNew ){ sqlite3_free(pExist); return SQLITE_NOMEM; @@ -197696,7 +198855,7 @@ static int sessionChangesetToHash( if( !pTab ){ SessionTable **ppTab; - pTab = sqlite3_malloc(sizeof(SessionTable) + nCol + nNew+1); + pTab = sqlite3_malloc64(sizeof(SessionTable) + nCol + nNew+1); if( !pTab ){ rc = SQLITE_NOMEM; break; @@ -198470,12 +199629,8 @@ struct Fts5PhraseIter { ** ** Usually, output parameter *piPhrase is set to the phrase number, *piCol ** to the column in which it occurs and *piOff the token offset of the -** first token of the phrase. The exception is if the table was created -** with the offsets=0 option specified. In this case *piOff is always -** set to -1. -** -** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM) -** if an error occurs. +** first token of the phrase. Returns SQLITE_OK if successful, or an error +** code (i.e. SQLITE_NOMEM) if an error occurs. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. @@ -198764,11 +199919,11 @@ struct Fts5ExtensionApi { ** the tokenizer substitutes "first" for "1st" and the query works ** as expected. ** -**
  • By adding multiple synonyms for a single term to the FTS index. -** In this case, when tokenizing query text, the tokenizer may -** provide multiple synonyms for a single term within the document. -** FTS5 then queries the index for each synonym individually. For -** example, faced with the query: +**
  • By querying the index for all synonyms of each query term +** separately. In this case, when tokenizing query text, the +** tokenizer may provide multiple synonyms for a single term +** within the document. FTS5 then queries the index for each +** synonym individually. For example, faced with the query: ** ** ** ... MATCH 'first place' @@ -198792,7 +199947,7 @@ struct Fts5ExtensionApi { ** "place". ** ** This way, even if the tokenizer does not provide synonyms -** when tokenizing query text (it should not - to do would be +** when tokenizing query text (it should not - to do so would be ** inefficient), it doesn't matter if the user queries for ** 'first + place' or '1st + place', as there are entries in the ** FTS index corresponding to both forms of the first token. @@ -199017,6 +200172,12 @@ SQLITE_API extern int sqlite3_fts5_may_be_corrupt; # define assert_nc(x) assert(x) #endif +/* +** A version of memcmp() that does not cause asan errors if one of the pointer +** parameters is NULL and the number of bytes to compare is zero. +*/ +#define fts5Memcmp(s1, s2, n) ((n)==0 ? 0 : memcmp((s1), (s2), (n))) + /* Mark a function parameter as unused, to suppress nuisance compiler ** warnings. */ #ifndef UNUSED_PARAM @@ -199204,7 +200365,7 @@ static void sqlite3Fts5Put32(u8*, int); static int sqlite3Fts5Get32(const u8*); #define FTS5_POS2COLUMN(iPos) (int)(iPos >> 32) -#define FTS5_POS2OFFSET(iPos) (int)(iPos & 0xFFFFFFFF) +#define FTS5_POS2OFFSET(iPos) (int)(iPos & 0x7FFFFFFF) typedef struct Fts5PoslistReader Fts5PoslistReader; struct Fts5PoslistReader { @@ -199239,7 +200400,7 @@ static int sqlite3Fts5PoslistNext64( ); /* Malloc utility */ -static void *sqlite3Fts5MallocZero(int *pRc, int nByte); +static void *sqlite3Fts5MallocZero(int *pRc, sqlite3_int64 nByte); static char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn); /* Character set tests (like isspace(), isalpha() etc.) */ @@ -199450,9 +200611,19 @@ static int sqlite3Fts5PutVarint(unsigned char *p, u64 v); /************************************************************************** -** Interface to code in fts5.c. +** Interface to code in fts5_main.c. */ +/* +** Virtual-table object. +*/ +typedef struct Fts5Table Fts5Table; +struct Fts5Table { + sqlite3_vtab base; /* Base class used by SQLite core */ + Fts5Config *pConfig; /* Virtual table configuration */ + Fts5Index *pIndex; /* Full-text index */ +}; + static int sqlite3Fts5GetTokenizer( Fts5Global*, const char **azArg, @@ -199462,7 +200633,9 @@ static int sqlite3Fts5GetTokenizer( char **pzErr ); -static Fts5Index *sqlite3Fts5IndexFromCsrid(Fts5Global*, i64, Fts5Config **); +static Fts5Table *sqlite3Fts5TableFromCsrid(Fts5Global*, i64); + +static int sqlite3Fts5FlushToDisk(Fts5Table*); /* ** End of interface to code in fts5.c. @@ -199718,7 +200891,7 @@ static int sqlite3Fts5UnicodeIsdiacritic(int c); static int sqlite3Fts5UnicodeFold(int c, int bRemoveDiacritic); static int sqlite3Fts5UnicodeCatParse(const char*, u8*); -static int sqlite3Fts5UnicodeCategory(int iCode); +static int sqlite3Fts5UnicodeCategory(u32 iCode); static void sqlite3Fts5UnicodeAscii(u8*, u8*); /* ** End of interface to code in fts5_unicode2.c. @@ -200622,41 +201795,70 @@ static void fts5yy_shift( fts5yyTraceShift(fts5yypParser, fts5yyNewState, "Shift"); } -/* The following table contains information about every rule that -** is used during the reduce. -*/ -static const struct { - fts5YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ - signed char nrhs; /* Negative of the number of RHS symbols in the rule */ -} fts5yyRuleInfo[] = { - { 16, -1 }, /* (0) input ::= expr */ - { 20, -4 }, /* (1) colset ::= MINUS LCP colsetlist RCP */ - { 20, -3 }, /* (2) colset ::= LCP colsetlist RCP */ - { 20, -1 }, /* (3) colset ::= STRING */ - { 20, -2 }, /* (4) colset ::= MINUS STRING */ - { 21, -2 }, /* (5) colsetlist ::= colsetlist STRING */ - { 21, -1 }, /* (6) colsetlist ::= STRING */ - { 17, -3 }, /* (7) expr ::= expr AND expr */ - { 17, -3 }, /* (8) expr ::= expr OR expr */ - { 17, -3 }, /* (9) expr ::= expr NOT expr */ - { 17, -5 }, /* (10) expr ::= colset COLON LP expr RP */ - { 17, -3 }, /* (11) expr ::= LP expr RP */ - { 17, -1 }, /* (12) expr ::= exprlist */ - { 19, -1 }, /* (13) exprlist ::= cnearset */ - { 19, -2 }, /* (14) exprlist ::= exprlist cnearset */ - { 18, -1 }, /* (15) cnearset ::= nearset */ - { 18, -3 }, /* (16) cnearset ::= colset COLON nearset */ - { 22, -1 }, /* (17) nearset ::= phrase */ - { 22, -2 }, /* (18) nearset ::= CARET phrase */ - { 22, -5 }, /* (19) nearset ::= STRING LP nearphrases neardist_opt RP */ - { 23, -1 }, /* (20) nearphrases ::= phrase */ - { 23, -2 }, /* (21) nearphrases ::= nearphrases phrase */ - { 25, 0 }, /* (22) neardist_opt ::= */ - { 25, -2 }, /* (23) neardist_opt ::= COMMA STRING */ - { 24, -4 }, /* (24) phrase ::= phrase PLUS STRING star_opt */ - { 24, -2 }, /* (25) phrase ::= STRING star_opt */ - { 26, -1 }, /* (26) star_opt ::= STAR */ - { 26, 0 }, /* (27) star_opt ::= */ +/* For rule J, fts5yyRuleInfoLhs[J] contains the symbol on the left-hand side +** of that rule */ +static const fts5YYCODETYPE fts5yyRuleInfoLhs[] = { + 16, /* (0) input ::= expr */ + 20, /* (1) colset ::= MINUS LCP colsetlist RCP */ + 20, /* (2) colset ::= LCP colsetlist RCP */ + 20, /* (3) colset ::= STRING */ + 20, /* (4) colset ::= MINUS STRING */ + 21, /* (5) colsetlist ::= colsetlist STRING */ + 21, /* (6) colsetlist ::= STRING */ + 17, /* (7) expr ::= expr AND expr */ + 17, /* (8) expr ::= expr OR expr */ + 17, /* (9) expr ::= expr NOT expr */ + 17, /* (10) expr ::= colset COLON LP expr RP */ + 17, /* (11) expr ::= LP expr RP */ + 17, /* (12) expr ::= exprlist */ + 19, /* (13) exprlist ::= cnearset */ + 19, /* (14) exprlist ::= exprlist cnearset */ + 18, /* (15) cnearset ::= nearset */ + 18, /* (16) cnearset ::= colset COLON nearset */ + 22, /* (17) nearset ::= phrase */ + 22, /* (18) nearset ::= CARET phrase */ + 22, /* (19) nearset ::= STRING LP nearphrases neardist_opt RP */ + 23, /* (20) nearphrases ::= phrase */ + 23, /* (21) nearphrases ::= nearphrases phrase */ + 25, /* (22) neardist_opt ::= */ + 25, /* (23) neardist_opt ::= COMMA STRING */ + 24, /* (24) phrase ::= phrase PLUS STRING star_opt */ + 24, /* (25) phrase ::= STRING star_opt */ + 26, /* (26) star_opt ::= STAR */ + 26, /* (27) star_opt ::= */ +}; + +/* For rule J, fts5yyRuleInfoNRhs[J] contains the negative of the number +** of symbols on the right-hand side of that rule. */ +static const signed char fts5yyRuleInfoNRhs[] = { + -1, /* (0) input ::= expr */ + -4, /* (1) colset ::= MINUS LCP colsetlist RCP */ + -3, /* (2) colset ::= LCP colsetlist RCP */ + -1, /* (3) colset ::= STRING */ + -2, /* (4) colset ::= MINUS STRING */ + -2, /* (5) colsetlist ::= colsetlist STRING */ + -1, /* (6) colsetlist ::= STRING */ + -3, /* (7) expr ::= expr AND expr */ + -3, /* (8) expr ::= expr OR expr */ + -3, /* (9) expr ::= expr NOT expr */ + -5, /* (10) expr ::= colset COLON LP expr RP */ + -3, /* (11) expr ::= LP expr RP */ + -1, /* (12) expr ::= exprlist */ + -1, /* (13) exprlist ::= cnearset */ + -2, /* (14) exprlist ::= exprlist cnearset */ + -1, /* (15) cnearset ::= nearset */ + -3, /* (16) cnearset ::= colset COLON nearset */ + -1, /* (17) nearset ::= phrase */ + -2, /* (18) nearset ::= CARET phrase */ + -5, /* (19) nearset ::= STRING LP nearphrases neardist_opt RP */ + -1, /* (20) nearphrases ::= phrase */ + -2, /* (21) nearphrases ::= nearphrases phrase */ + 0, /* (22) neardist_opt ::= */ + -2, /* (23) neardist_opt ::= COMMA STRING */ + -4, /* (24) phrase ::= phrase PLUS STRING star_opt */ + -2, /* (25) phrase ::= STRING star_opt */ + -1, /* (26) star_opt ::= STAR */ + 0, /* (27) star_opt ::= */ }; static void fts5yy_accept(fts5yyParser*); /* Forward Declaration */ @@ -200688,7 +201890,7 @@ static fts5YYACTIONTYPE fts5yy_reduce( fts5yymsp = fts5yypParser->fts5yytos; #ifndef NDEBUG if( fts5yyTraceFILE && fts5yyruleno<(int)(sizeof(fts5yyRuleName)/sizeof(fts5yyRuleName[0])) ){ - fts5yysize = fts5yyRuleInfo[fts5yyruleno].nrhs; + fts5yysize = fts5yyRuleInfoNRhs[fts5yyruleno]; if( fts5yysize ){ fprintf(fts5yyTraceFILE, "%sReduce %d [%s], go to state %d.\n", fts5yyTracePrompt, @@ -200703,7 +201905,7 @@ static fts5YYACTIONTYPE fts5yy_reduce( /* Check that the stack is large enough to grow by a single entry ** if the RHS of the rule is empty. This ensures that there is room ** enough on the stack to push the LHS value */ - if( fts5yyRuleInfo[fts5yyruleno].nrhs==0 ){ + if( fts5yyRuleInfoNRhs[fts5yyruleno]==0 ){ #ifdef fts5YYTRACKMAXSTACKDEPTH if( (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack)>fts5yypParser->fts5yyhwm ){ fts5yypParser->fts5yyhwm++; @@ -200887,9 +202089,9 @@ static fts5YYACTIONTYPE fts5yy_reduce( break; /********** End reduce actions ************************************************/ }; - assert( fts5yyrulenozOut = sqlite3_mprintf("%z%.*s", p->zOut, n, z); if( p->zOut==0 ) *pRc = SQLITE_NOMEM; @@ -201452,7 +202654,7 @@ static int fts5SentenceFinderAdd(Fts5SFinder *p, int iAdd){ int nNew = p->nFirstAlloc ? p->nFirstAlloc*2 : 64; int *aNew; - aNew = (int*)sqlite3_realloc(p->aFirst, nNew*sizeof(int)); + aNew = (int*)sqlite3_realloc64(p->aFirst, nNew*sizeof(int)); if( aNew==0 ) return SQLITE_NOMEM; p->aFirst = aNew; p->nFirstAlloc = nNew; @@ -201519,11 +202721,12 @@ static int fts5SnippetScore( int nInst; int nScore = 0; int iLast = 0; + sqlite3_int64 iEnd = (sqlite3_int64)iPos + nToken; rc = pApi->xInstCount(pFts, &nInst); for(i=0; ixInst(pFts, i, &ip, &ic, &iOff); - if( rc==SQLITE_OK && ic==iCol && iOff>=iPos && iOff<(iPos+nToken) ){ + if( rc==SQLITE_OK && ic==iCol && iOff>=iPos && iOffnDocsize ) iAdj = nDocsize - nToken; if( iAdj<0 ) iAdj = 0; *piPos = iAdj; @@ -201626,7 +202829,9 @@ static void fts5SnippetFunction( int jj; rc = pApi->xInst(pFts, ii, &ip, &ic, &io); - if( ic!=i || rc!=SQLITE_OK ) continue; + if( ic!=i ) continue; + if( io>nDocsize ) rc = FTS5_CORRUPT; + if( rc!=SQLITE_OK ) continue; memset(aSeen, 0, nPhrase); rc = fts5SnippetScore(pApi, pFts, nDocsize, aSeen, i, io, nToken, &nScore, &iAdj @@ -201752,13 +202957,13 @@ static int fts5Bm25GetData( int nPhrase; /* Number of phrases in query */ sqlite3_int64 nRow = 0; /* Number of rows in table */ sqlite3_int64 nToken = 0; /* Number of tokens in table */ - int nByte; /* Bytes of space to allocate */ + sqlite3_int64 nByte; /* Bytes of space to allocate */ int i; /* Allocate the Fts5Bm25Data object */ nPhrase = pApi->xPhraseCount(pFts); nByte = sizeof(Fts5Bm25Data) + nPhrase*2*sizeof(double); - p = (Fts5Bm25Data*)sqlite3_malloc(nByte); + p = (Fts5Bm25Data*)sqlite3_malloc64(nByte); if( p==0 ){ rc = SQLITE_NOMEM; }else{ @@ -201770,6 +202975,7 @@ static int fts5Bm25GetData( /* Calculate the average document length for this FTS5 table */ if( rc==SQLITE_OK ) rc = pApi->xRowCount(pFts, &nRow); + assert( rc!=SQLITE_OK || nRow>0 ); if( rc==SQLITE_OK ) rc = pApi->xColumnTotalSize(pFts, -1, &nToken); if( rc==SQLITE_OK ) p->avgdl = (double)nToken / (double)nRow; @@ -201895,8 +203101,6 @@ static int sqlite3Fts5AuxInit(fts5_api *pApi){ return rc; } - - /* ** 2014 May 31 ** @@ -201916,12 +203120,12 @@ static int sqlite3Fts5AuxInit(fts5_api *pApi){ static int sqlite3Fts5BufferSize(int *pRc, Fts5Buffer *pBuf, u32 nByte){ if( (u32)pBuf->nSpacenSpace ? pBuf->nSpace : 64; + u64 nNew = pBuf->nSpace ? pBuf->nSpace : 64; u8 *pNew; while( nNewp, nNew); + pNew = sqlite3_realloc64(pBuf->p, nNew); if( pNew==0 ){ *pRc = SQLITE_NOMEM; return 1; @@ -201951,7 +203155,7 @@ static void sqlite3Fts5Put32(u8 *aBuf, int iVal){ } static int sqlite3Fts5Get32(const u8 *aBuf){ - return (aBuf[0] << 24) + (aBuf[1] << 16) + (aBuf[2] << 8) + aBuf[3]; + return (int)((((u32)aBuf[0])<<24) + (aBuf[1]<<16) + (aBuf[2]<<8) + aBuf[3]); } /* @@ -202082,7 +203286,7 @@ static int sqlite3Fts5PoslistNext64( iOff = ((i64)iVal) << 32; fts5FastGetVarint32(a, i, iVal); } - *piOff = iOff + (iVal-2); + *piOff = iOff + ((iVal-2) & 0x7FFFFFFF); *pi = i; return 0; } @@ -202143,10 +203347,10 @@ static int sqlite3Fts5PoslistWriterAppend( return SQLITE_OK; } -static void *sqlite3Fts5MallocZero(int *pRc, int nByte){ +static void *sqlite3Fts5MallocZero(int *pRc, sqlite3_int64 nByte){ void *pRet = 0; if( *pRc==SQLITE_OK ){ - pRet = sqlite3_malloc(nByte); + pRet = sqlite3_malloc64(nByte); if( pRet==0 ){ if( nByte>0 ) *pRc = SQLITE_NOMEM; }else{ @@ -202589,7 +203793,7 @@ static int fts5ConfigParseSpecial( if( sqlite3_strnicmp("tokenize", zCmd, nCmd)==0 ){ const char *p = (const char*)zArg; - int nArg = (int)strlen(zArg) + 1; + sqlite3_int64 nArg = strlen(zArg) + 1; char **azArg = sqlite3Fts5MallocZero(&rc, sizeof(char*) * nArg); char *pDel = sqlite3Fts5MallocZero(&rc, nArg * 2); char *pSpace = pDel; @@ -202719,8 +203923,8 @@ static const char *fts5ConfigGobbleWord( ){ const char *zRet = 0; - int nIn = (int)strlen(zIn); - char *zOut = sqlite3_malloc(nIn+1); + sqlite3_int64 nIn = strlen(zIn); + char *zOut = sqlite3_malloc64(nIn+1); assert( *pRc==SQLITE_OK ); *pbQuoted = 0; @@ -202823,7 +204027,7 @@ static int sqlite3Fts5ConfigParse( int rc = SQLITE_OK; /* Return code */ Fts5Config *pRet; /* New object to return */ int i; - int nByte; + sqlite3_int64 nByte; *ppOut = pRet = (Fts5Config*)sqlite3_malloc(sizeof(Fts5Config)); if( pRet==0 ) return SQLITE_NOMEM; @@ -203467,7 +204671,7 @@ static int fts5ExprGetToken( return tok; } -static void *fts5ParseAlloc(u64 t){ return sqlite3_malloc((int)t); } +static void *fts5ParseAlloc(u64 t){ return sqlite3_malloc64((sqlite3_int64)t);} static void fts5ParseFree(void *p){ sqlite3_free(p); } static int sqlite3Fts5ExprNew( @@ -203612,8 +204816,8 @@ static int fts5ExprSynonymList( if( sqlite3Fts5IterEof(pIter)==0 && pIter->iRowid==iRowid ){ if( pIter->nData==0 ) continue; if( nIter==nAlloc ){ - int nByte = sizeof(Fts5PoslistReader) * nAlloc * 2; - Fts5PoslistReader *aNew = (Fts5PoslistReader*)sqlite3_malloc(nByte); + sqlite3_int64 nByte = sizeof(Fts5PoslistReader) * nAlloc * 2; + Fts5PoslistReader *aNew = (Fts5PoslistReader*)sqlite3_malloc64(nByte); if( aNew==0 ){ rc = SQLITE_NOMEM; goto synonym_poslist_out; @@ -203693,8 +204897,8 @@ static int fts5ExprPhraseIsMatch( /* If the aStatic[] array is not large enough, allocate a large array ** using sqlite3_malloc(). This approach could be improved upon. */ if( pPhrase->nTerm>ArraySize(aStatic) ){ - int nByte = sizeof(Fts5PoslistReader) * pPhrase->nTerm; - aIter = (Fts5PoslistReader*)sqlite3_malloc(nByte); + sqlite3_int64 nByte = sizeof(Fts5PoslistReader) * pPhrase->nTerm; + aIter = (Fts5PoslistReader*)sqlite3_malloc64(nByte); if( !aIter ) return SQLITE_NOMEM; } memset(aIter, 0, sizeof(Fts5PoslistReader) * pPhrase->nTerm); @@ -203828,7 +205032,7 @@ static int fts5ExprNearIsMatch(int *pRc, Fts5ExprNearset *pNear){ /* If the aStatic[] array is not large enough, allocate a large array ** using sqlite3_malloc(). This approach could be improved upon. */ if( pNear->nPhrase>ArraySize(aStatic) ){ - int nByte = sizeof(Fts5NearTrimmer) * pNear->nPhrase; + sqlite3_int64 nByte = sizeof(Fts5NearTrimmer) * pNear->nPhrase; a = (Fts5NearTrimmer*)sqlite3Fts5MallocZero(&rc, nByte); }else{ memset(aStatic, 0, sizeof(aStatic)); @@ -204737,8 +205941,9 @@ static Fts5ExprNearset *sqlite3Fts5ParseNearset( return pNear; } if( pNear==0 ){ - int nByte = sizeof(Fts5ExprNearset) + SZALLOC * sizeof(Fts5ExprPhrase*); - pRet = sqlite3_malloc(nByte); + sqlite3_int64 nByte; + nByte = sizeof(Fts5ExprNearset) + SZALLOC * sizeof(Fts5ExprPhrase*); + pRet = sqlite3_malloc64(nByte); if( pRet==0 ){ pParse->rc = SQLITE_NOMEM; }else{ @@ -204746,9 +205951,10 @@ static Fts5ExprNearset *sqlite3Fts5ParseNearset( } }else if( (pNear->nPhrase % SZALLOC)==0 ){ int nNew = pNear->nPhrase + SZALLOC; - int nByte = sizeof(Fts5ExprNearset) + nNew * sizeof(Fts5ExprPhrase*); + sqlite3_int64 nByte; - pRet = (Fts5ExprNearset*)sqlite3_realloc(pNear, nByte); + nByte = sizeof(Fts5ExprNearset) + nNew * sizeof(Fts5ExprPhrase*); + pRet = (Fts5ExprNearset*)sqlite3_realloc64(pNear, nByte); if( pRet==0 ){ pParse->rc = SQLITE_NOMEM; } @@ -204812,8 +206018,8 @@ static int fts5ParseTokenize( if( pPhrase && pPhrase->nTerm>0 && (tflags & FTS5_TOKEN_COLOCATED) ){ Fts5ExprTerm *pSyn; - int nByte = sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer) + nToken+1; - pSyn = (Fts5ExprTerm*)sqlite3_malloc(nByte); + sqlite3_int64 nByte = sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer) + nToken+1; + pSyn = (Fts5ExprTerm*)sqlite3_malloc64(nByte); if( pSyn==0 ){ rc = SQLITE_NOMEM; }else{ @@ -204829,7 +206035,7 @@ static int fts5ParseTokenize( Fts5ExprPhrase *pNew; int nNew = SZALLOC + (pPhrase ? pPhrase->nTerm : 0); - pNew = (Fts5ExprPhrase*)sqlite3_realloc(pPhrase, + pNew = (Fts5ExprPhrase*)sqlite3_realloc64(pPhrase, sizeof(Fts5ExprPhrase) + sizeof(Fts5ExprTerm) * nNew ); if( pNew==0 ){ @@ -204915,9 +206121,9 @@ static Fts5ExprPhrase *sqlite3Fts5ParseTerm( if( pAppend==0 ){ if( (pParse->nPhrase % 8)==0 ){ - int nByte = sizeof(Fts5ExprPhrase*) * (pParse->nPhrase + 8); + sqlite3_int64 nByte = sizeof(Fts5ExprPhrase*) * (pParse->nPhrase + 8); Fts5ExprPhrase **apNew; - apNew = (Fts5ExprPhrase**)sqlite3_realloc(pParse->apPhrase, nByte); + apNew = (Fts5ExprPhrase**)sqlite3_realloc64(pParse->apPhrase, nByte); if( apNew==0 ){ pParse->rc = SQLITE_NOMEM; fts5ExprPhraseFree(sCtx.pPhrase); @@ -204972,8 +206178,10 @@ static int sqlite3Fts5ExprClonePhrase( if( rc==SQLITE_OK ){ Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset; if( pColsetOrig ){ - int nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int); - Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte); + sqlite3_int64 nByte; + Fts5Colset *pColset; + nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int); + pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte); if( pColset ){ memcpy(pColset, pColsetOrig, nByte); } @@ -205093,7 +206301,7 @@ static Fts5Colset *fts5ParseColset( assert( pParse->rc==SQLITE_OK ); assert( iCol>=0 && iColpConfig->nCol ); - pNew = sqlite3_realloc(p, sizeof(Fts5Colset) + sizeof(int)*nCol); + pNew = sqlite3_realloc64(p, sizeof(Fts5Colset) + sizeof(int)*nCol); if( pNew==0 ){ pParse->rc = SQLITE_NOMEM; }else{ @@ -205189,7 +206397,7 @@ static Fts5Colset *sqlite3Fts5ParseColset( static Fts5Colset *fts5CloneColset(int *pRc, Fts5Colset *pOrig){ Fts5Colset *pRet; if( pOrig ){ - int nByte = sizeof(Fts5Colset) + (pOrig->nCol-1) * sizeof(int); + sqlite3_int64 nByte = sizeof(Fts5Colset) + (pOrig->nCol-1) * sizeof(int); pRet = (Fts5Colset*)sqlite3Fts5MallocZero(pRc, nByte); if( pRet ){ memcpy(pRet, pOrig, nByte); @@ -205343,7 +206551,7 @@ static Fts5ExprNode *sqlite3Fts5ParseNode( if( pParse->rc==SQLITE_OK ){ int nChild = 0; /* Number of children of returned node */ - int nByte; /* Bytes of space to allocate for this node */ + sqlite3_int64 nByte; /* Bytes of space to allocate for this node */ assert( (eType!=FTS5_STRING && !pNear) || (eType==FTS5_STRING && !pLeft && !pRight) @@ -205475,7 +206683,7 @@ static Fts5ExprNode *sqlite3Fts5ParseImplicitAnd( } static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){ - int nByte = 0; + sqlite3_int64 nByte = 0; Fts5ExprTerm *p; char *zQuoted; @@ -205483,7 +206691,7 @@ static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){ for(p=pTerm; p; p=p->pSynonym){ nByte += (int)strlen(pTerm->zTerm) * 2 + 3 + 2; } - zQuoted = sqlite3_malloc(nByte); + zQuoted = sqlite3_malloc64(nByte); if( zQuoted ){ int i = 0; @@ -205723,7 +206931,7 @@ static void fts5ExprFunction( } nConfig = 3 + (nArg-iArg); - azConfig = (const char**)sqlite3_malloc(sizeof(char*) * nConfig); + azConfig = (const char**)sqlite3_malloc64(sizeof(char*) * nConfig); if( azConfig==0 ){ sqlite3_result_error_nomem(pCtx); return; @@ -205809,7 +207017,7 @@ static void fts5ExprIsAlnum( sqlite3Fts5UnicodeCatParse("N*", aArr); sqlite3Fts5UnicodeCatParse("Co", aArr); iCode = sqlite3_value_int(apVal[0]); - sqlite3_result_int(pCtx, aArr[sqlite3Fts5UnicodeCategory(iCode)]); + sqlite3_result_int(pCtx, aArr[sqlite3Fts5UnicodeCategory((u32)iCode)]); } static void fts5ExprFold( @@ -205904,7 +207112,7 @@ struct Fts5PoslistPopulator { static Fts5PoslistPopulator *sqlite3Fts5ExprClearPoslists(Fts5Expr *pExpr, int bLive){ Fts5PoslistPopulator *pRet; - pRet = sqlite3_malloc(sizeof(Fts5PoslistPopulator)*pExpr->nPhrase); + pRet = sqlite3_malloc64(sizeof(Fts5PoslistPopulator)*pExpr->nPhrase); if( pRet ){ int i; memset(pRet, 0, sizeof(Fts5PoslistPopulator)*pExpr->nPhrase); @@ -206104,7 +207312,6 @@ static int sqlite3Fts5ExprPhraseCollist( return rc; } - /* ** 2014 August 11 ** @@ -206197,14 +207404,14 @@ static int sqlite3Fts5HashNew(Fts5Config *pConfig, Fts5Hash **ppNew, int *pnByte if( pNew==0 ){ rc = SQLITE_NOMEM; }else{ - int nByte; + sqlite3_int64 nByte; memset(pNew, 0, sizeof(Fts5Hash)); pNew->pnByte = pnByte; pNew->eDetail = pConfig->eDetail; pNew->nSlot = 1024; nByte = sizeof(Fts5HashEntry*) * pNew->nSlot; - pNew->aSlot = (Fts5HashEntry**)sqlite3_malloc(nByte); + pNew->aSlot = (Fts5HashEntry**)sqlite3_malloc64(nByte); if( pNew->aSlot==0 ){ sqlite3_free(pNew); *ppNew = 0; @@ -206272,7 +207479,7 @@ static int fts5HashResize(Fts5Hash *pHash){ Fts5HashEntry **apNew; Fts5HashEntry **apOld = pHash->aSlot; - apNew = (Fts5HashEntry**)sqlite3_malloc(nNew*sizeof(Fts5HashEntry*)); + apNew = (Fts5HashEntry**)sqlite3_malloc64(nNew*sizeof(Fts5HashEntry*)); if( !apNew ) return SQLITE_NOMEM; memset(apNew, 0, nNew*sizeof(Fts5HashEntry*)); @@ -206366,7 +207573,7 @@ static int sqlite3Fts5HashWrite( if( p==0 ){ /* Figure out how much space to allocate */ char *zKey; - int nByte = sizeof(Fts5HashEntry) + (nToken+1) + 1 + 64; + sqlite3_int64 nByte = sizeof(Fts5HashEntry) + (nToken+1) + 1 + 64; if( nByte<128 ) nByte = 128; /* Grow the Fts5Hash.aSlot[] array if necessary. */ @@ -206377,7 +207584,7 @@ static int sqlite3Fts5HashWrite( } /* Allocate new Fts5HashEntry and add it to the hash table. */ - p = (Fts5HashEntry*)sqlite3_malloc(nByte); + p = (Fts5HashEntry*)sqlite3_malloc64(nByte); if( !p ) return SQLITE_NOMEM; memset(p, 0, sizeof(Fts5HashEntry)); p->nAlloc = nByte; @@ -206416,12 +207623,12 @@ static int sqlite3Fts5HashWrite( ** + 5 bytes for the new position offset (32-bit max). */ if( (p->nAlloc - p->nData) < (9 + 4 + 1 + 3 + 5) ){ - int nNew = p->nAlloc * 2; + sqlite3_int64 nNew = p->nAlloc * 2; Fts5HashEntry *pNew; Fts5HashEntry **pp; - pNew = (Fts5HashEntry*)sqlite3_realloc(p, nNew); + pNew = (Fts5HashEntry*)sqlite3_realloc64(p, nNew); if( pNew==0 ) return SQLITE_NOMEM; - pNew->nAlloc = nNew; + pNew->nAlloc = (int)nNew; for(pp=&pHash->aSlot[iHash]; *pp!=p; pp=&(*pp)->pHashNext); *pp = pNew; p = pNew; @@ -206545,7 +207752,7 @@ static int fts5HashEntrySort( int i; *ppSorted = 0; - ap = sqlite3_malloc(sizeof(Fts5HashEntry*) * nMergeSlot); + ap = sqlite3_malloc64(sizeof(Fts5HashEntry*) * nMergeSlot); if( !ap ) return SQLITE_NOMEM; memset(ap, 0, sizeof(Fts5HashEntry*) * nMergeSlot); @@ -206590,7 +207797,8 @@ static int sqlite3Fts5HashQuery( for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){ zKey = fts5EntryKey(p); - if( memcmp(zKey, pTerm, nTerm)==0 && zKey[nTerm]==0 ) break; + assert( p->nKey+1==(int)strlen(zKey) ); + if( nTerm==p->nKey+1 && memcmp(zKey, pTerm, nTerm)==0 ) break; } if( p ){ @@ -206642,7 +207850,6 @@ static void sqlite3Fts5HashScanEntry( } } - /* ** 2014 May 31 ** @@ -207157,7 +208364,6 @@ struct Fts5Iter { Fts5IndexIter base; /* Base class containing output vars */ Fts5Index *pIndex; /* Index that owns this iterator */ - Fts5Structure *pStruct; /* Database structure for this iterator */ Fts5Buffer poslist; /* Buffer containing current poslist */ Fts5Colset *pColset; /* Restrict matches to these columns */ @@ -207218,7 +208424,7 @@ static u16 fts5GetU16(const u8 *aIn){ ** If an OOM error is encountered, return NULL and set the error code in ** the Fts5Index handle passed as the first argument. */ -static void *fts5IdxMalloc(Fts5Index *p, int nByte){ +static void *fts5IdxMalloc(Fts5Index *p, sqlite3_int64 nByte){ return sqlite3Fts5MallocZero(&p->rc, nByte); } @@ -207252,7 +208458,7 @@ static int fts5BufferCompareBlob( */ static int fts5BufferCompare(Fts5Buffer *pLeft, Fts5Buffer *pRight){ int nCmp = MIN(pLeft->n, pRight->n); - int res = memcmp(pLeft->p, pRight->p, nCmp); + int res = fts5Memcmp(pLeft->p, pRight->p, nCmp); return (res==0 ? (pLeft->n - pRight->n) : res); } @@ -207318,8 +208524,8 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){ if( rc==SQLITE_OK ){ u8 *aOut = 0; /* Read blob data into this buffer */ int nByte = sqlite3_blob_bytes(p->pReader); - int nAlloc = sizeof(Fts5Data) + nByte + FTS5_DATA_PADDING; - pRet = (Fts5Data*)sqlite3_malloc(nAlloc); + sqlite3_int64 nAlloc = sizeof(Fts5Data) + nByte + FTS5_DATA_PADDING; + pRet = (Fts5Data*)sqlite3_malloc64(nAlloc); if( pRet ){ pRet->nn = nByte; aOut = pRet->p = (u8*)&pRet[1]; @@ -207335,6 +208541,7 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){ pRet = 0; }else{ /* TODO1: Fix this */ + pRet->p[nByte] = 0x00; pRet->szLeaf = fts5GetU16(&pRet->p[2]); } } @@ -207374,7 +208581,8 @@ static int fts5IndexPrepareStmt( if( p->rc==SQLITE_OK ){ if( zSql ){ p->rc = sqlite3_prepare_v3(p->pConfig->db, zSql, -1, - SQLITE_PREPARE_PERSISTENT, ppStmt, 0); + SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_NO_VTAB, + ppStmt, 0); }else{ p->rc = SQLITE_NOMEM; } @@ -207415,23 +208623,12 @@ static void fts5DataDelete(Fts5Index *p, i64 iFirst, i64 iLast){ if( p->rc!=SQLITE_OK ) return; if( p->pDeleter==0 ){ - int rc; Fts5Config *pConfig = p->pConfig; char *zSql = sqlite3_mprintf( "DELETE FROM '%q'.'%q_data' WHERE id>=? AND id<=?", pConfig->zDb, pConfig->zName ); - if( zSql==0 ){ - rc = SQLITE_NOMEM; - }else{ - rc = sqlite3_prepare_v3(pConfig->db, zSql, -1, - SQLITE_PREPARE_PERSISTENT, &p->pDeleter, 0); - sqlite3_free(zSql); - } - if( rc!=SQLITE_OK ){ - p->rc = rc; - return; - } + if( fts5IndexPrepareStmt(p, &p->pDeleter, zSql) ) return; } sqlite3_bind_int64(p->pDeleter, 1, iFirst); @@ -207503,7 +208700,7 @@ static int fts5StructureDecode( int iLvl; int nLevel = 0; int nSegment = 0; - int nByte; /* Bytes of space to allocate at pRet */ + sqlite3_int64 nByte; /* Bytes of space to allocate at pRet */ Fts5Structure *pRet = 0; /* Structure object to return */ /* Grab the cookie value */ @@ -207514,6 +208711,11 @@ static int fts5StructureDecode( ** structure record. */ i += fts5GetVarint32(&pData[i], nLevel); i += fts5GetVarint32(&pData[i], nSegment); + if( nLevel>FTS5_MAX_SEGMENT || nLevel<0 + || nSegment>FTS5_MAX_SEGMENT || nSegment<0 + ){ + return FTS5_CORRUPT; + } nByte = ( sizeof(Fts5Structure) + /* Main structure */ sizeof(Fts5StructureLevel) * (nLevel-1) /* aLevel[] array */ @@ -207536,25 +208738,35 @@ static int fts5StructureDecode( }else{ i += fts5GetVarint32(&pData[i], pLvl->nMerge); i += fts5GetVarint32(&pData[i], nTotal); - assert( nTotal>=pLvl->nMerge ); + if( nTotalnMerge ) rc = FTS5_CORRUPT; pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(&rc, nTotal * sizeof(Fts5StructureSegment) ); + nSegment -= nTotal; } if( rc==SQLITE_OK ){ pLvl->nSeg = nTotal; for(iSeg=0; iSegaSeg[iSeg]; if( i>=nData ){ rc = FTS5_CORRUPT; break; } - i += fts5GetVarint32(&pData[i], pLvl->aSeg[iSeg].iSegid); - i += fts5GetVarint32(&pData[i], pLvl->aSeg[iSeg].pgnoFirst); - i += fts5GetVarint32(&pData[i], pLvl->aSeg[iSeg].pgnoLast); + i += fts5GetVarint32(&pData[i], pSeg->iSegid); + i += fts5GetVarint32(&pData[i], pSeg->pgnoFirst); + i += fts5GetVarint32(&pData[i], pSeg->pgnoLast); + if( pSeg->pgnoLastpgnoFirst ){ + rc = FTS5_CORRUPT; + break; + } } + if( iLvl>0 && pLvl[-1].nMerge && nTotal==0 ) rc = FTS5_CORRUPT; + if( iLvl==nLevel-1 && pLvl->nMerge ) rc = FTS5_CORRUPT; } } + if( nSegment!=0 && rc==SQLITE_OK ) rc = FTS5_CORRUPT; + if( rc!=SQLITE_OK ){ fts5StructureRelease(pRet); pRet = 0; @@ -207572,12 +208784,12 @@ static void fts5StructureAddLevel(int *pRc, Fts5Structure **ppStruct){ if( *pRc==SQLITE_OK ){ Fts5Structure *pStruct = *ppStruct; int nLevel = pStruct->nLevel; - int nByte = ( + sqlite3_int64 nByte = ( sizeof(Fts5Structure) + /* Main structure */ sizeof(Fts5StructureLevel) * (nLevel+1) /* aLevel[] array */ ); - pStruct = sqlite3_realloc(pStruct, nByte); + pStruct = sqlite3_realloc64(pStruct, nByte); if( pStruct ){ memset(&pStruct->aLevel[nLevel], 0, sizeof(Fts5StructureLevel)); pStruct->nLevel++; @@ -207602,10 +208814,10 @@ static void fts5StructureExtendLevel( if( *pRc==SQLITE_OK ){ Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl]; Fts5StructureSegment *aNew; - int nByte; + sqlite3_int64 nByte; nByte = (pLvl->nSeg + nExtra) * sizeof(Fts5StructureSegment); - aNew = sqlite3_realloc(pLvl->aSeg, nByte); + aNew = sqlite3_realloc64(pLvl->aSeg, nByte); if( aNew ){ if( bInsert==0 ){ memset(&aNew[pLvl->nSeg], 0, sizeof(Fts5StructureSegment) * nExtra); @@ -208119,10 +209331,10 @@ static Fts5DlidxIter *fts5DlidxIterInit( int bDone = 0; for(i=0; p->rc==SQLITE_OK && bDone==0; i++){ - int nByte = sizeof(Fts5DlidxIter) + i * sizeof(Fts5DlidxLvl); + sqlite3_int64 nByte = sizeof(Fts5DlidxIter) + i * sizeof(Fts5DlidxLvl); Fts5DlidxIter *pNew; - pNew = (Fts5DlidxIter*)sqlite3_realloc(pIter, nByte); + pNew = (Fts5DlidxIter*)sqlite3_realloc64(pIter, nByte); if( pNew==0 ){ p->rc = SQLITE_NOMEM; }else{ @@ -208292,12 +209504,13 @@ static void fts5SegIterLoadTerm(Fts5Index *p, Fts5SegIter *pIter, int nKeep){ int nNew; /* Bytes of new data */ iOff += fts5GetVarint32(&a[iOff], nNew); - if( iOff+nNew>pIter->pLeaf->nn ){ + if( iOff+nNew>pIter->pLeaf->szLeaf || nKeep>pIter->term.n || nNew==0 ){ p->rc = FTS5_CORRUPT; return; } pIter->term.n = nKeep; fts5BufferAppendBlob(&p->rc, &pIter->term, nNew, &a[iOff]); + assert( pIter->term.n<=pIter->term.nSpace ); iOff += nNew; pIter->iTermLeafOffset = iOff; pIter->iTermLeafPgno = pIter->iLeafPgno; @@ -208362,7 +209575,7 @@ static void fts5SegIterInit( if( p->rc==SQLITE_OK ){ pIter->iLeafOffset = 4; assert_nc( pIter->pLeaf->nn>4 ); - assert( fts5LeafFirstTermOff(pIter->pLeaf)==4 ); + assert_nc( fts5LeafFirstTermOff(pIter->pLeaf)==4 ); pIter->iPgidxOff = pIter->pLeaf->szLeaf+1; fts5SegIterLoadTerm(p, pIter, 0); fts5SegIterLoadNPos(p, pIter); @@ -208418,7 +209631,7 @@ static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){ /* If necessary, grow the pIter->aRowidOffset[] array. */ if( iRowidOffset>=pIter->nRowidOffset ){ int nNew = pIter->nRowidOffset + 8; - int *aNew = (int*)sqlite3_realloc(pIter->aRowidOffset, nNew*sizeof(int)); + int *aNew = (int*)sqlite3_realloc64(pIter->aRowidOffset,nNew*sizeof(int)); if( aNew==0 ){ p->rc = SQLITE_NOMEM; break; @@ -208872,10 +210085,10 @@ static void fts5LeafSeek( int szLeaf = pIter->pLeaf->szLeaf; int n = pIter->pLeaf->nn; - int nMatch = 0; - int nKeep = 0; - int nNew = 0; - int iTermOff; + u32 nMatch = 0; + u32 nKeep = 0; + u32 nNew = 0; + u32 iTermOff; int iPgidx; /* Current offset in pgidx */ int bEndOfPage = 0; @@ -208899,15 +210112,15 @@ static void fts5LeafSeek( assert( nKeep>=nMatch ); if( nKeep==nMatch ){ - int nCmp; - int i; - nCmp = MIN(nNew, nTerm-nMatch); + u32 nCmp; + u32 i; + nCmp = (u32)MIN(nNew, nTerm-nMatch); for(i=0; ipLeaf->p[iPgidx], iOff); if( iOff<4 || iOff>=pIter->pLeaf->szLeaf ){ p->rc = FTS5_CORRUPT; + return; }else{ nKeep = 0; iTermOff = iOff; @@ -208963,8 +210177,11 @@ static void fts5LeafSeek( } search_success: - pIter->iLeafOffset = iOff + nNew; + if( pIter->iLeafOffset>n || nNew<1 ){ + p->rc = FTS5_CORRUPT; + return; + } pIter->iTermLeafOffset = pIter->iLeafOffset; pIter->iTermLeafPgno = pIter->iLeafPgno; @@ -209071,7 +210288,7 @@ static void fts5SegIterSeekInit( ** 4) the FTS5INDEX_QUERY_SCAN flag was set and the iterator points ** to an entry with a term greater than or equal to (pTerm/nTerm). */ - assert( p->rc!=SQLITE_OK /* 1 */ + assert_nc( p->rc!=SQLITE_OK /* 1 */ || pIter->pLeaf==0 /* 2 */ || fts5BufferCompareBlob(&pIter->term, pTerm, nTerm)==0 /* 3 */ || (bGe && fts5BufferCompareBlob(&pIter->term, pTerm, nTerm)>0) /* 4 */ @@ -209169,7 +210386,7 @@ static void fts5AssertComparisonResult( assert( pRes->iFirst==i1 ); }else{ int nMin = MIN(p1->term.n, p2->term.n); - int res = memcmp(p1->term.p, p2->term.p, nMin); + int res = fts5Memcmp(p1->term.p, p2->term.p, nMin); if( res==0 ) res = p1->term.n - p2->term.n; if( res==0 ){ @@ -209392,7 +210609,6 @@ static void fts5MultiIterFree(Fts5Iter *pIter){ for(i=0; inSeg; i++){ fts5SegIterClear(&pIter->aSeg[i]); } - fts5StructureRelease(pIter->pStruct); fts5BufferFree(&pIter->poslist); sqlite3_free(pIter); } @@ -209740,7 +210956,8 @@ static void fts5SegiterPoslist( Fts5Colset *pColset, Fts5Buffer *pBuf ){ - if( 0==fts5BufferGrow(&p->rc, pBuf, pSeg->nPos) ){ + if( 0==fts5BufferGrow(&p->rc, pBuf, pSeg->nPos+FTS5_DATA_ZERO_PADDING) ){ + memset(&pBuf->p[pBuf->n+pSeg->nPos], 0, FTS5_DATA_ZERO_PADDING); if( pColset==0 ){ fts5ChunkIterate(p, pSeg, (void*)pBuf, fts5PoslistCallback); }else{ @@ -210038,9 +211255,7 @@ static void fts5MultiIterNew( if( pNew==0 ) return; pNew->bRev = (0!=(flags & FTS5INDEX_QUERY_DESC)); pNew->bSkipEmpty = (0!=(flags & FTS5INDEX_QUERY_SKIPEMPTY)); - pNew->pStruct = pStruct; pNew->pColset = pColset; - fts5StructureRef(pStruct); if( (flags & FTS5INDEX_QUERY_NOOUTPUT)==0 ){ fts5IterSetOutputCb(&p->rc, pNew); } @@ -210218,24 +211433,24 @@ static int fts5AllocateSegid(Fts5Index *p, Fts5Structure *pStruct){ for(iLvl=0; iLvlnLevel; iLvl++){ for(iSeg=0; iSegaLevel[iLvl].nSeg; iSeg++){ int iId = pStruct->aLevel[iLvl].aSeg[iSeg].iSegid; - if( iId<=FTS5_MAX_SEGMENT ){ - aUsed[(iId-1) / 32] |= 1 << ((iId-1) % 32); + if( iId<=FTS5_MAX_SEGMENT && iId>0 ){ + aUsed[(iId-1) / 32] |= (u32)1 << ((iId-1) % 32); } } } for(i=0; aUsed[i]==0xFFFFFFFF; i++); mask = aUsed[i]; - for(iSegid=0; mask & (1 << iSegid); iSegid++); + for(iSegid=0; mask & ((u32)1 << iSegid); iSegid++); iSegid += 1 + i*32; #ifdef SQLITE_DEBUG for(iLvl=0; iLvlnLevel; iLvl++){ for(iSeg=0; iSegaLevel[iLvl].nSeg; iSeg++){ - assert( iSegid!=pStruct->aLevel[iLvl].aSeg[iSeg].iSegid ); + assert_nc( iSegid!=pStruct->aLevel[iLvl].aSeg[iSeg].iSegid ); } } - assert( iSegid>0 && iSegid<=FTS5_MAX_SEGMENT ); + assert_nc( iSegid>0 && iSegid<=FTS5_MAX_SEGMENT ); { sqlite3_stmt *pIdxSelect = fts5IdxSelectStmt(p); @@ -210243,7 +211458,7 @@ static int fts5AllocateSegid(Fts5Index *p, Fts5Structure *pStruct){ u8 aBlob[2] = {0xff, 0xff}; sqlite3_bind_int(pIdxSelect, 1, iSegid); sqlite3_bind_blob(pIdxSelect, 2, aBlob, 2, SQLITE_STATIC); - assert( sqlite3_step(pIdxSelect)!=SQLITE_ROW ); + assert_nc( sqlite3_step(pIdxSelect)!=SQLITE_ROW ); p->rc = sqlite3_reset(pIdxSelect); sqlite3_bind_null(pIdxSelect, 2); } @@ -210313,7 +211528,7 @@ static int fts5WriteDlidxGrow( int nLvl ){ if( p->rc==SQLITE_OK && nLvl>=pWriter->nDlidx ){ - Fts5DlidxWriter *aDlidx = (Fts5DlidxWriter*)sqlite3_realloc( + Fts5DlidxWriter *aDlidx = (Fts5DlidxWriter*)sqlite3_realloc64( pWriter->aDlidx, sizeof(Fts5DlidxWriter) * nLvl ); if( aDlidx==0 ){ @@ -210392,8 +211607,10 @@ static void fts5WriteBtreeTerm( int nTerm, const u8 *pTerm /* First term on new page */ ){ fts5WriteFlushBtree(p, pWriter); - fts5BufferSet(&p->rc, &pWriter->btterm, nTerm, pTerm); - pWriter->iBtPage = pWriter->writer.pgno; + if( p->rc==SQLITE_OK ){ + fts5BufferSet(&p->rc, &pWriter->btterm, nTerm, pTerm); + pWriter->iBtPage = pWriter->writer.pgno; + } } /* @@ -210544,6 +211761,7 @@ static void fts5WriteAppendTerm( int nPrefix; /* Bytes of prefix compression for term */ Fts5PageWriter *pPage = &pWriter->writer; Fts5Buffer *pPgidx = &pWriter->writer.pgidx; + int nMin = MIN(pPage->term.n, nTerm); assert( p->rc==SQLITE_OK ); assert( pPage->buf.n>=4 ); @@ -210553,6 +211771,7 @@ static void fts5WriteAppendTerm( if( (pPage->buf.n + pPgidx->n + nTerm + 2)>=p->pConfig->pgsz ){ if( pPage->buf.n>4 ){ fts5WriteFlushLeaf(p, pWriter); + if( p->rc!=SQLITE_OK ) return; } fts5BufferGrow(&p->rc, &pPage->buf, nTerm+FTS5_DATA_PADDING); } @@ -210585,13 +211804,14 @@ static void fts5WriteAppendTerm( ** inefficient, but still correct. */ int n = nTerm; if( pPage->term.n ){ - n = 1 + fts5PrefixCompress(pPage->term.n, pPage->term.p, pTerm); + n = 1 + fts5PrefixCompress(nMin, pPage->term.p, pTerm); } fts5WriteBtreeTerm(p, pWriter, n, pTerm); + if( p->rc!=SQLITE_OK ) return; pPage = &pWriter->writer; } }else{ - nPrefix = fts5PrefixCompress(pPage->term.n, pPage->term.p, pTerm); + nPrefix = fts5PrefixCompress(nMin, pPage->term.p, pTerm); fts5BufferAppendVarint(&p->rc, &pPage->buf, nPrefix); } @@ -210638,7 +211858,7 @@ static void fts5WriteAppendRowid( if( pWriter->bFirstRowidInDoclist || pWriter->bFirstRowidInPage ){ fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid); }else{ - assert( p->rc || iRowid>pWriter->iPrevRowid ); + assert_nc( p->rc || iRowid>pWriter->iPrevRowid ); fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid - pWriter->iPrevRowid); } pWriter->iPrevRowid = iRowid; @@ -210760,7 +211980,7 @@ static void fts5TrimSegments(Fts5Index *p, Fts5Iter *pIter){ int i; Fts5Buffer buf; memset(&buf, 0, sizeof(Fts5Buffer)); - for(i=0; inSeg; i++){ + for(i=0; inSeg && p->rc==SQLITE_OK; i++){ Fts5SegIter *pSeg = &pIter->aSeg[i]; if( pSeg->pSeg==0 ){ /* no-op */ @@ -210778,35 +211998,43 @@ static void fts5TrimSegments(Fts5Index *p, Fts5Iter *pIter){ u8 aHdr[4] = {0x00, 0x00, 0x00, 0x00}; iLeafRowid = FTS5_SEGMENT_ROWID(iId, pSeg->iTermLeafPgno); - pData = fts5DataRead(p, iLeafRowid); + pData = fts5LeafRead(p, iLeafRowid); if( pData ){ - fts5BufferZero(&buf); - fts5BufferGrow(&p->rc, &buf, pData->nn); - fts5BufferAppendBlob(&p->rc, &buf, sizeof(aHdr), aHdr); - fts5BufferAppendVarint(&p->rc, &buf, pSeg->term.n); - fts5BufferAppendBlob(&p->rc, &buf, pSeg->term.n, pSeg->term.p); - fts5BufferAppendBlob(&p->rc, &buf, pData->szLeaf-iOff, &pData->p[iOff]); - if( p->rc==SQLITE_OK ){ - /* Set the szLeaf field */ - fts5PutU16(&buf.p[2], (u16)buf.n); - } + if( iOff>pData->szLeaf ){ + /* This can occur if the pages that the segments occupy overlap - if + ** a single page has been assigned to more than one segment. In + ** this case a prior iteration of this loop may have corrupted the + ** segment currently being trimmed. */ + p->rc = FTS5_CORRUPT; + }else{ + fts5BufferZero(&buf); + fts5BufferGrow(&p->rc, &buf, pData->nn); + fts5BufferAppendBlob(&p->rc, &buf, sizeof(aHdr), aHdr); + fts5BufferAppendVarint(&p->rc, &buf, pSeg->term.n); + fts5BufferAppendBlob(&p->rc, &buf, pSeg->term.n, pSeg->term.p); + fts5BufferAppendBlob(&p->rc, &buf, pData->szLeaf-iOff,&pData->p[iOff]); + if( p->rc==SQLITE_OK ){ + /* Set the szLeaf field */ + fts5PutU16(&buf.p[2], (u16)buf.n); + } - /* Set up the new page-index array */ - fts5BufferAppendVarint(&p->rc, &buf, 4); - if( pSeg->iLeafPgno==pSeg->iTermLeafPgno - && pSeg->iEndofDoclistszLeaf - ){ - int nDiff = pData->szLeaf - pSeg->iEndofDoclist; - fts5BufferAppendVarint(&p->rc, &buf, buf.n - 1 - nDiff - 4); - fts5BufferAppendBlob(&p->rc, &buf, - pData->nn - pSeg->iPgidxOff, &pData->p[pSeg->iPgidxOff] - ); - } + /* Set up the new page-index array */ + fts5BufferAppendVarint(&p->rc, &buf, 4); + if( pSeg->iLeafPgno==pSeg->iTermLeafPgno + && pSeg->iEndofDoclistszLeaf + ){ + int nDiff = pData->szLeaf - pSeg->iEndofDoclist; + fts5BufferAppendVarint(&p->rc, &buf, buf.n - 1 - nDiff - 4); + fts5BufferAppendBlob(&p->rc, &buf, + pData->nn - pSeg->iPgidxOff, &pData->p[pSeg->iPgidxOff] + ); + } + pSeg->pSeg->pgnoFirst = pSeg->iTermLeafPgno; + fts5DataDelete(p, FTS5_SEGMENT_ROWID(iId, 1), iLeafRowid); + fts5DataWrite(p, iLeafRowid, buf.p, buf.n); + } fts5DataRelease(pData); - pSeg->pSeg->pgnoFirst = pSeg->iTermLeafPgno; - fts5DataDelete(p, FTS5_SEGMENT_ROWID(iId, 1), iLeafRowid); - fts5DataWrite(p, iLeafRowid, buf.p, buf.n); } } } @@ -210898,7 +212126,7 @@ static void fts5IndexMergeLevel( const u8 *pTerm; pTerm = fts5MultiIterTerm(pIter, &nTerm); - if( nTerm!=term.n || memcmp(pTerm, term.p, nTerm) ){ + if( nTerm!=term.n || fts5Memcmp(pTerm, term.p, nTerm) ){ if( pnRem && writer.nLeafWritten>nRem ){ break; } @@ -211153,6 +212381,7 @@ static void fts5FlushOneHash(Fts5Index *p){ /* Write the term for this entry to disk. */ sqlite3Fts5HashScanEntry(pHash, &zTerm, &pDoclist, &nDoclist); fts5WriteAppendTerm(p, &writer, (int)strlen(zTerm), (const u8*)zTerm); + if( p->rc!=SQLITE_OK ) break; assert( writer.bFirstRowidInPage==0 ); if( pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){ @@ -211175,6 +212404,7 @@ static void fts5FlushOneHash(Fts5Index *p){ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid); writer.bFirstRowidInPage = 0; fts5WriteDlidxAppend(p, &writer, iRowid); + if( p->rc!=SQLITE_OK ) break; }else{ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iDelta); } @@ -211232,7 +212462,7 @@ static void fts5FlushOneHash(Fts5Index *p){ /* TODO2: Doclist terminator written here. */ /* pBuf->p[pBuf->n++] = '\0'; */ assert( pBuf->n<=pBuf->nSpace ); - sqlite3Fts5HashScanNext(pHash); + if( p->rc==SQLITE_OK ) sqlite3Fts5HashScanNext(pHash); } sqlite3Fts5HashClear(pHash); fts5WriteFinish(p, &writer, &pgnoLast); @@ -211276,7 +212506,7 @@ static Fts5Structure *fts5IndexOptimizeStruct( Fts5Structure *pStruct ){ Fts5Structure *pNew = 0; - int nByte = sizeof(Fts5Structure); + sqlite3_int64 nByte = sizeof(Fts5Structure); int nSeg = pStruct->nSegment; int i; @@ -211406,11 +212636,13 @@ static void fts5AppendPoslist( Fts5Buffer *pBuf ){ int nData = pMulti->base.nData; + int nByte = nData + 9 + 9 + FTS5_DATA_ZERO_PADDING; assert( nData>0 ); - if( p->rc==SQLITE_OK && 0==fts5BufferGrow(&p->rc, pBuf, nData+9+9) ){ + if( p->rc==SQLITE_OK && 0==fts5BufferGrow(&p->rc, pBuf, nByte) ){ fts5BufferSafeAppendVarint(pBuf, iDelta); fts5BufferSafeAppendVarint(pBuf, nData*2); fts5BufferSafeAppendBlob(pBuf, pMulti->base.pData, nData); + memset(&pBuf->p[pBuf->n], 0, FTS5_DATA_ZERO_PADDING); } } @@ -211591,6 +212823,8 @@ static void fts5MergePrefixLists( int iOff2 = 0; u8 *a1 = &i1.aPoslist[i1.nSize]; u8 *a2 = &i2.aPoslist[i2.nSize]; + int nCopy; + u8 *aCopy; i64 iPrev = 0; Fts5PoslistWriter writer; @@ -211622,7 +212856,7 @@ static void fts5MergePrefixLists( sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1); if( iPos1<0 ) break; }else{ - assert( iPos2!=iPrev ); + assert_nc( iPos2!=iPrev ); sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2); sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2); if( iPos2<0 ) break; @@ -211634,11 +212868,16 @@ static void fts5MergePrefixLists( if( iPos1!=iPrev ){ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos1); } - fts5BufferSafeAppendBlob(&tmp, &a1[iOff1], i1.nPoslist-iOff1); + aCopy = &a1[iOff1]; + nCopy = i1.nPoslist - iOff1; }else{ assert( iPos2>=0 && iPos2!=iPrev ); sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2); - fts5BufferSafeAppendBlob(&tmp, &a2[iOff2], i2.nPoslist-iOff2); + aCopy = &a2[iOff2]; + nCopy = i2.nPoslist - iOff2; + } + if( nCopy>0 ){ + fts5BufferSafeAppendBlob(&tmp, aCopy, nCopy); } /* WRITEPOSLISTSIZE */ @@ -211646,6 +212885,7 @@ static void fts5MergePrefixLists( fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n); fts5DoclistIterNext(&i1); fts5DoclistIterNext(&i2); + assert( out.n<=(p1->n+p2->n+9) ); if( i1.aPoslist==0 || i2.aPoslist==0 ) break; } } @@ -211747,7 +212987,7 @@ static void fts5SetupPrefixIter( } fts5MultiIterFree(p1); - pData = fts5IdxMalloc(p, sizeof(Fts5Data) + doclist.n); + pData = fts5IdxMalloc(p, sizeof(Fts5Data)+doclist.n+FTS5_DATA_ZERO_PADDING); if( pData ){ pData->p = (u8*)&pData[1]; pData->nn = pData->szLeaf = doclist.n; @@ -212509,11 +213749,11 @@ static void fts5IndexIntegrityCheckSegment( iOff = fts5LeafFirstTermOff(pLeaf); iRowidOff = fts5LeafFirstRowidOff(pLeaf); - if( iRowidOff>=iOff ){ + if( iRowidOff>=iOff || iOff>=pLeaf->szLeaf ){ p->rc = FTS5_CORRUPT; }else{ iOff += fts5GetVarint32(&pLeaf->p[iOff], nTerm); - res = memcmp(&pLeaf->p[iOff], zIdxTerm, MIN(nTerm, nIdxTerm)); + res = fts5Memcmp(&pLeaf->p[iOff], zIdxTerm, MIN(nTerm, nIdxTerm)); if( res==0 ) res = nTerm - nIdxTerm; if( res<0 ) p->rc = FTS5_CORRUPT; } @@ -212908,7 +214148,7 @@ static void fts5DecodeFunction( u8 *a = 0; Fts5Buffer s; /* Build up text to return here */ int rc = SQLITE_OK; /* Return code */ - int nSpace = 0; + sqlite3_int64 nSpace = 0; int eDetailNone = (sqlite3_user_data(pCtx)!=0); assert( nArg==2 ); @@ -212924,8 +214164,7 @@ static void fts5DecodeFunction( nSpace = n + FTS5_DATA_ZERO_PADDING; a = (u8*)sqlite3Fts5MallocZero(&rc, nSpace); if( a==0 ) goto decode_out; - memcpy(a, aBlob, n); - + if( n>0 ) memcpy(a, aBlob, n); fts5DecodeRowid(iRowid, &iSegid, &bDlidx, &iHeight, &iPgno); @@ -213020,6 +214259,9 @@ static void fts5DecodeFunction( iPgidxOff = szLeaf = fts5GetU16(&a[2]); if( iPgidxOffn ){ + rc = FTS5_CORRUPT; + goto decode_out; } } @@ -213031,14 +214273,22 @@ static void fts5DecodeFunction( }else{ iOff = szLeaf; } + if( iOff>n ){ + rc = FTS5_CORRUPT; + goto decode_out; + } fts5DecodePoslist(&rc, &s, &a[4], iOff-4); /* Decode any more doclist data that appears on the page before the ** first term. */ nDoclist = (iTermOff ? iTermOff : szLeaf) - iOff; + if( nDoclist+iOff>n ){ + rc = FTS5_CORRUPT; + goto decode_out; + } fts5DecodeDoclist(&rc, &s, &a[iOff], nDoclist); - while( iPgidxOffszLeaf ){ + rc = FTS5_CORRUPT; + break; + } if( bFirst==0 ){ iOff += fts5GetVarint32(&a[iOff], nByte); + if( nByte>term.n ){ + rc = FTS5_CORRUPT; + break; + } term.n = nByte; } iOff += fts5GetVarint32(&a[iOff], nByte); + if( iOff+nByte>n ){ + rc = FTS5_CORRUPT; + break; + } fts5BufferAppendBlob(&rc, &term, nByte, &a[iOff]); iOff += nByte; @@ -213182,8 +214444,8 @@ SQLITE_API int sqlite3_fts5_may_be_corrupt = 1; typedef struct Fts5Auxdata Fts5Auxdata; typedef struct Fts5Auxiliary Fts5Auxiliary; typedef struct Fts5Cursor Fts5Cursor; +typedef struct Fts5FullTable Fts5FullTable; typedef struct Fts5Sorter Fts5Sorter; -typedef struct Fts5Table Fts5Table; typedef struct Fts5TokenizerModule Fts5TokenizerModule; /* @@ -213264,13 +214526,8 @@ struct Fts5TokenizerModule { Fts5TokenizerModule *pNext; /* Next registered tokenizer module */ }; -/* -** Virtual-table object. -*/ -struct Fts5Table { - sqlite3_vtab base; /* Base class used by SQLite core */ - Fts5Config *pConfig; /* Virtual table configuration */ - Fts5Index *pIndex; /* Full-text index */ +struct Fts5FullTable { + Fts5Table p; /* Public class members from fts5Int.h */ Fts5Storage *pStorage; /* Document store */ Fts5Global *pGlobal; /* Global (connection wide) data */ Fts5Cursor *pSortCsr; /* Sort data from this cursor */ @@ -213408,7 +214665,7 @@ struct Fts5Auxdata { #define FTS5_SAVEPOINT 5 #define FTS5_RELEASE 6 #define FTS5_ROLLBACKTO 7 -static void fts5CheckTransactionState(Fts5Table *p, int op, int iSavepoint){ +static void fts5CheckTransactionState(Fts5FullTable *p, int op, int iSavepoint){ switch( op ){ case FTS5_BEGIN: assert( p->ts.eState==0 ); @@ -213447,7 +214704,7 @@ static void fts5CheckTransactionState(Fts5Table *p, int op, int iSavepoint){ case FTS5_ROLLBACKTO: assert( p->ts.eState==1 ); - assert( iSavepoint>=0 ); + assert( iSavepoint>=-1 ); assert( iSavepoint<=p->ts.iSavepoint ); p->ts.iSavepoint = iSavepoint; break; @@ -213460,18 +214717,18 @@ static void fts5CheckTransactionState(Fts5Table *p, int op, int iSavepoint){ /* ** Return true if pTab is a contentless table. */ -static int fts5IsContentless(Fts5Table *pTab){ - return pTab->pConfig->eContent==FTS5_CONTENT_NONE; +static int fts5IsContentless(Fts5FullTable *pTab){ + return pTab->p.pConfig->eContent==FTS5_CONTENT_NONE; } /* ** Delete a virtual table handle allocated by fts5InitVtab(). */ -static void fts5FreeVtab(Fts5Table *pTab){ +static void fts5FreeVtab(Fts5FullTable *pTab){ if( pTab ){ - sqlite3Fts5IndexClose(pTab->pIndex); + sqlite3Fts5IndexClose(pTab->p.pIndex); sqlite3Fts5StorageClose(pTab->pStorage); - sqlite3Fts5ConfigFree(pTab->pConfig); + sqlite3Fts5ConfigFree(pTab->p.pConfig); sqlite3_free(pTab); } } @@ -213480,7 +214737,7 @@ static void fts5FreeVtab(Fts5Table *pTab){ ** The xDisconnect() virtual table method. */ static int fts5DisconnectMethod(sqlite3_vtab *pVtab){ - fts5FreeVtab((Fts5Table*)pVtab); + fts5FreeVtab((Fts5FullTable*)pVtab); return SQLITE_OK; } @@ -213491,7 +214748,7 @@ static int fts5DestroyMethod(sqlite3_vtab *pVtab){ Fts5Table *pTab = (Fts5Table*)pVtab; int rc = sqlite3Fts5DropAll(pTab->pConfig); if( rc==SQLITE_OK ){ - fts5FreeVtab((Fts5Table*)pVtab); + fts5FreeVtab((Fts5FullTable*)pVtab); } return rc; } @@ -213520,28 +214777,28 @@ static int fts5InitVtab( const char **azConfig = (const char**)argv; int rc = SQLITE_OK; /* Return code */ Fts5Config *pConfig = 0; /* Results of parsing argc/argv */ - Fts5Table *pTab = 0; /* New virtual table object */ + Fts5FullTable *pTab = 0; /* New virtual table object */ /* Allocate the new vtab object and parse the configuration */ - pTab = (Fts5Table*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Table)); + pTab = (Fts5FullTable*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5FullTable)); if( rc==SQLITE_OK ){ rc = sqlite3Fts5ConfigParse(pGlobal, db, argc, azConfig, &pConfig, pzErr); assert( (rc==SQLITE_OK && *pzErr==0) || pConfig==0 ); } if( rc==SQLITE_OK ){ - pTab->pConfig = pConfig; + pTab->p.pConfig = pConfig; pTab->pGlobal = pGlobal; } /* Open the index sub-system */ if( rc==SQLITE_OK ){ - rc = sqlite3Fts5IndexOpen(pConfig, bCreate, &pTab->pIndex, pzErr); + rc = sqlite3Fts5IndexOpen(pConfig, bCreate, &pTab->p.pIndex, pzErr); } /* Open the storage sub-system */ if( rc==SQLITE_OK ){ rc = sqlite3Fts5StorageOpen( - pConfig, pTab->pIndex, bCreate, &pTab->pStorage, pzErr + pConfig, pTab->p.pIndex, bCreate, &pTab->pStorage, pzErr ); } @@ -213554,8 +214811,8 @@ static int fts5InitVtab( if( rc==SQLITE_OK ){ assert( pConfig->pzErrmsg==0 ); pConfig->pzErrmsg = pzErr; - rc = sqlite3Fts5IndexLoadConfig(pTab->pIndex); - sqlite3Fts5IndexRollback(pTab->pIndex); + rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); + sqlite3Fts5IndexRollback(pTab->p.pIndex); pConfig->pzErrmsg = 0; } @@ -213768,7 +215025,7 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ return SQLITE_OK; } -static int fts5NewTransaction(Fts5Table *pTab){ +static int fts5NewTransaction(Fts5FullTable *pTab){ Fts5Cursor *pCsr; for(pCsr=pTab->pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){ if( pCsr->base.pVtab==(sqlite3_vtab*)pTab ) return SQLITE_OK; @@ -213780,16 +215037,16 @@ static int fts5NewTransaction(Fts5Table *pTab){ ** Implementation of xOpen method. */ static int fts5OpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){ - Fts5Table *pTab = (Fts5Table*)pVTab; - Fts5Config *pConfig = pTab->pConfig; + Fts5FullTable *pTab = (Fts5FullTable*)pVTab; + Fts5Config *pConfig = pTab->p.pConfig; Fts5Cursor *pCsr = 0; /* New cursor object */ - int nByte; /* Bytes of space to allocate */ + sqlite3_int64 nByte; /* Bytes of space to allocate */ int rc; /* Return code */ rc = fts5NewTransaction(pTab); if( rc==SQLITE_OK ){ nByte = sizeof(Fts5Cursor) + pConfig->nCol * sizeof(int); - pCsr = (Fts5Cursor*)sqlite3_malloc(nByte); + pCsr = (Fts5Cursor*)sqlite3_malloc64(nByte); if( pCsr ){ Fts5Global *pGlobal = pTab->pGlobal; memset(pCsr, 0, nByte); @@ -213827,7 +215084,7 @@ static void fts5CsrNewrow(Fts5Cursor *pCsr){ } static void fts5FreeCursorComponents(Fts5Cursor *pCsr){ - Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); + Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); Fts5Auxdata *pData; Fts5Auxdata *pNext; @@ -213871,7 +215128,7 @@ static void fts5FreeCursorComponents(Fts5Cursor *pCsr){ */ static int fts5CloseMethod(sqlite3_vtab_cursor *pCursor){ if( pCursor ){ - Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab); + Fts5FullTable *pTab = (Fts5FullTable*)(pCursor->pVtab); Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; Fts5Cursor **pp; @@ -213928,7 +215185,7 @@ static int fts5SorterNext(Fts5Cursor *pCsr){ ** Set the FTS5CSR_REQUIRE_RESEEK flag on all FTS5_PLAN_MATCH cursors ** open on table pTab. */ -static void fts5TripCursors(Fts5Table *pTab){ +static void fts5TripCursors(Fts5FullTable *pTab){ Fts5Cursor *pCsr; for(pCsr=pTab->pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){ if( pCsr->ePlan==FTS5_PLAN_MATCH @@ -213955,11 +215212,11 @@ static int fts5CursorReseek(Fts5Cursor *pCsr, int *pbSkip){ int rc = SQLITE_OK; assert( *pbSkip==0 ); if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_RESEEK) ){ - Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); + Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); int bDesc = pCsr->bDesc; i64 iRowid = sqlite3Fts5ExprRowid(pCsr->pExpr); - rc = sqlite3Fts5ExprFirst(pCsr->pExpr, pTab->pIndex, iRowid, bDesc); + rc = sqlite3Fts5ExprFirst(pCsr->pExpr, pTab->p.pIndex, iRowid, bDesc); if( rc==SQLITE_OK && iRowid!=sqlite3Fts5ExprRowid(pCsr->pExpr) ){ *pbSkip = 1; } @@ -214056,18 +215313,22 @@ static int fts5PrepareStatement( return rc; } -static int fts5CursorFirstSorted(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){ - Fts5Config *pConfig = pTab->pConfig; +static int fts5CursorFirstSorted( + Fts5FullTable *pTab, + Fts5Cursor *pCsr, + int bDesc +){ + Fts5Config *pConfig = pTab->p.pConfig; Fts5Sorter *pSorter; int nPhrase; - int nByte; + sqlite3_int64 nByte; int rc; const char *zRank = pCsr->zRank; const char *zRankArgs = pCsr->zRankArgs; nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); nByte = sizeof(Fts5Sorter) + sizeof(int) * (nPhrase-1); - pSorter = (Fts5Sorter*)sqlite3_malloc(nByte); + pSorter = (Fts5Sorter*)sqlite3_malloc64(nByte); if( pSorter==0 ) return SQLITE_NOMEM; memset(pSorter, 0, nByte); pSorter->nIdx = nPhrase; @@ -214104,10 +215365,10 @@ static int fts5CursorFirstSorted(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){ return rc; } -static int fts5CursorFirst(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){ +static int fts5CursorFirst(Fts5FullTable *pTab, Fts5Cursor *pCsr, int bDesc){ int rc; Fts5Expr *pExpr = pCsr->pExpr; - rc = sqlite3Fts5ExprFirst(pExpr, pTab->pIndex, pCsr->iFirstRowid, bDesc); + rc = sqlite3Fts5ExprFirst(pExpr, pTab->p.pIndex, pCsr->iFirstRowid, bDesc); if( sqlite3Fts5ExprEof(pExpr) ){ CsrFlagSet(pCsr, FTS5CSR_EOF); } @@ -214122,7 +215383,7 @@ static int fts5CursorFirst(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){ ** parameters. */ static int fts5SpecialMatch( - Fts5Table *pTab, + Fts5FullTable *pTab, Fts5Cursor *pCsr, const char *zQuery ){ @@ -214133,18 +215394,18 @@ static int fts5SpecialMatch( while( z[0]==' ' ) z++; for(n=0; z[n] && z[n]!=' '; n++); - assert( pTab->base.zErrMsg==0 ); + assert( pTab->p.base.zErrMsg==0 ); pCsr->ePlan = FTS5_PLAN_SPECIAL; if( 0==sqlite3_strnicmp("reads", z, n) ){ - pCsr->iSpecial = sqlite3Fts5IndexReads(pTab->pIndex); + pCsr->iSpecial = sqlite3Fts5IndexReads(pTab->p.pIndex); } else if( 0==sqlite3_strnicmp("id", z, n) ){ pCsr->iSpecial = pCsr->iCsrId; } else{ /* An unrecognized directive. Return an error message. */ - pTab->base.zErrMsg = sqlite3_mprintf("unknown special query: %.*s", n, z); + pTab->p.base.zErrMsg = sqlite3_mprintf("unknown special query: %.*s", n, z); rc = SQLITE_ERROR; } @@ -214156,7 +215417,7 @@ static int fts5SpecialMatch( ** pTab. If one is found, return a pointer to the corresponding Fts5Auxiliary ** structure. Otherwise, if no such function exists, return NULL. */ -static Fts5Auxiliary *fts5FindAuxiliary(Fts5Table *pTab, const char *zName){ +static Fts5Auxiliary *fts5FindAuxiliary(Fts5FullTable *pTab, const char *zName){ Fts5Auxiliary *pAux; for(pAux=pTab->pGlobal->pAux; pAux; pAux=pAux->pNext){ @@ -214169,8 +215430,8 @@ static Fts5Auxiliary *fts5FindAuxiliary(Fts5Table *pTab, const char *zName){ static int fts5FindRankFunction(Fts5Cursor *pCsr){ - Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); - Fts5Config *pConfig = pTab->pConfig; + Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); + Fts5Config *pConfig = pTab->p.pConfig; int rc = SQLITE_OK; Fts5Auxiliary *pAux = 0; const char *zRank = pCsr->zRank; @@ -214186,7 +215447,7 @@ static int fts5FindRankFunction(Fts5Cursor *pCsr){ assert( rc==SQLITE_OK || pCsr->pRankArgStmt==0 ); if( rc==SQLITE_OK ){ if( SQLITE_ROW==sqlite3_step(pStmt) ){ - int nByte; + sqlite3_int64 nByte; pCsr->nRankArg = sqlite3_column_count(pStmt); nByte = sizeof(sqlite3_value*)*pCsr->nRankArg; pCsr->apRankArg = (sqlite3_value**)sqlite3Fts5MallocZero(&rc, nByte); @@ -214208,8 +215469,8 @@ static int fts5FindRankFunction(Fts5Cursor *pCsr){ if( rc==SQLITE_OK ){ pAux = fts5FindAuxiliary(pTab, zRank); if( pAux==0 ){ - assert( pTab->base.zErrMsg==0 ); - pTab->base.zErrMsg = sqlite3_mprintf("no such function: %s", zRank); + assert( pTab->p.base.zErrMsg==0 ); + pTab->p.base.zErrMsg = sqlite3_mprintf("no such function: %s", zRank); rc = SQLITE_ERROR; } } @@ -214284,8 +215545,8 @@ static int fts5FilterMethod( int nVal, /* Number of elements in apVal */ sqlite3_value **apVal /* Arguments for the indexing scheme */ ){ - Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab); - Fts5Config *pConfig = pTab->pConfig; + Fts5FullTable *pTab = (Fts5FullTable*)(pCursor->pVtab); + Fts5Config *pConfig = pTab->p.pConfig; Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; int rc = SQLITE_OK; /* Error code */ int iVal = 0; /* Counter for apVal[] */ @@ -214314,8 +215575,8 @@ static int fts5FilterMethod( assert( pCsr->zRank==0 ); assert( pCsr->zRankArgs==0 ); - assert( pzErrmsg==0 || pzErrmsg==&pTab->base.zErrMsg ); - pConfig->pzErrmsg = &pTab->base.zErrMsg; + assert( pzErrmsg==0 || pzErrmsg==&pTab->p.base.zErrMsg ); + pConfig->pzErrmsg = &pTab->p.base.zErrMsg; /* Decode the arguments passed through to this function. ** @@ -214381,7 +215642,7 @@ static int fts5FilterMethod( ** but a request for an internal parameter. */ rc = fts5SpecialMatch(pTab, pCsr, &zExpr[1]); }else{ - char **pzErr = &pTab->base.zErrMsg; + char **pzErr = &pTab->p.base.zErrMsg; rc = sqlite3Fts5ExprNew(pConfig, iCol, zExpr, &pCsr->pExpr, pzErr); if( rc==SQLITE_OK ){ if( bOrderByRank ){ @@ -214404,7 +215665,7 @@ static int fts5FilterMethod( ** by rowid (ePlan==FTS5_PLAN_ROWID). */ pCsr->ePlan = (pRowidEq ? FTS5_PLAN_ROWID : FTS5_PLAN_SCAN); rc = sqlite3Fts5StorageStmt( - pTab->pStorage, fts5StmtType(pCsr), &pCsr->pStmt, &pTab->base.zErrMsg + pTab->pStorage, fts5StmtType(pCsr), &pCsr->pStmt, &pTab->p.base.zErrMsg ); if( rc==SQLITE_OK ){ if( pCsr->ePlan==FTS5_PLAN_ROWID ){ @@ -214487,12 +215748,12 @@ static int fts5SeekCursor(Fts5Cursor *pCsr, int bErrormsg){ /* If the cursor does not yet have a statement handle, obtain one now. */ if( pCsr->pStmt==0 ){ - Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); + Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); int eStmt = fts5StmtType(pCsr); rc = sqlite3Fts5StorageStmt( - pTab->pStorage, eStmt, &pCsr->pStmt, (bErrormsg?&pTab->base.zErrMsg:0) + pTab->pStorage, eStmt, &pCsr->pStmt, (bErrormsg?&pTab->p.base.zErrMsg:0) ); - assert( rc!=SQLITE_OK || pTab->base.zErrMsg==0 ); + assert( rc!=SQLITE_OK || pTab->p.base.zErrMsg==0 ); assert( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_CONTENT) ); } @@ -214514,11 +215775,11 @@ static int fts5SeekCursor(Fts5Cursor *pCsr, int bErrormsg){ return rc; } -static void fts5SetVtabError(Fts5Table *p, const char *zFormat, ...){ +static void fts5SetVtabError(Fts5FullTable *p, const char *zFormat, ...){ va_list ap; /* ... printf arguments */ va_start(ap, zFormat); - assert( p->base.zErrMsg==0 ); - p->base.zErrMsg = sqlite3_vmprintf(zFormat, ap); + assert( p->p.base.zErrMsg==0 ); + p->p.base.zErrMsg = sqlite3_vmprintf(zFormat, ap); va_end(ap); } @@ -214538,11 +215799,11 @@ static void fts5SetVtabError(Fts5Table *p, const char *zFormat, ...){ ** more commands are added to this function. */ static int fts5SpecialInsert( - Fts5Table *pTab, /* Fts5 table object */ + Fts5FullTable *pTab, /* Fts5 table object */ const char *zCmd, /* Text inserted into table-name column */ sqlite3_value *pVal /* Value inserted into rank column */ ){ - Fts5Config *pConfig = pTab->pConfig; + Fts5Config *pConfig = pTab->p.pConfig; int rc = SQLITE_OK; int bError = 0; @@ -214577,9 +215838,9 @@ static int fts5SpecialInsert( pConfig->bPrefixIndex = sqlite3_value_int(pVal); #endif }else{ - rc = sqlite3Fts5IndexLoadConfig(pTab->pIndex); + rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex); if( rc==SQLITE_OK ){ - rc = sqlite3Fts5ConfigSetValue(pTab->pConfig, zCmd, pVal, &bError); + rc = sqlite3Fts5ConfigSetValue(pTab->p.pConfig, zCmd, pVal, &bError); } if( rc==SQLITE_OK ){ if( bError ){ @@ -214593,7 +215854,7 @@ static int fts5SpecialInsert( } static int fts5SpecialDelete( - Fts5Table *pTab, + Fts5FullTable *pTab, sqlite3_value **apVal ){ int rc = SQLITE_OK; @@ -214607,7 +215868,7 @@ static int fts5SpecialDelete( static void fts5StorageInsert( int *pRc, - Fts5Table *pTab, + Fts5FullTable *pTab, sqlite3_value **apVal, i64 *piRowid ){ @@ -214641,8 +215902,8 @@ static int fts5UpdateMethod( sqlite3_value **apVal, /* Array of arguments */ sqlite_int64 *pRowid /* OUT: The affected (or effected) rowid */ ){ - Fts5Table *pTab = (Fts5Table*)pVtab; - Fts5Config *pConfig = pTab->pConfig; + Fts5FullTable *pTab = (Fts5FullTable*)pVtab; + Fts5Config *pConfig = pTab->p.pConfig; int eType0; /* value_type() of apVal[0] */ int rc = SQLITE_OK; /* Return code */ @@ -214651,12 +215912,11 @@ static int fts5UpdateMethod( assert( pVtab->zErrMsg==0 ); assert( nArg==1 || nArg==(2+pConfig->nCol+2) ); - assert( nArg==1 - || sqlite3_value_type(apVal[1])==SQLITE_INTEGER - || sqlite3_value_type(apVal[1])==SQLITE_NULL + assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER + || sqlite3_value_type(apVal[0])==SQLITE_NULL ); - assert( pTab->pConfig->pzErrmsg==0 ); - pTab->pConfig->pzErrmsg = &pTab->base.zErrMsg; + assert( pTab->p.pConfig->pzErrmsg==0 ); + pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg; /* Put any active cursors into REQUIRE_SEEK state. */ fts5TripCursors(pTab); @@ -214697,7 +215957,7 @@ static int fts5UpdateMethod( /* Filter out attempts to run UPDATE or DELETE on contentless tables. ** This is not suported. */ if( eType0==SQLITE_INTEGER && fts5IsContentless(pTab) ){ - pTab->base.zErrMsg = sqlite3_mprintf( + pTab->p.base.zErrMsg = sqlite3_mprintf( "cannot %s contentless fts5 table: %s", (nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName ); @@ -214710,46 +215970,52 @@ static int fts5UpdateMethod( rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0); } - /* INSERT */ - else if( eType0!=SQLITE_INTEGER ){ - /* If this is a REPLACE, first remove the current entry (if any) */ - if( eConflict==SQLITE_REPLACE - && sqlite3_value_type(apVal[1])==SQLITE_INTEGER - ){ - i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */ - rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0); - } - fts5StorageInsert(&rc, pTab, apVal, pRowid); - } - - /* UPDATE */ + /* INSERT or UPDATE */ else{ - i64 iOld = sqlite3_value_int64(apVal[0]); /* Old rowid */ - i64 iNew = sqlite3_value_int64(apVal[1]); /* New rowid */ - if( iOld!=iNew ){ - if( eConflict==SQLITE_REPLACE ){ - rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); - if( rc==SQLITE_OK ){ - rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0); - } - fts5StorageInsert(&rc, pTab, apVal, pRowid); - }else{ - rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, pRowid); - if( rc==SQLITE_OK ){ - rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); - } - if( rc==SQLITE_OK ){ - rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal, *pRowid); - } + int eType1 = sqlite3_value_numeric_type(apVal[1]); + + if( eType1!=SQLITE_INTEGER && eType1!=SQLITE_NULL ){ + rc = SQLITE_MISMATCH; + } + + else if( eType0!=SQLITE_INTEGER ){ + /* If this is a REPLACE, first remove the current entry (if any) */ + if( eConflict==SQLITE_REPLACE && eType1==SQLITE_INTEGER ){ + i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */ + rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0); } - }else{ - rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); fts5StorageInsert(&rc, pTab, apVal, pRowid); } + + /* UPDATE */ + else{ + i64 iOld = sqlite3_value_int64(apVal[0]); /* Old rowid */ + i64 iNew = sqlite3_value_int64(apVal[1]); /* New rowid */ + if( eType1==SQLITE_INTEGER && iOld!=iNew ){ + if( eConflict==SQLITE_REPLACE ){ + rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); + if( rc==SQLITE_OK ){ + rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0); + } + fts5StorageInsert(&rc, pTab, apVal, pRowid); + }else{ + rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, pRowid); + if( rc==SQLITE_OK ){ + rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); + } + if( rc==SQLITE_OK ){ + rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal,*pRowid); + } + } + }else{ + rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0); + fts5StorageInsert(&rc, pTab, apVal, pRowid); + } + } } } - pTab->pConfig->pzErrmsg = 0; + pTab->p.pConfig->pzErrmsg = 0; return rc; } @@ -214758,12 +216024,12 @@ static int fts5UpdateMethod( */ static int fts5SyncMethod(sqlite3_vtab *pVtab){ int rc; - Fts5Table *pTab = (Fts5Table*)pVtab; + Fts5FullTable *pTab = (Fts5FullTable*)pVtab; fts5CheckTransactionState(pTab, FTS5_SYNC, 0); - pTab->pConfig->pzErrmsg = &pTab->base.zErrMsg; + pTab->p.pConfig->pzErrmsg = &pTab->p.base.zErrMsg; fts5TripCursors(pTab); rc = sqlite3Fts5StorageSync(pTab->pStorage); - pTab->pConfig->pzErrmsg = 0; + pTab->p.pConfig->pzErrmsg = 0; return rc; } @@ -214771,8 +216037,8 @@ static int fts5SyncMethod(sqlite3_vtab *pVtab){ ** Implementation of xBegin() method. */ static int fts5BeginMethod(sqlite3_vtab *pVtab){ - fts5CheckTransactionState((Fts5Table*)pVtab, FTS5_BEGIN, 0); - fts5NewTransaction((Fts5Table*)pVtab); + fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_BEGIN, 0); + fts5NewTransaction((Fts5FullTable*)pVtab); return SQLITE_OK; } @@ -214783,7 +216049,7 @@ static int fts5BeginMethod(sqlite3_vtab *pVtab){ */ static int fts5CommitMethod(sqlite3_vtab *pVtab){ UNUSED_PARAM(pVtab); /* Call below is a no-op for NDEBUG builds */ - fts5CheckTransactionState((Fts5Table*)pVtab, FTS5_COMMIT, 0); + fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_COMMIT, 0); return SQLITE_OK; } @@ -214793,7 +216059,7 @@ static int fts5CommitMethod(sqlite3_vtab *pVtab){ */ static int fts5RollbackMethod(sqlite3_vtab *pVtab){ int rc; - Fts5Table *pTab = (Fts5Table*)pVtab; + Fts5FullTable *pTab = (Fts5FullTable*)pVtab; fts5CheckTransactionState(pTab, FTS5_ROLLBACK, 0); rc = sqlite3Fts5StorageRollback(pTab->pStorage); return rc; @@ -214817,13 +216083,13 @@ static int fts5ApiColumnTotalSize( sqlite3_int64 *pnToken ){ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); + Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); return sqlite3Fts5StorageSize(pTab->pStorage, iCol, pnToken); } static int fts5ApiRowCount(Fts5Context *pCtx, i64 *pnRow){ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); + Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); return sqlite3Fts5StorageRowCount(pTab->pStorage, pnRow); } @@ -214858,7 +216124,9 @@ static int fts5ApiColumnText( ){ int rc = SQLITE_OK; Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - if( fts5IsContentless((Fts5Table*)(pCsr->base.pVtab)) ){ + if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab)) + || pCsr->ePlan==FTS5_PLAN_SPECIAL + ){ *pz = 0; *pn = 0; }else{ @@ -214927,10 +216195,11 @@ static int fts5CacheInstArray(Fts5Cursor *pCsr){ int rc = SQLITE_OK; Fts5PoslistReader *aIter; /* One iterator for each phrase */ int nIter; /* Number of iterators/phrases */ + int nCol = ((Fts5Table*)pCsr->base.pVtab)->pConfig->nCol; nIter = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); if( pCsr->aInstIter==0 ){ - int nByte = sizeof(Fts5PoslistReader) * nIter; + sqlite3_int64 nByte = sizeof(Fts5PoslistReader) * nIter; pCsr->aInstIter = (Fts5PoslistReader*)sqlite3Fts5MallocZero(&rc, nByte); } aIter = pCsr->aInstIter; @@ -214965,7 +216234,7 @@ static int fts5CacheInstArray(Fts5Cursor *pCsr){ nInst++; if( nInst>=pCsr->nInstAlloc ){ pCsr->nInstAlloc = pCsr->nInstAlloc ? pCsr->nInstAlloc*2 : 32; - aInst = (int*)sqlite3_realloc( + aInst = (int*)sqlite3_realloc64( pCsr->aInst, pCsr->nInstAlloc*sizeof(int)*3 ); if( aInst ){ @@ -214980,6 +216249,10 @@ static int fts5CacheInstArray(Fts5Cursor *pCsr){ aInst[0] = iBest; aInst[1] = FTS5_POS2COLUMN(aIter[iBest].iPos); aInst[2] = FTS5_POS2OFFSET(aIter[iBest].iPos); + if( aInst[1]<0 || aInst[1]>=nCol ){ + rc = FTS5_CORRUPT; + break; + } sqlite3Fts5PoslistReaderNext(&aIter[iBest]); } } @@ -215052,8 +216325,8 @@ static int fts5ColumnSizeCb( static int fts5ApiColumnSize(Fts5Context *pCtx, int iCol, int *pnToken){ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); - Fts5Config *pConfig = pTab->pConfig; + Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); + Fts5Config *pConfig = pTab->p.pConfig; int rc = SQLITE_OK; if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_DOCSIZE) ){ @@ -215309,7 +216582,7 @@ static int fts5ApiQueryPhrase( int(*xCallback)(const Fts5ExtensionApi*, Fts5Context*, void*) ){ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx; - Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab); + Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab); int rc; Fts5Cursor *pNew = 0; @@ -215386,25 +216659,19 @@ static void fts5ApiCallback( /* -** Given cursor id iId, return a pointer to the corresponding Fts5Index +** Given cursor id iId, return a pointer to the corresponding Fts5Table ** object. Or NULL If the cursor id does not exist. -** -** If successful, set *ppConfig to point to the associated config object -** before returning. */ -static Fts5Index *sqlite3Fts5IndexFromCsrid( +static Fts5Table *sqlite3Fts5TableFromCsrid( Fts5Global *pGlobal, /* FTS5 global context for db handle */ - i64 iCsrId, /* Id of cursor to find */ - Fts5Config **ppConfig /* OUT: Configuration object */ + i64 iCsrId /* Id of cursor to find */ ){ Fts5Cursor *pCsr; - Fts5Table *pTab; - pCsr = fts5CursorFromCsrid(pGlobal, iCsrId); - pTab = (Fts5Table*)pCsr->base.pVtab; - *ppConfig = pTab->pConfig; - - return pTab->pIndex; + if( pCsr ){ + return (Fts5Table*)pCsr->base.pVtab; + } + return 0; } /* @@ -215484,8 +216751,8 @@ static int fts5ColumnMethod( sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */ int iCol /* Index of column to read value from */ ){ - Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab); - Fts5Config *pConfig = pTab->pConfig; + Fts5FullTable *pTab = (Fts5FullTable*)(pCursor->pVtab); + Fts5Config *pConfig = pTab->p.pConfig; Fts5Cursor *pCsr = (Fts5Cursor*)pCursor; int rc = SQLITE_OK; @@ -215537,7 +216804,7 @@ static int fts5FindFunctionMethod( void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT: Result */ void **ppArg /* OUT: User data for *pxFunc */ ){ - Fts5Table *pTab = (Fts5Table*)pVtab; + Fts5FullTable *pTab = (Fts5FullTable*)pVtab; Fts5Auxiliary *pAux; UNUSED_PARAM(nUnused); @@ -215559,21 +216826,24 @@ static int fts5RenameMethod( sqlite3_vtab *pVtab, /* Virtual table handle */ const char *zName /* New name of table */ ){ - Fts5Table *pTab = (Fts5Table*)pVtab; + Fts5FullTable *pTab = (Fts5FullTable*)pVtab; return sqlite3Fts5StorageRename(pTab->pStorage, zName); } +static int sqlite3Fts5FlushToDisk(Fts5Table *pTab){ + fts5TripCursors((Fts5FullTable*)pTab); + return sqlite3Fts5StorageSync(((Fts5FullTable*)pTab)->pStorage); +} + /* ** The xSavepoint() method. ** ** Flush the contents of the pending-terms table to disk. */ static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ - Fts5Table *pTab = (Fts5Table*)pVtab; UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ - fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint); - fts5TripCursors(pTab); - return sqlite3Fts5StorageSync(pTab->pStorage); + fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_SAVEPOINT, iSavepoint); + return sqlite3Fts5FlushToDisk((Fts5Table*)pVtab); } /* @@ -215582,11 +216852,9 @@ static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){ ** This is a no-op. */ static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ - Fts5Table *pTab = (Fts5Table*)pVtab; UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ - fts5CheckTransactionState(pTab, FTS5_RELEASE, iSavepoint); - fts5TripCursors(pTab); - return sqlite3Fts5StorageSync(pTab->pStorage); + fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_RELEASE, iSavepoint); + return sqlite3Fts5FlushToDisk((Fts5Table*)pVtab); } /* @@ -215595,7 +216863,7 @@ static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){ ** Discard the contents of the pending terms table. */ static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){ - Fts5Table *pTab = (Fts5Table*)pVtab; + Fts5FullTable *pTab = (Fts5FullTable*)pVtab; UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */ fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint); fts5TripCursors(pTab); @@ -215796,7 +217064,7 @@ static void fts5SourceIdFunc( ){ assert( nArg==0 ); UNUSED_PARAM2(nArg, apUnused); - sqlite3_result_text(pCtx, "fts5: 2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238b4f9", -1, SQLITE_TRANSIENT); + sqlite3_result_text(pCtx, "fts5: 2019-02-25 16:06:06 bd49a8271d650fa89e446b42e513b595a717b9212c91dd384aab871fc1d0f6d7", -1, SQLITE_TRANSIENT); } /* @@ -216045,7 +217313,7 @@ static int fts5StorageGetStmt( char *zBind; int i; - zBind = sqlite3_malloc(1 + nCol*2); + zBind = sqlite3_malloc64(1 + nCol*2); if( zBind ){ for(i=0; idb, zSql, -1, - SQLITE_PREPARE_PERSISTENT, &p->aStmt[eStmt], 0); + int f = SQLITE_PREPARE_PERSISTENT; + if( eStmt>FTS5_STMT_LOOKUP ) f |= SQLITE_PREPARE_NO_VTAB; + rc = sqlite3_prepare_v3(pC->db, zSql, -1, f, &p->aStmt[eStmt], 0); sqlite3_free(zSql); if( rc!=SQLITE_OK && pzErrMsg ){ *pzErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pC->db)); @@ -216211,11 +217480,11 @@ static int sqlite3Fts5StorageOpen( ){ int rc = SQLITE_OK; Fts5Storage *p; /* New object */ - int nByte; /* Bytes of space to allocate */ + sqlite3_int64 nByte; /* Bytes of space to allocate */ nByte = sizeof(Fts5Storage) /* Fts5Storage object */ + pConfig->nCol * sizeof(i64); /* Fts5Storage.aTotalSize[] */ - *pp = p = (Fts5Storage*)sqlite3_malloc(nByte); + *pp = p = (Fts5Storage*)sqlite3_malloc64(nByte); if( !p ) return SQLITE_NOMEM; memset(p, 0, nByte); @@ -216226,7 +217495,7 @@ static int sqlite3Fts5StorageOpen( if( bCreate ){ if( pConfig->eContent==FTS5_CONTENT_NORMAL ){ int nDefn = 32 + pConfig->nCol*10; - char *zDefn = sqlite3_malloc(32 + pConfig->nCol * 10); + char *zDefn = sqlite3_malloc64(32 + (sqlite3_int64)pConfig->nCol * 10); if( zDefn==0 ){ rc = SQLITE_NOMEM; }else{ @@ -216517,7 +217786,7 @@ static int sqlite3Fts5StorageRebuild(Fts5Storage *p){ Fts5Config *pConfig = p->pConfig; sqlite3_stmt *pScan = 0; Fts5InsertCtx ctx; - int rc; + int rc, rc2; memset(&ctx, 0, sizeof(Fts5InsertCtx)); ctx.pStorage = p; @@ -216556,6 +217825,8 @@ static int sqlite3Fts5StorageRebuild(Fts5Storage *p){ } } sqlite3_free(buf.p); + rc2 = sqlite3_reset(pScan); + if( rc==SQLITE_OK ) rc = rc2; /* Write the averages record */ if( rc==SQLITE_OK ){ @@ -216805,7 +218076,7 @@ static int sqlite3Fts5StorageIntegrity(Fts5Storage *p){ memset(&ctx, 0, sizeof(Fts5IntegrityCtx)); ctx.pConfig = p->pConfig; - aTotalSize = (i64*)sqlite3_malloc(pConfig->nCol * (sizeof(int)+sizeof(i64))); + aTotalSize = (i64*)sqlite3_malloc64(pConfig->nCol*(sizeof(int)+sizeof(i64))); if( !aTotalSize ) return SQLITE_NOMEM; aColSize = (int*)&aTotalSize[pConfig->nCol]; memset(aTotalSize, 0, sizeof(i64) * pConfig->nCol); @@ -217005,7 +218276,13 @@ static int sqlite3Fts5StorageSize(Fts5Storage *p, int iCol, i64 *pnToken){ static int sqlite3Fts5StorageRowCount(Fts5Storage *p, i64 *pnRow){ int rc = fts5StorageLoadTotals(p, 0); if( rc==SQLITE_OK ){ + /* nTotalRow being zero does not necessarily indicate a corrupt + ** database - it might be that the FTS5 table really does contain zero + ** rows. However this function is only called from the xRowCount() API, + ** and there is no way for that API to be invoked if the table contains + ** no rows. Hence the FTS5_CORRUPT return. */ *pnRow = p->nTotalRow; + if( p->nTotalRow<=0 ) rc = FTS5_CORRUPT; } return rc; } @@ -217215,7 +218492,7 @@ static int fts5AsciiTokenize( nByte = ie-is; if( nByte>nFold ){ if( pFold!=aFold ) sqlite3_free(pFold); - pFold = sqlite3_malloc(nByte*2); + pFold = sqlite3_malloc64((sqlite3_int64)nByte*2); if( pFold==0 ){ rc = SQLITE_NOMEM; break; @@ -217297,13 +218574,18 @@ struct Unicode61Tokenizer { unsigned char aTokenChar[128]; /* ASCII range token characters */ char *aFold; /* Buffer to fold text into */ int nFold; /* Size of aFold[] in bytes */ - int bRemoveDiacritic; /* True if remove_diacritics=1 is set */ + int eRemoveDiacritic; /* True if remove_diacritics=1 is set */ int nException; int *aiException; unsigned char aCategory[32]; /* True for token char categories */ }; +/* Values for eRemoveDiacritic (must match internals of fts5_unicode2.c) */ +#define FTS5_REMOVE_DIACRITICS_NONE 0 +#define FTS5_REMOVE_DIACRITICS_SIMPLE 1 +#define FTS5_REMOVE_DIACRITICS_COMPLEX 2 + static int fts5UnicodeAddExceptions( Unicode61Tokenizer *p, /* Tokenizer object */ const char *z, /* Characters to treat as exceptions */ @@ -217314,13 +218596,14 @@ static int fts5UnicodeAddExceptions( int *aNew; if( n>0 ){ - aNew = (int*)sqlite3_realloc(p->aiException, (n+p->nException)*sizeof(int)); + aNew = (int*)sqlite3_realloc64(p->aiException, + (n+p->nException)*sizeof(int)); if( aNew ){ int nNew = p->nException; const unsigned char *zCsr = (const unsigned char*)z; const unsigned char *zTerm = (const unsigned char*)&z[n]; while( zCsriCode ) break; + if( (u32)aNew[i]>iCode ) break; } memmove(&aNew[i+1], &aNew[i], (nNew-i)*sizeof(int)); aNew[i] = iCode; @@ -217424,7 +218707,7 @@ static int fts5UnicodeCreate( int i; memset(p, 0, sizeof(Unicode61Tokenizer)); - p->bRemoveDiacritic = 1; + p->eRemoveDiacritic = FTS5_REMOVE_DIACRITICS_SIMPLE; p->nFold = 64; p->aFold = sqlite3_malloc(p->nFold * sizeof(char)); if( p->aFold==0 ){ @@ -217445,10 +218728,15 @@ static int fts5UnicodeCreate( for(i=0; rc==SQLITE_OK && ieRemoveDiacritic = (zArg[0] - '0'); + assert( p->eRemoveDiacritic==FTS5_REMOVE_DIACRITICS_NONE + || p->eRemoveDiacritic==FTS5_REMOVE_DIACRITICS_SIMPLE + || p->eRemoveDiacritic==FTS5_REMOVE_DIACRITICS_COMPLEX + ); } - p->bRemoveDiacritic = (zArg[0]=='1'); }else if( 0==sqlite3_stricmp(azArg[i], "tokenchars") ){ rc = fts5UnicodeAddExceptions(p, zArg, 1); @@ -217482,7 +218770,7 @@ static int fts5UnicodeCreate( */ static int fts5UnicodeIsAlnum(Unicode61Tokenizer *p, int iCode){ return ( - p->aCategory[sqlite3Fts5UnicodeCategory(iCode)] + p->aCategory[sqlite3Fts5UnicodeCategory((u32)iCode)] ^ fts5UnicodeIsException(p, iCode) ); } @@ -217511,7 +218799,7 @@ static int fts5UnicodeTokenize( /* Each iteration of this loop gobbles up a contiguous run of separators, ** then the next token. */ while( rc==SQLITE_OK ){ - int iCode; /* non-ASCII codepoint read from input */ + u32 iCode; /* non-ASCII codepoint read from input */ char *zOut = aFold; int is; int ie; @@ -217543,7 +218831,7 @@ static int fts5UnicodeTokenize( /* Grow the output buffer so that there is sufficient space to fit the ** largest possible utf-8 character. */ if( zOut>pEnd ){ - aFold = sqlite3_malloc(nFold*2); + aFold = sqlite3_malloc64((sqlite3_int64)nFold*2); if( aFold==0 ){ rc = SQLITE_NOMEM; goto tokenize_done; @@ -217562,7 +218850,7 @@ static int fts5UnicodeTokenize( READ_UTF8(zCsr, zTerm, iCode); if( fts5UnicodeIsAlnum(p,iCode)||sqlite3Fts5UnicodeIsdiacritic(iCode) ){ non_ascii_tokenchar: - iCode = sqlite3Fts5UnicodeFold(iCode, p->bRemoveDiacritic); + iCode = sqlite3Fts5UnicodeFold(iCode, p->eRemoveDiacritic); if( iCode ) WRITE_UTF8(zOut, iCode); }else{ break; @@ -218338,10 +219626,8 @@ static int sqlite3Fts5TokenizerInit(fts5_api *pApi){ return rc; } - - /* -** 2012 May 25 +** 2012-05-25 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -218370,32 +219656,48 @@ static int sqlite3Fts5TokenizerInit(fts5_api *pApi){ ** E"). The resuls of passing a codepoint that corresponds to an ** uppercase letter are undefined. */ -static int fts5_remove_diacritic(int c){ +static int fts5_remove_diacritic(int c, int bComplex){ unsigned short aDia[] = { 0, 1797, 1848, 1859, 1891, 1928, 1940, 1995, 2024, 2040, 2060, 2110, 2168, 2206, 2264, 2286, 2344, 2383, 2472, 2488, 2516, 2596, 2668, 2732, 2782, 2842, 2894, 2954, 2984, 3000, 3028, 3336, - 3456, 3696, 3712, 3728, 3744, 3896, 3912, 3928, - 3968, 4008, 4040, 4106, 4138, 4170, 4202, 4234, - 4266, 4296, 4312, 4344, 4408, 4424, 4472, 4504, - 6148, 6198, 6264, 6280, 6360, 6429, 6505, 6529, - 61448, 61468, 61534, 61592, 61642, 61688, 61704, 61726, - 61784, 61800, 61836, 61880, 61914, 61948, 61998, 62122, - 62154, 62200, 62218, 62302, 62364, 62442, 62478, 62536, - 62554, 62584, 62604, 62640, 62648, 62656, 62664, 62730, - 62924, 63050, 63082, 63274, 63390, + 3456, 3696, 3712, 3728, 3744, 3766, 3832, 3896, + 3912, 3928, 3944, 3968, 4008, 4040, 4056, 4106, + 4138, 4170, 4202, 4234, 4266, 4296, 4312, 4344, + 4408, 4424, 4442, 4472, 4488, 4504, 6148, 6198, + 6264, 6280, 6360, 6429, 6505, 6529, 61448, 61468, + 61512, 61534, 61592, 61610, 61642, 61672, 61688, 61704, + 61726, 61784, 61800, 61816, 61836, 61880, 61896, 61914, + 61948, 61998, 62062, 62122, 62154, 62184, 62200, 62218, + 62252, 62302, 62364, 62410, 62442, 62478, 62536, 62554, + 62584, 62604, 62640, 62648, 62656, 62664, 62730, 62766, + 62830, 62890, 62924, 62974, 63032, 63050, 63082, 63118, + 63182, 63242, 63274, 63310, 63368, 63390, }; - char aChar[] = { - '\0', 'a', 'c', 'e', 'i', 'n', 'o', 'u', 'y', 'y', 'a', 'c', - 'd', 'e', 'e', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'o', 'r', - 's', 't', 'u', 'u', 'w', 'y', 'z', 'o', 'u', 'a', 'i', 'o', - 'u', 'g', 'k', 'o', 'j', 'g', 'n', 'a', 'e', 'i', 'o', 'r', - 'u', 's', 't', 'h', 'a', 'e', 'o', 'y', '\0', '\0', '\0', '\0', - '\0', '\0', '\0', '\0', 'a', 'b', 'd', 'd', 'e', 'f', 'g', 'h', - 'h', 'i', 'k', 'l', 'l', 'm', 'n', 'p', 'r', 'r', 's', 't', - 'u', 'v', 'w', 'w', 'x', 'y', 'z', 'h', 't', 'w', 'y', 'a', - 'e', 'i', 'o', 'u', 'y', +#define HIBIT ((unsigned char)0x80) + unsigned char aChar[] = { + '\0', 'a', 'c', 'e', 'i', 'n', + 'o', 'u', 'y', 'y', 'a', 'c', + 'd', 'e', 'e', 'g', 'h', 'i', + 'j', 'k', 'l', 'n', 'o', 'r', + 's', 't', 'u', 'u', 'w', 'y', + 'z', 'o', 'u', 'a', 'i', 'o', + 'u', 'u'|HIBIT, 'a'|HIBIT, 'g', 'k', 'o', + 'o'|HIBIT, 'j', 'g', 'n', 'a'|HIBIT, 'a', + 'e', 'i', 'o', 'r', 'u', 's', + 't', 'h', 'a', 'e', 'o'|HIBIT, 'o', + 'o'|HIBIT, 'y', '\0', '\0', '\0', '\0', + '\0', '\0', '\0', '\0', 'a', 'b', + 'c'|HIBIT, 'd', 'd', 'e'|HIBIT, 'e', 'e'|HIBIT, + 'f', 'g', 'h', 'h', 'i', 'i'|HIBIT, + 'k', 'l', 'l'|HIBIT, 'l', 'm', 'n', + 'o'|HIBIT, 'p', 'r', 'r'|HIBIT, 'r', 's', + 's'|HIBIT, 't', 'u', 'u'|HIBIT, 'v', 'w', + 'w', 'x', 'y', 'z', 'h', 't', + 'w', 'y', 'a', 'a'|HIBIT, 'a'|HIBIT, 'a'|HIBIT, + 'e', 'e'|HIBIT, 'e'|HIBIT, 'i', 'o', 'o'|HIBIT, + 'o'|HIBIT, 'o'|HIBIT, 'u', 'u'|HIBIT, 'u'|HIBIT, 'y', }; unsigned int key = (((unsigned int)c)<<3) | 0x00000007; @@ -218412,7 +219714,8 @@ static int fts5_remove_diacritic(int c){ } } assert( key>=aDia[iRes] ); - return ((c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : (int)aChar[iRes]); + if( bComplex==0 && (aChar[iRes] & 0x80) ) return c; + return (c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : ((int)aChar[iRes] & 0x7F); } @@ -218425,8 +219728,8 @@ static int sqlite3Fts5UnicodeIsdiacritic(int c){ unsigned int mask1 = 0x000361F8; if( c<768 || c>817 ) return 0; return (c < 768+32) ? - (mask0 & (1 << (c-768))) : - (mask1 & (1 << (c-768-32))); + (mask0 & ((unsigned int)1 << (c-768))) : + (mask1 & ((unsigned int)1 << (c-768-32))); } @@ -218439,7 +219742,7 @@ static int sqlite3Fts5UnicodeIsdiacritic(int c){ ** The results are undefined if the value passed to this function ** is less than zero. */ -static int sqlite3Fts5UnicodeFold(int c, int bRemoveDiacritic){ +static int sqlite3Fts5UnicodeFold(int c, int eRemoveDiacritic){ /* Each entry in the following array defines a rule for folding a range ** of codepoints to lower case. The rule applies to a range of nRange ** codepoints starting at codepoint iCode. @@ -218562,7 +219865,9 @@ static int sqlite3Fts5UnicodeFold(int c, int bRemoveDiacritic){ assert( ret>0 ); } - if( bRemoveDiacritic ) ret = fts5_remove_diacritic(ret); + if( eRemoveDiacritic ){ + ret = fts5_remove_diacritic(ret, eRemoveDiacritic==2); + } } else if( c>=66560 && c<66600 ){ @@ -218573,12 +219878,6 @@ static int sqlite3Fts5UnicodeFold(int c, int bRemoveDiacritic){ } -#if 0 -static int sqlite3Fts5UnicodeNCat(void) { - return 32; -} -#endif - static int sqlite3Fts5UnicodeCatParse(const char *zCat, u8 *aArray){ aArray[0] = 1; switch( zCat[0] ){ @@ -219060,7 +220359,7 @@ static u16 aFts5UnicodeData[] = { 34, 3074, 7692, 63, 63, }; -static int sqlite3Fts5UnicodeCategory(int iCode) { +static int sqlite3Fts5UnicodeCategory(u32 iCode) { int iRes = -1; int iHi; int iLo; @@ -219098,13 +220397,12 @@ static void sqlite3Fts5UnicodeAscii(u8 *aArray, u8 *aAscii){ int bToken = aArray[ aFts5UnicodeData[iTbl] & 0x1F ]; int n = (aFts5UnicodeData[iTbl] >> 5) + i; for(; i<128 && i3 && n<=9 ); return n; } @@ -219450,7 +220748,6 @@ static int sqlite3Fts5GetVarintLen(u32 iVal){ return 5; } - /* ** 2015 May 08 ** @@ -219508,7 +220805,7 @@ struct Fts5VocabTable { struct Fts5VocabCursor { sqlite3_vtab_cursor base; sqlite3_stmt *pStmt; /* Statement holding lock on pIndex */ - Fts5Index *pIndex; /* Associated FTS5 index */ + Fts5Table *pFts5; /* Associated FTS5 table */ int bEof; /* True if this cursor is at EOF */ Fts5IndexIter *pIter; /* Term/rowid iterator object */ @@ -219517,7 +220814,6 @@ struct Fts5VocabCursor { char *zLeTerm; /* (term <= $zLeTerm) paramater, or NULL */ /* These are used by 'col' tables only */ - Fts5Config *pConfig; /* Fts5 table configuration */ int iCol; i64 *aCnt; i64 *aDoc; @@ -219780,8 +221076,7 @@ static int fts5VocabOpenMethod( sqlite3_vtab_cursor **ppCsr ){ Fts5VocabTable *pTab = (Fts5VocabTable*)pVTab; - Fts5Index *pIndex = 0; - Fts5Config *pConfig = 0; + Fts5Table *pFts5 = 0; Fts5VocabCursor *pCsr = 0; int rc = SQLITE_OK; sqlite3_stmt *pStmt = 0; @@ -219800,31 +221095,34 @@ static int fts5VocabOpenMethod( if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){ i64 iId = sqlite3_column_int64(pStmt, 0); - pIndex = sqlite3Fts5IndexFromCsrid(pTab->pGlobal, iId, &pConfig); + pFts5 = sqlite3Fts5TableFromCsrid(pTab->pGlobal, iId); } - if( rc==SQLITE_OK && pIndex==0 ){ - rc = sqlite3_finalize(pStmt); - pStmt = 0; - if( rc==SQLITE_OK ){ - pVTab->zErrMsg = sqlite3_mprintf( - "no such fts5 table: %s.%s", pTab->zFts5Db, pTab->zFts5Tbl - ); - rc = SQLITE_ERROR; + if( rc==SQLITE_OK ){ + if( pFts5==0 ){ + rc = sqlite3_finalize(pStmt); + pStmt = 0; + if( rc==SQLITE_OK ){ + pVTab->zErrMsg = sqlite3_mprintf( + "no such fts5 table: %s.%s", pTab->zFts5Db, pTab->zFts5Tbl + ); + rc = SQLITE_ERROR; + } + }else{ + rc = sqlite3Fts5FlushToDisk(pFts5); } } if( rc==SQLITE_OK ){ - int nByte = pConfig->nCol * sizeof(i64) * 2 + sizeof(Fts5VocabCursor); + int nByte = pFts5->pConfig->nCol * sizeof(i64)*2 + sizeof(Fts5VocabCursor); pCsr = (Fts5VocabCursor*)sqlite3Fts5MallocZero(&rc, nByte); } if( pCsr ){ - pCsr->pIndex = pIndex; + pCsr->pFts5 = pFts5; pCsr->pStmt = pStmt; - pCsr->pConfig = pConfig; pCsr->aCnt = (i64*)&pCsr[1]; - pCsr->aDoc = &pCsr->aCnt[pConfig->nCol]; + pCsr->aDoc = &pCsr->aCnt[pFts5->pConfig->nCol]; }else{ sqlite3_finalize(pStmt); } @@ -219840,6 +221138,7 @@ static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){ sqlite3_free(pCsr->zLeTerm); pCsr->nLeTerm = -1; pCsr->zLeTerm = 0; + pCsr->bEof = 0; } /* @@ -219878,7 +221177,7 @@ static int fts5VocabInstanceNewTerm(Fts5VocabCursor *pCsr){ } static int fts5VocabInstanceNext(Fts5VocabCursor *pCsr){ - int eDetail = pCsr->pConfig->eDetail; + int eDetail = pCsr->pFts5->pConfig->eDetail; int rc = SQLITE_OK; Fts5IndexIter *pIter = pCsr->pIter; i64 *pp = &pCsr->iInstPos; @@ -219913,7 +221212,7 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; Fts5VocabTable *pTab = (Fts5VocabTable*)pCursor->pVtab; int rc = SQLITE_OK; - int nCol = pCsr->pConfig->nCol; + int nCol = pCsr->pFts5->pConfig->nCol; pCsr->rowid++; @@ -219935,6 +221234,7 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ int nTerm; zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm); + assert( nTerm>=0 ); if( pCsr->nLeTerm>=0 ){ int nCmp = MIN(nTerm, pCsr->nLeTerm); int bCmp = memcmp(pCsr->zLeTerm, zTerm, nCmp); @@ -219951,7 +221251,7 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ assert( pTab->eType==FTS5_VOCAB_COL || pTab->eType==FTS5_VOCAB_ROW ); while( rc==SQLITE_OK ){ - int eDetail = pCsr->pConfig->eDetail; + int eDetail = pCsr->pFts5->pConfig->eDetail; const u8 *pPos; int nPos; /* Position list */ i64 iPos = 0; /* 64-bit position read from poslist */ int iOff = 0; /* Current offset within position list */ @@ -219974,7 +221274,6 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ int iCol = -1; while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){ int ii = FTS5_POS2COLUMN(iPos); - pCsr->aCnt[ii]++; if( iCol!=ii ){ if( ii>=nCol ){ rc = FTS5_CORRUPT; @@ -219983,6 +221282,7 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ pCsr->aDoc[ii]++; iCol = ii; } + pCsr->aCnt[ii]++; } }else if( eDetail==FTS5_DETAIL_COLUMNS ){ while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff,&iPos) ){ @@ -220011,7 +221311,9 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ if( rc==SQLITE_OK ){ zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm); - if( nTerm!=pCsr->term.n || memcmp(zTerm, pCsr->term.p, nTerm) ){ + if( nTerm!=pCsr->term.n + || (nTerm>0 && memcmp(zTerm, pCsr->term.p, nTerm)) + ){ break; } if( sqlite3Fts5IterEof(pCsr->pIter) ) break; @@ -220022,7 +221324,7 @@ static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){ if( rc==SQLITE_OK && pCsr->bEof==0 && pTab->eType==FTS5_VOCAB_COL ){ while( pCsr->aDoc[pCsr->iCol]==0 ) pCsr->iCol++; - assert( pCsr->iColpConfig->nCol ); + assert( pCsr->iColpFts5->pConfig->nCol ); } return rc; } @@ -220069,6 +221371,7 @@ static int fts5VocabFilterMethod( } if( pLe ){ const char *zCopy = (const char *)sqlite3_value_text(pLe); + if( zCopy==0 ) zCopy = ""; pCsr->nLeTerm = sqlite3_value_bytes(pLe); pCsr->zLeTerm = sqlite3_malloc(pCsr->nLeTerm+1); if( pCsr->zLeTerm==0 ){ @@ -220080,14 +221383,15 @@ static int fts5VocabFilterMethod( } if( rc==SQLITE_OK ){ - rc = sqlite3Fts5IndexQuery(pCsr->pIndex, zTerm, nTerm, f, 0, &pCsr->pIter); + Fts5Index *pIndex = pCsr->pFts5->pIndex; + rc = sqlite3Fts5IndexQuery(pIndex, zTerm, nTerm, f, 0, &pCsr->pIter); } if( rc==SQLITE_OK && eType==FTS5_VOCAB_INSTANCE ){ rc = fts5VocabInstanceNewTerm(pCsr); } - if( rc==SQLITE_OK - && !pCsr->bEof - && (eType!=FTS5_VOCAB_INSTANCE || pCsr->pConfig->eDetail!=FTS5_DETAIL_NONE) + if( rc==SQLITE_OK && !pCsr->bEof + && (eType!=FTS5_VOCAB_INSTANCE + || pCsr->pFts5->pConfig->eDetail!=FTS5_DETAIL_NONE) ){ rc = fts5VocabNextMethod(pCursor); } @@ -220110,7 +221414,7 @@ static int fts5VocabColumnMethod( int iCol /* Index of column to read value from */ ){ Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor; - int eDetail = pCsr->pConfig->eDetail; + int eDetail = pCsr->pFts5->pConfig->eDetail; int eType = ((Fts5VocabTable*)(pCursor->pVtab))->eType; i64 iVal = 0; @@ -220122,7 +221426,7 @@ static int fts5VocabColumnMethod( assert( iCol==1 || iCol==2 || iCol==3 ); if( iCol==1 ){ if( eDetail!=FTS5_DETAIL_NONE ){ - const char *z = pCsr->pConfig->azCol[pCsr->iCol]; + const char *z = pCsr->pFts5->pConfig->azCol[pCsr->iCol]; sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC); } }else if( iCol==2 ){ @@ -220150,8 +221454,8 @@ static int fts5VocabColumnMethod( }else if( eDetail==FTS5_DETAIL_COLUMNS ){ ii = (int)pCsr->iInstPos; } - if( ii>=0 && iipConfig->nCol ){ - const char *z = pCsr->pConfig->azCol[ii]; + if( ii>=0 && iipFts5->pConfig->nCol ){ + const char *z = pCsr->pFts5->pConfig->azCol[ii]; sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC); } break; @@ -220524,9 +221828,9 @@ SQLITE_API int sqlite3_stmt_init( #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ /************** End of stmt.c ************************************************/ -#if __LINE__!=220527 +#if __LINE__!=221831 #undef SQLITE_SOURCE_ID -#define SQLITE_SOURCE_ID "2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238alt2" +#define SQLITE_SOURCE_ID "2019-02-25 16:06:06 bd49a8271d650fa89e446b42e513b595a717b9212c91dd384aab871fc1d0alt2" #endif /* Return the source-id for this library */ SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } diff --git a/TMessagesProj/jni/sqlite/sqlite3.h b/TMessagesProj/jni/sqlite/sqlite3.h index f36ae57a6..348db7466 100644 --- a/TMessagesProj/jni/sqlite/sqlite3.h +++ b/TMessagesProj/jni/sqlite/sqlite3.h @@ -123,9 +123,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.26.0" -#define SQLITE_VERSION_NUMBER 3026000 -#define SQLITE_SOURCE_ID "2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238b4f9" +#define SQLITE_VERSION "3.27.2" +#define SQLITE_VERSION_NUMBER 3027002 +#define SQLITE_SOURCE_ID "2019-02-25 16:06:06 bd49a8271d650fa89e446b42e513b595a717b9212c91dd384aab871fc1d0f6d7" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -823,6 +823,15 @@ struct sqlite3_io_methods { ** file space based on this hint in order to help writes to the database ** file run faster. ** +**
  • [[SQLITE_FCNTL_SIZE_LIMIT]] +** The [SQLITE_FCNTL_SIZE_LIMIT] opcode is used by in-memory VFS that +** implements [sqlite3_deserialize()] to set an upper bound on the size +** of the in-memory database. The argument is a pointer to a [sqlite3_int64]. +** If the integer pointed to is negative, then it is filled in with the +** current limit. Otherwise the limit is set to the larger of the value +** of the integer pointed to and the current database size. The integer +** pointed to is set to the new limit. +** **
  • [[SQLITE_FCNTL_CHUNK_SIZE]] ** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS ** extends and truncates the database file in chunks of a size specified @@ -1131,6 +1140,7 @@ struct sqlite3_io_methods { #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33 #define SQLITE_FCNTL_LOCK_TIMEOUT 34 #define SQLITE_FCNTL_DATA_VERSION 35 +#define SQLITE_FCNTL_SIZE_LIMIT 36 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE @@ -1972,6 +1982,17 @@ struct sqlite3_mem_methods { ** negative value for this option restores the default behaviour. ** This option is only available if SQLite is compiled with the ** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option. +** +** [[SQLITE_CONFIG_MEMDB_MAXSIZE]] +**
    SQLITE_CONFIG_MEMDB_MAXSIZE +**
    The SQLITE_CONFIG_MEMDB_MAXSIZE option accepts a single parameter +** [sqlite3_int64] parameter which is the default maximum size for an in-memory +** database created using [sqlite3_deserialize()]. This default maximum +** size can be adjusted up or down for individual databases using the +** [SQLITE_FCNTL_SIZE_LIMIT] [sqlite3_file_control|file-control]. If this +** configuration setting is never used, then the default maximum is determined +** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that +** compile-time option is not set, then the default maximum is 1073741824. ** */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ @@ -2002,6 +2023,7 @@ struct sqlite3_mem_methods { #define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ +#define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */ /* ** CAPI3REF: Database Connection Configuration Options @@ -2347,7 +2369,7 @@ SQLITE_API int sqlite3_changes(sqlite3*); ** not. ^Changes to a view that are intercepted by INSTEAD OF triggers ** are not counted. ** -** This the [sqlite3_total_changes(D)] interface only reports the number +** The [sqlite3_total_changes(D)] interface only reports the number ** of rows that changed due to SQL statement run against database ** connection D. Any changes by other database connections are ignored. ** To detect changes against a database file from other database @@ -2991,9 +3013,9 @@ SQLITE_API int sqlite3_set_authorizer( ** time is in units of nanoseconds, however the current implementation ** is only capable of millisecond resolution so the six least significant ** digits in the time are meaningless. Future versions of SQLite -** might provide greater resolution on the profiler callback. The -** sqlite3_profile() function is considered experimental and is -** subject to change in future versions of SQLite. +** might provide greater resolution on the profiler callback. Invoking +** either [sqlite3_trace()] or [sqlite3_trace_v2()] will cancel the +** profile callback. */ SQLITE_API SQLITE_DEPRECATED void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); @@ -3407,6 +3429,8 @@ SQLITE_API int sqlite3_open_v2( ** is not a database file pathname pointer that SQLite passed into the xOpen ** VFS method, then the behavior of this routine is undefined and probably ** undesirable. +** +** See the [URI filename] documentation for additional information. */ SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); @@ -3629,18 +3653,23 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); ** deplete the limited store of lookaside memory. Future versions of ** SQLite may act on this hint differently. ** -** [[SQLITE_PREPARE_NORMALIZE]] ^(
    SQLITE_PREPARE_NORMALIZE
    -**
    The SQLITE_PREPARE_NORMALIZE flag indicates that a normalized -** representation of the SQL statement should be calculated and then -** associated with the prepared statement, which can be obtained via -** the [sqlite3_normalized_sql()] interface.)^ The semantics used to -** normalize a SQL statement are unspecified and subject to change. -** At a minimum, literal values will be replaced with suitable -** placeholders. +** [[SQLITE_PREPARE_NORMALIZE]]
    SQLITE_PREPARE_NORMALIZE
    +**
    The SQLITE_PREPARE_NORMALIZE flag is a no-op. This flag used +** to be required for any prepared statement that wanted to use the +** [sqlite3_normalized_sql()] interface. However, the +** [sqlite3_normalized_sql()] interface is now available to all +** prepared statements, regardless of whether or not they use this +** flag. +** +** [[SQLITE_PREPARE_NO_VTAB]]
    SQLITE_PREPARE_NO_VTAB
    +**
    The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler +** to return an error (error code SQLITE_ERROR) if the statement uses +** any virtual tables. ** */ #define SQLITE_PREPARE_PERSISTENT 0x01 #define SQLITE_PREPARE_NORMALIZE 0x02 +#define SQLITE_PREPARE_NO_VTAB 0x04 /* ** CAPI3REF: Compiling An SQL Statement @@ -9996,7 +10025,7 @@ SQLITE_API int sqlite3changeset_next(sqlite3_changeset_iter *pIter); ** sqlite3changeset_next() is called on the iterator or until the ** conflict-handler function returns. If pnCol is not NULL, then *pnCol is ** set to the number of columns in the table affected by the change. If -** pbIncorrect is not NULL, then *pbIndirect is set to true (1) if the change +** pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change ** is an indirect change, or false (0) otherwise. See the documentation for ** [sqlite3session_indirect()] for a description of direct and indirect ** changes. Finally, if pOp is not NULL, then *pOp is set to one of @@ -11230,12 +11259,8 @@ struct Fts5PhraseIter { ** ** Usually, output parameter *piPhrase is set to the phrase number, *piCol ** to the column in which it occurs and *piOff the token offset of the -** first token of the phrase. The exception is if the table was created -** with the offsets=0 option specified. In this case *piOff is always -** set to -1. -** -** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM) -** if an error occurs. +** first token of the phrase. Returns SQLITE_OK if successful, or an error +** code (i.e. SQLITE_NOMEM) if an error occurs. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. @@ -11524,11 +11549,11 @@ struct Fts5ExtensionApi { ** the tokenizer substitutes "first" for "1st" and the query works ** as expected. ** -**
  • By adding multiple synonyms for a single term to the FTS index. -** In this case, when tokenizing query text, the tokenizer may -** provide multiple synonyms for a single term within the document. -** FTS5 then queries the index for each synonym individually. For -** example, faced with the query: +**
  • By querying the index for all synonyms of each query term +** separately. In this case, when tokenizing query text, the +** tokenizer may provide multiple synonyms for a single term +** within the document. FTS5 then queries the index for each +** synonym individually. For example, faced with the query: ** ** ** ... MATCH 'first place' @@ -11552,7 +11577,7 @@ struct Fts5ExtensionApi { ** "place". ** ** This way, even if the tokenizer does not provide synonyms -** when tokenizing query text (it should not - to do would be +** when tokenizing query text (it should not - to do so would be ** inefficient), it doesn't matter if the user queries for ** 'first + place' or '1st + place', as there are entries in the ** FTS index corresponding to both forms of the first token. diff --git a/TMessagesProj/jni/utilities.cpp b/TMessagesProj/jni/utilities.cpp new file mode 100644 index 000000000..9aae588c3 --- /dev/null +++ b/TMessagesProj/jni/utilities.cpp @@ -0,0 +1,18 @@ +#include +#include +#include +#include + +thread_local static char buf[PATH_MAX + 1]; + +extern "C" JNIEXPORT jstring Java_org_telegram_messenger_Utilities_readlink(JNIEnv *env, jclass clazz, jstring path) { + const char *fileName = env->GetStringUTFChars(path, NULL); + ssize_t result = readlink(fileName, buf, PATH_MAX); + jstring value = 0; + if (result != -1) { + buf[result] = '\0'; + value = env->NewStringUTF(buf); + } + env->ReleaseStringUTFChars(path, fileName); + return value; +} diff --git a/TMessagesProj/jni/utils.c b/TMessagesProj/jni/utils.c deleted file mode 100644 index 1321a8213..000000000 --- a/TMessagesProj/jni/utils.c +++ /dev/null @@ -1,14 +0,0 @@ -#include "utils.h" - -void throwException(JNIEnv *env, char *format, ...) { - jclass exClass = (*env)->FindClass(env, "java/lang/UnsupportedOperationException"); - if (!exClass) { - return; - } - char dest[256]; - va_list argptr; - va_start(argptr, format); - vsprintf(dest, format, argptr); - va_end(argptr); - (*env)->ThrowNew(env, exClass, dest); -} diff --git a/TMessagesProj/src/main/AndroidManifest.xml b/TMessagesProj/src/main/AndroidManifest.xml index da1b3f4de..9aeea77a2 100644 --- a/TMessagesProj/src/main/AndroidManifest.xml +++ b/TMessagesProj/src/main/AndroidManifest.xml @@ -67,7 +67,7 @@ + + diff --git a/TMessagesProj/src/main/assets/dark.attheme b/TMessagesProj/src/main/assets/dark.attheme index 2710a2b7f..267154f89 100644 --- a/TMessagesProj/src/main/assets/dark.attheme +++ b/TMessagesProj/src/main/assets/dark.attheme @@ -1,5 +1,6 @@ chat_unreadMessagesStartText=-620756993 chat_inFileBackgroundSelected=-13347218 +windowBackgroundChecked=-11107667 chat_editDoneIcon=-9456666 radioBackgroundChecked=-10636048 dialogTextBlue=-9456666 @@ -23,6 +24,7 @@ switchTrack=-11314335 chat_inPreviewInstantSelectedText=-11099429 chat_attachAudioBackground=-1012649 actionBarDefaultSubmenuBackground=-14144465 +switchTrackBlueThumb=-14473945 avatar_nameInMessageViolet=-5925398 emptyListPlaceholder=-11247768 chat_inAudioSelectedProgress=-14925469 @@ -78,6 +80,7 @@ windowBackgroundWhiteBlueHeader=-9456666 files_folderIconBackground=-13947340 chat_messagePanelVoiceBackground=-11495209 passport_authorizeBackgroundSelected=-9590295 +switchTrackBlueChecked=-5908226 sharedMedia_linkPlaceholderText=-11117472 player_seekBarBackground=1196577362 divider=384431615 @@ -153,6 +156,7 @@ chat_inVenueInfoSelectedText=-8085320 dialogTextBlue2=-9456666 avatar_backgroundGroupCreateSpanBlue=-12751207 dialogTextBlue3=-9456666 +switchTrackBlueThumbChecked=-11107667 dialogTextBlue4=-9456666 windowBackgroundWhiteGreenText=-10101914 chat_emojiPanelIcon=-10458511 @@ -270,6 +274,7 @@ avatar_nameInMessageBlue=-9456666 dialogTextBlack=-1 actionBarDefault=-14276309 profile_actionIcon=-1 +windowBackgroundUnchecked=-14473945 actionBarDefaultSelector=-11972268 chats_menuTopShadow=-15724528 chat_outAudioPerfomerText=-6965025 @@ -279,10 +284,12 @@ chat_inVenueInfoText=-8351328 chat_replyPanelIcons=-9456666 fastScrollInactive=-12103850 chat_outSentClockSelected=-6764038 +switchTrackBlueSelectorChecked=848091135 musicPicker_checkbox=-11621658 chat_outFileBackground=-11829594 chats_name=-1 chat_attachSendBackground=-10242065 +switchTrackBlueSelector=431611386 dialogBadgeBackground=-10371847 chat_outBubbleSelected=-12487769 avatar_backgroundInProfileBlue=-11232035 diff --git a/TMessagesProj/src/main/assets/darkblue.attheme b/TMessagesProj/src/main/assets/darkblue.attheme index 2690c1904..27655b6dd 100644 --- a/TMessagesProj/src/main/assets/darkblue.attheme +++ b/TMessagesProj/src/main/assets/darkblue.attheme @@ -1,5 +1,6 @@ chat_unreadMessagesStartText=-620756993 chat_inFileBackgroundSelected=-13218977 +windowBackgroundChecked=-11632213 chat_editDoneIcon=-10177041 radioBackgroundChecked=-10177041 dialogTextBlue=-10177041 @@ -22,6 +23,7 @@ chat_inPreviewInstantSelectedText=-5648402 chat_attachAudioBackground=-619421 location_sendLocationBackground=-9919529 actionBarDefaultSubmenuBackground=-13878451 +switchTrackBlueThumb=-14866637 avatar_nameInMessageViolet=-6643205 emptyListPlaceholder=-8549479 chat_inAudioSelectedProgress=-1 @@ -74,6 +76,7 @@ chat_recordTime=-1 windowBackgroundWhiteBlueHeader=-8796932 files_folderIconBackground=-13286315 passport_authorizeBackgroundSelected=-11627561 +switchTrackBlueChecked=-8333825 player_seekBarBackground=1196577362 groupcreate_onlineText=-10177041 divider=-1795162112 @@ -148,6 +151,7 @@ chat_inVenueInfoSelectedText=-7490861 dialogTextBlue2=-10177041 avatar_backgroundGroupCreateSpanBlue=-13803892 dialogTextBlue3=-10177041 +switchTrackBlueThumbChecked=-11632213 dialogTextBlue4=-10177041 windowBackgroundWhiteGreenText=-10371737 chat_emojiPanelIcon=-9996665 @@ -183,6 +187,7 @@ chat_outContactPhoneText=-7357217 chat_inAudioTitleText=-8796932 chat_messageLinkIn=-8796932 chats_menuBackground=-14866637 +windowBackgroundCheckText=-1 chat_serviceBackground=-2110438831 chats_secretIcon=-9316522 chat_inFileBackground=-13417903 @@ -236,6 +241,7 @@ checkboxSquareBackground=-10177051 chat_mediaSentClock=-1291845633 files_folderIcon=-8220513 chats_menuCloudBackgroundCats=-11232035 +switchTrackBlue=-10984850 chat_topPanelClose=-10590606 profile_adminIcon=-8549479 chats_verifiedBackground=-10177041 @@ -257,6 +263,7 @@ dialogTextBlack=-1 actionBarDefault=-14602949 location_placeLocationBackground=-9919529 profile_actionIcon=-1 +windowBackgroundUnchecked=-14866637 actionBarDefaultSelector=-13483693 chats_menuTopShadow=-1542185690 chat_outAudioPerfomerText=-7357217 @@ -270,8 +277,10 @@ fastScrollInactive=-12760487 chat_outSentClockSelected=-4268038 featuredStickers_addedIcon=-10177041 chat_goDownButtonCounter=-14866637 +switchTrackBlueSelectorChecked=848091135 chat_outFileBackground=-11175517 chats_name=-1446156 +switchTrackBlueSelector=431611386 dialogBadgeBackground=-10177041 chat_outBubbleSelected=-11829841 avatar_backgroundInProfileBlue=-11232035 diff --git a/TMessagesProj/src/main/java/com/google/android/README.md b/TMessagesProj/src/main/java/com/google/android/README.md index 7a08cb3be..069b3d5aa 100644 --- a/TMessagesProj/src/main/java/com/google/android/README.md +++ b/TMessagesProj/src/main/java/com/google/android/README.md @@ -2,3 +2,4 @@ change SimpleExoPlayer.java.java change VideoListener.java change AspectRatioFrameLayout.java change DefaultExtractorsFactory.java +change MP4Extractor.java - MAXIMUM_READ_AHEAD_BYTES_STREAM to 1MB diff --git a/TMessagesProj/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/TMessagesProj/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java index 873641736..7ac6de993 100755 --- a/TMessagesProj/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java +++ b/TMessagesProj/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java @@ -394,7 +394,7 @@ import java.util.concurrent.CopyOnWriteArrayList; } @Override - public void release() { + public void release(boolean async) { Log.i(TAG, "Release " + Integer.toHexString(System.identityHashCode(this)) + " [" + ExoPlayerLibraryInfo.VERSION_SLASHY + "] [" + Util.DEVICE_DEBUG_INFO + "] [" + ExoPlayerLibraryInfo.registeredModules() + "]"); diff --git a/TMessagesProj/src/main/java/com/google/android/exoplayer2/Format.java b/TMessagesProj/src/main/java/com/google/android/exoplayer2/Format.java index 6c54c07cd..a72dfd8ac 100755 --- a/TMessagesProj/src/main/java/com/google/android/exoplayer2/Format.java +++ b/TMessagesProj/src/main/java/com/google/android/exoplayer2/Format.java @@ -921,8 +921,8 @@ public final class Format implements Parcelable { this.height = height; this.frameRate = frameRate; this.rotationDegrees = rotationDegrees == Format.NO_VALUE ? 0 : rotationDegrees; - this.pixelWidthHeightRatio = - pixelWidthHeightRatio == Format.NO_VALUE ? 1 : pixelWidthHeightRatio; + this.pixelWidthHeightRatio = 1; + //pixelWidthHeightRatio == Format.NO_VALUE ? 1 : pixelWidthHeightRatio; TODO remove after ffmpeg support this.projectionData = projectionData; this.stereoMode = stereoMode; this.colorInfo = colorInfo; diff --git a/TMessagesProj/src/main/java/com/google/android/exoplayer2/Player.java b/TMessagesProj/src/main/java/com/google/android/exoplayer2/Player.java index e3441fb2a..223206a23 100755 --- a/TMessagesProj/src/main/java/com/google/android/exoplayer2/Player.java +++ b/TMessagesProj/src/main/java/com/google/android/exoplayer2/Player.java @@ -760,7 +760,7 @@ public interface Player { * Releases the player. This method must be called when the player is no longer required. The * player must not be used after calling this method. */ - void release(); + void release(boolean async); /** * Returns the number of renderers. diff --git a/TMessagesProj/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java b/TMessagesProj/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java index f45e8894e..3335703e3 100755 --- a/TMessagesProj/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java +++ b/TMessagesProj/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java @@ -54,6 +54,9 @@ import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.video.VideoFrameMetadataListener; import com.google.android.exoplayer2.video.VideoRendererEventListener; import com.google.android.exoplayer2.video.spherical.CameraMotionListener; + +import org.telegram.messenger.Utilities; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -986,9 +989,13 @@ public class SimpleExoPlayer extends BasePlayer } @Override - public void release() { + public void release(boolean async) { audioFocusManager.handleStop(); - player.release(); + if (async) { + Utilities.globalQueue.postRunnable(() -> player.release(async)); + } else { + player.release(async); + } removeSurfaceCallbacks(); if (surface != null) { if (ownsSurface) { diff --git a/TMessagesProj/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java b/TMessagesProj/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java index 5356fdb54..8009fef67 100755 --- a/TMessagesProj/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java +++ b/TMessagesProj/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java @@ -88,7 +88,7 @@ public final class Mp4Extractor implements Extractor, SeekMap { * For poorly interleaved streams, the maximum byte difference one track is allowed to be read * ahead before the source will be reloaded at a new position to read another track. */ - private static final long MAXIMUM_READ_AHEAD_BYTES_STREAM = 10 * 1024 * 1024; + private static final long MAXIMUM_READ_AHEAD_BYTES_STREAM = 1024 * 1024; private final @Flags int flags; diff --git a/TMessagesProj/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java b/TMessagesProj/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java index e842d4f25..7fd231f4e 100755 --- a/TMessagesProj/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java +++ b/TMessagesProj/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java @@ -356,7 +356,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; } else if (isPendingReset()) { return pendingResetPositionUs; } - long largestQueuedTimestampUs = C.TIME_UNSET; + long largestQueuedTimestampUs = Long.MAX_VALUE; if (haveAudioVideoTracks) { // Ignore non-AV tracks, which may be sparse or poorly interleaved. largestQueuedTimestampUs = Long.MAX_VALUE; @@ -368,7 +368,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; } } } - if (largestQueuedTimestampUs == C.TIME_UNSET) { + if (largestQueuedTimestampUs == Long.MAX_VALUE) { largestQueuedTimestampUs = getLargestQueuedTimestampUs(); } return largestQueuedTimestampUs == Long.MIN_VALUE ? lastSeekPositionUs diff --git a/TMessagesProj/src/main/java/com/google/android/exoplayer2/ui/AspectRatioFrameLayout.java b/TMessagesProj/src/main/java/com/google/android/exoplayer2/ui/AspectRatioFrameLayout.java index 4379a32a1..222b23c13 100755 --- a/TMessagesProj/src/main/java/com/google/android/exoplayer2/ui/AspectRatioFrameLayout.java +++ b/TMessagesProj/src/main/java/com/google/android/exoplayer2/ui/AspectRatioFrameLayout.java @@ -210,13 +210,17 @@ public class AspectRatioFrameLayout extends FrameLayout { } break; case RESIZE_MODE_FILL: + if (aspectDeformation <= 0) { + height = (int) (width / videoAspectRatio); + } else { + width = (int) (height * videoAspectRatio); + } default: // Ignore target aspect ratio break; } aspectRatioUpdateDispatcher.scheduleUpdate(videoAspectRatio, viewAspectRatio, true); - super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); + super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); int count = getChildCount(); for (int a = 0; a < count; a++) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java b/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java index 75832aaca..4c5991cbc 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java @@ -1853,14 +1853,33 @@ public class AndroidUtilities { } public static String formatFileSize(long size) { + return formatFileSize(size, false); + } + + public static String formatFileSize(long size, boolean removeZero) { if (size < 1024) { return String.format("%d B", size); } else if (size < 1024 * 1024) { - return String.format("%.1f KB", size / 1024.0f); + float value = size / 1024.0f; + if (removeZero && (value - (int) value) * 10 == 0) { + return String.format("%d KB", (int) value); + } else { + return String.format("%.1f KB", value); + } } else if (size < 1024 * 1024 * 1024) { - return String.format("%.1f MB", size / 1024.0f / 1024.0f); + float value = size / 1024.0f / 1024.0f; + if (removeZero && (value - (int) value) * 10 == 0) { + return String.format("%d MB", (int) value); + } else { + return String.format("%.1f MB", value); + } } else { - return String.format("%.1f GB", size / 1024.0f / 1024.0f / 1024.0f); + float value = size / 1024.0f / 1024.0f / 1024.0f; + if (removeZero && (value - (int) value) * 10 == 0) { + return String.format("%d GB", (int) value); + } else { + return String.format("%.1f GB", value); + } } } @@ -2132,9 +2151,10 @@ public class AndroidUtilities { return rights == null || Math.abs(rights.until_date - System.currentTimeMillis() / 1000) > 5 * 365 * 24 * 60 * 60; } - public static void setRectToRect(Matrix matrix, RectF src, RectF dst, int rotation, Matrix.ScaleToFit align) { + public static void setRectToRect(Matrix matrix, RectF src, RectF dst, int rotation, boolean translate) { float tx, sx; float ty, sy; + boolean xLarger = false; if (rotation == 90 || rotation == 270) { sx = dst.height() / src.width(); sy = dst.width() / src.height(); @@ -2142,17 +2162,16 @@ public class AndroidUtilities { sx = dst.width() / src.width(); sy = dst.height() / src.height(); } - if (align != Matrix.ScaleToFit.FILL) { - if (sx > sy) { - sx = sy; - } else { - sy = sx; - } + if (sx < sy) { + sx = sy; + xLarger = true; + } else { + sy = sx; } - tx = -src.left * sx; - ty = -src.top * sy; - matrix.setTranslate(dst.left, dst.top); + if (translate) { + matrix.setTranslate(dst.left, dst.top); + } if (rotation == 90) { matrix.preRotate(90); matrix.preTranslate(0, -dst.width()); @@ -2164,8 +2183,31 @@ public class AndroidUtilities { matrix.preTranslate(-dst.height(), 0); } + if (translate) { + tx = -src.left * sx; + ty = -src.top * sy; + } else { + tx = dst.left - src.left * sx; + ty = dst.top - src.top * sy; + } + + float diff; + if (xLarger) { + diff = dst.width() - src.width() * sy; + } else { + diff = dst.height() - src.height() * sy; + } + diff = diff / 2.0f; + if (xLarger) { + tx += diff; + } else { + ty += diff; + } + matrix.preScale(sx, sy); - matrix.preTranslate(tx, ty); + if (translate) { + matrix.preTranslate(tx, ty); + } } public static boolean handleProxyIntent(Activity activity, Intent intent) { @@ -2538,4 +2580,10 @@ public class AndroidUtilities { } return link; } + + public static float distanceInfluenceForSnapDuration(float f) { + f -= 0.5F; + f *= 0.47123894F; + return (float) Math.sin((double) f); + } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/AnimatedFileDrawableStream.java b/TMessagesProj/src/main/java/org/telegram/messenger/AnimatedFileDrawableStream.java new file mode 100644 index 000000000..a3b605078 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/AnimatedFileDrawableStream.java @@ -0,0 +1,100 @@ +package org.telegram.messenger; + +import org.telegram.tgnet.TLRPC; + +import java.util.concurrent.CountDownLatch; + +public class AnimatedFileDrawableStream implements FileLoadOperationStream { + + private FileLoadOperation loadOperation; + private CountDownLatch countDownLatch; + private TLRPC.Document document; + private Object parentObject; + private int currentAccount; + private volatile boolean canceled; + private final Object sync = new Object(); + private int lastOffset; + + public AnimatedFileDrawableStream(TLRPC.Document d, Object p, int a) { + document = d; + parentObject = p; + currentAccount = a; + loadOperation = FileLoader.getInstance(currentAccount).loadStreamFile(this, document, parentObject, 0); + } + + public int read(int offset, int readLength) { + synchronized (sync) { + if (canceled) { + return 0; + } + } + if (readLength == 0) { + return 0; + } else { + int availableLength = 0; + try { + while (availableLength == 0) { + availableLength = loadOperation.getDownloadedLengthFromOffset(offset, readLength); + if (availableLength == 0) { + if (loadOperation.isPaused() || lastOffset != offset) { + FileLoader.getInstance(currentAccount).loadStreamFile(this, document, parentObject, offset); + } + synchronized (sync) { + if (canceled) { + return 0; + } + countDownLatch = new CountDownLatch(1); + } + FileLoader.getInstance(currentAccount).setLoadingVideo(document, false, true); + countDownLatch.await(); + } + } + lastOffset = offset + availableLength; + } catch (Exception e) { + FileLog.e(e); + } + return availableLength; + } + } + + public void cancel() { + cancel(true); + } + + public void cancel(boolean removeLoading) { + synchronized (sync) { + if (countDownLatch != null) { + countDownLatch.countDown(); + if (removeLoading && !canceled) { + FileLoader.getInstance(currentAccount).removeLoadingVideo(document, false, true); + } + } + canceled = true; + } + } + + public void reset() { + synchronized (sync) { + canceled = false; + } + } + + public TLRPC.Document getDocument() { + return document; + } + + public Object getParentObject() { + return document; + } + + public int getCurrentAccount() { + return currentAccount; + } + + @Override + public void newDataAvailable() { + if (countDownLatch != null) { + countDownLatch.countDown(); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java index 694c9cf79..ce07aebd9 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java @@ -89,7 +89,12 @@ public class ApplicationLoader extends Application { BroadcastReceiver networkStateReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - currentNetworkInfo = connectivityManager.getActiveNetworkInfo(); + try { + currentNetworkInfo = connectivityManager.getActiveNetworkInfo(); + } catch (Throwable ignore) { + + } + boolean isSlow = isConnectionSlow(); for (int a = 0; a < UserConfig.MAX_ACCOUNT_COUNT; a++) { ConnectionsManager.getInstance(a).checkConnection(); @@ -151,11 +156,24 @@ public class ApplicationLoader extends Application { WearDataLayerListenerService.updateWatchConnectionState(); } + public ApplicationLoader() { + super(); + } + @Override public void onCreate() { + try { + applicationContext = getApplicationContext(); + } catch (Throwable ignore) { + + } + super.onCreate(); - applicationContext = getApplicationContext(); + if (applicationContext == null) { + applicationContext = getApplicationContext(); + } + NativeLoader.initNativeLibs(ApplicationLoader.applicationContext); ConnectionsManager.native_setJava(false); new ForegroundDetector(this); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java b/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java index b57f7fa1c..1f33449a8 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java @@ -18,8 +18,8 @@ public class BuildVars { public static boolean LOGS_ENABLED = false; public static boolean USE_CLOUD_STRINGS = true; public static boolean CHECK_UPDATES = false; - public static int BUILD_VERSION = 1497; - public static String BUILD_VERSION_STRING = "5.3.0"; + public static int BUILD_VERSION = 1517; + public static String BUILD_VERSION_STRING = "5.4.0"; public static int APP_ID = 0; //obtain your own APP_ID at https://core.telegram.org/api/obtaining_api_id public static String APP_HASH = ""; //obtain your own APP_HASH at https://core.telegram.org/api/obtaining_api_id public static String HOCKEY_APP_HASH = "your-hockeyapp-api-key-here"; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java b/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java index d16e5c772..840483a3e 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java @@ -2008,8 +2008,8 @@ public class ContactsController { try { Uri rawContactUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon().appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, systemAccount.name).appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, systemAccount.type).build(); int value = contentResolver.delete(rawContactUri, ContactsContract.RawContacts.SYNC2 + " = " + user.id, null); - } catch (Exception e) { - FileLog.e(e); + } catch (Exception ignore) { + } } @@ -2049,8 +2049,8 @@ public class ContactsController { if (result != null && result.length > 0 && result[0].uri != null) { res = Long.parseLong(result[0].uri.getLastPathSegment()); } - } catch (Exception e) { - FileLog.e(e); + } catch (Exception ignore) { + } synchronized (observerLock) { ignoreChanges = false; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueue.java b/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueue.java index 2da5fdfc1..f10f13d0b 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueue.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueue.java @@ -53,14 +53,14 @@ public class DispatchQueue extends Thread { public void postRunnable(Runnable runnable, long delay) { try { syncLatch.await(); - if (delay <= 0) { - handler.post(runnable); - } else { - handler.postDelayed(runnable, delay); - } } catch (Exception e) { FileLog.e(e); } + if (delay <= 0) { + handler.post(runnable); + } else { + handler.postDelayed(runnable, delay); + } } public void cleanupQueue() { @@ -76,6 +76,10 @@ public class DispatchQueue extends Thread { } + public void recycle() { + handler.getLooper().quit(); + } + @Override public void run() { Looper.prepare(); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/DownloadController.java b/TMessagesProj/src/main/java/org/telegram/messenger/DownloadController.java index eae229e2a..06735a858 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/DownloadController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/DownloadController.java @@ -17,6 +17,7 @@ import android.net.ConnectivityManager; import android.util.LongSparseArray; import android.util.SparseArray; +import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; import java.lang.ref.WeakReference; @@ -33,27 +34,25 @@ public class DownloadController implements NotificationCenter.NotificationCenter int getObserverTag(); } - public static final int AUTODOWNLOAD_MASK_PHOTO = 1; - public static final int AUTODOWNLOAD_MASK_AUDIO = 2; - public static final int AUTODOWNLOAD_MASK_VIDEO = 4; - public static final int AUTODOWNLOAD_MASK_DOCUMENT = 8; - public static final int AUTODOWNLOAD_MASK_MUSIC = 16; - public static final int AUTODOWNLOAD_MASK_GIF = 32; - public static final int AUTODOWNLOAD_MASK_VIDEOMESSAGE = 64; - public boolean globalAutodownloadEnabled; - public int mobileDataDownloadMask[] = new int[4]; - public int wifiDownloadMask[] = new int[4]; - public int roamingDownloadMask[] = new int[4]; - public int mobileMaxFileSize[] = new int[7]; - public int wifiMaxFileSize[] = new int[7]; - public int roamingMaxFileSize[] = new int[7]; + public static final int AUTODOWNLOAD_TYPE_PHOTO = 1; + public static final int AUTODOWNLOAD_TYPE_AUDIO = 2; + public static final int AUTODOWNLOAD_TYPE_VIDEO = 4; + public static final int AUTODOWNLOAD_TYPE_DOCUMENT = 8; + + public static final int PRESET_NUM_CONTACT = 0; + public static final int PRESET_NUM_PM = 1; + public static final int PRESET_NUM_GROUP = 2; + public static final int PRESET_NUM_CHANNEL = 3; + + public static final int PRESET_SIZE_NUM_PHOTO = 0; + public static final int PRESET_SIZE_NUM_VIDEO = 1; + public static final int PRESET_SIZE_NUM_DOCUMENT = 2; + public static final int PRESET_SIZE_NUM_AUDIO = 3; + private int lastCheckMask = 0; private ArrayList photoDownloadQueue = new ArrayList<>(); private ArrayList audioDownloadQueue = new ArrayList<>(); - private ArrayList videoMessageDownloadQueue = new ArrayList<>(); private ArrayList documentDownloadQueue = new ArrayList<>(); - private ArrayList musicDownloadQueue = new ArrayList<>(); - private ArrayList gifDownloadQueue = new ArrayList<>(); private ArrayList videoDownloadQueue = new ArrayList<>(); private HashMap downloadQueueKeys = new HashMap<>(); @@ -65,8 +64,137 @@ public class DownloadController implements NotificationCenter.NotificationCenter private ArrayList deleteLaterArray = new ArrayList<>(); private int lastTag = 0; + private boolean loadingAutoDownloadConfig; + private LongSparseArray typingTimes = new LongSparseArray<>(); + public static class Preset { + public int[] mask = new int[4]; + public int[] sizes = new int[4]; + public boolean preloadVideo; + public boolean preloadMusic; + public boolean lessCallData; + public boolean enabled; + + public Preset(int m, int p, int v, int f, boolean pv, boolean pm, boolean e, boolean l) { + for (int a = 0; a < mask.length; a++) { + mask[a] = m; + } + sizes[PRESET_SIZE_NUM_PHOTO] = p; + sizes[PRESET_SIZE_NUM_VIDEO] = v; + sizes[PRESET_SIZE_NUM_DOCUMENT] = f; + sizes[PRESET_SIZE_NUM_AUDIO] = 512 * 1024; + preloadVideo = pv; + preloadMusic = pm; + lessCallData = l; + enabled = e; + } + + public Preset(int[] m, int p, int v, int f, boolean pv, boolean pm, boolean e, boolean l) { + System.arraycopy(m, 0, mask, 0, mask.length); + sizes[PRESET_SIZE_NUM_PHOTO] = p; + sizes[PRESET_SIZE_NUM_VIDEO] = v; + sizes[PRESET_SIZE_NUM_DOCUMENT] = f; + sizes[PRESET_SIZE_NUM_AUDIO] = 512 * 1024; + preloadVideo = pv; + preloadMusic = pm; + lessCallData = l; + enabled = e; + } + + public Preset(String str) { + String[] args = str.split("_"); + if (args.length >= 11) { + mask[0] = Utilities.parseInt(args[0]); + mask[1] = Utilities.parseInt(args[1]); + mask[2] = Utilities.parseInt(args[2]); + mask[3] = Utilities.parseInt(args[3]); + sizes[PRESET_SIZE_NUM_PHOTO] = Utilities.parseInt(args[4]); + sizes[PRESET_SIZE_NUM_VIDEO] = Utilities.parseInt(args[5]); + sizes[PRESET_SIZE_NUM_DOCUMENT] = Utilities.parseInt(args[6]); + sizes[PRESET_SIZE_NUM_AUDIO] = Utilities.parseInt(args[7]); + preloadVideo = Utilities.parseInt(args[8]) == 1; + preloadMusic = Utilities.parseInt(args[9]) == 1; + enabled = Utilities.parseInt(args[10]) == 1; + if (args.length >= 12) { + lessCallData = Utilities.parseInt(args[11]) == 1; + } + } + } + + public void set(Preset preset) { + System.arraycopy(preset.mask, 0, mask, 0, mask.length); + System.arraycopy(preset.sizes, 0, sizes, 0, sizes.length); + preloadVideo = preset.preloadVideo; + preloadMusic = preset.preloadMusic; + lessCallData = preset.lessCallData; + //enabled = preset.enabled; + } + + public void set(TLRPC.TL_autoDownloadSettings settings) { + preloadMusic = settings.audio_preload_next; + preloadVideo = settings.video_preload_large; + lessCallData = settings.phonecalls_less_data; + //enabled = !settings.disabled; + sizes[PRESET_SIZE_NUM_PHOTO] = Math.max(500 * 1024, settings.photo_size_max); + sizes[PRESET_SIZE_NUM_VIDEO] = Math.max(500 * 1024, settings.video_size_max); + sizes[PRESET_SIZE_NUM_DOCUMENT] = Math.max(500 * 1024, settings.file_size_max); + for (int a = 0; a < mask.length; a++) { + if (settings.photo_size_max != 0 && !settings.disabled) { + mask[a] |= AUTODOWNLOAD_TYPE_PHOTO; + } else { + mask[a] &=~ AUTODOWNLOAD_TYPE_PHOTO; + } + if (settings.video_size_max != 0 && !settings.disabled) { + mask[a] |= AUTODOWNLOAD_TYPE_VIDEO; + } else { + mask[a] &=~ AUTODOWNLOAD_TYPE_VIDEO; + } + if (settings.file_size_max != 0 && !settings.disabled) { + mask[a] |= AUTODOWNLOAD_TYPE_DOCUMENT; + } else { + mask[a] &=~ AUTODOWNLOAD_TYPE_DOCUMENT; + } + } + } + + @Override + public String toString() { + return mask[0] + "_" + mask[1] + "_" + mask[2] + "_" + mask[3] + + "_" + sizes[PRESET_SIZE_NUM_PHOTO] + + "_" + sizes[PRESET_SIZE_NUM_VIDEO] + + "_" + sizes[PRESET_SIZE_NUM_DOCUMENT] + + "_" + sizes[PRESET_SIZE_NUM_AUDIO] + + "_" + (preloadVideo ? 1 : 0) + + "_" + (preloadMusic ? 1 : 0) + + "_" + (enabled ? 1 : 0) + + "_" + (lessCallData ? 1 : 0); + } + + public boolean equals(Preset obj) { + return mask[0] == obj.mask[0] && + mask[1] == obj.mask[1] && + mask[2] == obj.mask[2] && + mask[3] == obj.mask[3] && + sizes[0] == obj.sizes[0] && + sizes[1] == obj.sizes[1] && + sizes[2] == obj.sizes[2] && + sizes[3] == obj.sizes[3] && + preloadVideo == obj.preloadVideo && + preloadMusic == obj.preloadMusic; + } + } + + public Preset lowPreset; + public Preset mediumPreset; + public Preset highPreset; + public Preset mobilePreset; + public Preset wifiPreset; + public Preset roamingPreset; + public int currentMobilePreset; + public int currentWifiPreset; + public int currentRoamingPreset; + private int currentAccount; private static volatile DownloadController Instance[] = new DownloadController[UserConfig.MAX_ACCOUNT_COUNT]; @@ -86,32 +214,63 @@ public class DownloadController implements NotificationCenter.NotificationCenter public DownloadController(int instance) { currentAccount = instance; SharedPreferences preferences = MessagesController.getMainSettings(currentAccount); - for (int a = 0; a < 4; a++) { - String key = "mobileDataDownloadMask" + (a == 0 ? "" : a); - if (a == 0 || preferences.contains(key)) { - mobileDataDownloadMask[a] = preferences.getInt(key, AUTODOWNLOAD_MASK_PHOTO | AUTODOWNLOAD_MASK_AUDIO | AUTODOWNLOAD_MASK_MUSIC | AUTODOWNLOAD_MASK_GIF | AUTODOWNLOAD_MASK_VIDEOMESSAGE); - wifiDownloadMask[a] = preferences.getInt("wifiDownloadMask" + (a == 0 ? "" : a), AUTODOWNLOAD_MASK_PHOTO | AUTODOWNLOAD_MASK_AUDIO | AUTODOWNLOAD_MASK_MUSIC | AUTODOWNLOAD_MASK_GIF | AUTODOWNLOAD_MASK_VIDEOMESSAGE); - roamingDownloadMask[a] = preferences.getInt("roamingDownloadMask" + (a == 0 ? "" : a), 0); - } else { - mobileDataDownloadMask[a] = mobileDataDownloadMask[0]; - wifiDownloadMask[a] = wifiDownloadMask[0]; - roamingDownloadMask[a] = roamingDownloadMask[0]; + lowPreset = new Preset(preferences.getString("preset0", "1_1_1_1_1048576_512000_512000_524288_0_0_1_1")); + mediumPreset = new Preset(preferences.getString("preset1", "13_13_13_13_1048576_10485760_1048576_524288_1_1_1_0")); + highPreset = new Preset(preferences.getString("preset2", "13_13_13_13_1048576_15728640_3145728_524288_1_1_1_0")); + boolean newConfig; + if (newConfig = preferences.contains("newConfig") || !UserConfig.getInstance(currentAccount).isClientActivated()) { + mobilePreset = new Preset(preferences.getString("mobilePreset", mediumPreset.toString())); + wifiPreset = new Preset(preferences.getString("wifiPreset", highPreset.toString())); + roamingPreset = new Preset(preferences.getString("roamingPreset", lowPreset.toString())); + currentMobilePreset = preferences.getInt("currentMobilePreset", 3); + currentWifiPreset = preferences.getInt("currentWifiPreset", 3); + currentRoamingPreset = preferences.getInt("currentRoamingPreset", 3); + if (!newConfig) { + preferences.edit().putBoolean("newConfig", true).commit(); } - } - for (int a = 0; a < 7; a++) { - int sdefault; - if (a == 1) { - sdefault = 2 * 1024 * 1024; - } else if (a == 6) { - sdefault = 5 * 1024 * 1024; - } else { - sdefault = 10 * 1024 * 1024; + } else { + int mobileDataDownloadMask[] = new int[4]; + int wifiDownloadMask[] = new int[4]; + int roamingDownloadMask[] = new int[4]; + int mobileMaxFileSize[] = new int[7]; + int wifiMaxFileSize[] = new int[7]; + int roamingMaxFileSize[] = new int[7]; + + for (int a = 0; a < 4; a++) { + String key = "mobileDataDownloadMask" + (a == 0 ? "" : a); + if (a == 0 || preferences.contains(key)) { + mobileDataDownloadMask[a] = preferences.getInt(key, AUTODOWNLOAD_TYPE_PHOTO | AUTODOWNLOAD_TYPE_VIDEO | AUTODOWNLOAD_TYPE_DOCUMENT); + wifiDownloadMask[a] = preferences.getInt("wifiDownloadMask" + (a == 0 ? "" : a), AUTODOWNLOAD_TYPE_PHOTO | AUTODOWNLOAD_TYPE_VIDEO | AUTODOWNLOAD_TYPE_DOCUMENT); + roamingDownloadMask[a] = preferences.getInt("roamingDownloadMask" + (a == 0 ? "" : a), AUTODOWNLOAD_TYPE_PHOTO); + } else { + mobileDataDownloadMask[a] = mobileDataDownloadMask[0]; + wifiDownloadMask[a] = wifiDownloadMask[0]; + roamingDownloadMask[a] = roamingDownloadMask[0]; + } } - mobileMaxFileSize[a] = preferences.getInt("mobileMaxDownloadSize" + a, sdefault); - wifiMaxFileSize[a] = preferences.getInt("wifiMaxDownloadSize" + a, sdefault); - roamingMaxFileSize[a] = preferences.getInt("roamingMaxDownloadSize" + a, sdefault); + + mobileMaxFileSize[2] = preferences.getInt("mobileMaxDownloadSize" + 2, mediumPreset.sizes[PRESET_SIZE_NUM_VIDEO]); + mobileMaxFileSize[3] = preferences.getInt("mobileMaxDownloadSize" + 3, mediumPreset.sizes[PRESET_SIZE_NUM_DOCUMENT]); + wifiMaxFileSize[2] = preferences.getInt("wifiMaxDownloadSize" + 2, highPreset.sizes[PRESET_SIZE_NUM_VIDEO]); + wifiMaxFileSize[3] = preferences.getInt("wifiMaxDownloadSize" + 3, highPreset.sizes[PRESET_SIZE_NUM_DOCUMENT]); + roamingMaxFileSize[2] = preferences.getInt("roamingMaxDownloadSize" + 2, lowPreset.sizes[PRESET_SIZE_NUM_VIDEO]); + roamingMaxFileSize[3] = preferences.getInt("roamingMaxDownloadSize" + 3, lowPreset.sizes[PRESET_SIZE_NUM_DOCUMENT]); + + boolean globalAutodownloadEnabled = preferences.getBoolean("globalAutodownloadEnabled", true); + mobilePreset = new Preset(mobileDataDownloadMask, mediumPreset.sizes[PRESET_SIZE_NUM_PHOTO], mobileMaxFileSize[2], mobileMaxFileSize[3], true, true, globalAutodownloadEnabled, false); + wifiPreset = new Preset(wifiDownloadMask, highPreset.sizes[PRESET_SIZE_NUM_PHOTO], wifiMaxFileSize[2], wifiMaxFileSize[3], true, true, globalAutodownloadEnabled, false); + roamingPreset = new Preset(roamingDownloadMask, lowPreset.sizes[PRESET_SIZE_NUM_PHOTO], roamingMaxFileSize[2], roamingMaxFileSize[3], false, false, globalAutodownloadEnabled, true); + + SharedPreferences.Editor editor = preferences.edit(); + editor.putBoolean("newConfig", true); + editor.putString("mobilePreset", mobilePreset.toString()); + editor.putString("wifiPreset", wifiPreset.toString()); + editor.putString("roamingPreset", roamingPreset.toString()); + editor.putInt("currentMobilePreset", currentMobilePreset = 3); + editor.putInt("currentWifiPreset", currentWifiPreset = 3); + editor.putInt("currentRoamingPreset", currentRoamingPreset = 3); + editor.commit(); } - globalAutodownloadEnabled = preferences.getBoolean("globalAutodownloadEnabled", true); AndroidUtilities.runOnUIThread(() -> { NotificationCenter.getInstance(currentAccount).addObserver(DownloadController.this, NotificationCenter.fileDidFailedLoad); @@ -120,6 +279,7 @@ public class DownloadController implements NotificationCenter.NotificationCenter NotificationCenter.getInstance(currentAccount).addObserver(DownloadController.this, NotificationCenter.FileUploadProgressChanged); NotificationCenter.getInstance(currentAccount).addObserver(DownloadController.this, NotificationCenter.httpFileDidLoad); NotificationCenter.getInstance(currentAccount).addObserver(DownloadController.this, NotificationCenter.httpFileDidFailedLoad); + loadAutoDownloadConfig(false); }); BroadcastReceiver networkStateReceiver = new BroadcastReceiver() { @@ -136,72 +296,145 @@ public class DownloadController implements NotificationCenter.NotificationCenter } } - public static int maskToIndex(int mask) { - if (mask == AUTODOWNLOAD_MASK_PHOTO) { - return 0; - } else if (mask == AUTODOWNLOAD_MASK_AUDIO) { - return 1; - } else if (mask == AUTODOWNLOAD_MASK_VIDEO) { - return 2; - } else if (mask == AUTODOWNLOAD_MASK_DOCUMENT) { - return 3; - } else if (mask == AUTODOWNLOAD_MASK_MUSIC) { - return 4; - } else if (mask == AUTODOWNLOAD_MASK_GIF) { - return 5; - } else if (mask == AUTODOWNLOAD_MASK_VIDEOMESSAGE) { - return 6; + public void loadAutoDownloadConfig(boolean force) { + if (loadingAutoDownloadConfig || !force && Math.abs(System.currentTimeMillis() - UserConfig.getInstance(currentAccount).autoDownloadConfigLoadTime) < 24 * 60 * 60 * 1000) { + return; } - return 0; + loadingAutoDownloadConfig = true; + TLRPC.TL_account_getAutoDownloadSettings req = new TLRPC.TL_account_getAutoDownloadSettings(); + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + loadingAutoDownloadConfig = false; + UserConfig.getInstance(currentAccount).autoDownloadConfigLoadTime = System.currentTimeMillis(); + UserConfig.getInstance(currentAccount).saveConfig(false); + if (response != null) { + TLRPC.TL_account_autoDownloadSettings res = (TLRPC.TL_account_autoDownloadSettings) response; + lowPreset.set(res.low); + mediumPreset.set(res.medium); + highPreset.set(res.high); + for (int a = 0; a < 3; a++) { + Preset preset; + if (a == 0) { + preset = mobilePreset; + } else if (a == 1) { + preset = wifiPreset; + } else { + preset = roamingPreset; + } + if (preset.equals(lowPreset)) { + preset.set(res.low); + } else if (preset.equals(mediumPreset)) { + preset.set(res.medium); + } else if (preset.equals(highPreset)) { + preset.set(res.high); + } + } + + SharedPreferences.Editor editor = MessagesController.getMainSettings(currentAccount).edit(); + editor.putString("mobilePreset", mobilePreset.toString()); + editor.putString("wifiPreset", wifiPreset.toString()); + editor.putString("roamingPreset", roamingPreset.toString()); + editor.putString("preset0", lowPreset.toString()); + editor.putString("preset1", mediumPreset.toString()); + editor.putString("preset2", highPreset.toString()); + editor.commit(); + String str1 = lowPreset.toString(); + String str2 = mediumPreset.toString(); + String str3 = highPreset.toString(); + checkAutodownloadSettings(); + } + })); + } + + public Preset getCurrentMobilePreset() { + if (currentMobilePreset == 0) { + return lowPreset; + } else if (currentMobilePreset == 1) { + return mediumPreset; + } else if (currentMobilePreset == 2) { + return highPreset; + } else { + return mobilePreset; + } + } + + public Preset getCurrentWiFiPreset() { + if (currentWifiPreset == 0) { + return lowPreset; + } else if (currentWifiPreset == 1) { + return mediumPreset; + } else if (currentWifiPreset == 2) { + return highPreset; + } else { + return wifiPreset; + } + } + + public Preset getCurrentRoamingPreset() { + if (currentRoamingPreset == 0) { + return lowPreset; + } else if (currentRoamingPreset == 1) { + return mediumPreset; + } else if (currentRoamingPreset == 2) { + return highPreset; + } else { + return roamingPreset; + } + } + + public static int typeToIndex(int type) { + if (type == AUTODOWNLOAD_TYPE_PHOTO) { + return PRESET_SIZE_NUM_PHOTO; + } else if (type == AUTODOWNLOAD_TYPE_AUDIO) { + return PRESET_SIZE_NUM_AUDIO; + } else if (type == AUTODOWNLOAD_TYPE_VIDEO) { + return PRESET_SIZE_NUM_VIDEO; + } else if (type == AUTODOWNLOAD_TYPE_DOCUMENT) { + return PRESET_SIZE_NUM_DOCUMENT; + } + return PRESET_SIZE_NUM_PHOTO; } public void cleanup() { photoDownloadQueue.clear(); audioDownloadQueue.clear(); - videoMessageDownloadQueue.clear(); documentDownloadQueue.clear(); videoDownloadQueue.clear(); - musicDownloadQueue.clear(); - gifDownloadQueue.clear(); downloadQueueKeys.clear(); typingTimes.clear(); } public int getAutodownloadMask() { - if (!globalAutodownloadEnabled) { - return 0; - } int result = 0; int masksArray[]; if (ApplicationLoader.isConnectedToWiFi()) { - masksArray = wifiDownloadMask; + if (!wifiPreset.enabled) { + return 0; + } + masksArray = getCurrentWiFiPreset().mask; } else if (ApplicationLoader.isRoaming()) { - masksArray = roamingDownloadMask; + if (!roamingPreset.enabled) { + return 0; + } + masksArray = getCurrentRoamingPreset().mask; } else { - masksArray = mobileDataDownloadMask; + if (!mobilePreset.enabled) { + return 0; + } + masksArray = getCurrentMobilePreset().mask; } - for (int a = 0; a < 4; a++) { + for (int a = 0; a < masksArray.length; a++) { int mask = 0; - if ((masksArray[a] & AUTODOWNLOAD_MASK_PHOTO) != 0) { - mask |= AUTODOWNLOAD_MASK_PHOTO; + if ((masksArray[a] & AUTODOWNLOAD_TYPE_PHOTO) != 0) { + mask |= AUTODOWNLOAD_TYPE_PHOTO; } - if ((masksArray[a] & AUTODOWNLOAD_MASK_AUDIO) != 0) { - mask |= AUTODOWNLOAD_MASK_AUDIO; + if ((masksArray[a] & AUTODOWNLOAD_TYPE_AUDIO) != 0) { + mask |= AUTODOWNLOAD_TYPE_AUDIO; } - if ((masksArray[a] & AUTODOWNLOAD_MASK_VIDEOMESSAGE) != 0) { - mask |= AUTODOWNLOAD_MASK_VIDEOMESSAGE; + if ((masksArray[a] & AUTODOWNLOAD_TYPE_VIDEO) != 0) { + mask |= AUTODOWNLOAD_TYPE_VIDEO; } - if ((masksArray[a] & AUTODOWNLOAD_MASK_VIDEO) != 0) { - mask |= AUTODOWNLOAD_MASK_VIDEO; - } - if ((masksArray[a] & AUTODOWNLOAD_MASK_DOCUMENT) != 0) { - mask |= AUTODOWNLOAD_MASK_DOCUMENT; - } - if ((masksArray[a] & AUTODOWNLOAD_MASK_MUSIC) != 0) { - mask |= AUTODOWNLOAD_MASK_MUSIC; - } - if ((masksArray[a] & AUTODOWNLOAD_MASK_GIF) != 0) { - mask |= AUTODOWNLOAD_MASK_GIF; + if ((masksArray[a] & AUTODOWNLOAD_TYPE_DOCUMENT) != 0) { + mask |= AUTODOWNLOAD_TYPE_DOCUMENT; } result |= mask << (a * 8); } @@ -209,31 +442,22 @@ public class DownloadController implements NotificationCenter.NotificationCenter } protected int getAutodownloadMaskAll() { - if (!globalAutodownloadEnabled) { + if (!mobilePreset.enabled && !roamingPreset.enabled && !wifiPreset.enabled) { return 0; } int mask = 0; for (int a = 0; a < 4; a++) { - if ((mobileDataDownloadMask[a] & AUTODOWNLOAD_MASK_PHOTO) != 0 || (wifiDownloadMask[a] & AUTODOWNLOAD_MASK_PHOTO) != 0 || (roamingDownloadMask[a] & AUTODOWNLOAD_MASK_PHOTO) != 0) { - mask |= AUTODOWNLOAD_MASK_PHOTO; + if ((getCurrentMobilePreset().mask[a] & AUTODOWNLOAD_TYPE_PHOTO) != 0 || (getCurrentWiFiPreset().mask[a] & AUTODOWNLOAD_TYPE_PHOTO) != 0 || (getCurrentRoamingPreset().mask[a] & AUTODOWNLOAD_TYPE_PHOTO) != 0) { + mask |= AUTODOWNLOAD_TYPE_PHOTO; } - if ((mobileDataDownloadMask[a] & AUTODOWNLOAD_MASK_AUDIO) != 0 || (wifiDownloadMask[a] & AUTODOWNLOAD_MASK_AUDIO) != 0 || (roamingDownloadMask[a] & AUTODOWNLOAD_MASK_AUDIO) != 0) { - mask |= AUTODOWNLOAD_MASK_AUDIO; + if ((getCurrentMobilePreset().mask[a] & AUTODOWNLOAD_TYPE_AUDIO) != 0 || (getCurrentWiFiPreset().mask[a] & AUTODOWNLOAD_TYPE_AUDIO) != 0 || (getCurrentRoamingPreset().mask[a] & AUTODOWNLOAD_TYPE_AUDIO) != 0) { + mask |= AUTODOWNLOAD_TYPE_AUDIO; } - if ((mobileDataDownloadMask[a] & AUTODOWNLOAD_MASK_VIDEOMESSAGE) != 0 || (wifiDownloadMask[a] & AUTODOWNLOAD_MASK_VIDEOMESSAGE) != 0 || (roamingDownloadMask[a] & AUTODOWNLOAD_MASK_VIDEOMESSAGE) != 0) { - mask |= AUTODOWNLOAD_MASK_VIDEOMESSAGE; + if ((getCurrentMobilePreset().mask[a] & AUTODOWNLOAD_TYPE_VIDEO) != 0 || (getCurrentWiFiPreset().mask[a] & AUTODOWNLOAD_TYPE_VIDEO) != 0 || (getCurrentRoamingPreset().mask[a] & AUTODOWNLOAD_TYPE_VIDEO) != 0) { + mask |= AUTODOWNLOAD_TYPE_VIDEO; } - if ((mobileDataDownloadMask[a] & AUTODOWNLOAD_MASK_VIDEO) != 0 || (wifiDownloadMask[a] & AUTODOWNLOAD_MASK_VIDEO) != 0 || (roamingDownloadMask[a] & AUTODOWNLOAD_MASK_VIDEO) != 0) { - mask |= AUTODOWNLOAD_MASK_VIDEO; - } - if ((mobileDataDownloadMask[a] & AUTODOWNLOAD_MASK_DOCUMENT) != 0 || (wifiDownloadMask[a] & AUTODOWNLOAD_MASK_DOCUMENT) != 0 || (roamingDownloadMask[a] & AUTODOWNLOAD_MASK_DOCUMENT) != 0) { - mask |= AUTODOWNLOAD_MASK_DOCUMENT; - } - if ((mobileDataDownloadMask[a] & AUTODOWNLOAD_MASK_MUSIC) != 0 || (wifiDownloadMask[a] & AUTODOWNLOAD_MASK_MUSIC) != 0 || (roamingDownloadMask[a] & AUTODOWNLOAD_MASK_MUSIC) != 0) { - mask |= AUTODOWNLOAD_MASK_MUSIC; - } - if ((mobileDataDownloadMask[a] & AUTODOWNLOAD_MASK_GIF) != 0 || (wifiDownloadMask[a] & AUTODOWNLOAD_MASK_GIF) != 0 || (roamingDownloadMask[a] & AUTODOWNLOAD_MASK_GIF) != 0) { - mask |= AUTODOWNLOAD_MASK_GIF; + if ((getCurrentMobilePreset().mask[a] & AUTODOWNLOAD_TYPE_DOCUMENT) != 0 || (getCurrentWiFiPreset().mask[a] & AUTODOWNLOAD_TYPE_DOCUMENT) != 0 || (getCurrentRoamingPreset().mask[a] & AUTODOWNLOAD_TYPE_DOCUMENT) != 0) { + mask |= AUTODOWNLOAD_TYPE_DOCUMENT; } } return mask; @@ -245,9 +469,9 @@ public class DownloadController implements NotificationCenter.NotificationCenter return; } lastCheckMask = currentMask; - if ((currentMask & AUTODOWNLOAD_MASK_PHOTO) != 0) { + if ((currentMask & AUTODOWNLOAD_TYPE_PHOTO) != 0) { if (photoDownloadQueue.isEmpty()) { - newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_PHOTO); + newDownloadObjectsAvailable(AUTODOWNLOAD_TYPE_PHOTO); } } else { for (int a = 0; a < photoDownloadQueue.size(); a++) { @@ -260,9 +484,9 @@ public class DownloadController implements NotificationCenter.NotificationCenter } photoDownloadQueue.clear(); } - if ((currentMask & AUTODOWNLOAD_MASK_AUDIO) != 0) { + if ((currentMask & AUTODOWNLOAD_TYPE_AUDIO) != 0) { if (audioDownloadQueue.isEmpty()) { - newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_AUDIO); + newDownloadObjectsAvailable(AUTODOWNLOAD_TYPE_AUDIO); } } else { for (int a = 0; a < audioDownloadQueue.size(); a++) { @@ -271,20 +495,9 @@ public class DownloadController implements NotificationCenter.NotificationCenter } audioDownloadQueue.clear(); } - if ((currentMask & AUTODOWNLOAD_MASK_VIDEOMESSAGE) != 0) { - if (videoMessageDownloadQueue.isEmpty()) { - newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_VIDEOMESSAGE); - } - } else { - for (int a = 0; a < videoMessageDownloadQueue.size(); a++) { - DownloadObject downloadObject = videoMessageDownloadQueue.get(a); - FileLoader.getInstance(currentAccount).cancelLoadFile((TLRPC.Document) downloadObject.object); - } - videoMessageDownloadQueue.clear(); - } - if ((currentMask & AUTODOWNLOAD_MASK_DOCUMENT) != 0) { + if ((currentMask & AUTODOWNLOAD_TYPE_DOCUMENT) != 0) { if (documentDownloadQueue.isEmpty()) { - newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_DOCUMENT); + newDownloadObjectsAvailable(AUTODOWNLOAD_TYPE_DOCUMENT); } } else { for (int a = 0; a < documentDownloadQueue.size(); a++) { @@ -294,9 +507,9 @@ public class DownloadController implements NotificationCenter.NotificationCenter } documentDownloadQueue.clear(); } - if ((currentMask & AUTODOWNLOAD_MASK_VIDEO) != 0) { + if ((currentMask & AUTODOWNLOAD_TYPE_VIDEO) != 0) { if (videoDownloadQueue.isEmpty()) { - newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_VIDEO); + newDownloadObjectsAvailable(AUTODOWNLOAD_TYPE_VIDEO); } } else { for (int a = 0; a < videoDownloadQueue.size(); a++) { @@ -305,82 +518,40 @@ public class DownloadController implements NotificationCenter.NotificationCenter } videoDownloadQueue.clear(); } - if ((currentMask & AUTODOWNLOAD_MASK_MUSIC) != 0) { - if (musicDownloadQueue.isEmpty()) { - newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_MUSIC); - } - } else { - for (int a = 0; a < musicDownloadQueue.size(); a++) { - DownloadObject downloadObject = musicDownloadQueue.get(a); - TLRPC.Document document = (TLRPC.Document) downloadObject.object; - FileLoader.getInstance(currentAccount).cancelLoadFile(document); - } - musicDownloadQueue.clear(); - } - if ((currentMask & AUTODOWNLOAD_MASK_GIF) != 0) { - if (gifDownloadQueue.isEmpty()) { - newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_GIF); - } - } else { - for (int a = 0; a < gifDownloadQueue.size(); a++) { - DownloadObject downloadObject = gifDownloadQueue.get(a); - TLRPC.Document document = (TLRPC.Document) downloadObject.object; - FileLoader.getInstance(currentAccount).cancelLoadFile(document); - } - gifDownloadQueue.clear(); - } - int mask = getAutodownloadMaskAll(); if (mask == 0) { MessagesStorage.getInstance(currentAccount).clearDownloadQueue(0); } else { - if ((mask & AUTODOWNLOAD_MASK_PHOTO) == 0) { - MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_MASK_PHOTO); + if ((mask & AUTODOWNLOAD_TYPE_PHOTO) == 0) { + MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_TYPE_PHOTO); } - if ((mask & AUTODOWNLOAD_MASK_AUDIO) == 0) { - MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_MASK_AUDIO); + if ((mask & AUTODOWNLOAD_TYPE_AUDIO) == 0) { + MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_TYPE_AUDIO); } - if ((mask & AUTODOWNLOAD_MASK_VIDEOMESSAGE) == 0) { - MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_MASK_VIDEOMESSAGE); + if ((mask & AUTODOWNLOAD_TYPE_VIDEO) == 0) { + MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_TYPE_VIDEO); } - if ((mask & AUTODOWNLOAD_MASK_VIDEO) == 0) { - MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_MASK_VIDEO); - } - if ((mask & AUTODOWNLOAD_MASK_DOCUMENT) == 0) { - MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_MASK_DOCUMENT); - } - if ((mask & AUTODOWNLOAD_MASK_MUSIC) == 0) { - MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_MASK_MUSIC); - } - if ((mask & AUTODOWNLOAD_MASK_GIF) == 0) { - MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_MASK_GIF); + if ((mask & AUTODOWNLOAD_TYPE_DOCUMENT) == 0) { + MessagesStorage.getInstance(currentAccount).clearDownloadQueue(AUTODOWNLOAD_TYPE_DOCUMENT); } } } public boolean canDownloadMedia(MessageObject messageObject) { - return canDownloadMedia(messageObject.messageOwner); + return canDownloadMedia(messageObject.messageOwner) == 1; } - public boolean canDownloadMedia(TLRPC.Message message) { - if (!globalAutodownloadEnabled) { - return false; - } + public int canDownloadMedia(TLRPC.Message message) { int type; - if (MessageObject.isPhoto(message) || MessageObject.isStickerMessage(message)) { - type = AUTODOWNLOAD_MASK_PHOTO; + boolean isVideo; + if ((isVideo = MessageObject.isVideoMessage(message)) || MessageObject.isGifMessage(message) || MessageObject.isRoundVideoMessage(message)) { + type = AUTODOWNLOAD_TYPE_VIDEO; } else if (MessageObject.isVoiceMessage(message)) { - type = AUTODOWNLOAD_MASK_AUDIO; - } else if (MessageObject.isRoundVideoMessage(message)) { - type = AUTODOWNLOAD_MASK_VIDEOMESSAGE; - } else if (MessageObject.isVideoMessage(message)) { - type = AUTODOWNLOAD_MASK_VIDEO; - } else if (MessageObject.isMusicMessage(message)) { - type = AUTODOWNLOAD_MASK_MUSIC; - } else if (MessageObject.isGifMessage(message)) { - type = AUTODOWNLOAD_MASK_GIF; + type = AUTODOWNLOAD_TYPE_AUDIO; + } else if (MessageObject.isPhoto(message) || MessageObject.isStickerMessage(message)) { + type = AUTODOWNLOAD_TYPE_PHOTO; } else { - type = AUTODOWNLOAD_MASK_DOCUMENT; + type = AUTODOWNLOAD_TYPE_DOCUMENT; } int mask; int index; @@ -394,10 +565,18 @@ public class DownloadController implements NotificationCenter.NotificationCenter index = 1; } } else if (peer.chat_id != 0) { - index = 2; + if (message.from_id != 0 && ContactsController.getInstance(currentAccount).contactsDict.containsKey(message.from_id)) { + index = 0; + } else { + index = 2; + } } else { if (MessageObject.isMegagroup(message)) { - index = 2; + if (message.from_id != 0 && ContactsController.getInstance(currentAccount).contactsDict.containsKey(message.from_id)) { + index = 0; + } else { + index = 2; + } } else { index = 3; } @@ -405,63 +584,132 @@ public class DownloadController implements NotificationCenter.NotificationCenter } else { index = 1; } + Preset preset; if (ApplicationLoader.isConnectedToWiFi()) { - mask = wifiDownloadMask[index]; - maxSize = wifiMaxFileSize[maskToIndex(type)]; + if (!wifiPreset.enabled) { + return 0; + } + preset = getCurrentWiFiPreset(); + } else if (ApplicationLoader.isRoaming()) { - mask = roamingDownloadMask[index]; - maxSize = roamingMaxFileSize[maskToIndex(type)]; + if (!roamingPreset.enabled) { + return 0; + } + preset = getCurrentRoamingPreset(); } else { - mask = mobileDataDownloadMask[index]; - maxSize = mobileMaxFileSize[maskToIndex(type)]; + if (!mobilePreset.enabled) { + return 0; + } + preset = getCurrentMobilePreset(); + } + mask = preset.mask[index]; + maxSize = preset.sizes[typeToIndex(type)]; + int size = MessageObject.getMessageSize(message); + if (isVideo && preset.preloadVideo && size > maxSize && maxSize > 2 * 1024 * 1024) { + return (mask & type) != 0 ? 2 : 0; + } else { + return (type == AUTODOWNLOAD_TYPE_PHOTO || size != 0 && size <= maxSize) && (type == AUTODOWNLOAD_TYPE_AUDIO || (mask & type) != 0) ? 1 : 0; + } + } + + protected boolean canDownloadNextTrack() { + if (ApplicationLoader.isConnectedToWiFi()) { + return wifiPreset.enabled && getCurrentWiFiPreset().preloadMusic; + } else if (ApplicationLoader.isRoaming()) { + return roamingPreset.enabled && getCurrentRoamingPreset().preloadMusic; + } else { + return mobilePreset.enabled && getCurrentMobilePreset().preloadMusic; } - return (type == AUTODOWNLOAD_MASK_PHOTO || MessageObject.getMessageSize(message) <= maxSize) && (mask & type) != 0; } protected int getCurrentDownloadMask() { - if (!globalAutodownloadEnabled) { - return 0; - } if (ApplicationLoader.isConnectedToWiFi()) { + if (!wifiPreset.enabled) { + return 0; + } int mask = 0; for (int a = 0; a < 4; a++) { - mask |= wifiDownloadMask[a]; + mask |= getCurrentWiFiPreset().mask[a]; } return mask; } else if (ApplicationLoader.isRoaming()) { + if (!roamingPreset.enabled) { + return 0; + } int mask = 0; for (int a = 0; a < 4; a++) { - mask |= roamingDownloadMask[a]; + mask |= getCurrentRoamingPreset().mask[a]; } return mask; } else { + if (!mobilePreset.enabled) { + return 0; + } int mask = 0; for (int a = 0; a < 4; a++) { - mask |= mobileDataDownloadMask[a]; + mask |= getCurrentMobilePreset().mask[a]; } return mask; } } + public void savePresetToServer(int type) { + TLRPC.TL_account_saveAutoDownloadSettings req = new TLRPC.TL_account_saveAutoDownloadSettings(); + Preset preset; + boolean enabled; + if (type == 0) { + preset = getCurrentMobilePreset(); + enabled = mobilePreset.enabled; + } else if (type == 1) { + preset = getCurrentWiFiPreset(); + enabled = wifiPreset.enabled; + } else { + preset = getCurrentRoamingPreset(); + enabled = roamingPreset.enabled; + } + req.settings = new TLRPC.TL_autoDownloadSettings(); + req.settings.audio_preload_next = preset.preloadMusic; + req.settings.video_preload_large = preset.preloadVideo; + req.settings.phonecalls_less_data = preset.lessCallData; + req.settings.disabled = !enabled; + boolean photo = false; + boolean video = false; + boolean document = false; + for (int a = 0; a < preset.mask.length; a++) { + if ((preset.mask[a] & AUTODOWNLOAD_TYPE_PHOTO) != 0) { + photo = true; + } + if ((preset.mask[a] & AUTODOWNLOAD_TYPE_VIDEO) != 0) { + video = true; + } + if ((preset.mask[a] & AUTODOWNLOAD_TYPE_DOCUMENT) != 0) { + document = true; + } + if (photo && video && document) { + break; + } + } + req.settings.photo_size_max = photo ? preset.sizes[PRESET_SIZE_NUM_PHOTO] : 0; + req.settings.video_size_max = video ? preset.sizes[PRESET_SIZE_NUM_VIDEO] : 0; + req.settings.file_size_max = document ? preset.sizes[PRESET_SIZE_NUM_DOCUMENT] : 0; + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> { + + }); + } + protected void processDownloadObjects(int type, ArrayList objects) { if (objects.isEmpty()) { return; } ArrayList queue = null; - if (type == AUTODOWNLOAD_MASK_PHOTO) { + if (type == AUTODOWNLOAD_TYPE_PHOTO) { queue = photoDownloadQueue; - } else if (type == AUTODOWNLOAD_MASK_AUDIO) { + } else if (type == AUTODOWNLOAD_TYPE_AUDIO) { queue = audioDownloadQueue; - } else if (type == AUTODOWNLOAD_MASK_VIDEOMESSAGE) { - queue = videoMessageDownloadQueue; - } else if (type == AUTODOWNLOAD_MASK_VIDEO) { + } else if (type == AUTODOWNLOAD_TYPE_VIDEO) { queue = videoDownloadQueue; - } else if (type == AUTODOWNLOAD_MASK_DOCUMENT) { + } else if (type == AUTODOWNLOAD_TYPE_DOCUMENT) { queue = documentDownloadQueue; - } else if (type == AUTODOWNLOAD_MASK_MUSIC) { - queue = musicDownloadQueue; - } else if (type == AUTODOWNLOAD_MASK_GIF) { - queue = gifDownloadQueue; } for (int a = 0; a < objects.size(); a++) { DownloadObject downloadObject = objects.get(a); @@ -494,26 +742,17 @@ public class DownloadController implements NotificationCenter.NotificationCenter protected void newDownloadObjectsAvailable(int downloadMask) { int mask = getCurrentDownloadMask(); - if ((mask & AUTODOWNLOAD_MASK_PHOTO) != 0 && (downloadMask & AUTODOWNLOAD_MASK_PHOTO) != 0 && photoDownloadQueue.isEmpty()) { - MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_MASK_PHOTO); + if ((mask & AUTODOWNLOAD_TYPE_PHOTO) != 0 && (downloadMask & AUTODOWNLOAD_TYPE_PHOTO) != 0 && photoDownloadQueue.isEmpty()) { + MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_TYPE_PHOTO); } - if ((mask & AUTODOWNLOAD_MASK_AUDIO) != 0 && (downloadMask & AUTODOWNLOAD_MASK_AUDIO) != 0 && audioDownloadQueue.isEmpty()) { - MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_MASK_AUDIO); + if ((mask & AUTODOWNLOAD_TYPE_AUDIO) != 0 && (downloadMask & AUTODOWNLOAD_TYPE_AUDIO) != 0 && audioDownloadQueue.isEmpty()) { + MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_TYPE_AUDIO); } - if ((mask & AUTODOWNLOAD_MASK_VIDEOMESSAGE) != 0 && (downloadMask & AUTODOWNLOAD_MASK_VIDEOMESSAGE) != 0 && videoMessageDownloadQueue.isEmpty()) { - MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_MASK_VIDEOMESSAGE); + if ((mask & AUTODOWNLOAD_TYPE_VIDEO) != 0 && (downloadMask & AUTODOWNLOAD_TYPE_VIDEO) != 0 && videoDownloadQueue.isEmpty()) { + MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_TYPE_VIDEO); } - if ((mask & AUTODOWNLOAD_MASK_VIDEO) != 0 && (downloadMask & AUTODOWNLOAD_MASK_VIDEO) != 0 && videoDownloadQueue.isEmpty()) { - MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_MASK_VIDEO); - } - if ((mask & AUTODOWNLOAD_MASK_DOCUMENT) != 0 && (downloadMask & AUTODOWNLOAD_MASK_DOCUMENT) != 0 && documentDownloadQueue.isEmpty()) { - MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_MASK_DOCUMENT); - } - if ((mask & AUTODOWNLOAD_MASK_MUSIC) != 0 && (downloadMask & AUTODOWNLOAD_MASK_MUSIC) != 0 && musicDownloadQueue.isEmpty()) { - MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_MASK_MUSIC); - } - if ((mask & AUTODOWNLOAD_MASK_GIF) != 0 && (downloadMask & AUTODOWNLOAD_MASK_GIF) != 0 && gifDownloadQueue.isEmpty()) { - MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_MASK_GIF); + if ((mask & AUTODOWNLOAD_TYPE_DOCUMENT) != 0 && (downloadMask & AUTODOWNLOAD_TYPE_DOCUMENT) != 0 && documentDownloadQueue.isEmpty()) { + MessagesStorage.getInstance(currentAccount).getDownloadQueue(AUTODOWNLOAD_TYPE_DOCUMENT); } } @@ -524,40 +763,25 @@ public class DownloadController implements NotificationCenter.NotificationCenter if (state == 0 || state == 2) { MessagesStorage.getInstance(currentAccount).removeFromDownloadQueue(downloadObject.id, downloadObject.type, false /*state != 0*/); } - if (downloadObject.type == AUTODOWNLOAD_MASK_PHOTO) { + if (downloadObject.type == AUTODOWNLOAD_TYPE_PHOTO) { photoDownloadQueue.remove(downloadObject); if (photoDownloadQueue.isEmpty()) { - newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_PHOTO); + newDownloadObjectsAvailable(AUTODOWNLOAD_TYPE_PHOTO); } - } else if (downloadObject.type == AUTODOWNLOAD_MASK_AUDIO) { + } else if (downloadObject.type == AUTODOWNLOAD_TYPE_AUDIO) { audioDownloadQueue.remove(downloadObject); if (audioDownloadQueue.isEmpty()) { - newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_AUDIO); + newDownloadObjectsAvailable(AUTODOWNLOAD_TYPE_AUDIO); } - } else if (downloadObject.type == AUTODOWNLOAD_MASK_VIDEOMESSAGE) { - videoMessageDownloadQueue.remove(downloadObject); - if (videoMessageDownloadQueue.isEmpty()) { - newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_VIDEOMESSAGE); - } - } else if (downloadObject.type == AUTODOWNLOAD_MASK_VIDEO) { + } else if (downloadObject.type == AUTODOWNLOAD_TYPE_VIDEO) { videoDownloadQueue.remove(downloadObject); if (videoDownloadQueue.isEmpty()) { - newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_VIDEO); + newDownloadObjectsAvailable(AUTODOWNLOAD_TYPE_VIDEO); } - } else if (downloadObject.type == AUTODOWNLOAD_MASK_DOCUMENT) { + } else if (downloadObject.type == AUTODOWNLOAD_TYPE_DOCUMENT) { documentDownloadQueue.remove(downloadObject); if (documentDownloadQueue.isEmpty()) { - newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_DOCUMENT); - } - } else if (downloadObject.type == AUTODOWNLOAD_MASK_MUSIC) { - musicDownloadQueue.remove(downloadObject); - if (musicDownloadQueue.isEmpty()) { - newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_MUSIC); - } - } else if (downloadObject.type == AUTODOWNLOAD_MASK_GIF) { - gifDownloadQueue.remove(downloadObject); - if (gifDownloadQueue.isEmpty()) { - newDownloadObjectsAvailable(AUTODOWNLOAD_MASK_GIF); + newDownloadObjectsAvailable(AUTODOWNLOAD_TYPE_DOCUMENT); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java index c89b67030..7c27b2fec 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java @@ -9,6 +9,7 @@ package org.telegram.messenger; import android.util.SparseArray; +import android.util.SparseIntArray; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.NativeByteBuffer; @@ -43,7 +44,19 @@ public class FileLoadOperation { } } - private ArrayList streamListeners; + private static class PreloadRange { + private int fileOffset; + private int start; + private int length; + + private PreloadRange(int o, int s, int l) { + fileOffset = o; + start = s; + length = l; + } + } + + private ArrayList streamListeners; private final static int stateIdle = 0; private final static int stateDownloading = 1; @@ -58,6 +71,27 @@ public class FileLoadOperation { private final static int bigFileSizeFrom = 1024 * 1024; private final static int maxCdnParts = 1024 * 1024 * 1536 / downloadChunkSizeBig; + private final static int preloadMaxBytes = 2 * 1024 * 1024; + + private SparseArray preloadedBytesRanges; + private SparseIntArray requestedPreloadedBytesRanges; + private RandomAccessFile preloadStream; + private int preloadStreamFileOffset; + private int totalPreloadedBytes; + private boolean isPreloadVideoOperation; + private boolean preloadFinished; + private File cacheFilePreload; + private boolean supportsPreloading; + private int nextPreloadDownloadOffset; + private int nextAtomOffset; + private int foundMoovSize; + private int preloadNotRequestedBytesCount; + private int moovFound; + private byte[] preloadTempBuffer = new byte[16]; + private int preloadTempBufferCount; + + private boolean nextPartWasPreloaded; + private ArrayList notLoadedBytesRanges; private volatile ArrayList notLoadedBytesRangesCopy; private ArrayList notRequestedBytesRanges; @@ -177,7 +211,7 @@ public class FileLoadOperation { webLocation = webDocument.location; totalBytesCount = webDocument.size; initialDatacenterId = datacenterId = MessagesController.getInstance(currentAccount).webFileDatacenterId; - String defaultExt = FileLoader.getExtensionByMime(webDocument.mime_type); + String defaultExt = FileLoader.getMimeTypePart(webDocument.mime_type); if (webDocument.mime_type.startsWith("image/")) { currentType = ConnectionsManager.FileTypePhoto; } else if (webDocument.mime_type.equals("audio/ogg")) { @@ -212,6 +246,12 @@ public class FileLoadOperation { } initialDatacenterId = datacenterId = documentLocation.dc_id; allowDisordererFileSave = true; + for (int a = 0, N = documentLocation.attributes.size(); a < N; a++) { + if (documentLocation.attributes.get(a) instanceof TLRPC.TL_documentAttributeVideo) { + supportsPreloading = true; + break; + } + } } totalBytesCount = documentLocation.size; if (key != null) { @@ -230,27 +270,13 @@ public class FileLoadOperation { } if ("audio/ogg".equals(documentLocation.mime_type)) { currentType = ConnectionsManager.FileTypeAudio; - } else if ("video/mp4".equals(documentLocation.mime_type)) { + } else if (FileLoader.isVideoMimeType(documentLocation.mime_type)) { currentType = ConnectionsManager.FileTypeVideo; } else { currentType = ConnectionsManager.FileTypeFile; } if (ext.length() <= 1) { - if (documentLocation.mime_type != null) { - switch (documentLocation.mime_type) { - case "video/mp4": - ext = ".mp4"; - break; - case "audio/ogg": - ext = ".ogg"; - break; - default: - ext = ""; - break; - } - } else { - ext = ""; - } + ext = FileLoader.getExtensionByMimeType(documentLocation.mime_type); } } catch (Exception e) { FileLog.e(e); @@ -292,7 +318,7 @@ public class FileLoadOperation { } public boolean wasStarted() { - return started; + return started && !paused; } public int getCurrentType() { @@ -364,9 +390,6 @@ public class FileLoadOperation { filePartsStream.writeInt(count); for (int a = 0; a < count; a++) { range = ranges.get(a); - /*if (BuildVars.LOGS_ENABLED) { - FileLog.d(cacheFileFinal + " save not loaded part " + range.start + " - " + range.end); - }*/ filePartsStream.writeInt(range.start); filePartsStream.writeInt(range.end); } @@ -454,8 +477,8 @@ public class FileLoadOperation { }); try { countDownLatch.await(); - } catch (Exception e) { - FileLog.e(e); + } catch (Exception ignore) { + } return result[0]; } @@ -477,7 +500,7 @@ public class FileLoadOperation { }); } - private void copytNotLoadedRanges() { + private void copyNotLoadedRanges() { if (notLoadedBytesRanges == null) { return; } @@ -488,14 +511,14 @@ public class FileLoadOperation { if (state != stateDownloading) { return; } - Utilities.stageQueue.postRunnable(() -> paused = true); + paused = true; } public boolean start() { return start(null, 0); } - public boolean start(final FileStreamLoadOperation stream, final int streamOffset) { + public boolean start(final FileLoadOperationStream stream, final int streamOffset) { if (currentDownloadChunkSize == 0) { currentDownloadChunkSize = totalBytesCount >= bigFileSizeFrom ? downloadChunkSizeBig : downloadChunkSize; currentMaxDownloadRequests = totalBytesCount >= bigFileSizeFrom ? maxDownloadRequestsBig : maxDownloadRequests; @@ -511,8 +534,13 @@ public class FileLoadOperation { streamStartOffset = streamOffset / currentDownloadChunkSize * currentDownloadChunkSize; streamListeners.add(stream); if (alreadyStarted) { - //clearOperaion(null); + if (preloadedBytesRanges != null && getDownloadedLengthFromOffsetInternal(notLoadedBytesRanges, streamStartOffset, 1) == 0) { + if (preloadedBytesRanges.get(streamStartOffset) != null) { + nextPartWasPreloaded = true; + } + } startDownloadRequest(); + nextPartWasPreloaded = false; } }); } else if (wasPaused && alreadyStarted) { @@ -536,6 +564,7 @@ public class FileLoadOperation { String fileNameFinal; String fileNameTemp; String fileNameParts = null; + String fileNamePreload = null; String fileNameIv = null; if (webLocation != null) { String md5 = Utilities.MD5(webFile.url); @@ -574,6 +603,7 @@ public class FileLoadOperation { if (notLoadedBytesRanges != null) { fileNameParts = location.volume_id + "_" + location.local_id + ".pt"; } + fileNamePreload = location.volume_id + "_" + location.local_id + ".preload"; } } else { if (datacenterId == 0 || location.id == 0) { @@ -595,6 +625,7 @@ public class FileLoadOperation { if (notLoadedBytesRanges != null) { fileNameParts = datacenterId + "_" + location.id + ".pt"; } + fileNamePreload = datacenterId + "_" + location.id + ".preload"; } } } @@ -642,6 +673,82 @@ public class FileLoadOperation { } } + boolean[] preloaded = new boolean[]{false}; + if (supportsPreloading && fileNamePreload != null) { + cacheFilePreload = new File(tempPath, fileNamePreload); + boolean closeStream = false; + try { + preloadStream = new RandomAccessFile(cacheFilePreload, "rws"); + long len = preloadStream.length(); + int readOffset = 0; + preloadStreamFileOffset = 1; + if (len - readOffset > 1) { + preloaded[0] = preloadStream.readByte() != 0; + readOffset += 1; + while (readOffset < len) { + if (len - readOffset < 4) { + break; + } + int offset = preloadStream.readInt(); + readOffset += 4; + if (len - readOffset < 4 || offset < 0 || offset > totalBytesCount) { + break; + } + int size = preloadStream.readInt(); + readOffset += 4; + if (len - readOffset < size || size > currentDownloadChunkSize) { + break; + } + PreloadRange range = new PreloadRange(readOffset, offset, size); + readOffset += size; + preloadStream.seek(readOffset); + if (len - readOffset < 12) { + break; + } + foundMoovSize = preloadStream.readInt(); + if (foundMoovSize != 0) { + moovFound = nextPreloadDownloadOffset > totalBytesCount / 2 ? 2 : 1; + preloadNotRequestedBytesCount = foundMoovSize; + } + nextPreloadDownloadOffset = preloadStream.readInt(); + nextAtomOffset = preloadStream.readInt(); + readOffset += 12; + + if (preloadedBytesRanges == null) { + preloadedBytesRanges = new SparseArray<>(); + } + if (requestedPreloadedBytesRanges == null) { + requestedPreloadedBytesRanges = new SparseIntArray(); + } + preloadedBytesRanges.put(offset, range); + requestedPreloadedBytesRanges.put(offset, 1); + + totalPreloadedBytes += size; + preloadStreamFileOffset += 20 + size; + } + } + preloadStream.seek(preloadStreamFileOffset); + } catch (Exception e) { + FileLog.e(e); + } + if (!isPreloadVideoOperation && preloadedBytesRanges == null) { + cacheFilePreload = null; + try { + if (preloadStream != null) { + try { + preloadStream.getChannel().close(); + } catch (Exception e) { + FileLog.e(e); + } + preloadStream.close(); + preloadStream = null; + } + } catch (Exception e) { + FileLog.e(e); + } + } + } + if (fileNameParts != null) { cacheFileParts = new File(tempPath, fileNameParts); try { @@ -691,10 +798,15 @@ public class FileLoadOperation { range = notLoadedBytesRanges.get(a); downloadedBytes -= (range.end - range.start); } + requestedBytesCount = downloadedBytes; } if (BuildVars.LOGS_ENABLED) { - FileLog.d("start loading file to temp = " + cacheFileTemp + " final = " + cacheFileFinal); + if (isPreloadVideoOperation) { + FileLog.d("start preloading file to temp = " + cacheFileTemp); + } else { + FileLog.d("start loading file to temp = " + cacheFileTemp + " final = " + cacheFileFinal); + } } if (fileNameIv != null) { @@ -714,8 +826,8 @@ public class FileLoadOperation { requestedBytesCount = downloadedBytes = 0; } } - if (downloadedBytes != 0 && totalBytesCount > 0) { - copytNotLoadedRanges(); + if (!isPreloadVideoOperation && downloadedBytes != 0 && totalBytesCount > 0) { + copyNotLoadedRanges(); delegate.didChangedLoadProgress(FileLoadOperation.this, Math.min(1.0f, (float) downloadedBytes / (float) totalBytesCount)); } try { @@ -732,7 +844,7 @@ public class FileLoadOperation { } started = true; Utilities.stageQueue.postRunnable(() -> { - if (totalBytesCount != 0 && downloadedBytes == totalBytesCount) { + if (totalBytesCount != 0 && (isPreloadVideoOperation && preloaded[0] || downloadedBytes == totalBytesCount)) { try { onFinishLoadingFile(false); } catch (Exception e) { @@ -757,6 +869,39 @@ public class FileLoadOperation { return paused; } + public void setIsPreloadVideoOperation(boolean value) { + if (isPreloadVideoOperation == value || value && totalBytesCount <= preloadMaxBytes) { + return; + } + if (!value && isPreloadVideoOperation) { + if (state == stateFinished) { + isPreloadVideoOperation = value; + state = stateIdle; + preloadFinished = false; + start(); + } else if (state == stateDownloading) { + Utilities.stageQueue.postRunnable(() -> { + requestedBytesCount = 0; + clearOperaion(null, true); + isPreloadVideoOperation = value; + startDownloadRequest(); + }); + } else { + isPreloadVideoOperation = value; + } + } else { + isPreloadVideoOperation = value; + } + } + + public boolean isPreloadVideoOperation() { + return isPreloadVideoOperation; + } + + public boolean isPreloadFinished() { + return preloadFinished; + } + public void cancel() { Utilities.stageQueue.postRunnable(() -> { if (state == stateFinished || state == stateFailed) { @@ -788,6 +933,19 @@ public class FileLoadOperation { } catch (Exception e) { FileLog.e(e); } + try { + if (preloadStream != null) { + try { + preloadStream.getChannel().close(); + } catch (Exception e) { + FileLog.e(e); + } + preloadStream.close(); + preloadStream = null; + } + } catch (Exception e) { + FileLog.e(e); + } try { if (fileReadStream != null) { try { @@ -847,50 +1005,61 @@ public class FileLoadOperation { } state = stateFinished; cleanup(); - if (cacheIvTemp != null) { - cacheIvTemp.delete(); - cacheIvTemp = null; - } - if (cacheFileParts != null) { - cacheFileParts.delete(); - cacheFileParts = null; - } - if (cacheFileTemp != null) { - boolean renameResult = cacheFileTemp.renameTo(cacheFileFinal); - if (!renameResult) { - if (BuildVars.LOGS_ENABLED) { - FileLog.e("unable to rename temp = " + cacheFileTemp + " to final = " + cacheFileFinal + " retry = " + renameRetryCount); - } - renameRetryCount++; - if (renameRetryCount < 3) { - state = stateDownloading; - Utilities.stageQueue.postRunnable(() -> { - try { - onFinishLoadingFile(increment); - } catch (Exception e) { - onFail(false, 0); - } - }, 200); - return; - } - cacheFileFinal = cacheFileTemp; + if (isPreloadVideoOperation) { + preloadFinished = true; + if (BuildVars.DEBUG_VERSION) { + FileLog.d("finished preloading file to " + cacheFileTemp + " loaded " + totalPreloadedBytes + " of " + totalBytesCount); + } + } else { + if (cacheIvTemp != null) { + cacheIvTemp.delete(); + cacheIvTemp = null; + } + if (cacheFileParts != null) { + cacheFileParts.delete(); + cacheFileParts = null; + } + if (cacheFilePreload != null) { + cacheFilePreload.delete(); + cacheFilePreload = null; + } + if (cacheFileTemp != null) { + boolean renameResult = cacheFileTemp.renameTo(cacheFileFinal); + if (!renameResult) { + if (BuildVars.LOGS_ENABLED) { + FileLog.e("unable to rename temp = " + cacheFileTemp + " to final = " + cacheFileFinal + " retry = " + renameRetryCount); + } + renameRetryCount++; + if (renameRetryCount < 3) { + state = stateDownloading; + Utilities.stageQueue.postRunnable(() -> { + try { + onFinishLoadingFile(increment); + } catch (Exception e) { + onFail(false, 0); + } + }, 200); + return; + } + cacheFileFinal = cacheFileTemp; + } + } + if (BuildVars.LOGS_ENABLED) { + FileLog.d("finished downloading file to " + cacheFileFinal); + } + if (increment) { + if (currentType == ConnectionsManager.FileTypeAudio) { + StatsController.getInstance(currentAccount).incrementReceivedItemsCount(ApplicationLoader.getCurrentNetworkType(), StatsController.TYPE_AUDIOS, 1); + } else if (currentType == ConnectionsManager.FileTypeVideo) { + StatsController.getInstance(currentAccount).incrementReceivedItemsCount(ApplicationLoader.getCurrentNetworkType(), StatsController.TYPE_VIDEOS, 1); + } else if (currentType == ConnectionsManager.FileTypePhoto) { + StatsController.getInstance(currentAccount).incrementReceivedItemsCount(ApplicationLoader.getCurrentNetworkType(), StatsController.TYPE_PHOTOS, 1); + } else if (currentType == ConnectionsManager.FileTypeFile) { + StatsController.getInstance(currentAccount).incrementReceivedItemsCount(ApplicationLoader.getCurrentNetworkType(), StatsController.TYPE_FILES, 1); + } } - } - if (BuildVars.LOGS_ENABLED) { - FileLog.d("finished downloading file to " + cacheFileFinal); } delegate.didFinishLoadingFile(FileLoadOperation.this, cacheFileFinal); - if (increment) { - if (currentType == ConnectionsManager.FileTypeAudio) { - StatsController.getInstance(currentAccount).incrementReceivedItemsCount(ApplicationLoader.getCurrentNetworkType(), StatsController.TYPE_AUDIOS, 1); - } else if (currentType == ConnectionsManager.FileTypeVideo) { - StatsController.getInstance(currentAccount).incrementReceivedItemsCount(ApplicationLoader.getCurrentNetworkType(), StatsController.TYPE_VIDEOS, 1); - } else if (currentType == ConnectionsManager.FileTypePhoto) { - StatsController.getInstance(currentAccount).incrementReceivedItemsCount(ApplicationLoader.getCurrentNetworkType(), StatsController.TYPE_PHOTOS, 1); - } else if (currentType == ConnectionsManager.FileTypeFile) { - StatsController.getInstance(currentAccount).incrementReceivedItemsCount(ApplicationLoader.getCurrentNetworkType(), StatsController.TYPE_FILES, 1); - } - } } private void delayRequestInfo(RequestInfo requestInfo) { @@ -904,6 +1073,42 @@ public class FileLoadOperation { } } + private int findNextPreloadDownloadOffset(int atomOffset, int partOffset, NativeByteBuffer partBuffer) { + int partSize = partBuffer.limit(); + while (true) { + if (atomOffset < partOffset - (preloadTempBuffer != null ? 16 : 0) || atomOffset >= partOffset + partSize) { + return 0; + } + if (atomOffset >= partOffset + partSize - 16) { + preloadTempBufferCount = partOffset + partSize - atomOffset; + partBuffer.position(partBuffer.limit() - preloadTempBufferCount); + partBuffer.readBytes(preloadTempBuffer, 0, preloadTempBufferCount, false); + return partOffset + partSize; + } + if (preloadTempBufferCount != 0) { + partBuffer.position(0); + partBuffer.readBytes(preloadTempBuffer, preloadTempBufferCount, 16 - preloadTempBufferCount, false); + preloadTempBufferCount = 0; + } else { + partBuffer.position(atomOffset - partOffset); + partBuffer.readBytes(preloadTempBuffer, 0, 16, false); + } + int atomSize = (((int) preloadTempBuffer[0] & 0xFF) << 24) + (((int) preloadTempBuffer[1] & 0xFF) << 16) + (((int) preloadTempBuffer[2] & 0xFF) << 8) + ((int) preloadTempBuffer[3] & 0xFF); + if (atomSize == 0) { + return 0; + } else if (atomSize == 1) { + atomSize = (((int) preloadTempBuffer[12] & 0xFF) << 24) + (((int) preloadTempBuffer[13] & 0xFF) << 16) + (((int) preloadTempBuffer[14] & 0xFF) << 8) + ((int) preloadTempBuffer[15] & 0xFF); + } + if (preloadTempBuffer[4] == 'm' && preloadTempBuffer[5] == 'o' && preloadTempBuffer[6] == 'o' && preloadTempBuffer[7] == 'v') { + return -atomSize; + } + if (atomSize + atomOffset >= partOffset + partSize) { + return atomSize + atomOffset; + } + atomOffset += atomSize; + } + } + private void requestFileOffsets(int offset) { if (requestingCdnOffsets) { return; @@ -999,85 +1204,134 @@ public class FileLoadOperation { Utilities.aesCtrDecryption(bytes.buffer, cdnKey, cdnIv, 0, bytes.limit()); } - downloadedBytes += currentBytesSize; boolean finishedDownloading; - if (totalBytesCount > 0) { - finishedDownloading = downloadedBytes >= totalBytesCount; - } else { - finishedDownloading = currentBytesSize != currentDownloadChunkSize || (totalBytesCount == downloadedBytes || downloadedBytes % currentDownloadChunkSize != 0) && (totalBytesCount <= 0 || totalBytesCount <= downloadedBytes); - } - if (key != null) { - Utilities.aesIgeEncryption(bytes.buffer, key, iv, false, true, 0, bytes.limit()); - if (finishedDownloading && bytesCountPadding != 0) { - bytes.limit(bytes.limit() - bytesCountPadding); - } - } - if (encryptFile) { - int offset = requestInfo.offset / 16; - encryptIv[15] = (byte) (offset & 0xff); - encryptIv[14] = (byte) ((offset >> 8) & 0xff); - encryptIv[13] = (byte) ((offset >> 16) & 0xff); - encryptIv[12] = (byte) ((offset >> 24) & 0xff); - Utilities.aesCtrDecryption(bytes.buffer, encryptKey, encryptIv, 0, bytes.limit()); - } - if (notLoadedBytesRanges != null) { - fileOutputStream.seek(requestInfo.offset); + if (isPreloadVideoOperation) { + preloadStream.writeInt(requestInfo.offset); + preloadStream.writeInt(currentBytesSize); + preloadStreamFileOffset += 8; + FileChannel channel = preloadStream.getChannel(); + channel.write(bytes.buffer); if (BuildVars.DEBUG_VERSION) { - FileLog.d("save file part " + cacheFileFinal + " offset " + requestInfo.offset); + FileLog.d("save preload file part " + cacheFilePreload + " offset " + requestInfo.offset + " size " + currentBytesSize); } - } - FileChannel channel = fileOutputStream.getChannel(); - channel.write(bytes.buffer); - addPart(notLoadedBytesRanges, requestInfo.offset, requestInfo.offset + currentBytesSize, true); - if (isCdn) { - int cdnCheckPart = requestInfo.offset / cdnChunkCheckSize; + if (preloadedBytesRanges == null) { + preloadedBytesRanges = new SparseArray<>(); + } + preloadedBytesRanges.put(requestInfo.offset, new PreloadRange(preloadStreamFileOffset, requestInfo.offset, currentBytesSize)); - int size = notCheckedCdnRanges.size(); - Range range; - boolean checked = true; - for (int a = 0; a < size; a++) { - range = notCheckedCdnRanges.get(a); - if (range.start <= cdnCheckPart && cdnCheckPart <= range.end) { - checked = false; - break; + totalPreloadedBytes += currentBytesSize; + preloadStreamFileOffset += currentBytesSize; + + if (moovFound == 0) { + int offset = findNextPreloadDownloadOffset(nextAtomOffset, requestInfo.offset, bytes); + if (offset < 0) { + offset *= -1; + nextPreloadDownloadOffset += currentDownloadChunkSize; + if (nextPreloadDownloadOffset < totalBytesCount / 2) { + preloadNotRequestedBytesCount = foundMoovSize = preloadMaxBytes / 2 + offset; + moovFound = 1; + } else { + preloadNotRequestedBytesCount = foundMoovSize = preloadMaxBytes; + moovFound = 2; + } + nextPreloadDownloadOffset = -1; + } else { + nextPreloadDownloadOffset = offset / currentDownloadChunkSize * currentDownloadChunkSize; + } + nextAtomOffset = offset; + } + preloadStream.writeInt(foundMoovSize); + preloadStream.writeInt(nextPreloadDownloadOffset); + preloadStream.writeInt(nextAtomOffset); + preloadStreamFileOffset += 12; + finishedDownloading = nextPreloadDownloadOffset == 0 || moovFound != 0 && foundMoovSize < 0 || totalPreloadedBytes > preloadMaxBytes || nextPreloadDownloadOffset >= totalBytesCount; + if (finishedDownloading) { + preloadStream.seek(0); + preloadStream.write((byte) 1); + } else if (moovFound != 0) { + foundMoovSize -= currentDownloadChunkSize; + } + } else { + downloadedBytes += currentBytesSize; + if (totalBytesCount > 0) { + finishedDownloading = downloadedBytes >= totalBytesCount; + } else { + finishedDownloading = currentBytesSize != currentDownloadChunkSize || (totalBytesCount == downloadedBytes || downloadedBytes % currentDownloadChunkSize != 0) && (totalBytesCount <= 0 || totalBytesCount <= downloadedBytes); + } + if (key != null) { + Utilities.aesIgeEncryption(bytes.buffer, key, iv, false, true, 0, bytes.limit()); + if (finishedDownloading && bytesCountPadding != 0) { + bytes.limit(bytes.limit() - bytesCountPadding); } } - if (!checked) { - int fileOffset = cdnCheckPart * cdnChunkCheckSize; - int availableSize = getDownloadedLengthFromOffsetInternal(notLoadedBytesRanges, fileOffset, cdnChunkCheckSize); - if (availableSize != 0 && (availableSize == cdnChunkCheckSize || totalBytesCount > 0 && availableSize == totalBytesCount - fileOffset || totalBytesCount <= 0 && finishedDownloading)) { - TLRPC.TL_fileHash hash = cdnHashes.get(fileOffset); - if (fileReadStream == null) { - cdnCheckBytes = new byte[cdnChunkCheckSize]; - fileReadStream = new RandomAccessFile(cacheFileTemp, "r"); + if (encryptFile) { + int offset = requestInfo.offset / 16; + encryptIv[15] = (byte) (offset & 0xff); + encryptIv[14] = (byte) ((offset >> 8) & 0xff); + encryptIv[13] = (byte) ((offset >> 16) & 0xff); + encryptIv[12] = (byte) ((offset >> 24) & 0xff); + Utilities.aesCtrDecryption(bytes.buffer, encryptKey, encryptIv, 0, bytes.limit()); + } + + if (notLoadedBytesRanges != null) { + fileOutputStream.seek(requestInfo.offset); + if (BuildVars.DEBUG_VERSION) { + FileLog.d("save file part " + cacheFileFinal + " offset " + requestInfo.offset); + } + } + FileChannel channel = fileOutputStream.getChannel(); + channel.write(bytes.buffer); + addPart(notLoadedBytesRanges, requestInfo.offset, requestInfo.offset + currentBytesSize, true); + if (isCdn) { + int cdnCheckPart = requestInfo.offset / cdnChunkCheckSize; + + int size = notCheckedCdnRanges.size(); + Range range; + boolean checked = true; + for (int a = 0; a < size; a++) { + range = notCheckedCdnRanges.get(a); + if (range.start <= cdnCheckPart && cdnCheckPart <= range.end) { + checked = false; + break; } - fileReadStream.seek(fileOffset); - fileReadStream.readFully(cdnCheckBytes, 0, availableSize); - byte[] sha256 = Utilities.computeSHA256(cdnCheckBytes, 0, availableSize); - if (!Arrays.equals(sha256, hash.hash)) { - if (BuildVars.LOGS_ENABLED) { - if (location != null) { - FileLog.e("invalid cdn hash " + location + " id = " + location.id + " local_id = " + location.local_id + " access_hash = " + location.access_hash + " volume_id = " + location.volume_id + " secret = " + location.secret); - } else if (webLocation != null) { - FileLog.e("invalid cdn hash " + webLocation + " id = " + getFileName()); - } + } + if (!checked) { + int fileOffset = cdnCheckPart * cdnChunkCheckSize; + int availableSize = getDownloadedLengthFromOffsetInternal(notLoadedBytesRanges, fileOffset, cdnChunkCheckSize); + if (availableSize != 0 && (availableSize == cdnChunkCheckSize || totalBytesCount > 0 && availableSize == totalBytesCount - fileOffset || totalBytesCount <= 0 && finishedDownloading)) { + TLRPC.TL_fileHash hash = cdnHashes.get(fileOffset); + if (fileReadStream == null) { + cdnCheckBytes = new byte[cdnChunkCheckSize]; + fileReadStream = new RandomAccessFile(cacheFileTemp, "r"); } - onFail(false, 0); - cacheFileTemp.delete(); - return false; + fileReadStream.seek(fileOffset); + fileReadStream.readFully(cdnCheckBytes, 0, availableSize); + byte[] sha256 = Utilities.computeSHA256(cdnCheckBytes, 0, availableSize); + if (!Arrays.equals(sha256, hash.hash)) { + if (BuildVars.LOGS_ENABLED) { + if (location != null) { + FileLog.e("invalid cdn hash " + location + " id = " + location.id + " local_id = " + location.local_id + " access_hash = " + location.access_hash + " volume_id = " + location.volume_id + " secret = " + location.secret); + } else if (webLocation != null) { + FileLog.e("invalid cdn hash " + webLocation + " id = " + getFileName()); + } + } + onFail(false, 0); + cacheFileTemp.delete(); + return false; + } + cdnHashes.remove(fileOffset); + addPart(notCheckedCdnRanges, cdnCheckPart, cdnCheckPart + 1, false); } - cdnHashes.remove(fileOffset); - addPart(notCheckedCdnRanges, cdnCheckPart, cdnCheckPart + 1, false); } } - } - if (fiv != null) { - fiv.seek(0); - fiv.write(iv); - } - if (totalBytesCount > 0 && state == stateDownloading) { - copytNotLoadedRanges(); - delegate.didChangedLoadProgress(FileLoadOperation.this, Math.min(1.0f, (float) downloadedBytes / (float) totalBytesCount)); + if (fiv != null) { + fiv.seek(0); + fiv.write(iv); + } + if (totalBytesCount > 0 && state == stateDownloading) { + copyNotLoadedRanges(); + delegate.didChangedLoadProgress(FileLoadOperation.this, Math.min(1.0f, (float) downloadedBytes / (float) totalBytesCount)); + } } for (int a = 0; a < delayedRequestInfos.size(); a++) { @@ -1164,12 +1418,16 @@ public class FileLoadOperation { } } - private void clearOperaion(RequestInfo currentInfo) { + private void clearOperaion(RequestInfo currentInfo, boolean preloadChanged) { int minOffset = Integer.MAX_VALUE; for (int a = 0; a < requestInfos.size(); a++) { RequestInfo info = requestInfos.get(a); minOffset = Math.min(info.offset, minOffset); - removePart(notRequestedBytesRanges, info.offset, info.offset + currentDownloadChunkSize); + if (isPreloadVideoOperation) { + requestedPreloadedBytesRanges.delete(info.offset); + } else { + removePart(notRequestedBytesRanges, info.offset, info.offset + currentDownloadChunkSize); + } if (currentInfo == info) { continue; } @@ -1180,7 +1438,11 @@ public class FileLoadOperation { requestInfos.clear(); for (int a = 0; a < delayedRequestInfos.size(); a++) { RequestInfo info = delayedRequestInfos.get(a); - removePart(notRequestedBytesRanges, info.offset, info.offset + currentDownloadChunkSize); + if (isPreloadVideoOperation) { + requestedPreloadedBytesRanges.delete(info.offset); + } else { + removePart(notRequestedBytesRanges, info.offset, info.offset + currentDownloadChunkSize); + } if (info.response != null) { info.response.disableFree = false; info.response.freeResources(); @@ -1195,7 +1457,9 @@ public class FileLoadOperation { } delayedRequestInfos.clear(); requestsCount = 0; - if (notLoadedBytesRanges == null) { + if (!preloadChanged && isPreloadVideoOperation) { + requestedBytesCount = totalPreloadedBytes; + } else if (notLoadedBytesRanges == null) { requestedBytesCount = downloadedBytes = minOffset; } } @@ -1204,7 +1468,7 @@ public class FileLoadOperation { if (requestingReference) { return; } - clearOperaion(requestInfo); + clearOperaion(requestInfo, false); requestingReference = true; if (parentObject instanceof MessageObject) { MessageObject messageObject = (MessageObject) parentObject; @@ -1216,45 +1480,86 @@ public class FileLoadOperation { } protected void startDownloadRequest() { - if (paused || state != stateDownloading || requestInfos.size() + delayedRequestInfos.size() >= currentMaxDownloadRequests) { + if (paused || + state != stateDownloading || + !nextPartWasPreloaded && (requestInfos.size() + delayedRequestInfos.size() >= currentMaxDownloadRequests) || + isPreloadVideoOperation && (requestedBytesCount > preloadMaxBytes || moovFound != 0 && requestInfos.size() > 0)) { return; } int count = 1; - if (totalBytesCount > 0) { + if (!nextPartWasPreloaded && (!isPreloadVideoOperation || moovFound != 0) && totalBytesCount > 0) { count = Math.max(0, currentMaxDownloadRequests - requestInfos.size()); } for (int a = 0; a < count; a++) { int downloadOffset; - if (notRequestedBytesRanges != null) { - int size = notRequestedBytesRanges.size(); - int minStart = Integer.MAX_VALUE; - int minStreamStart = Integer.MAX_VALUE; - for (int b = 0; b < size; b++) { - Range range = notRequestedBytesRanges.get(b); - if (streamStartOffset != 0) { - if (range.start <= streamStartOffset && range.end > streamStartOffset) { - minStreamStart = streamStartOffset; - minStart = Integer.MAX_VALUE; + if (isPreloadVideoOperation) { + if (moovFound != 0 && preloadNotRequestedBytesCount <= 0) { + return; + } + if (nextPreloadDownloadOffset == -1) { + downloadOffset = 0; + boolean found = false; + int tries = preloadMaxBytes / currentDownloadChunkSize + 2; + while (tries != 0) { + if (requestedPreloadedBytesRanges.get(downloadOffset, 0) == 0) { + found = true; break; } - if (streamStartOffset < range.start && range.start < minStreamStart) { - minStreamStart = range.start; + downloadOffset += currentDownloadChunkSize; + if (downloadOffset > totalBytesCount) { + break; } + if (moovFound == 2 && downloadOffset == currentDownloadChunkSize * 8) { + downloadOffset = (totalBytesCount - preloadMaxBytes / 2) / currentDownloadChunkSize * currentDownloadChunkSize; + } + tries--; + } + if (!found && requestInfos.isEmpty()) { + onFinishLoadingFile(false); } - minStart = Math.min(minStart, range.start); - } - if (minStreamStart != Integer.MAX_VALUE) { - downloadOffset = minStreamStart; - } else if (minStart != Integer.MAX_VALUE) { - downloadOffset = minStart; } else { - break; + downloadOffset = nextPreloadDownloadOffset; } + if (requestedPreloadedBytesRanges == null) { + requestedPreloadedBytesRanges = new SparseIntArray(); + } + requestedPreloadedBytesRanges.put(downloadOffset, 1); + if (BuildVars.DEBUG_VERSION) { + FileLog.d("start next preload from " + downloadOffset + " size " + totalBytesCount + " for " + cacheFilePreload); + } + preloadNotRequestedBytesCount -= currentDownloadChunkSize; } else { - downloadOffset = requestedBytesCount; + if (notRequestedBytesRanges != null) { + int size = notRequestedBytesRanges.size(); + int minStart = Integer.MAX_VALUE; + int minStreamStart = Integer.MAX_VALUE; + for (int b = 0; b < size; b++) { + Range range = notRequestedBytesRanges.get(b); + if (streamStartOffset != 0) { + if (range.start <= streamStartOffset && range.end > streamStartOffset) { + minStreamStart = streamStartOffset; + minStart = Integer.MAX_VALUE; + break; + } + if (streamStartOffset < range.start && range.start < minStreamStart) { + minStreamStart = range.start; + } + } + minStart = Math.min(minStart, range.start); + } + if (minStreamStart != Integer.MAX_VALUE) { + downloadOffset = minStreamStart; + } else if (minStart != Integer.MAX_VALUE) { + downloadOffset = minStart; + } else { + break; + } + } else { + downloadOffset = requestedBytesCount; + } } - if (notRequestedBytesRanges != null) { + if (!isPreloadVideoOperation && notRequestedBytesRanges != null) { addPart(notRequestedBytesRanges, downloadOffset, downloadOffset + currentDownloadChunkSize, false); } @@ -1294,6 +1599,28 @@ public class FileLoadOperation { final RequestInfo requestInfo = new RequestInfo(); requestInfos.add(requestInfo); requestInfo.offset = downloadOffset; + + if (!isPreloadVideoOperation && supportsPreloading && preloadStream != null && preloadedBytesRanges != null) { + PreloadRange range = preloadedBytesRanges.get(requestInfo.offset); + if (range != null) { + requestInfo.response = new TLRPC.TL_upload_file(); + try { + NativeByteBuffer buffer = new NativeByteBuffer(range.length); + preloadStream.seek(range.fileOffset); + preloadStream.getChannel().read(buffer.buffer); + buffer.buffer.position(0); + requestInfo.response.bytes = buffer; + Utilities.stageQueue.postRunnable(() -> { + processRequestResult(requestInfo, null); + requestInfo.response.freeResources(); + }); + continue; + } catch (Exception ignore) { + + } + } + } + requestInfo.requestToken = ConnectionsManager.getInstance(currentAccount).sendRequest(request, (response, error) -> { if (!requestInfos.contains(requestInfo)) { return; @@ -1305,7 +1632,7 @@ public class FileLoadOperation { } else if (request instanceof TLRPC.TL_upload_getCdnFile) { if (error.text.equals("FILE_TOKEN_INVALID")) { isCdn = false; - clearOperaion(requestInfo); + clearOperaion(requestInfo, false); startDownloadRequest(); return; } @@ -1337,12 +1664,12 @@ public class FileLoadOperation { cdnIv = res.encryption_iv; cdnKey = res.encryption_key; cdnToken = res.file_token; - clearOperaion(requestInfo); + clearOperaion(requestInfo, false); startDownloadRequest(); } } else if (response instanceof TLRPC.TL_upload_cdnFileReuploadNeeded) { if (!reuploadingCdn) { - clearOperaion(requestInfo); + clearOperaion(requestInfo, false); reuploadingCdn = true; TLRPC.TL_upload_cdnFileReuploadNeeded res = (TLRPC.TL_upload_cdnFileReuploadNeeded) response; TLRPC.TL_upload_reuploadCdnFile req = new TLRPC.TL_upload_reuploadCdnFile(); @@ -1365,7 +1692,7 @@ public class FileLoadOperation { } else { if (error1.text.equals("FILE_TOKEN_INVALID") || error1.text.equals("REQUEST_TOKEN_INVALID")) { isCdn = false; - clearOperaion(requestInfo); + clearOperaion(requestInfo, false); startDownloadRequest(); } else { onFail(false, 0); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperationStream.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperationStream.java new file mode 100644 index 000000000..1de5bdd79 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperationStream.java @@ -0,0 +1,5 @@ +package org.telegram.messenger; + +public interface FileLoadOperationStream { + void newDataAvailable(); +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java index 6f27dfb41..9b7187ec7 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java @@ -60,6 +60,8 @@ public class FileLoader { private ConcurrentHashMap loadOperationPathsUI = new ConcurrentHashMap<>(10, 1, 2); private HashMap uploadSizes = new HashMap<>(); + private HashMap loadingVideos = new HashMap<>(); + private static SparseArray mediaDirs = null; private FileLoaderDelegate delegate = null; @@ -118,6 +120,61 @@ public class FileLoader { return parentObjectReferences.get(reference); } + public void setLoadingVideoInternal(TLRPC.Document document, boolean player) { + String key = getAttachFileName(document); + String dKey = key + (player ? "p" : ""); + loadingVideos.put(dKey, true); + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.videoLoadingStateChanged, key); + } + + public void setLoadingVideo(TLRPC.Document document, boolean player, boolean schedule) { + if (document == null) { + return; + } + if (schedule) { + AndroidUtilities.runOnUIThread(() -> setLoadingVideoInternal(document, player)); + } else { + setLoadingVideoInternal(document, player); + } + } + + public void setLoadingVideoForPlayer(TLRPC.Document document, boolean player) { + if (document == null) { + return; + } + String key = getAttachFileName(document); + if (loadingVideos.containsKey(key + (player ? "" : "p"))) { + loadingVideos.put(key + (player ? "p" : ""), true); + } + } + + private void removeLoadingVideoInternal(TLRPC.Document document, boolean player) { + String key = getAttachFileName(document); + String dKey = key + (player ? "p" : ""); + if (loadingVideos.remove(dKey) != null) { + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.videoLoadingStateChanged, key); + } + } + + public void removeLoadingVideo(TLRPC.Document document, boolean player, boolean schedule) { + if (document == null) { + return; + } + if (schedule) { + AndroidUtilities.runOnUIThread(() -> removeLoadingVideoInternal(document, player)); + } else { + removeLoadingVideoInternal(document, player); + } + } + + public boolean isLoadingVideo(TLRPC.Document document, boolean player) { + return document != null && loadingVideos.containsKey(getAttachFileName(document) + (player ? "p" : "")); + } + + public boolean isLoadingVideoAny(TLRPC.Document document) { + return isLoadingVideo(document, false) || isLoadingVideo(document, true); + } + public void cancelUploadFile(final String location, final boolean enc) { fileLoaderQueue.postRunnable(() -> { FileUploadOperation operation; @@ -440,22 +497,22 @@ public class FileLoader { private void pauseCurrentFileLoadOperations(FileLoadOperation newOperation) { for (int a = 0; a < activeFileLoadOperation.size(); a++) { FileLoadOperation operation = activeFileLoadOperation.get(a); - if (operation == newOperation) { + if (operation == newOperation || operation.getDatacenterId() != newOperation.getDatacenterId()) { continue; } activeFileLoadOperation.remove(operation); a--; - operation.pause(); int datacenterId = operation.getDatacenterId(); LinkedList loadOperationQueue = getLoadOperationQueue(datacenterId); loadOperationQueue.add(0, operation); if (operation.wasStarted()) { currentLoadOperationsCount.put(datacenterId, currentLoadOperationsCount.get(datacenterId) - 1); } + operation.pause(); } } - private FileLoadOperation loadFileInternal(final TLRPC.Document document, final SecureDocument secureDocument, final WebFile webDocument, final TLRPC.FileLocation location, Object parentObject, final String locationExt, final int locationSize, final int priority, final FileStreamLoadOperation stream, final int streamOffset, final int cacheType) { + private FileLoadOperation loadFileInternal(final TLRPC.Document document, final SecureDocument secureDocument, final WebFile webDocument, final TLRPC.FileLocation location, Object parentObject, final String locationExt, final int locationSize, final int priority, final FileLoadOperationStream stream, final int streamOffset, final int cacheType) { String fileName = null; if (location != null) { fileName = getAttachFileName(location, locationExt); @@ -469,14 +526,17 @@ public class FileLoader { if (fileName == null || fileName.contains("" + Integer.MIN_VALUE)) { return null; } - if (!TextUtils.isEmpty(fileName) && !fileName.contains("" + Integer.MIN_VALUE)) { + if (cacheType != 10 && !TextUtils.isEmpty(fileName) && !fileName.contains("" + Integer.MIN_VALUE)) { loadOperationPathsUI.put(fileName, true); } FileLoadOperation operation; operation = loadOperationPaths.get(fileName); if (operation != null) { - if (streamOffset != 0 || priority > 0) { + if (cacheType != 10 && operation.isPreloadVideoOperation()) { + operation.setIsPreloadVideoOperation(false); + } + if (stream != null || priority > 0) { int datacenterId = operation.getDatacenterId(); LinkedList audioLoadOperationQueue = getAudioLoadOperationQueue(datacenterId); @@ -494,9 +554,9 @@ public class FileLoader { } if (downloadQueue != null) { int index = downloadQueue.indexOf(operation); - if (index > 0) { + if (index >= 0) { downloadQueue.remove(index); - if (streamOffset != 0) { + if (stream != null) { if (downloadQueue == audioLoadOperationQueue) { if (operation.start(stream, streamOffset)) { currentAudioLoadOperationsCount.put(datacenterId, currentAudioLoadOperationsCount.get(datacenterId) + 1); @@ -564,21 +624,29 @@ public class FileLoader { type = MEDIA_DIR_DOCUMENT; } } - if (cacheType == 0) { + if (cacheType == 0 || cacheType == 10) { storeDir = getDirectory(type); } else if (cacheType == 2) { operation.setEncryptFile(true); } operation.setPaths(currentAccount, storeDir, tempDir); + if (cacheType == 10) { + operation.setIsPreloadVideoOperation(true); + } final String finalFileName = fileName; final int finalType = type; FileLoadOperation.FileLoadOperationDelegate fileLoadOperationDelegate = new FileLoadOperation.FileLoadOperationDelegate() { @Override public void didFinishLoadingFile(FileLoadOperation operation, File finalFile) { - loadOperationPathsUI.remove(finalFileName); - if (delegate != null) { - delegate.fileDidLoaded(finalFileName, finalFile, finalType); + if (!operation.isPreloadVideoOperation() && operation.isPreloadFinished()) { + return; + } + if (!operation.isPreloadVideoOperation()) { + loadOperationPathsUI.remove(finalFileName); + if (delegate != null) { + delegate.fileDidLoaded(finalFileName, finalFile, finalType); + } } checkDownloadQueue(operation.getDatacenterId(), document, webDocument, location, finalFileName); } @@ -612,7 +680,7 @@ public class FileLoader { if (type == MEDIA_DIR_AUDIO) { int maxCount = priority > 0 ? 3 : 1; int count = currentAudioLoadOperationsCount.get(datacenterId); - if (streamOffset != 0 || count < maxCount) { + if (stream != null || count < maxCount) { if (operation.start(stream, streamOffset)) { currentAudioLoadOperationsCount.put(datacenterId, count + 1); } @@ -622,7 +690,7 @@ public class FileLoader { } else if (location != null || MessageObject.isImageWebDocument(webDocument)) { int maxCount = priority > 0 ? 6 : 2; int count = currentPhotoLoadOperationsCount.get(datacenterId); - if (streamOffset != 0 || count < maxCount) { + if (stream != null || count < maxCount) { if (operation.start(stream, streamOffset)) { currentPhotoLoadOperationsCount.put(datacenterId, count + 1); } @@ -632,7 +700,7 @@ public class FileLoader { } else { int maxCount = priority > 0 ? 3 : 1; int count = currentLoadOperationsCount.get(datacenterId); - if (streamOffset != 0 || count < maxCount) { + if (stream != null || count < maxCount) { if (operation.start(stream, streamOffset)) { currentLoadOperationsCount.put(datacenterId, count + 1); activeFileLoadOperation.add(operation); @@ -675,13 +743,13 @@ public class FileLoader { } else { fileName = null; } - if (!TextUtils.isEmpty(fileName) && !fileName.contains("" + Integer.MIN_VALUE)) { + if (cacheType != 10 && !TextUtils.isEmpty(fileName) && !fileName.contains("" + Integer.MIN_VALUE)) { loadOperationPathsUI.put(fileName, true); } fileLoaderQueue.postRunnable(() -> loadFileInternal(document, secureDocument, webDocument, location, parentObject, locationExt, locationSize, priority, null, 0, cacheType)); } - protected FileLoadOperation loadStreamFile(final FileStreamLoadOperation stream, final TLRPC.Document document, final Object parentObject, final int offset) { + protected FileLoadOperation loadStreamFile(final FileLoadOperationStream stream, final TLRPC.Document document, final Object parentObject, final int offset) { final CountDownLatch semaphore = new CountDownLatch(1); final FileLoadOperation[] result = new FileLoadOperation[1]; fileLoaderQueue.postRunnable(() -> { @@ -826,7 +894,7 @@ public class FileLoader { } else if (message.media instanceof TLRPC.TL_messageMediaInvoice) { TLRPC.WebDocument document = ((TLRPC.TL_messageMediaInvoice) message.media).photo; if (document != null) { - return Utilities.MD5(document.url) + "." + ImageLoader.getHttpUrlExtension(document.url, getExtensionByMime(document.mime_type)); + return Utilities.MD5(document.url) + "." + ImageLoader.getHttpUrlExtension(document.url, getMimeTypePart(document.mime_type)); } } } @@ -1009,7 +1077,7 @@ public class FileLoader { return fileName != null ? fileName : ""; } - public static String getExtensionByMime(String mime) { + public static String getMimeTypePart(String mime) { int index; if ((index = mime.lastIndexOf('/')) != -1) { return mime.substring(index + 1); @@ -1017,6 +1085,20 @@ public class FileLoader { return ""; } + public static String getExtensionByMimeType(String mime) { + if (mime != null) { + switch (mime) { + case "video/mp4": + return ".mp4"; + case "video/x-matroska": + return ".mkv"; + case "audio/ogg": + return ".ogg"; + } + } + return ""; + } + public static File getInternalCacheDir() { return ApplicationLoader.applicationContext.getCacheDir(); } @@ -1056,21 +1138,7 @@ public class FileLoader { } } if (docExt.length() <= 1) { - if (document.mime_type != null) { - switch (document.mime_type) { - case "video/mp4": - docExt = ".mp4"; - break; - case "audio/ogg": - docExt = ".ogg"; - break; - default: - docExt = ""; - break; - } - } else { - docExt = ""; - } + docExt = getExtensionByMimeType(document.mime_type); } if (docExt.length() > 1) { return document.dc_id + "_" + document.id + docExt; @@ -1085,7 +1153,7 @@ public class FileLoader { return secureFile.dc_id + "_" + secureFile.id + ".jpg"; } else if (attach instanceof WebFile) { WebFile document = (WebFile) attach; - return Utilities.MD5(document.url) + "." + ImageLoader.getHttpUrlExtension(document.url, getExtensionByMime(document.mime_type)); + return Utilities.MD5(document.url) + "." + ImageLoader.getHttpUrlExtension(document.url, getMimeTypePart(document.mime_type)); } else if (attach instanceof TLRPC.PhotoSize) { TLRPC.PhotoSize photo = (TLRPC.PhotoSize) attach; if (photo.location == null || photo.location instanceof TLRPC.TL_fileLocationUnavailable) { @@ -1154,4 +1222,8 @@ public class FileLoader { } }); } + + public static boolean isVideoMimeType(String mime) { + return "video/mp4".equals(mime) || SharedConfig.streamMkv && "video/x-matroska".equals(mime); + } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java index adaefed8d..f2d507a2e 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java @@ -56,7 +56,7 @@ public class FileRefController { if (parentObject instanceof MessageObject) { MessageObject messageObject = (MessageObject) parentObject; int channelId = messageObject.getChannelId(); - return "message" + messageObject.getId() + "_" + channelId; + return "message" + messageObject.getRealId() + "_" + channelId; } else if (parentObject instanceof TLRPC.Message) { TLRPC.Message message = (TLRPC.Message) parentObject; int channelId = message.to_id != null ? message.to_id.channel_id : 0; @@ -200,7 +200,7 @@ public class FileRefController { } if (parentObject instanceof MessageObject) { MessageObject messageObject = (MessageObject) parentObject; - if (messageObject.getId() < 0 && messageObject.messageOwner.media.webpage != null) { + if (messageObject.getRealId() < 0 && messageObject.messageOwner.media.webpage != null) { parentObject = messageObject.messageOwner.media.webpage; } } @@ -266,11 +266,11 @@ public class FileRefController { if (channelId != 0) { TLRPC.TL_channels_getMessages req = new TLRPC.TL_channels_getMessages(); req.channel = MessagesController.getInstance(currentAccount).getInputChannel(channelId); - req.id.add(messageObject.getId()); + req.id.add(messageObject.getRealId()); ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> onRequestComplete(locationKey, parentKey, response, true)); } else { TLRPC.TL_messages_getMessages req = new TLRPC.TL_messages_getMessages(); - req.id.add(messageObject.getId()); + req.id.add(messageObject.getRealId()); ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> onRequestComplete(locationKey, parentKey, response, true)); } } else if (parentObject instanceof TLRPC.TL_wallPaper) { @@ -872,6 +872,6 @@ public class FileRefController { } public static boolean isFileRefError(String error) { - return "FILEREF_EXPIRED".equals(error) || "FILE_REFERENCE_EXPIRED".equals(error) || "FILE_REFERENCE_EMPTY".equals(error); + return "FILEREF_EXPIRED".equals(error) || "FILE_REFERENCE_EXPIRED".equals(error) || "FILE_REFERENCE_EMPTY".equals(error) || error != null && error.startsWith("FILE_REFERENCE_"); } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileStreamLoadOperation.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileStreamLoadOperation.java index cbd19392b..6644ed329 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileStreamLoadOperation.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileStreamLoadOperation.java @@ -23,7 +23,7 @@ import java.io.IOException; import java.io.RandomAccessFile; import java.util.concurrent.CountDownLatch; -public class FileStreamLoadOperation extends BaseDataSource { +public class FileStreamLoadOperation extends BaseDataSource implements FileLoadOperationStream { private FileLoadOperation loadOperation; @@ -76,8 +76,10 @@ public class FileStreamLoadOperation extends BaseDataSource { } opened = true; transferStarted(dataSpec); - file = new RandomAccessFile(loadOperation.getCurrentFile(), "r"); - file.seek(currentOffset); + if (loadOperation != null) { + file = new RandomAccessFile(loadOperation.getCurrentFile(), "r"); + file.seek(currentOffset); + } return bytesRemaining; } @@ -96,9 +98,8 @@ public class FileStreamLoadOperation extends BaseDataSource { while (availableLength == 0) { availableLength = loadOperation.getDownloadedLengthFromOffset(currentOffset, readLength); if (availableLength == 0) { - if (loadOperation.isPaused()) { - FileLoader.getInstance(currentAccount).loadStreamFile(this, document, parentObject, currentOffset); - } + FileLog.d("not found bytes " + offset); + FileLoader.getInstance(currentAccount).loadStreamFile(this, document, parentObject, currentOffset); countDownLatch = new CountDownLatch(1); countDownLatch.await(); } @@ -142,7 +143,8 @@ public class FileStreamLoadOperation extends BaseDataSource { } } - protected void newDataAvailable() { + @Override + public void newDataAvailable() { if (countDownLatch != null) { countDownLatch.countDown(); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileUploadOperation.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileUploadOperation.java index 860192884..2ddba1436 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileUploadOperation.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileUploadOperation.java @@ -199,7 +199,7 @@ public class FileUploadOperation { storeFileUploadInfo(); } } - availableSize = newAvailableSize; + availableSize = finalSize > 0 ? finalSize : newAvailableSize; if (currentUploadRequetsCount < maxRequestsCount) { startUploadRequest(); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ImageLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/ImageLoader.java index a8b329f3d..97547188b 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ImageLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ImageLoader.java @@ -77,12 +77,13 @@ public class ImageLoader { private ConcurrentHashMap fileProgresses = new ConcurrentHashMap<>(); private HashMap thumbGenerateTasks = new HashMap<>(); private HashMap forceLoadingImages = new HashMap<>(); - private static byte[] bytes; - private static byte[] bytesThumb; + private static ThreadLocal bytesLocal = new ThreadLocal<>(); + private static ThreadLocal bytesThumbLocal = new ThreadLocal<>(); private static byte[] header = new byte[12]; private static byte[] headerThumb = new byte[12]; private int currentHttpTasksCount = 0; private int currentArtworkTasksCount = 0; + private boolean canForce8888; private ConcurrentHashMap testWebFile = new ConcurrentHashMap<>(); @@ -99,10 +100,13 @@ public class ImageLoader { private File telegramPath = null; + public static final String VIDEO_FILTER = "g"; + private class ThumbGenerateInfo { private TLRPC.Document parentDocument; private String filter; private ArrayList imageReceiverArray = new ArrayList<>(); + private boolean big; } private class HttpFileTask extends AsyncTask { @@ -646,19 +650,19 @@ public class ImageLoader { removeTask(); return; } - int size = Math.min(180, Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) / 4); + int size = info.big ? Math.max(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) : Math.min(180, Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) / 4); Bitmap originalBitmap = null; if (mediaType == FileLoader.MEDIA_DIR_IMAGE) { originalBitmap = ImageLoader.loadBitmap(originalPath.toString(), null, size, size, false); } else if (mediaType == FileLoader.MEDIA_DIR_VIDEO) { - originalBitmap = ThumbnailUtils.createVideoThumbnail(originalPath.toString(), MediaStore.Video.Thumbnails.MINI_KIND); + originalBitmap = ThumbnailUtils.createVideoThumbnail(originalPath.toString(), info.big ? MediaStore.Video.Thumbnails.FULL_SCREEN_KIND : MediaStore.Video.Thumbnails.MINI_KIND); } else if (mediaType == FileLoader.MEDIA_DIR_DOCUMENT) { String path = originalPath.toString().toLowerCase(); - if (!path.endsWith(".jpg") && !path.endsWith(".jpeg") && !path.endsWith(".png") && !path.endsWith(".gif")) { - removeTask(); - return; + if (path.endsWith("mp4")) { + originalBitmap = ThumbnailUtils.createVideoThumbnail(originalPath.toString(), info.big ? MediaStore.Video.Thumbnails.FULL_SCREEN_KIND : MediaStore.Video.Thumbnails.MINI_KIND); + } else if (path.endsWith(".jpg") || path.endsWith(".jpeg") || path.endsWith(".png") || path.endsWith(".gif")) { + originalBitmap = ImageLoader.loadBitmap(path, null, size, size, false); } - originalBitmap = ImageLoader.loadBitmap(path, null, size, size, false); } if (originalBitmap == null) { removeTask(); @@ -672,13 +676,15 @@ public class ImageLoader { return; } float scaleFactor = Math.min((float) w / size, (float) h / size); - Bitmap scaledBitmap = Bitmaps.createScaledBitmap(originalBitmap, (int) (w / scaleFactor), (int) (h / scaleFactor), true); - if (scaledBitmap != originalBitmap) { - originalBitmap.recycle(); - originalBitmap = scaledBitmap; + if (scaleFactor > 1) { + Bitmap scaledBitmap = Bitmaps.createScaledBitmap(originalBitmap, (int) (w / scaleFactor), (int) (h / scaleFactor), true); + if (scaledBitmap != originalBitmap) { + originalBitmap.recycle(); + originalBitmap = scaledBitmap; + } } FileOutputStream stream = new FileOutputStream(thumbFile); - originalBitmap.compress(Bitmap.CompressFormat.JPEG, 60, stream); + originalBitmap.compress(Bitmap.CompressFormat.JPEG, info.big ? 83 : 60, stream); try { stream.close(); } catch (Exception e) { @@ -696,7 +702,7 @@ public class ImageLoader { for (int a = 0; a < finalImageReceiverArray.size(); a++) { ImageReceiver imgView = finalImageReceiverArray.get(a); - imgView.setImageBitmapByKey(bitmapDrawable, kf, false, false); + imgView.setImageBitmapByKey(bitmapDrawable, kf, ImageReceiver.TYPE_IMAGE, false); } memCache.put(kf, bitmapDrawable); @@ -738,9 +744,11 @@ public class ImageLoader { } TLRPC.TL_photoStrippedSize photoSize = (TLRPC.TL_photoStrippedSize) cacheImage.location; int len = photoSize.bytes.length - 3 + Bitmaps.header.length + Bitmaps.footer.length; + byte[] bytes = bytesLocal.get(); byte[] data = bytes != null && bytes.length >= len ? bytes : null; if (data == null) { bytes = data = new byte[len]; + bytesLocal.set(bytes); } System.arraycopy(Bitmaps.header, 0, data, 0, Bitmaps.header.length); System.arraycopy(photoSize.bytes, 3, data, Bitmaps.header.length, photoSize.bytes.length - 3); @@ -760,7 +768,12 @@ public class ImageLoader { return; } } - AnimatedFileDrawable fileDrawable = new AnimatedFileDrawable(cacheImage.finalFilePath, cacheImage.filter != null && cacheImage.filter.equals("d")); + AnimatedFileDrawable fileDrawable; + if (VIDEO_FILTER.equals(cacheImage.filter) && !(cacheImage.location instanceof TLRPC.TL_documentEncrypted)) { + fileDrawable = new AnimatedFileDrawable(cacheImage.finalFilePath, false, cacheImage.size, cacheImage.location instanceof TLRPC.Document ? (TLRPC.Document) cacheImage.location : null, cacheImage.parentObject, cacheImage.currentAccount); + } else { + fileDrawable = new AnimatedFileDrawable(cacheImage.finalFilePath, "d".equals(cacheImage.filter), 0, null, null, cacheImage.currentAccount); + } Thread.interrupted(); onPostExecute(fileDrawable); } else { @@ -792,7 +805,7 @@ public class ImageLoader { try { randomAccessFile = new RandomAccessFile(cacheFileFinal, "r"); byte[] bytes; - if (cacheImage.selfThumb) { + if (cacheImage.imageType == ImageReceiver.TYPE_THUMB) { bytes = headerThumb; } else { bytes = header; @@ -817,7 +830,7 @@ public class ImageLoader { } } - if (cacheImage.selfThumb) { + if (cacheImage.imageType == ImageReceiver.TYPE_THUMB) { int blurType = 0; boolean checkInversion = false; if (cacheImage.filter != null) { @@ -864,9 +877,11 @@ public class ImageLoader { RandomAccessFile f = new RandomAccessFile(cacheFileFinal, "r"); int len = (int) f.length(); int offset = 0; + byte[] bytesThumb = bytesThumbLocal.get(); byte[] data = bytesThumb != null && bytesThumb.length >= len ? bytesThumb : null; if (data == null) { bytesThumb = data = new byte[len]; + bytesThumbLocal.set(bytesThumb); } f.readFully(data, 0, len); f.close(); @@ -972,7 +987,7 @@ public class ImageLoader { float h_filter = 0; boolean blur = false; boolean checkInversion = false; - boolean force8888 = false; + boolean force8888 = canForce8888; if (cacheImage.filter != null) { String args[] = cacheImage.filter.split("_"); if (args.length >= 2) { @@ -1001,9 +1016,11 @@ public class ImageLoader { if (secureDocumentKey != null) { RandomAccessFile f = new RandomAccessFile(cacheFileFinal, "r"); int len = (int) f.length(); + byte[] bytes = bytesLocal.get(); byte[] data = bytes != null && bytes.length >= len ? bytes : null; if (data == null) { bytes = data = new byte[len]; + bytesLocal.set(bytes); } f.readFully(data, 0, len); f.close(); @@ -1041,7 +1058,7 @@ public class ImageLoader { } } else if (mediaThumbPath != null) { opts.inJustDecodeBounds = true; - opts.inPreferredConfig = Bitmap.Config.RGB_565; + opts.inPreferredConfig = force8888 ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; FileInputStream is = new FileInputStream(cacheFileFinal); image = BitmapFactory.decodeStream(is, null, opts); is.close(); @@ -1098,9 +1115,11 @@ public class ImageLoader { RandomAccessFile f = new RandomAccessFile(cacheFileFinal, "r"); int len = (int) f.length(); int offset = 0; + byte[] bytes = bytesLocal.get(); byte[] data = bytes != null && bytes.length >= len ? bytes : null; if (data == null) { bytes = data = new byte[len]; + bytesLocal.set(bytes); } f.readFully(data, 0, len); f.close(); @@ -1248,8 +1267,10 @@ public class ImageLoader { protected String ext; protected SecureDocument secureDocument; protected Object location; + protected Object parentObject; + protected int size; protected boolean animatedFile; - protected boolean selfThumb; + protected int imageType; protected int currentAccount; @@ -1264,25 +1285,25 @@ public class ImageLoader { protected ArrayList imageReceiverArray = new ArrayList<>(); protected ArrayList keys = new ArrayList<>(); protected ArrayList filters = new ArrayList<>(); - protected ArrayList thumbs = new ArrayList<>(); + protected ArrayList thumbs = new ArrayList<>(); - public void addImageReceiver(ImageReceiver imageReceiver, String key, String filter, boolean thumb) { + public void addImageReceiver(ImageReceiver imageReceiver, String key, String filter, int type) { if (imageReceiverArray.contains(imageReceiver)) { return; } imageReceiverArray.add(imageReceiver); keys.add(key); filters.add(filter); - thumbs.add(thumb); - imageLoadingByTag.put(imageReceiver.getTag(thumb), this); + thumbs.add(type); + imageLoadingByTag.put(imageReceiver.getTag(type), this); } - public void replaceImageReceiver(ImageReceiver imageReceiver, String key, String filter, boolean thumb) { + public void replaceImageReceiver(ImageReceiver imageReceiver, String key, String filter, int type) { int index = imageReceiverArray.indexOf(imageReceiver); if (index == -1) { return; } - if (thumbs.get(index) != thumb) { + if (thumbs.get(index) != type) { index = imageReceiverArray.subList(index + 1, imageReceiverArray.size()).indexOf(imageReceiver); if (index == -1) { return; @@ -1293,23 +1314,23 @@ public class ImageLoader { } public void removeImageReceiver(ImageReceiver imageReceiver) { - Boolean thumb = selfThumb; + int currentImageType = imageType; for (int a = 0; a < imageReceiverArray.size(); a++) { ImageReceiver obj = imageReceiverArray.get(a); if (obj == null || obj == imageReceiver) { imageReceiverArray.remove(a); keys.remove(a); filters.remove(a); - thumb = thumbs.remove(a); + currentImageType = thumbs.remove(a); if (obj != null) { - imageLoadingByTag.remove(obj.getTag(thumb)); + imageLoadingByTag.remove(obj.getTag(currentImageType)); } a--; } } if (imageReceiverArray.size() == 0) { for (int a = 0; a < imageReceiverArray.size(); a++) { - imageLoadingByTag.remove(imageReceiverArray.get(a).getTag(thumb)); + imageLoadingByTag.remove(imageReceiverArray.get(a).getTag(currentImageType)); } imageReceiverArray.clear(); if (location != null) { @@ -1326,7 +1347,7 @@ public class ImageLoader { } } if (cacheTask != null) { - if (selfThumb) { + if (currentImageType == ImageReceiver.TYPE_THUMB) { cacheThumbOutQueue.cancelRunnable(cacheTask); } else { cacheOutQueue.cancelRunnable(cacheTask); @@ -1362,24 +1383,31 @@ public class ImageLoader { AnimatedFileDrawable fileDrawable = (AnimatedFileDrawable) image; for (int a = 0; a < finalImageReceiverArray.size(); a++) { ImageReceiver imgView = finalImageReceiverArray.get(a); - if (imgView.setImageBitmapByKey(a == 0 ? fileDrawable : fileDrawable.makeCopy(), key, selfThumb, false)) { - imageSet = true; + AnimatedFileDrawable toSet = (a == 0 ? fileDrawable : fileDrawable.makeCopy()); + if (imgView.setImageBitmapByKey(toSet, key, imageType, false)) { + if (toSet == fileDrawable) { + imageSet = true; + } + } else { + if (toSet != fileDrawable) { + toSet.recycle(); + } } } if (!imageSet) { - ((AnimatedFileDrawable) image).recycle(); + fileDrawable.recycle(); } } else { for (int a = 0; a < finalImageReceiverArray.size(); a++) { ImageReceiver imgView = finalImageReceiverArray.get(a); - imgView.setImageBitmapByKey(image, key, selfThumb, false); + imgView.setImageBitmapByKey(image, key, imageType, false); } } }); } for (int a = 0; a < imageReceiverArray.size(); a++) { ImageReceiver imageReceiver = imageReceiverArray.get(a); - imageLoadingByTag.remove(imageReceiver.getTag(selfThumb)); + imageLoadingByTag.remove(imageReceiver.getTag(imageType)); } imageReceiverArray.clear(); if (url != null) { @@ -1409,7 +1437,14 @@ public class ImageLoader { public ImageLoader() { thumbGeneratingQueue.setPriority(Thread.MIN_PRIORITY); - int cacheSize = Math.min(15, ((ActivityManager) ApplicationLoader.applicationContext.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass() / 7) * 1024 * 1024; + int memoryClass = ((ActivityManager) ApplicationLoader.applicationContext.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass(); + int maxSize; + if (canForce8888 = memoryClass >= 256) { + maxSize = 30; + } else { + maxSize = 15; + } + int cacheSize = Math.min(maxSize, memoryClass / 7) * 1024 * 1024; memCache = new LruCache(cacheSize) { @Override @@ -1801,24 +1836,28 @@ public class ImageLoader { } } - public void cancelLoadingForImageReceiver(final ImageReceiver imageReceiver, final int type) { + public void cancelLoadingForImageReceiver(final ImageReceiver imageReceiver, final boolean cancelAll) { if (imageReceiver == null) { return; } imageLoadQueue.postRunnable(() -> { - int start = 0; - int count = 2; - if (type == 1) { - count = 1; - } else if (type == 2) { - start = 1; - } - for (int a = start; a < count; a++) { - int TAG = imageReceiver.getTag(a == 0); - if (a == 0) { - removeFromWaitingForThumb(TAG, imageReceiver); + for (int a = 0; a < 3; a++) { + int imageType; + if (a > 0 && !cancelAll) { + return; } + if (a == 0) { + imageType = ImageReceiver.TYPE_THUMB; + } else if (a == 1) { + imageType = ImageReceiver.TYPE_IMAGE; + } else { + imageType = ImageReceiver.TYPE_MEDIA; + } + int TAG = imageReceiver.getTag(imageType); if (TAG != 0) { + if (a == 0) { + removeFromWaitingForThumb(TAG, imageReceiver); + } CacheImage ei = imageLoadingByTag.get(TAG); if (ei != null) { ei.removeImageReceiver(imageReceiver); @@ -1915,20 +1954,20 @@ public class ImageLoader { if (imageReceiver == null) { return; } - final String key = imageReceiver.getKey(); + final String key = imageReceiver.getImageKey(); if (key == null) { return; } imageLoadQueue.postRunnable(() -> forceLoadingImages.remove(key)); } - private void createLoadOperationForImageReceiver(final ImageReceiver imageReceiver, final String key, final String url, final String ext, final Object imageLocation, final String filter, final int size, final int cacheType, final int thumb) { + private void createLoadOperationForImageReceiver(final ImageReceiver imageReceiver, final String key, final String url, final String ext, final Object imageLocation, final String filter, final int size, final int cacheType, final int imageType, final int thumb) { if (imageReceiver == null || url == null || key == null) { return; } - int TAG = imageReceiver.getTag(thumb != 0); + int TAG = imageReceiver.getTag(imageType); if (TAG == 0) { - imageReceiver.setTag(TAG = lastImageNum, thumb != 0); + imageReceiver.setTag(TAG = lastImageNum, imageType); lastImageNum++; if (lastImageNum == Integer.MAX_VALUE) { lastImageNum = 0; @@ -1938,9 +1977,10 @@ public class ImageLoader { final int finalTag = TAG; final boolean finalIsNeedsQualityThumb = imageReceiver.isNeedsQualityThumb(); final Object parentObject = imageReceiver.getParentObject(); + final TLRPC.Document qualityDocument = imageReceiver.getQulityThumbDocument(); final boolean shouldGenerateQualityThumb = imageReceiver.isShouldGenerateQualityThumb(); final int currentAccount = imageReceiver.getCurrentAccount(); - final boolean currentKeyQuality = imageReceiver.isCurrentKeyQuality(); + final boolean currentKeyQuality = imageType == ImageReceiver.TYPE_IMAGE && imageReceiver.isCurrentKeyQuality(); imageLoadQueue.postRunnable(() -> { boolean added = false; if (thumb != 2) { @@ -1952,7 +1992,7 @@ public class ImageLoader { added = true; } else if (alreadyLoadingImage == alreadyLoadingUrl) { if (alreadyLoadingCache == null) { - alreadyLoadingImage.replaceImageReceiver(imageReceiver, key, filter, thumb != 0); + alreadyLoadingImage.replaceImageReceiver(imageReceiver, key, filter, imageType); } added = true; } else { @@ -1961,11 +2001,11 @@ public class ImageLoader { } if (!added && alreadyLoadingCache != null) { - alreadyLoadingCache.addImageReceiver(imageReceiver, key, filter, thumb != 0); + alreadyLoadingCache.addImageReceiver(imageReceiver, key, filter, imageType); added = true; } if (!added && alreadyLoadingUrl != null) { - alreadyLoadingUrl.addImageReceiver(imageReceiver, key, filter, thumb != 0); + alreadyLoadingUrl.addImageReceiver(imageReceiver, key, filter, imageType); added = true; } } @@ -1996,8 +2036,38 @@ public class ImageLoader { } } else if (thumb == 0 && currentKeyQuality) { onlyCache = true; - MessageObject parentMessageObject = (MessageObject) parentObject; - TLRPC.Document parentDocument = parentMessageObject.getDocument(); + + TLRPC.Document parentDocument; + String localPath; + File cachePath; + boolean forceCache; + String fileName; + int fileType; + boolean bigThumb; + if (parentObject instanceof MessageObject) { + MessageObject parentMessageObject = (MessageObject) parentObject; + parentDocument = parentMessageObject.getDocument(); + localPath = parentMessageObject.messageOwner.attachPath; + cachePath = FileLoader.getPathToMessage(parentMessageObject.messageOwner); + fileType = parentMessageObject.getFileType(); + bigThumb = false; + } else if (qualityDocument != null) { + parentDocument = qualityDocument; + cachePath = FileLoader.getPathToAttach(parentDocument, true); + if (MessageObject.isVideoDocument(parentDocument)) { + fileType = FileLoader.MEDIA_DIR_VIDEO; + } else { + fileType = FileLoader.MEDIA_DIR_DOCUMENT; + } + localPath = null; + bigThumb = true; + } else { + parentDocument = null; + localPath = null; + cachePath = null; + fileType = 0; + bigThumb = false; + } if (parentDocument != null) { if (finalIsNeedsQualityThumb) { cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), "q_" + parentDocument.dc_id + "_" + parentDocument.id + ".jpg"); @@ -2009,23 +2079,24 @@ public class ImageLoader { } File attachPath = null; - if (parentMessageObject.messageOwner.attachPath != null && parentMessageObject.messageOwner.attachPath.length() > 0) { - attachPath = new File(parentMessageObject.messageOwner.attachPath); + if (!TextUtils.isEmpty(localPath)) { + attachPath = new File(localPath); if (!attachPath.exists()) { attachPath = null; } } if (attachPath == null) { - attachPath = FileLoader.getPathToMessage(parentMessageObject.messageOwner); + attachPath = cachePath; } if (cacheFile == null) { - String location = parentMessageObject.getFileName(); + String location = FileLoader.getAttachFileName(parentDocument); ThumbGenerateInfo info = waitingForQualityThumb.get(location); if (info == null) { info = new ThumbGenerateInfo(); info.parentDocument = parentDocument; info.filter = filter; + info.big = bigThumb; waitingForQualityThumb.put(location, info); } if (!info.imageReceiverArray.contains(imageReceiver)) { @@ -2033,7 +2104,7 @@ public class ImageLoader { } waitingForQualityThumbByTag.put(finalTag, location); if (attachPath.exists() && shouldGenerateQualityThumb) { - generateThumb(parentMessageObject.getFileType(), attachPath, info); + generateThumb(fileType, attachPath, info); } return; } @@ -2063,7 +2134,7 @@ public class ImageLoader { img.secureDocument = (SecureDocument) imageLocation; onlyCache = img.secureDocument.secureFile.dc_id == Integer.MIN_VALUE; cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), url); - } else if (cacheType != 0 || size <= 0 || imageLocation instanceof String || isEncrypted) { + } else if (!VIDEO_FILTER.equals(filter) && (cacheType != 0 || size <= 0 || imageLocation instanceof String || isEncrypted)) { cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), url); if (cacheFile.exists()) { cacheFileExists = true; @@ -2071,11 +2142,22 @@ public class ImageLoader { cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), url + ".enc"); } } else if (imageLocation instanceof TLRPC.Document) { - if (MessageObject.isVideoDocument((TLRPC.Document) imageLocation)) { + TLRPC.Document document = (TLRPC.Document) imageLocation; + if (document instanceof TLRPC.TL_documentEncrypted) { + cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), url); + } else if (MessageObject.isVideoDocument(document)) { cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_VIDEO), url); } else { cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_DOCUMENT), url); } + if (VIDEO_FILTER.equals(filter)) { + img.animatedFile = true; + img.size = document.size; + onlyCache = true; + if (!cacheFile.exists()) { + cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), document.dc_id + "_" + document.id + ".temp"); + } + } } else if (imageLocation instanceof WebFile) { cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_DOCUMENT), url); } else { @@ -2083,16 +2165,17 @@ public class ImageLoader { } } - img.selfThumb = thumb != 0; + img.imageType = imageType; img.key = key; img.filter = filter; img.location = imageLocation; img.ext = ext; img.currentAccount = currentAccount; + img.parentObject = parentObject; if (cacheType == 2) { img.encryptionKeyPath = new File(FileLoader.getInternalCacheDir(), url + ".enc.key"); } - img.addImageReceiver(imageReceiver, key, filter, thumb != 0); + img.addImageReceiver(imageReceiver, key, filter, imageType); if (onlyCache || cacheFileExists || cacheFile.exists()) { img.finalFilePath = cacheFile; img.location = imageLocation; @@ -2160,25 +2243,37 @@ public class ImageLoader { } boolean imageSet = false; - String key = imageReceiver.getKey(); - if (key != null) { - BitmapDrawable bitmapDrawable = memCache.get(key); + String mediaKey = imageReceiver.getMediaKey(); + if (mediaKey != null) { + BitmapDrawable bitmapDrawable = memCache.get(mediaKey); if (bitmapDrawable != null) { - cancelLoadingForImageReceiver(imageReceiver, 0); - imageReceiver.setImageBitmapByKey(bitmapDrawable, key, false, true); + cancelLoadingForImageReceiver(imageReceiver, true); + imageReceiver.setImageBitmapByKey(bitmapDrawable, mediaKey, ImageReceiver.TYPE_MEDIA, true); imageSet = true; if (!imageReceiver.isForcePreview()) { return; } } } + String imageKey = imageReceiver.getImageKey(); + if (!imageSet && imageKey != null) { + BitmapDrawable bitmapDrawable = memCache.get(imageKey); + if (bitmapDrawable != null) { + cancelLoadingForImageReceiver(imageReceiver, true); + imageReceiver.setImageBitmapByKey(bitmapDrawable, imageKey, ImageReceiver.TYPE_IMAGE, true); + imageSet = true; + if (!imageReceiver.isForcePreview() && mediaKey == null) { + return; + } + } + } boolean thumbSet = false; String thumbKey = imageReceiver.getThumbKey(); if (thumbKey != null) { BitmapDrawable bitmapDrawable = memCache.get(thumbKey); if (bitmapDrawable != null) { - imageReceiver.setImageBitmapByKey(bitmapDrawable, thumbKey, true, true); - cancelLoadingForImageReceiver(imageReceiver, 1); + imageReceiver.setImageBitmapByKey(bitmapDrawable, thumbKey, ImageReceiver.TYPE_THUMB, true); + cancelLoadingForImageReceiver(imageReceiver, false); if (imageSet && imageReceiver.isForcePreview()) { return; } @@ -2188,61 +2283,79 @@ public class ImageLoader { boolean qualityThumb = false; Object parentObject = imageReceiver.getParentObject(); + TLRPC.Document qualityDocument = imageReceiver.getQulityThumbDocument(); Object thumbLocation = imageReceiver.getThumbLocation(); + Object mediaLocation = imageReceiver.getMediaLocation(); Object imageLocation = imageReceiver.getImageLocation(); - if (imageLocation == null && imageReceiver.isNeedsQualityThumb() && imageReceiver.isCurrentKeyQuality() && parentObject instanceof MessageObject) { - imageLocation = ((MessageObject) parentObject).getDocument(); - qualityThumb = true; + if (imageLocation == null && imageReceiver.isNeedsQualityThumb() && imageReceiver.isCurrentKeyQuality()) { + if (parentObject instanceof MessageObject) { + imageLocation = ((MessageObject) parentObject).getDocument(); + qualityThumb = true; + } else if (qualityDocument != null) { + imageLocation = qualityDocument; + qualityThumb = true; + } } boolean saveImageToCache = false; - String url = null; + String imageUrl = null; String thumbUrl = null; - key = null; + String mediaUrl = null; + imageKey = null; thumbKey = null; + mediaKey = null; String ext = imageReceiver.getExt(); if (ext == null) { ext = "jpg"; } - if (imageLocation != null) { - if (imageLocation instanceof String) { - String location = (String) imageLocation; + + for (int a = 0; a < 2; a++) { + Object object; + if (a == 0) { + object = imageLocation; + } else { + object = mediaLocation; + } + if (object == null) { + continue; + } + String key = null; + String url = null; + if (object instanceof String) { + String location = (String) object; key = Utilities.MD5(location); url = key + "." + getHttpUrlExtension(location, "jpg"); - } else if (imageLocation instanceof TLRPC.FileLocation) { - TLRPC.FileLocation location = (TLRPC.FileLocation) imageLocation; + } else if (object instanceof TLRPC.FileLocation) { + TLRPC.FileLocation location = (TLRPC.FileLocation) object; key = location.volume_id + "_" + location.local_id; url = key + "." + ext; if (imageReceiver.getExt() != null || location.key != null || location.volume_id == Integer.MIN_VALUE && location.local_id < 0) { saveImageToCache = true; } - } else if (imageLocation instanceof TLRPC.TL_photoStrippedSize) { - TLRPC.TL_photoStrippedSize location = (TLRPC.TL_photoStrippedSize) imageLocation; + } else if (object instanceof TLRPC.TL_photoStrippedSize) { + TLRPC.TL_photoStrippedSize location = (TLRPC.TL_photoStrippedSize) object; key = "stripped" + FileRefController.getKeyForParentObject(parentObject); url = key + "." + ext; - } else if (imageLocation instanceof TLRPC.TL_photoSize) { - TLRPC.TL_photoSize photoSize = (TLRPC.TL_photoSize) imageLocation; + } else if (object instanceof TLRPC.TL_photoSize) { + TLRPC.TL_photoSize photoSize = (TLRPC.TL_photoSize) object; key = photoSize.location.volume_id + "_" + photoSize.location.local_id; url = key + "." + ext; if (imageReceiver.getExt() != null || photoSize.location.key != null || photoSize.location.volume_id == Integer.MIN_VALUE && photoSize.location.local_id < 0) { saveImageToCache = true; } - } else if (imageLocation instanceof WebFile) { - WebFile document = (WebFile) imageLocation; - String defaultExt = FileLoader.getExtensionByMime(document.mime_type); + } else if (object instanceof WebFile) { + WebFile document = (WebFile) object; + String defaultExt = FileLoader.getMimeTypePart(document.mime_type); key = Utilities.MD5(document.url); url = key + "." + getHttpUrlExtension(document.url, defaultExt); - } else if (imageLocation instanceof SecureDocument) { - SecureDocument document = (SecureDocument) imageLocation; + } else if (object instanceof SecureDocument) { + SecureDocument document = (SecureDocument) object; key = document.secureFile.dc_id + "_" + document.secureFile.id; url = key + "." + ext; - if (thumbKey != null) { - thumbUrl = thumbKey + "." + ext; - } - } else if (imageLocation instanceof TLRPC.Document) { - TLRPC.Document document = (TLRPC.Document) imageLocation; + } else if (object instanceof TLRPC.Document) { + TLRPC.Document document = (TLRPC.Document) object; if (document.id != 0 && document.dc_id != 0) { - if (qualityThumb) { + if (a == 0 && qualityThumb) { key = "q_" + document.dc_id + "_" + document.id; } else { key = document.dc_id + "_" + document.id; @@ -2256,23 +2369,35 @@ public class ImageLoader { docExt = docExt.substring(idx); } if (docExt.length() <= 1) { - if (document.mime_type != null && document.mime_type.equals("video/mp4")) { + if ("video/mp4".equals(document.mime_type)) { docExt = ".mp4"; + } else if ("video/x-matroska".equals(document.mime_type)) { + docExt = ".mkv"; } else { docExt = ""; } } url = key + docExt; - if (thumbKey != null) { - thumbUrl = thumbKey + "." + ext; - } - saveImageToCache = !MessageObject.isGifDocument(document) && !MessageObject.isRoundVideoDocument((TLRPC.Document) imageLocation) && !MessageObject.canPreviewDocument(document); + saveImageToCache = !MessageObject.isVideoDocument(document) && !MessageObject.isGifDocument(document) && !MessageObject.isRoundVideoDocument(document) && !MessageObject.canPreviewDocument(document); } } - if (imageLocation == thumbLocation) { - imageLocation = null; - key = null; - url = null; + if (a == 0) { + imageKey = key; + imageUrl = url; + } else { + mediaKey = key; + mediaUrl = url; + } + if (object == thumbLocation) { + if (a == 0) { + imageLocation = null; + imageKey = null; + imageUrl = null; + } else { + mediaLocation = null; + mediaKey = null; + mediaUrl = null; + } } } @@ -2294,29 +2419,45 @@ public class ImageLoader { thumbUrl = thumbKey + "." + ext; } - String filter = imageReceiver.getFilter(); + String mediaFilter = imageReceiver.getMediaFilter(); + String imageFilter = imageReceiver.getImageFilter(); String thumbFilter = imageReceiver.getThumbFilter(); - if (key != null && filter != null) { - key += "@" + filter; + if (mediaKey != null && mediaFilter != null) { + mediaKey += "@" + mediaFilter; + } + if (imageKey != null && imageFilter != null) { + imageKey += "@" + imageFilter; } if (thumbKey != null && thumbFilter != null) { thumbKey += "@" + thumbFilter; } - int cacheType; - int thumbCacheType; if (imageLocation instanceof String) { - cacheType = 1; - thumbCacheType = 1; - } else { - cacheType = imageReceiver.getCacheType(); - if (cacheType == 0 && saveImageToCache) { - cacheType = 1; + createLoadOperationForImageReceiver(imageReceiver, thumbKey, thumbUrl, ext, thumbLocation, thumbFilter, 0, 1, ImageReceiver.TYPE_THUMB, thumbSet ? 2 : 1); + createLoadOperationForImageReceiver(imageReceiver, imageKey, imageUrl, ext, imageLocation, imageFilter, imageReceiver.getSize(), 1, ImageReceiver.TYPE_IMAGE, 0); + } else if (mediaLocation != null) { + int mediaCacheType = imageReceiver.getCacheType(); + int imageCacheType = 1; + if (mediaCacheType == 0 && saveImageToCache) { + mediaCacheType = 1; } - thumbCacheType = cacheType == 0 ? 1 : cacheType; + int thumbCacheType = mediaCacheType == 0 ? 1 : mediaCacheType; + if (!thumbSet) { + createLoadOperationForImageReceiver(imageReceiver, thumbKey, thumbUrl, ext, thumbLocation, thumbFilter, 0, thumbCacheType, ImageReceiver.TYPE_THUMB, thumbSet ? 2 : 1); + } + if (!imageSet) { + createLoadOperationForImageReceiver(imageReceiver, imageKey, imageUrl, ext, imageLocation, imageFilter, 0, imageCacheType, ImageReceiver.TYPE_IMAGE, 0); + } + createLoadOperationForImageReceiver(imageReceiver, mediaKey, mediaUrl, ext, mediaLocation, mediaFilter, imageReceiver.getSize(), mediaCacheType, ImageReceiver.TYPE_MEDIA, 0); + } else { + int imageCacheType = imageReceiver.getCacheType(); + if (imageCacheType == 0 && saveImageToCache) { + imageCacheType = 1; + } + int thumbCacheType = imageCacheType == 0 ? 1 : imageCacheType; + createLoadOperationForImageReceiver(imageReceiver, thumbKey, thumbUrl, ext, thumbLocation, thumbFilter, 0, thumbCacheType, ImageReceiver.TYPE_THUMB, thumbSet ? 2 : 1); + createLoadOperationForImageReceiver(imageReceiver, imageKey, imageUrl, ext, imageLocation, imageFilter, imageReceiver.getSize(), imageCacheType, ImageReceiver.TYPE_IMAGE, 0); } - createLoadOperationForImageReceiver(imageReceiver, thumbKey, thumbUrl, ext, thumbLocation, thumbFilter, 0, thumbCacheType, thumbSet ? 2 : 1); - createLoadOperationForImageReceiver(imageReceiver, key, url, ext, imageLocation, filter, imageReceiver.getSize(), cacheType, 0); } private void httpFileLoadError(final String location) { @@ -2361,7 +2502,7 @@ public class ImageLoader { for (int a = 0; a < img.imageReceiverArray.size(); a++) { String key = img.keys.get(a); String filter = img.filters.get(a); - Boolean thumb = img.thumbs.get(a); + int imageType = img.thumbs.get(a); ImageReceiver imageReceiver = img.imageReceiverArray.get(a); CacheImage cacheImage = imageLoadingByKeys.get(key); if (cacheImage == null) { @@ -2371,7 +2512,7 @@ public class ImageLoader { cacheImage.finalFilePath = finalFile; cacheImage.key = key; cacheImage.location = img.location; - cacheImage.selfThumb = thumb; + cacheImage.imageType = imageType; cacheImage.ext = img.ext; cacheImage.encryptionKeyPath = img.encryptionKeyPath; cacheImage.cacheTask = new CacheOutTask(cacheImage); @@ -2380,11 +2521,11 @@ public class ImageLoader { imageLoadingByKeys.put(key, cacheImage); tasks.add(cacheImage.cacheTask); } - cacheImage.addImageReceiver(imageReceiver, key, filter, thumb); + cacheImage.addImageReceiver(imageReceiver, key, filter, imageType); } for (int a = 0; a < tasks.size(); a++) { CacheOutTask task = tasks.get(a); - if (task.cacheImage.selfThumb) { + if (task.cacheImage.imageType == ImageReceiver.TYPE_THUMB) { cacheThumbOutQueue.postRunnable(task); } else { cacheOutQueue.postRunnable(task); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ImageReceiver.java b/TMessagesProj/src/main/java/org/telegram/messenger/ImageReceiver.java index fc00d3bd9..d02ceff2c 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ImageReceiver.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ImageReceiver.java @@ -21,7 +21,6 @@ import android.graphics.RectF; import android.graphics.Shader; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; -import android.support.annotation.Keep; import android.view.View; import org.telegram.tgnet.TLObject; @@ -38,6 +37,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg public static class BitmapHolder { private String key; + private boolean recycleOnRelease; public Bitmap bitmap; public BitmapHolder(Bitmap b, String k) { @@ -48,6 +48,11 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg } } + public BitmapHolder(Bitmap b) { + bitmap = b; + recycleOnRelease = true; + } + public int getWidth() { return bitmap != null ? bitmap.getWidth() : 0; } @@ -62,6 +67,9 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg public void release() { if (key == null) { + if (recycleOnRelease && bitmap != null) { + bitmap.recycle(); + } bitmap = null; return; } @@ -82,16 +90,22 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg public Drawable thumb; public Object thumbLocation; public String thumbFilter; + public Object mediaLocation; + public String mediaFilter; public int size; public int cacheType; public Object parentObject; public String ext; } + public final static int TYPE_IMAGE = 0; + public final static int TYPE_THUMB = 1; + private final static int TYPE_CROSSFDADE = 2; + public final static int TYPE_MEDIA = 3; + private int currentAccount; private View parentView; - private int tag; - private int thumbTag; + private int param; private Object currentParentObject; private boolean canceledLoading; @@ -102,17 +116,34 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg private SetImageBackup setImageBackup; private Object currentImageLocation; - private String currentKey; - private String currentThumbKey; - private String currentFilter; - private String currentThumbFilter; - private String currentExt; + private String currentImageKey; + private String currentImageFilter; + private int imageTag; + private Drawable currentImageDrawable; + private BitmapShader imageShader; + private int imageOrientation; + private Object currentThumbLocation; + private String currentThumbKey; + private String currentThumbFilter; + private int thumbTag; + private Drawable currentThumbDrawable; + private BitmapShader thumbShader; + private int thumbOrientation; + + private Object currentMediaLocation; + private String currentMediaKey; + private String currentMediaFilter; + private int mediaTag; + private Drawable currentMediaDrawable; + private BitmapShader mediaShader; + + private Drawable staticThumbDrawable; + + private String currentExt; + private int currentSize; private int currentCacheType; - private Drawable currentImage; - private Drawable currentThumb; - private Drawable staticThumb; private boolean allowStartAnimation = true; private boolean allowDecodeSingleFrame; @@ -124,6 +155,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg private boolean needsQualityThumb; private boolean shouldGenerateQualityThumb; + private TLRPC.Document qulityThumbDocument; private boolean currentKeyQuality; private boolean invalidateAll; @@ -134,16 +166,13 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg private boolean forcePreview; private boolean forceCrossfade; private int roundRadius; - private BitmapShader bitmapShader; - private BitmapShader bitmapShaderThumb; + private Paint roundPaint; private RectF roundRect = new RectF(); private RectF bitmapRect = new RectF(); private Matrix shaderMatrix = new Matrix(); private float overrideAlpha = 1.0f; private int isPressed; - private int orientation; - private int thumbOrientation; private boolean centerRotation; private ImageReceiverDelegate delegate; private float currentAlpha; @@ -165,7 +194,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg public void cancelLoadImage() { forceLoding = false; - ImageLoader.getInstance().cancelLoadingForImageReceiver(this, 0); + ImageLoader.getInstance().cancelLoadingForImageReceiver(this, true); canceledLoading = true; } @@ -193,46 +222,92 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg setImage(fileLocation, filter, null, thumbLocation, thumbFilter, 0, ext, parentObject, cacheType); } - public void setImage(TLObject fileLocation, String filter, TLObject thumbLocation, String thumbFilter, int size, String ext, Object parentObject, int cacheType) { + public void setImage(Object fileLocation, String filter, TLObject thumbLocation, String thumbFilter, int size, String ext, Object parentObject, int cacheType) { setImage(fileLocation, filter, null, thumbLocation, thumbFilter, size, ext, parentObject, cacheType); } public void setImage(Object fileLocation, String filter, Drawable thumb, Object thumbLocation, String thumbFilter, int size, String ext, Object parentObject, int cacheType) { - if (setImageBackup != null) { - setImageBackup.fileLocation = null; - setImageBackup.thumbLocation = null; - setImageBackup.thumb = null; - } + setImage(null, null, fileLocation, filter, thumb, thumbLocation, thumbFilter, size, ext, parentObject, cacheType); + } - if ((fileLocation == null && thumbLocation == null) - || (fileLocation != null && !(fileLocation instanceof TLRPC.TL_fileLocation) + private String getLocationKey(Object fileLocation, Object parentObject) { + if (fileLocation instanceof SecureDocument) { + SecureDocument document = (SecureDocument) fileLocation; + return document.secureFile.dc_id + "_" + document.secureFile.id; + } else if (fileLocation instanceof TLRPC.FileLocation) { + TLRPC.FileLocation location = (TLRPC.FileLocation) fileLocation; + return location.volume_id + "_" + location.local_id; + } else if (fileLocation instanceof TLRPC.TL_photoStrippedSize) { + TLRPC.TL_photoStrippedSize location = (TLRPC.TL_photoStrippedSize) fileLocation; + if (location.bytes.length > 0) { + return "stripped" + FileRefController.getKeyForParentObject(parentObject); + } + } else if (fileLocation instanceof TLRPC.TL_photoSize || fileLocation instanceof TLRPC.TL_photoCachedSize) { + TLRPC.PhotoSize photoSize = (TLRPC.PhotoSize) fileLocation; + return photoSize.location.volume_id + "_" + photoSize.location.local_id; + } else if (fileLocation instanceof WebFile) { + WebFile location = (WebFile) fileLocation; + return Utilities.MD5(location.url); + } else if (fileLocation instanceof TLRPC.Document) { + TLRPC.Document location = (TLRPC.Document) fileLocation; + if (location.dc_id != 0) { + return location.dc_id + "_" + location.id; + } + } else if (fileLocation instanceof String) { + return Utilities.MD5((String) fileLocation); + } + return null; + } + + private boolean isInvalidLocation(Object fileLocation) { + return fileLocation != null && + !(fileLocation instanceof TLRPC.TL_fileLocation) && !(fileLocation instanceof TLRPC.TL_fileEncryptedLocation) && !(fileLocation instanceof TLRPC.TL_document) && !(fileLocation instanceof WebFile) && !(fileLocation instanceof TLRPC.TL_documentEncrypted) && !(fileLocation instanceof TLRPC.PhotoSize) && !(fileLocation instanceof SecureDocument) - && !(fileLocation instanceof String))) { - for (int a = 0; a < 3; a++) { + && !(fileLocation instanceof String); + } + + public void setImage(Object mediaLocation, String mediaFilter, Object fileLocation, String imageFilter, Drawable thumb, Object thumbLocation, String thumbFilter, int size, String ext, Object parentObject, int cacheType) { + if (setImageBackup != null) { + setImageBackup.fileLocation = null; + setImageBackup.thumbLocation = null; + setImageBackup.mediaLocation = null; + setImageBackup.thumb = null; + } + + if ((fileLocation == null && thumbLocation == null && mediaLocation == null) || isInvalidLocation(fileLocation) || isInvalidLocation(mediaLocation)) { + for (int a = 0; a < 4; a++) { recycleBitmap(null, a); } - currentKey = null; - currentExt = ext; + currentImageLocation = null; + currentImageKey = null; + currentImageFilter = null; + currentMediaLocation = null; + currentMediaKey = null; + currentMediaFilter = null; + currentThumbLocation = null; currentThumbKey = null; currentThumbFilter = null; - currentImageLocation = null; - currentFilter = null; + + currentMediaDrawable = null; + mediaShader = null; + currentImageDrawable = null; + imageShader = null; + thumbShader = null; + crossfadeShader = null; + + currentExt = ext; currentParentObject = null; currentCacheType = 0; - staticThumb = thumb; - currentAlpha = 1; - currentThumbLocation = null; + staticThumbDrawable = thumb; + currentAlpha = 1.0f; currentSize = 0; - currentImage = null; - bitmapShader = null; - bitmapShaderThumb = null; - crossfadeShader = null; - ImageLoader.getInstance().cancelLoadingForImageReceiver(this, 0); + + ImageLoader.getInstance().cancelLoadingForImageReceiver(this, true); if (parentView != null) { if (invalidateAll) { parentView.invalidate(); @@ -241,139 +316,120 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg } } if (delegate != null) { - delegate.didSetImage(this, currentImage != null || currentThumb != null || staticThumb != null, currentImage == null); + delegate.didSetImage(this, currentImageDrawable != null || currentThumbDrawable != null || staticThumbDrawable != null || currentMediaDrawable != null, currentImageDrawable == null && currentMediaDrawable == null); } return; } - - if (!(thumbLocation instanceof String) && !(thumbLocation instanceof TLRPC.PhotoSize) && !(thumbLocation instanceof TLRPC.TL_fileLocation) && !(thumbLocation instanceof TLRPC.TL_fileEncryptedLocation)) { + if (isInvalidLocation(thumbLocation)) { thumbLocation = null; } - - String key = null; - if (fileLocation instanceof SecureDocument) { - SecureDocument document = (SecureDocument) fileLocation; - key = document.secureFile.dc_id + "_" + document.secureFile.id; - } else if (fileLocation instanceof TLRPC.FileLocation) { - TLRPC.FileLocation location = (TLRPC.FileLocation) fileLocation; - key = location.volume_id + "_" + location.local_id; - } else if (fileLocation instanceof TLRPC.TL_photoStrippedSize) { - TLRPC.TL_photoStrippedSize location = (TLRPC.TL_photoStrippedSize) fileLocation; - key = "stripped" + FileRefController.getKeyForParentObject(parentObject); - } else if (fileLocation instanceof TLRPC.TL_photoSize || fileLocation instanceof TLRPC.TL_photoCachedSize) { - TLRPC.PhotoSize photoSize = (TLRPC.PhotoSize) fileLocation; - key = photoSize.location.volume_id + "_" + photoSize.location.local_id; - } else if (fileLocation instanceof WebFile) { - WebFile location = (WebFile) fileLocation; - key = Utilities.MD5(location.url); - } else if (fileLocation instanceof TLRPC.Document) { - TLRPC.Document location = (TLRPC.Document) fileLocation; - if (location.dc_id != 0) { - key = location.dc_id + "_" + location.id; - } else { - fileLocation = null; - } - } else if (fileLocation instanceof String) { - key = Utilities.MD5((String) fileLocation); + String imageKey = getLocationKey(fileLocation, parentObject); + if (imageKey == null && fileLocation != null) { + fileLocation = null; } - currentKeyQuality = false; - if (key == null && needsQualityThumb && parentObject instanceof MessageObject) { - TLRPC.Document document = ((MessageObject) parentObject).getDocument(); + if (imageKey == null && needsQualityThumb && (parentObject instanceof MessageObject || qulityThumbDocument != null)) { + TLRPC.Document document = qulityThumbDocument != null ? qulityThumbDocument : ((MessageObject) parentObject).getDocument(); if (document != null && document.dc_id != 0 && document.id != 0) { - key = "q_" + document.dc_id + "_" + document.id; + imageKey = "q_" + document.dc_id + "_" + document.id; currentKeyQuality = true; } } - if (key != null && filter != null) { - key += "@" + filter; + if (imageKey != null && imageFilter != null) { + imageKey += "@" + imageFilter; } - if (currentKey != null && currentKey.equals(key)) { + String mediaKey = getLocationKey(mediaLocation, parentObject); + if (mediaKey == null && mediaLocation != null) { + mediaLocation = null; + } + if (mediaKey != null && mediaFilter != null) { + mediaKey += "@" + mediaFilter; + } + + if (mediaKey == null && currentImageKey != null && currentImageKey.equals(imageKey) || currentMediaKey != null && currentMediaKey.equals(mediaKey)) { if (delegate != null) { - delegate.didSetImage(this, currentImage != null || currentThumb != null || staticThumb != null, currentImage == null); + delegate.didSetImage(this, currentImageDrawable != null || currentThumbDrawable != null || staticThumbDrawable != null || currentMediaDrawable != null, currentImageDrawable == null && currentMediaDrawable == null); } if (!canceledLoading && !forcePreview) { return; } } - String thumbKey = null; - if (thumbLocation instanceof TLRPC.FileLocation) { - TLRPC.FileLocation location = (TLRPC.FileLocation) thumbLocation; - thumbKey = location.volume_id + "_" + location.local_id; - } else if (thumbLocation instanceof TLRPC.TL_photoStrippedSize) { - TLRPC.TL_photoStrippedSize location = (TLRPC.TL_photoStrippedSize) thumbLocation; - thumbKey = "stripped" + FileRefController.getKeyForParentObject(parentObject); - } else if (thumbLocation instanceof TLRPC.TL_photoSize || thumbLocation instanceof TLRPC.TL_photoCachedSize) { - TLRPC.PhotoSize photoSize = (TLRPC.PhotoSize) thumbLocation; - thumbKey = photoSize.location.volume_id + "_" + photoSize.location.local_id; - } else if (thumbLocation instanceof String) { - thumbKey = Utilities.MD5((String) thumbLocation); - } + String thumbKey = getLocationKey(thumbLocation, parentObject); if (thumbKey != null && thumbFilter != null) { thumbKey += "@" + thumbFilter; } if (crossfadeWithOldImage) { - if (currentImage != null) { - recycleBitmap(thumbKey, 1); - recycleBitmap(null, 2); - crossfadeShader = bitmapShader; - crossfadeImage = currentImage; - crossfadeKey = currentKey; + if (currentImageDrawable != null) { + recycleBitmap(thumbKey, TYPE_THUMB); + recycleBitmap(null, TYPE_CROSSFDADE); + recycleBitmap(mediaKey, TYPE_MEDIA); + crossfadeShader = imageShader; + crossfadeImage = currentImageDrawable; + crossfadeKey = currentImageKey; crossfadingWithThumb = false; - currentImage = null; - currentKey = null; - } else if (currentThumb != null) { - recycleBitmap(key, 0); - recycleBitmap(null, 2); - crossfadeShader = bitmapShaderThumb; - crossfadeImage = currentThumb; + currentImageDrawable = null; + currentImageKey = null; + } else if (currentThumbDrawable != null) { + recycleBitmap(imageKey, TYPE_IMAGE); + recycleBitmap(null, TYPE_CROSSFDADE); + recycleBitmap(mediaKey, TYPE_MEDIA); + crossfadeShader = thumbShader; + crossfadeImage = currentThumbDrawable; crossfadeKey = currentThumbKey; crossfadingWithThumb = false; - currentThumb = null; + currentThumbDrawable = null; currentThumbKey = null; - } else if (staticThumb != null) { - recycleBitmap(key, 0); - recycleBitmap(thumbKey, 1); - recycleBitmap(null, 2); - crossfadeShader = bitmapShaderThumb; - crossfadeImage = staticThumb; + } else if (staticThumbDrawable != null) { + recycleBitmap(imageKey, TYPE_IMAGE); + recycleBitmap(thumbKey, TYPE_THUMB); + recycleBitmap(null, TYPE_CROSSFDADE); + recycleBitmap(mediaKey, TYPE_MEDIA); + crossfadeShader = thumbShader; + crossfadeImage = staticThumbDrawable; crossfadingWithThumb = false; crossfadeKey = null; - currentThumb = null; + currentThumbDrawable = null; currentThumbKey = null; } else { - recycleBitmap(key, 0); - recycleBitmap(thumbKey, 1); - recycleBitmap(null, 2); + recycleBitmap(imageKey, TYPE_IMAGE); + recycleBitmap(thumbKey, TYPE_THUMB); + recycleBitmap(null, TYPE_CROSSFDADE); + recycleBitmap(mediaKey, TYPE_MEDIA); crossfadeShader = null; } } else { - recycleBitmap(key, 0); - recycleBitmap(thumbKey, 1); - recycleBitmap(null, 2); + recycleBitmap(imageKey, TYPE_IMAGE); + recycleBitmap(thumbKey, TYPE_THUMB); + recycleBitmap(null, TYPE_CROSSFDADE); + recycleBitmap(mediaKey, TYPE_MEDIA); crossfadeShader = null; } - currentParentObject = parentObject; - currentThumbKey = thumbKey; - currentKey = key; - currentExt = ext; currentImageLocation = fileLocation; - currentFilter = filter; + currentImageKey = imageKey; + currentImageFilter = imageFilter; + currentMediaLocation = mediaLocation; + currentMediaKey = mediaKey; + currentMediaFilter = mediaFilter; + currentThumbLocation = thumbLocation; + currentThumbKey = thumbKey; currentThumbFilter = thumbFilter; + + currentParentObject = parentObject; + currentExt = ext; currentSize = size; currentCacheType = cacheType; - currentThumbLocation = thumbLocation; - staticThumb = thumb; - bitmapShader = null; - bitmapShaderThumb = null; + staticThumbDrawable = thumb; + imageShader = null; + thumbShader = null; + mediaShader = null; currentAlpha = 1.0f; if (delegate != null) { - delegate.didSetImage(this, currentImage != null || currentThumb != null || staticThumb != null, currentImage == null); + delegate.didSetImage(this, currentImageDrawable != null || currentThumbDrawable != null || staticThumbDrawable != null || currentMediaDrawable != null, currentImageDrawable == null && currentMediaDrawable == null); } ImageLoader.getInstance().loadImageForImageReceiver(this); @@ -387,7 +443,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg } public boolean canInvertBitmap() { - return currentImage instanceof ExtendedBitmapDrawable || currentThumb instanceof ExtendedBitmapDrawable || staticThumb instanceof ExtendedBitmapDrawable; + return currentMediaDrawable instanceof ExtendedBitmapDrawable || currentImageDrawable instanceof ExtendedBitmapDrawable || currentThumbDrawable instanceof ExtendedBitmapDrawable || staticThumbDrawable instanceof ExtendedBitmapDrawable; } public void setColorFilter(ColorFilter filter) { @@ -413,7 +469,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg while (angle > 360) { angle -= 360; } - orientation = thumbOrientation = angle; + imageOrientation = thumbOrientation = angle; centerRotation = center; } @@ -422,22 +478,16 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg } public Drawable getStaticThumb() { - return staticThumb; + return staticThumbDrawable; } public int getAnimatedOrientation() { - if (currentImage instanceof AnimatedFileDrawable) { - return ((AnimatedFileDrawable) currentImage).getOrientation(); - } else if (staticThumb instanceof AnimatedFileDrawable) { - return ((AnimatedFileDrawable) staticThumb).getOrientation(); - } else if (currentImage instanceof ExtendedBitmapDrawable) { - return ((ExtendedBitmapDrawable) currentImage).getOrientation(); - } - return 0; + AnimatedFileDrawable animation = getAnimation(); + return animation != null ? animation.getOrientation() : 0; } public int getOrientation() { - return orientation; + return imageOrientation; } public void setImageBitmap(Bitmap bitmap) { @@ -445,74 +495,100 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg } public void setImageBitmap(Drawable bitmap) { - ImageLoader.getInstance().cancelLoadingForImageReceiver(this, 0); + ImageLoader.getInstance().cancelLoadingForImageReceiver(this, true); if (crossfadeWithOldImage) { - if (currentImage != null) { - recycleBitmap(null, 1); - recycleBitmap(null, 2); - crossfadeShader = bitmapShader; - crossfadeImage = currentImage; - crossfadeKey = currentKey; + if (currentImageDrawable != null) { + recycleBitmap(null, TYPE_THUMB); + recycleBitmap(null, TYPE_CROSSFDADE); + recycleBitmap(null, TYPE_MEDIA); + crossfadeShader = imageShader; + crossfadeImage = currentImageDrawable; + crossfadeKey = currentImageKey; crossfadingWithThumb = true; - } else if (currentThumb != null) { - recycleBitmap(null, 0); - recycleBitmap(null, 2); - crossfadeShader = bitmapShaderThumb; - crossfadeImage = currentThumb; + } else if (currentThumbDrawable != null) { + recycleBitmap(null, TYPE_IMAGE); + recycleBitmap(null, TYPE_CROSSFDADE); + recycleBitmap(null, TYPE_MEDIA); + crossfadeShader = thumbShader; + crossfadeImage = currentThumbDrawable; crossfadeKey = currentThumbKey; crossfadingWithThumb = true; - } else if (staticThumb != null) { - recycleBitmap(null, 0); - recycleBitmap(null, 1); - recycleBitmap(null, 2); - crossfadeShader = bitmapShaderThumb; - crossfadeImage = staticThumb; + } else if (staticThumbDrawable != null) { + recycleBitmap(null, TYPE_IMAGE); + recycleBitmap(null, TYPE_THUMB); + recycleBitmap(null, TYPE_CROSSFDADE); + recycleBitmap(null, TYPE_MEDIA); + crossfadeShader = thumbShader; + crossfadeImage = staticThumbDrawable; crossfadingWithThumb = true; crossfadeKey = null; } else { - for (int a = 0; a < 3; a++) { + for (int a = 0; a < 4; a++) { recycleBitmap(null, a); } crossfadeShader = null; } } else { - for (int a = 0; a < 3; a++) { + for (int a = 0; a < 4; a++) { recycleBitmap(null, a); } } - if (staticThumb instanceof RecyclableDrawable) { - RecyclableDrawable drawable = (RecyclableDrawable) staticThumb; + if (staticThumbDrawable instanceof RecyclableDrawable) { + RecyclableDrawable drawable = (RecyclableDrawable) staticThumbDrawable; drawable.recycle(); } - staticThumb = bitmap; - if (roundRadius != 0 && bitmap instanceof BitmapDrawable) { - Bitmap object = ((BitmapDrawable) bitmap).getBitmap(); - bitmapShaderThumb = new BitmapShader(object, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); - } else { - bitmapShaderThumb = null; + if (bitmap instanceof AnimatedFileDrawable) { + AnimatedFileDrawable fileDrawable = (AnimatedFileDrawable) bitmap; + fileDrawable.setParentView(parentView); + if (allowStartAnimation) { + fileDrawable.start(); + } + fileDrawable.setAllowDecodeSingleFrame(allowDecodeSingleFrame); } - currentThumbLocation = null; - currentKey = null; - currentExt = null; - currentThumbKey = null; - currentKeyQuality = false; - currentImage = null; - currentThumbFilter = null; + staticThumbDrawable = bitmap; + if (roundRadius != 0 && bitmap instanceof BitmapDrawable) { + if (bitmap instanceof AnimatedFileDrawable) { + ((AnimatedFileDrawable) bitmap).setRoundRadius(roundRadius); + } else { + Bitmap object = ((BitmapDrawable) bitmap).getBitmap(); + thumbShader = new BitmapShader(object, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); + } + } else { + thumbShader = null; + } + currentMediaLocation = null; + currentMediaDrawable = null; + currentMediaKey = null; + currentMediaFilter = null; + mediaShader = null; + currentImageLocation = null; - currentFilter = null; + currentImageDrawable = null; + currentImageKey = null; + currentImageFilter = null; + imageShader = null; + + currentThumbLocation = null; + currentThumbKey = null; + currentThumbFilter = null; + + currentKeyQuality = false; + currentExt = null; currentSize = 0; currentCacheType = 0; - bitmapShader = null; + currentAlpha = 1; + if (setImageBackup != null) { setImageBackup.fileLocation = null; setImageBackup.thumbLocation = null; + setImageBackup.mediaLocation = null; setImageBackup.thumb = null; } - currentAlpha = 1; + if (delegate != null) { - delegate.didSetImage(this, currentThumb != null || staticThumb != null, true); + delegate.didSetImage(this, currentThumbDrawable != null || staticThumbDrawable != null, true); } if (parentView != null) { if (invalidateAll) { @@ -524,25 +600,27 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg if (forceCrossfade && crossfadeWithOldImage && crossfadeImage != null) { currentAlpha = 0.0f; lastUpdateAlphaTime = System.currentTimeMillis(); - crossfadeWithThumb = currentThumb != null || staticThumb != null; + crossfadeWithThumb = currentThumbDrawable != null || staticThumbDrawable != null; } } public void clearImage() { - for (int a = 0; a < 3; a++) { + for (int a = 0; a < 4; a++) { recycleBitmap(null, a); } - ImageLoader.getInstance().cancelLoadingForImageReceiver(this, 0); + ImageLoader.getInstance().cancelLoadingForImageReceiver(this, true); } public void onDetachedFromWindow() { - if (currentImageLocation != null || currentThumbLocation != null || staticThumb != null) { + if (currentImageLocation != null || currentMediaLocation != null || currentThumbLocation != null || staticThumbDrawable != null) { if (setImageBackup == null) { setImageBackup = new SetImageBackup(); } + setImageBackup.mediaLocation = currentMediaLocation; + setImageBackup.mediaFilter = currentMediaFilter; setImageBackup.fileLocation = currentImageLocation; - setImageBackup.filter = currentFilter; - setImageBackup.thumb = staticThumb; + setImageBackup.filter = currentImageFilter; + setImageBackup.thumb = staticThumbDrawable; setImageBackup.thumbLocation = currentThumbLocation; setImageBackup.thumbFilter = currentThumbFilter; setImageBackup.size = currentSize; @@ -556,18 +634,17 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg public boolean onAttachedToWindow() { NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.didReplacedPhotoInMemCache); - if (setImageBackup != null && (setImageBackup.fileLocation != null || setImageBackup.thumbLocation != null || setImageBackup.thumb != null)) { - setImage(setImageBackup.fileLocation, setImageBackup.filter, setImageBackup.thumb, setImageBackup.thumbLocation, setImageBackup.thumbFilter, setImageBackup.size, setImageBackup.ext, setImageBackup.parentObject, setImageBackup.cacheType); + if (setImageBackup != null && (setImageBackup.fileLocation != null || setImageBackup.thumbLocation != null || setImageBackup.mediaLocation != null || setImageBackup.thumb != null)) { + setImage(setImageBackup.mediaLocation, setImageBackup.mediaFilter, setImageBackup.fileLocation, setImageBackup.filter, setImageBackup.thumb, setImageBackup.thumbLocation, setImageBackup.thumbFilter, setImageBackup.size, setImageBackup.ext, setImageBackup.parentObject, setImageBackup.cacheType); return true; } return false; } - private void drawDrawable(Canvas canvas, Drawable drawable, int alpha, BitmapShader shader, boolean thumb) { + private void drawDrawable(Canvas canvas, Drawable drawable, int alpha, BitmapShader shader, int orientation) { if (drawable instanceof BitmapDrawable) { BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; - int o = thumb ? thumbOrientation : orientation; Paint paint; if (shader != null) { paint = roundPaint; @@ -578,7 +655,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg if (hasFilter && isPressed == 0) { if (shader != null) { roundPaint.setColorFilter(null); - } else if (staticThumb != drawable) { + } else if (staticThumbDrawable != drawable) { bitmapDrawable.setColorFilter(null); } } else if (!hasFilter && isPressed != 0) { @@ -606,7 +683,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg int bitmapW; int bitmapH; if (bitmapDrawable instanceof AnimatedFileDrawable) { - if (o % 360 == 90 || o % 360 == 270) { + if (orientation % 360 == 90 || orientation % 360 == 270) { bitmapW = bitmapDrawable.getIntrinsicHeight(); bitmapH = bitmapDrawable.getIntrinsicWidth(); } else { @@ -614,7 +691,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg bitmapH = bitmapDrawable.getIntrinsicHeight(); } } else { - if (o % 360 == 90 || o % 360 == 270) { + if (orientation % 360 == 90 || orientation % 360 == 270) { bitmapW = bitmapDrawable.getBitmap().getHeight(); bitmapH = bitmapDrawable.getBitmap().getWidth(); } else { @@ -630,7 +707,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg float scale = Math.min(scaleW, scaleH); roundRect.set(imageX, imageY, imageX + imageW, imageY + imageH); shaderMatrix.reset(); - if (Math.abs(scaleW - scaleH) > 0.00001f) { + if (Math.abs(scaleW - scaleH) > 0.0005f) { if (bitmapW / scaleH > imageW) { drawRegion.set(imageX - ((int) (bitmapW / scaleH) - imageW) / 2, imageY, imageX + ((int) (bitmapW / scaleH) + imageW) / 2, imageY + imageH); } else { @@ -640,9 +717,9 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg drawRegion.set(imageX, imageY, imageX + imageW, imageY + imageH); } if (isVisible) { - if (Math.abs(scaleW - scaleH) > 0.00001f) { - int w = (int) Math.floor(imageW * scale); - int h = (int) Math.floor(imageH * scale); + if (Math.abs(scaleW - scaleH) > 0.0005f) { + int w = (int) Math.ceil(imageW * scale); + int h = (int) Math.ceil(imageH * scale); bitmapRect.set((bitmapW - w) / 2, (bitmapH - h) / 2, (bitmapW + w) / 2, (bitmapH + h) / 2); shaderMatrix.setRectToRect(bitmapRect, roundRect, Matrix.ScaleToFit.START); } else { @@ -666,14 +743,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg bitmapDrawable.setAlpha(alpha); bitmapDrawable.draw(canvas); } catch (Exception e) { - if (bitmapDrawable == currentImage && currentKey != null) { - ImageLoader.getInstance().removeImage(currentKey); - currentKey = null; - } else if (bitmapDrawable == currentThumb && currentThumbKey != null) { - ImageLoader.getInstance().removeImage(currentThumbKey); - currentThumbKey = null; - } - setImage(currentImageLocation, currentFilter, currentThumb, currentThumbLocation, currentThumbFilter, currentSize, currentExt, currentParentObject, currentCacheType); + onBitmapException(bitmapDrawable); FileLog.e(e); } } @@ -683,11 +753,11 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg canvas.save(); canvas.clipRect(imageX, imageY, imageX + imageW, imageY + imageH); - if (o % 360 != 0) { + if (orientation % 360 != 0) { if (centerRotation) { - canvas.rotate(o, imageW / 2, imageH / 2); + canvas.rotate(orientation, imageW / 2, imageH / 2); } else { - canvas.rotate(o, 0, 0); + canvas.rotate(orientation, 0, 0); } } @@ -701,7 +771,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg if (bitmapDrawable instanceof AnimatedFileDrawable) { ((AnimatedFileDrawable) bitmapDrawable).setActualDrawRect(imageX, imageY, imageW, imageH); } - if (o % 360 == 90 || o % 360 == 270) { + if (orientation % 360 == 90 || orientation % 360 == 270) { int width = (drawRegion.right - drawRegion.left) / 2; int height = (drawRegion.bottom - drawRegion.top) / 2; int centerX = (drawRegion.right + drawRegion.left) / 2; @@ -715,14 +785,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg bitmapDrawable.setAlpha(alpha); bitmapDrawable.draw(canvas); } catch (Exception e) { - if (bitmapDrawable == currentImage && currentKey != null) { - ImageLoader.getInstance().removeImage(currentKey); - currentKey = null; - } else if (bitmapDrawable == currentThumb && currentThumbKey != null) { - ImageLoader.getInstance().removeImage(currentThumbKey); - currentThumbKey = null; - } - setImage(currentImageLocation, currentFilter, currentThumb, currentThumbLocation, currentThumbFilter, currentSize, currentExt, currentParentObject, currentCacheType); + onBitmapException(bitmapDrawable); FileLog.e(e); } } @@ -730,18 +793,18 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg canvas.restore(); } else { canvas.save(); - if (o % 360 != 0) { + if (orientation % 360 != 0) { if (centerRotation) { - canvas.rotate(o, imageW / 2, imageH / 2); + canvas.rotate(orientation, imageW / 2, imageH / 2); } else { - canvas.rotate(o, 0, 0); + canvas.rotate(orientation, 0, 0); } } drawRegion.set(imageX, imageY, imageX + imageW, imageY + imageH); if (bitmapDrawable instanceof AnimatedFileDrawable) { ((AnimatedFileDrawable) bitmapDrawable).setActualDrawRect(imageX, imageY, imageW, imageH); } - if (o % 360 == 90 || o % 360 == 270) { + if (orientation % 360 == 90 || orientation % 360 == 270) { int width = (drawRegion.right - drawRegion.left) / 2; int height = (drawRegion.bottom - drawRegion.top) / 2; int centerX = (drawRegion.right + drawRegion.left) / 2; @@ -755,14 +818,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg bitmapDrawable.setAlpha(alpha); bitmapDrawable.draw(canvas); } catch (Exception e) { - if (bitmapDrawable == currentImage && currentKey != null) { - ImageLoader.getInstance().removeImage(currentKey); - currentKey = null; - } else if (bitmapDrawable == currentThumb && currentThumbKey != null) { - ImageLoader.getInstance().removeImage(currentThumbKey); - currentThumbKey = null; - } - setImage(currentImageLocation, currentFilter, currentThumb, currentThumbLocation, currentThumbFilter, currentSize, currentExt, currentParentObject, currentCacheType); + onBitmapException(bitmapDrawable); FileLog.e(e); } } @@ -784,6 +840,20 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg } } + private void onBitmapException(Drawable bitmapDrawable) { + if (bitmapDrawable == currentMediaDrawable && currentMediaKey != null) { + ImageLoader.getInstance().removeImage(currentMediaKey); + currentMediaKey = null; + } else if (bitmapDrawable == currentImageDrawable && currentImageKey != null) { + ImageLoader.getInstance().removeImage(currentImageKey); + currentImageKey = null; + } else if (bitmapDrawable == currentThumbDrawable && currentThumbKey != null) { + ImageLoader.getInstance().removeImage(currentThumbKey); + currentThumbKey = null; + } + setImage(currentMediaLocation, currentMediaFilter, currentImageLocation, currentImageFilter, currentThumbDrawable, currentThumbLocation, currentThumbFilter, currentSize, currentExt, currentParentObject, currentCacheType); + } + private void checkAlphaAnimation(boolean skip) { if (manualAlphaAnimator) { return; @@ -818,62 +888,76 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg public boolean draw(Canvas canvas) { try { Drawable drawable = null; - boolean animationNotReady = currentImage instanceof AnimatedFileDrawable && !((AnimatedFileDrawable) currentImage).hasBitmap(); - boolean isThumb = false; - BitmapShader customShader = null; - if (!forcePreview && currentImage != null && !animationNotReady) { - drawable = currentImage; + AnimatedFileDrawable animation = getAnimation(); + boolean animationNotReady = animation != null && !animation.hasBitmap(); + int orientation = 0; + BitmapShader shaderToUse = null; + if (!forcePreview && currentMediaDrawable != null && !animationNotReady) { + drawable = currentMediaDrawable; + shaderToUse = mediaShader; + orientation = imageOrientation; + } else if (!forcePreview && currentImageDrawable != null && (!animationNotReady || currentMediaDrawable != null)) { + drawable = currentImageDrawable; + shaderToUse = imageShader; + orientation = imageOrientation; + animationNotReady = false; } else if (crossfadeImage != null && !crossfadingWithThumb) { drawable = crossfadeImage; - customShader = crossfadeShader; - } else if (staticThumb instanceof BitmapDrawable) { - drawable = staticThumb; - isThumb = true; - } else if (currentThumb != null) { - drawable = currentThumb; - isThumb = true; + shaderToUse = crossfadeShader; + orientation = imageOrientation; + } else if (staticThumbDrawable instanceof BitmapDrawable) { + drawable = staticThumbDrawable; + shaderToUse = thumbShader; + orientation = thumbOrientation; + } else if (currentThumbDrawable != null) { + drawable = currentThumbDrawable; + shaderToUse = thumbShader; + orientation = thumbOrientation; } if (drawable != null) { if (crossfadeAlpha != 0) { if (crossfadeWithThumb && animationNotReady) { - drawDrawable(canvas, drawable, (int) (overrideAlpha * 255), bitmapShaderThumb, isThumb); + drawDrawable(canvas, drawable, (int) (overrideAlpha * 255), shaderToUse, orientation); } else { if (crossfadeWithThumb && currentAlpha != 1.0f) { Drawable thumbDrawable = null; - BitmapShader customThumbShader = null; - if (drawable == currentImage) { + BitmapShader thumbShaderToUse = null; + if (drawable == currentImageDrawable || drawable == currentMediaDrawable) { if (crossfadeImage != null) { thumbDrawable = crossfadeImage; - customThumbShader = crossfadeShader; - } else if (staticThumb != null) { - thumbDrawable = staticThumb; - } else if (currentThumb != null) { - thumbDrawable = currentThumb; + thumbShaderToUse = crossfadeShader; + } else if (currentThumbDrawable != null) { + thumbDrawable = currentThumbDrawable; + thumbShaderToUse = thumbShader; + } else if (staticThumbDrawable != null) { + thumbDrawable = staticThumbDrawable; + thumbShaderToUse = thumbShader; } - } else if (drawable == currentThumb || drawable == crossfadeImage) { - if (staticThumb != null) { - thumbDrawable = staticThumb; + } else if (drawable == currentThumbDrawable || drawable == crossfadeImage) { + if (staticThumbDrawable != null) { + thumbDrawable = staticThumbDrawable; + thumbShaderToUse = thumbShader; } - } else if (drawable == staticThumb) { + } else if (drawable == staticThumbDrawable) { if (crossfadeImage != null) { thumbDrawable = crossfadeImage; - customThumbShader = crossfadeShader; + thumbShaderToUse = crossfadeShader; } } if (thumbDrawable != null) { - drawDrawable(canvas, thumbDrawable, (int) (overrideAlpha * 255), customThumbShader != null ? customThumbShader : bitmapShaderThumb, true); + drawDrawable(canvas, thumbDrawable, (int) (overrideAlpha * 255), thumbShaderToUse, thumbOrientation); } } - drawDrawable(canvas, drawable, (int) (overrideAlpha * currentAlpha * 255), customShader != null ? customShader : (isThumb ? bitmapShaderThumb : bitmapShader), isThumb); + drawDrawable(canvas, drawable, (int) (overrideAlpha * currentAlpha * 255), shaderToUse, orientation); } } else { - drawDrawable(canvas, drawable, (int) (overrideAlpha * 255), customShader != null ? customShader : (isThumb ? bitmapShaderThumb : bitmapShader), isThumb); + drawDrawable(canvas, drawable, (int) (overrideAlpha * 255), shaderToUse, orientation); } checkAlphaAnimation(animationNotReady && crossfadeWithThumb); return true; - } else if (staticThumb != null) { - drawDrawable(canvas, staticThumb, (int) (overrideAlpha * 255), null, true); + } else if (staticThumbDrawable != null) { + drawDrawable(canvas, staticThumbDrawable, (int) (overrideAlpha * 255), null, thumbOrientation); checkAlphaAnimation(animationNotReady); return true; } else { @@ -893,33 +977,35 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg return currentAlpha; } - @Keep public void setCurrentAlpha(float value) { currentAlpha = value; } public Drawable getDrawable() { - if (currentImage != null) { - return currentImage; - } else if (currentThumb != null) { - return currentThumb; - } else if (staticThumb != null) { - return staticThumb; + if (currentMediaDrawable != null) { + return currentMediaDrawable; + } else if (currentImageDrawable != null) { + return currentImageDrawable; + } else if (currentThumbDrawable != null) { + return currentThumbDrawable; + } else if (staticThumbDrawable != null) { + return staticThumbDrawable; } return null; } public Bitmap getBitmap() { - if (currentImage instanceof AnimatedFileDrawable) { - return ((AnimatedFileDrawable) currentImage).getAnimatedBitmap(); - } else if (staticThumb instanceof AnimatedFileDrawable) { - return ((AnimatedFileDrawable) staticThumb).getAnimatedBitmap(); - } else if (currentImage instanceof BitmapDrawable) { - return ((BitmapDrawable) currentImage).getBitmap(); - } else if (currentThumb instanceof BitmapDrawable) { - return ((BitmapDrawable) currentThumb).getBitmap(); - } else if (staticThumb instanceof BitmapDrawable) { - return ((BitmapDrawable) staticThumb).getBitmap(); + AnimatedFileDrawable animation = getAnimation(); + if (animation != null && animation.hasBitmap()) { + return animation.getAnimatedBitmap(); + } else if (currentMediaDrawable instanceof BitmapDrawable && !(currentMediaDrawable instanceof AnimatedFileDrawable)) { + return ((BitmapDrawable) currentMediaDrawable).getBitmap(); + } else if (currentImageDrawable instanceof BitmapDrawable && !(currentImageDrawable instanceof AnimatedFileDrawable)) { + return ((BitmapDrawable) currentImageDrawable).getBitmap(); + } else if (currentThumbDrawable instanceof BitmapDrawable && !(currentThumbDrawable instanceof AnimatedFileDrawable)) { + return ((BitmapDrawable) currentThumbDrawable).getBitmap(); + } else if (staticThumbDrawable instanceof BitmapDrawable) { + return ((BitmapDrawable) staticThumbDrawable).getBitmap(); } return null; } @@ -927,18 +1013,20 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg public BitmapHolder getBitmapSafe() { Bitmap bitmap = null; String key = null; - if (currentImage instanceof AnimatedFileDrawable) { - bitmap = ((AnimatedFileDrawable) currentImage).getAnimatedBitmap(); - } else if (staticThumb instanceof AnimatedFileDrawable) { - bitmap = ((AnimatedFileDrawable) staticThumb).getAnimatedBitmap(); - } else if (currentImage instanceof BitmapDrawable) { - bitmap = ((BitmapDrawable) currentImage).getBitmap(); - key = currentKey; - } else if (currentThumb instanceof BitmapDrawable) { - bitmap = ((BitmapDrawable) currentThumb).getBitmap(); + AnimatedFileDrawable animation = getAnimation(); + if (animation != null && animation.hasBitmap()) { + bitmap = animation.getAnimatedBitmap(); + } else if (currentMediaDrawable instanceof BitmapDrawable && !(currentMediaDrawable instanceof AnimatedFileDrawable)) { + bitmap = ((BitmapDrawable) currentMediaDrawable).getBitmap(); + key = currentMediaKey; + } else if (currentImageDrawable instanceof BitmapDrawable && !(currentImageDrawable instanceof AnimatedFileDrawable)) { + bitmap = ((BitmapDrawable) currentImageDrawable).getBitmap(); + key = currentImageKey; + } else if (currentThumbDrawable instanceof BitmapDrawable && !(currentThumbDrawable instanceof AnimatedFileDrawable)) { + bitmap = ((BitmapDrawable) currentThumbDrawable).getBitmap(); key = currentThumbKey; - } else if (staticThumb instanceof BitmapDrawable) { - bitmap = ((BitmapDrawable) staticThumb).getBitmap(); + } else if (staticThumbDrawable instanceof BitmapDrawable) { + bitmap = ((BitmapDrawable) staticThumbDrawable).getBitmap(); } if (bitmap != null) { return new BitmapHolder(bitmap, key); @@ -947,10 +1035,10 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg } public Bitmap getThumbBitmap() { - if (currentThumb instanceof BitmapDrawable) { - return ((BitmapDrawable) currentThumb).getBitmap(); - } else if (staticThumb instanceof BitmapDrawable) { - return ((BitmapDrawable) staticThumb).getBitmap(); + if (currentThumbDrawable instanceof BitmapDrawable) { + return ((BitmapDrawable) currentThumbDrawable).getBitmap(); + } else if (staticThumbDrawable instanceof BitmapDrawable) { + return ((BitmapDrawable) staticThumbDrawable).getBitmap(); } return null; } @@ -958,11 +1046,11 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg public BitmapHolder getThumbBitmapSafe() { Bitmap bitmap = null; String key = null; - if (currentThumb instanceof BitmapDrawable) { - bitmap = ((BitmapDrawable) currentThumb).getBitmap(); + if (currentThumbDrawable instanceof BitmapDrawable) { + bitmap = ((BitmapDrawable) currentThumbDrawable).getBitmap(); key = currentThumbKey; - } else if (staticThumb instanceof BitmapDrawable) { - bitmap = ((BitmapDrawable) staticThumb).getBitmap(); + } else if (staticThumbDrawable instanceof BitmapDrawable) { + bitmap = ((BitmapDrawable) staticThumbDrawable).getBitmap(); } if (bitmap != null) { return new BitmapHolder(bitmap, key); @@ -971,35 +1059,33 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg } public int getBitmapWidth() { - if (currentImage instanceof AnimatedFileDrawable) { - return orientation % 360 == 0 || orientation % 360 == 180 ? currentImage.getIntrinsicWidth() : currentImage.getIntrinsicHeight(); - } else if (staticThumb instanceof AnimatedFileDrawable) { - return orientation % 360 == 0 || orientation % 360 == 180 ? staticThumb.getIntrinsicWidth() : staticThumb.getIntrinsicHeight(); + AnimatedFileDrawable animation = getAnimation(); + if (animation != null) { + return imageOrientation % 360 == 0 || imageOrientation % 360 == 180 ? animation.getIntrinsicWidth() : animation.getIntrinsicHeight(); } Bitmap bitmap = getBitmap(); if (bitmap == null) { - if (staticThumb != null) { - return staticThumb.getIntrinsicWidth(); + if (staticThumbDrawable != null) { + return staticThumbDrawable.getIntrinsicWidth(); } return 1; } - return orientation % 360 == 0 || orientation % 360 == 180 ? bitmap.getWidth() : bitmap.getHeight(); + return imageOrientation % 360 == 0 || imageOrientation % 360 == 180 ? bitmap.getWidth() : bitmap.getHeight(); } public int getBitmapHeight() { - if (currentImage instanceof AnimatedFileDrawable) { - return orientation % 360 == 0 || orientation % 360 == 180 ? currentImage.getIntrinsicHeight() : currentImage.getIntrinsicWidth(); - } else if (staticThumb instanceof AnimatedFileDrawable) { - return orientation % 360 == 0 || orientation % 360 == 180 ? staticThumb.getIntrinsicHeight() : staticThumb.getIntrinsicWidth(); + AnimatedFileDrawable animation = getAnimation(); + if (animation != null) { + return imageOrientation % 360 == 0 || imageOrientation % 360 == 180 ? animation.getIntrinsicHeight() : animation.getIntrinsicWidth(); } Bitmap bitmap = getBitmap(); if (bitmap == null) { - if (staticThumb != null) { - return staticThumb.getIntrinsicHeight(); + if (staticThumbDrawable != null) { + return staticThumbDrawable.getIntrinsicHeight(); } return 1; } - return orientation % 360 == 0 || orientation % 360 == 180 ? bitmap.getHeight() : bitmap.getWidth(); + return imageOrientation % 360 == 0 || imageOrientation % 360 == 180 ? bitmap.getHeight() : bitmap.getWidth(); } public void setVisible(boolean value, boolean invalidate) { @@ -1028,20 +1114,20 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg crossfadeAlpha = value; } - public boolean hasImage() { - return currentImage != null || currentThumb != null || currentKey != null || staticThumb != null; + public boolean hasImageSet() { + return currentImageDrawable != null || currentMediaDrawable != null || currentThumbDrawable != null || staticThumbDrawable != null || currentImageKey != null || currentMediaKey != null; } public boolean hasBitmapImage() { - return currentImage != null || currentThumb != null || staticThumb != null; + return currentImageDrawable != null || currentThumbDrawable != null || staticThumbDrawable != null || currentMediaDrawable != null; } public boolean hasNotThumb() { - return currentImage != null; + return currentImageDrawable != null || currentMediaDrawable != null; } public boolean hasStaticThumb() { - return staticThumb != null; + return staticThumbDrawable != null; } public void setAspectFit(boolean value) { @@ -1054,9 +1140,9 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg public void setParentView(View view) { parentView = view; - if (currentImage instanceof AnimatedFileDrawable) { - AnimatedFileDrawable fileDrawable = (AnimatedFileDrawable) currentImage; - fileDrawable.setParentView(parentView); + AnimatedFileDrawable animation = getAnimation(); + if (animation != null) { + animation.setParentView(parentView); } } @@ -1112,7 +1198,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg } public float getImageAspectRatio() { - return orientation % 180 != 0 ? drawRegion.height() / (float) drawRegion.width() : drawRegion.width() / (float) drawRegion.height(); + return imageOrientation % 180 != 0 ? drawRegion.height() / (float) drawRegion.width() : drawRegion.width() / (float) drawRegion.height(); } public String getExt() { @@ -1127,26 +1213,38 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg return drawRegion; } - public String getFilter() { - return currentFilter; + public String getImageKey() { + return currentImageKey; } - public String getThumbFilter() { - return currentThumbFilter; + public String getImageFilter() { + return currentImageFilter; } - public String getKey() { - return currentKey; + public String getMediaKey() { + return currentMediaKey; + } + + public String getMediaFilter() { + return currentMediaFilter; } public String getThumbKey() { return currentThumbKey; } + public String getThumbFilter() { + return currentThumbFilter; + } + public int getSize() { return currentSize; } + public Object getMediaLocation() { + return currentMediaLocation; + } + public Object getImageLocation() { return currentImageLocation; } @@ -1191,6 +1289,14 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg needsQualityThumb = value; } + public void setQualityThumbDocument(TLRPC.Document document) { + qulityThumbDocument = document; + } + + public TLRPC.Document getQulityThumbDocument() { + return qulityThumbDocument; + } + public void setCrossfadeWithOldImage(boolean value) { crossfadeWithOldImage = value; } @@ -1228,38 +1334,55 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg } public void startAnimation() { - if (currentImage instanceof AnimatedFileDrawable) { - ((AnimatedFileDrawable) currentImage).start(); + AnimatedFileDrawable animation = getAnimation(); + if (animation != null) { + animation.start(); } } public void stopAnimation() { - if (currentImage instanceof AnimatedFileDrawable) { - ((AnimatedFileDrawable) currentImage).stop(); + AnimatedFileDrawable animation = getAnimation(); + if (animation != null) { + animation.stop(); } } public boolean isAnimationRunning() { - return currentImage instanceof AnimatedFileDrawable && ((AnimatedFileDrawable) currentImage).isRunning(); + AnimatedFileDrawable animation = getAnimation(); + return animation != null && animation.isRunning(); } public AnimatedFileDrawable getAnimation() { - return currentImage instanceof AnimatedFileDrawable ? (AnimatedFileDrawable) currentImage : null; + AnimatedFileDrawable animatedFileDrawable; + if (currentMediaDrawable instanceof AnimatedFileDrawable) { + return (AnimatedFileDrawable) currentMediaDrawable; + } else if (currentImageDrawable instanceof AnimatedFileDrawable) { + return (AnimatedFileDrawable) currentImageDrawable; + } else if (currentThumbDrawable instanceof AnimatedFileDrawable) { + return (AnimatedFileDrawable) currentThumbDrawable; + } else if (staticThumbDrawable instanceof AnimatedFileDrawable) { + return (AnimatedFileDrawable) staticThumbDrawable; + } + return null; } - protected int getTag(boolean thumb) { - if (thumb) { + protected int getTag(int type) { + if (type == TYPE_THUMB) { return thumbTag; + } else if (type == TYPE_MEDIA) { + return mediaTag; } else { - return tag; + return imageTag; } } - protected void setTag(int value, boolean thumb) { - if (thumb) { + protected void setTag(int value, int type) { + if (type == TYPE_THUMB) { thumbTag = value; + } else if (type == TYPE_MEDIA) { + mediaTag = value; } else { - tag = value; + imageTag = value; } } @@ -1271,65 +1394,94 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg return param; } - protected boolean setImageBitmapByKey(BitmapDrawable bitmap, String key, boolean thumb, boolean memCache) { + protected boolean setImageBitmapByKey(BitmapDrawable bitmap, String key, int type, boolean memCache) { if (bitmap == null || key == null) { return false; } - if (!thumb) { - if (!key.equals(currentKey)) { + if (type == TYPE_IMAGE) { + if (!key.equals(currentImageKey)) { return false; } if (!(bitmap instanceof AnimatedFileDrawable)) { - ImageLoader.getInstance().incrementUseCount(currentKey); + ImageLoader.getInstance().incrementUseCount(currentImageKey); } - currentImage = bitmap; + currentImageDrawable = bitmap; if (bitmap instanceof ExtendedBitmapDrawable) { - orientation = ((ExtendedBitmapDrawable) bitmap).getOrientation(); + imageOrientation = ((ExtendedBitmapDrawable) bitmap).getOrientation(); } if (roundRadius != 0 && bitmap instanceof BitmapDrawable) { if (bitmap instanceof AnimatedFileDrawable) { ((AnimatedFileDrawable) bitmap).setRoundRadius(roundRadius); } else { Bitmap object = bitmap.getBitmap(); - bitmapShader = new BitmapShader(object, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); + imageShader = new BitmapShader(object, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); } } else { - bitmapShader = null; + imageShader = null; } if (!memCache && !forcePreview || forceCrossfade) { - if (currentThumb == null && staticThumb == null || currentAlpha == 1.0f || forceCrossfade) { + boolean allowCorssfade = true; + if (currentMediaDrawable instanceof AnimatedFileDrawable && ((AnimatedFileDrawable) currentMediaDrawable).hasBitmap()) { + allowCorssfade = false; + } + if (allowCorssfade && (currentThumbDrawable == null && staticThumbDrawable == null || currentAlpha == 1.0f || forceCrossfade)) { currentAlpha = 0.0f; lastUpdateAlphaTime = System.currentTimeMillis(); - crossfadeWithThumb = crossfadeImage != null || currentThumb != null || staticThumb != null; + crossfadeWithThumb = crossfadeImage != null || currentThumbDrawable != null || staticThumbDrawable != null; } } else { currentAlpha = 1.0f; } - if (bitmap instanceof AnimatedFileDrawable) { - AnimatedFileDrawable fileDrawable = (AnimatedFileDrawable) bitmap; - fileDrawable.setParentView(parentView); - if (allowStartAnimation) { - fileDrawable.start(); + } else if (type == TYPE_MEDIA) { + if (!key.equals(currentMediaKey)) { + return false; + } + if (!(bitmap instanceof AnimatedFileDrawable)) { + ImageLoader.getInstance().incrementUseCount(currentMediaKey); + } + currentMediaDrawable = bitmap; + if (roundRadius != 0 && bitmap instanceof BitmapDrawable) { + if (bitmap instanceof AnimatedFileDrawable) { + ((AnimatedFileDrawable) bitmap).setRoundRadius(roundRadius); } else { - fileDrawable.setAllowDecodeSingleFrame(allowDecodeSingleFrame); + Bitmap object = bitmap.getBitmap(); + mediaShader = new BitmapShader(object, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); } + } else { + mediaShader = null; } - if (parentView != null) { - if (invalidateAll) { - parentView.invalidate(); + if (currentImageDrawable == null) { + if (!memCache && !forcePreview || forceCrossfade) { + if (currentThumbDrawable == null && staticThumbDrawable == null || currentAlpha == 1.0f || forceCrossfade) { + currentAlpha = 0.0f; + lastUpdateAlphaTime = System.currentTimeMillis(); + crossfadeWithThumb = crossfadeImage != null || currentThumbDrawable != null || staticThumbDrawable != null; + } } else { - parentView.invalidate(imageX, imageY, imageX + imageW, imageY + imageH); + currentAlpha = 1.0f; + } + } + } else if (type == TYPE_THUMB) { + if (currentThumbDrawable != null) { + return false; + } + if (!forcePreview) { + AnimatedFileDrawable animation = getAnimation(); + if (animation != null && animation.hasBitmap()) { + return false; + } + if (currentImageDrawable != null && !(currentImageDrawable instanceof AnimatedFileDrawable) || currentMediaDrawable != null && !(currentMediaDrawable instanceof AnimatedFileDrawable)) { + return false; } } - } else if (currentThumb == null && (currentImage == null || (currentImage instanceof AnimatedFileDrawable && !((AnimatedFileDrawable) currentImage).hasBitmap()) || forcePreview)) { if (!key.equals(currentThumbKey)) { return false; } ImageLoader.getInstance().incrementUseCount(currentThumbKey); - currentThumb = bitmap; + currentThumbDrawable = bitmap; if (bitmap instanceof ExtendedBitmapDrawable) { thumbOrientation = ((ExtendedBitmapDrawable) bitmap).getOrientation(); } @@ -1339,10 +1491,10 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg ((AnimatedFileDrawable) bitmap).setRoundRadius(roundRadius); } else { Bitmap object = bitmap.getBitmap(); - bitmapShaderThumb = new BitmapShader(object, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); + thumbShader = new BitmapShader(object, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); } } else { - bitmapShaderThumb = null; + thumbShader = null; } if (!memCache && crossfadeAlpha != 2) { @@ -1351,23 +1503,29 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg } else { currentAlpha = 0.0f; lastUpdateAlphaTime = System.currentTimeMillis(); - crossfadeWithThumb = staticThumb != null && currentKey == null; + crossfadeWithThumb = staticThumbDrawable != null && currentImageKey == null && currentMediaKey == null; } } else { currentAlpha = 1.0f; } - - if (!(staticThumb instanceof BitmapDrawable) && parentView != null) { - if (invalidateAll) { - parentView.invalidate(); - } else { - parentView.invalidate(imageX, imageY, imageX + imageW, imageY + imageH); - } + } + if (bitmap instanceof AnimatedFileDrawable) { + AnimatedFileDrawable fileDrawable = (AnimatedFileDrawable) bitmap; + fileDrawable.setParentView(parentView); + if (allowStartAnimation) { + fileDrawable.start(); + } + fileDrawable.setAllowDecodeSingleFrame(allowDecodeSingleFrame); + } + if (parentView != null) { + if (invalidateAll) { + parentView.invalidate(); + } else { + parentView.invalidate(imageX, imageY, imageX + imageW, imageY + imageH); } } - if (delegate != null) { - delegate.didSetImage(this, currentImage != null || currentThumb != null || staticThumb != null, currentImage == null); + delegate.didSetImage(this, currentImageDrawable != null || currentThumbDrawable != null || staticThumbDrawable != null || currentMediaDrawable != null, currentImageDrawable == null && currentMediaDrawable == null); } return true; } @@ -1375,15 +1533,18 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg private void recycleBitmap(String newKey, int type) { String key; Drawable image; - if (type == 2) { + if (type == TYPE_MEDIA) { + key = currentMediaKey; + image = currentMediaDrawable; + } else if (type == TYPE_CROSSFDADE) { key = crossfadeKey; image = crossfadeImage; - } else if (type == 1) { + } else if (type == TYPE_THUMB) { key = currentThumbKey; - image = currentThumb; + image = currentThumbDrawable; } else { - key = currentKey; - image = currentImage; + key = currentImageKey; + image = currentImageDrawable; } if (key != null && key.startsWith("-")) { String replacedKey = ImageLoader.getInstance().getReplacedKey(key); @@ -1406,15 +1567,18 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg } } } - if (type == 2) { + if (type == TYPE_MEDIA) { + currentMediaKey = null; + currentMediaDrawable = null; + } else if (type == TYPE_CROSSFDADE) { crossfadeKey = null; crossfadeImage = null; - } else if (type == 1) { - currentThumb = null; + } else if (type == TYPE_THUMB) { + currentThumbDrawable = null; currentThumbKey = null; } else { - currentImage = null; - currentKey = null; + currentImageDrawable = null; + currentImageKey = null; } } @@ -1422,22 +1586,25 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg public void didReceivedNotification(int id, int account, Object... args) { if (id == NotificationCenter.didReplacedPhotoInMemCache) { String oldKey = (String) args[0]; - if (currentKey != null && currentKey.equals(oldKey)) { - currentKey = (String) args[1]; + if (currentMediaKey != null && currentMediaKey.equals(oldKey)) { + currentMediaKey = (String) args[1]; + currentMediaLocation = args[2]; + if (setImageBackup != null) { + setImageBackup.mediaLocation = args[2]; + } + } + if (currentImageKey != null && currentImageKey.equals(oldKey)) { + currentImageKey = (String) args[1]; currentImageLocation = args[2]; + if (setImageBackup != null) { + setImageBackup.fileLocation = args[2]; + } } if (currentThumbKey != null && currentThumbKey.equals(oldKey)) { currentThumbKey = (String) args[1]; currentThumbLocation = args[2]; - } - if (setImageBackup != null) { - if (currentKey != null && currentKey.equals(oldKey)) { - currentKey = (String) args[1]; - currentImageLocation = args[2]; - } - if (currentThumbKey != null && currentThumbKey.equals(oldKey)) { - currentThumbKey = (String) args[1]; - currentThumbLocation = args[2]; + if (setImageBackup != null) { + setImageBackup.thumbLocation = args[2]; } } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java index 28bd9ef7d..45113b75a 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java @@ -366,6 +366,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, private float seekToProgressPending; private long lastProgress = 0; private MessageObject playingMessageObject; + private MessageObject goingToShowMessageObject; private Timer progressTimer = null; private final Object progressTimerSync = new Object(); private ArrayList playlist = new ArrayList<>(); @@ -376,6 +377,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, private boolean playMusicAgain; private AudioInfo audioInfo; private VideoPlayer videoPlayer; + private boolean playerWasReady; private TextureView currentTextureView; private PipRoundVideoView pipRoundVideoView; private int pipSwitchingState; @@ -389,6 +391,16 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, private float currentAspectRatioFrameLayoutRatio; private boolean currentAspectRatioFrameLayoutReady; + private Runnable setLoadingRunnable = new Runnable() { + @Override + public void run() { + if (playingMessageObject == null) { + return; + } + FileLoader.getInstance(playingMessageObject.currentAccount).setLoadingVideo(playingMessageObject.getDocument(), true, false); + } + }; + private AudioRecord audioRecorder; private TLRPC.TL_document recordingAudio; private int recordingCurrentAccount; @@ -869,11 +881,11 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, if (videoPlayer != null) { duration = videoPlayer.getDuration(); progress = videoPlayer.getCurrentPosition(); - bufferedValue = videoPlayer.getBufferedPosition() / (float) duration; - value = duration >= 0 ? progress / (float) duration : 0.0f; - if (progress < 0) { + if (duration == C.TIME_UNSET || progress == C.TIME_UNSET || progress < 0 || duration <= 0) { return; } + bufferedValue = videoPlayer.getBufferedPosition() / (float) duration; + value = duration >= 0 ? progress / (float) duration : 0.0f; if (value >= 1) { return; } @@ -1420,7 +1432,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, if (!paused) { videoPlayer.play(); } else { - videoPlayer.pause(); + pauseMessage(playingMessageObject); } } else { boolean post = audioPlayer != null; @@ -1519,13 +1531,13 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, } public void cleanupPlayer(boolean notify, boolean stopService) { - cleanupPlayer(notify, stopService, false); + cleanupPlayer(notify, stopService, false, false); } - public void cleanupPlayer(boolean notify, boolean stopService, boolean byVoiceEnd) { + public void cleanupPlayer(boolean notify, boolean stopService, boolean byVoiceEnd, boolean transferPlayerToPhotoViewer) { if (audioPlayer != null) { try { - audioPlayer.releasePlayer(); + audioPlayer.releasePlayer(true); } catch (Exception e) { FileLog.e(e); } @@ -1534,14 +1546,31 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, currentAspectRatioFrameLayout = null; currentTextureViewContainer = null; currentAspectRatioFrameLayoutReady = false; + isDrawingWasReady = false; currentTextureView = null; - videoPlayer.releasePlayer(); - videoPlayer = null; + goingToShowMessageObject = null; + if (transferPlayerToPhotoViewer) { + PhotoViewer.getInstance().injectVideoPlayer(videoPlayer); + goingToShowMessageObject = playingMessageObject; + NotificationCenter.getInstance(playingMessageObject.currentAccount).postNotificationName(NotificationCenter.messagePlayingGoingToStop, playingMessageObject, true); + } else { + long position = videoPlayer.getCurrentPosition(); + if (playingMessageObject != null && playingMessageObject.isVideo() && position > 0 && position != C.TIME_UNSET) { + playingMessageObject.audioProgressMs = (int) position; + NotificationCenter.getInstance(playingMessageObject.currentAccount).postNotificationName(NotificationCenter.messagePlayingGoingToStop, playingMessageObject, false); + } + videoPlayer.releasePlayer(true); + videoPlayer = null; + } try { baseActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } catch (Exception e) { FileLog.e(e); } + if (playingMessageObject != null && !transferPlayerToPhotoViewer) { + AndroidUtilities.cancelRunOnUIThread(setLoadingRunnable); + FileLoader.getInstance(playingMessageObject.currentAccount).removeLoadingVideo(playingMessageObject.getDocument(), true, false); + } } stopProgressTimer(); lastProgress = 0; @@ -1606,6 +1635,14 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, } } + public boolean isGoingToShowMessageObject(MessageObject messageObject) { + return goingToShowMessageObject == messageObject; + } + + public void resetGoingToShowMessageObject() { + goingToShowMessageObject = null; + } + private boolean isSamePlayingMessage(MessageObject messageObject) { return playingMessageObject != null && playingMessageObject.getDialogId() == messageObject.getDialogId() && playingMessageObject.getId() == messageObject.getId() && ((playingMessageObject.eventId == 0) == (messageObject.eventId == 0)); } @@ -1754,7 +1791,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, if (audioPlayer != null || videoPlayer != null) { if (audioPlayer != null) { try { - audioPlayer.releasePlayer(); + audioPlayer.releasePlayer(true); } catch (Exception e) { FileLog.e(e); } @@ -1764,13 +1801,15 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, currentTextureViewContainer = null; currentAspectRatioFrameLayoutReady = false; currentTextureView = null; - videoPlayer.releasePlayer(); + videoPlayer.releasePlayer(true); videoPlayer = null; try { baseActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } catch (Exception e) { FileLog.e(e); } + AndroidUtilities.cancelRunOnUIThread(setLoadingRunnable); + FileLoader.getInstance(playingMessageObject.currentAccount).removeLoadingVideo(playingMessageObject.getDocument(), true, false); } stopProgressTimer(); lastProgress = 0; @@ -1848,7 +1887,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, } private void checkIsNextMusicFileDownloaded(int currentAccount) { - if ((DownloadController.getInstance(currentAccount).getCurrentDownloadMask() & DownloadController.AUTODOWNLOAD_MASK_MUSIC) == 0) { + if (!DownloadController.getInstance(currentAccount).canDownloadNextTrack()) { return; } ArrayList currentPlayList = SharedConfig.shuffleMusic ? shuffledPlaylist : playlist; @@ -1872,9 +1911,6 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, } MessageObject nextAudio = currentPlayList.get(nextIndex); - if (!DownloadController.getInstance(currentAccount).canDownloadMedia(nextAudio)) { - return; - } File file = null; if (!TextUtils.isEmpty(nextAudio.messageOwner.attachPath)) { file = new File(nextAudio.messageOwner.attachPath); @@ -1926,7 +1962,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, } } - public void setCurrentRoundVisible(boolean visible) { + public void setCurrentVideoVisible(boolean visible) { if (currentAspectRatioFrameLayout == null) { return; } @@ -1988,9 +2024,9 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, if (currentAspectRatioFrameLayout != null) { currentAspectRatioFrameLayout.setAspectRatio(currentAspectRatioFrameLayoutRatio, currentAspectRatioFrameLayoutRotation); } - if (currentTextureViewContainer.getVisibility() != View.VISIBLE) { - currentTextureViewContainer.setVisibility(View.VISIBLE); - } + //if (currentTextureViewContainer.getVisibility() != View.VISIBLE) { + // currentTextureViewContainer.setVisibility(View.VISIBLE); + //} } } @@ -2082,7 +2118,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, } } final File cacheFile = file != null ? file : FileLoader.getPathToMessage(messageObject.messageOwner); - boolean canStream = SharedConfig.streamMedia && messageObject.isMusic() && (int) messageObject.getDialogId() != 0; + boolean canStream = SharedConfig.streamMedia && (messageObject.isMusic() || messageObject.isVideo() && messageObject.canStreamVideo()) && (int) messageObject.getDialogId() != 0; if (cacheFile != null && cacheFile != file && !(exists = cacheFile.exists()) && !canStream) { FileLoader.getInstance(messageObject.currentAccount).loadFile(messageObject.getDocument(), messageObject, 0, 0); downloadingCurrentMessage = true; @@ -2120,7 +2156,14 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, isDrawingWasReady = false; currentAspectRatioFrameLayout.setDrawingReady(false); } - if (messageObject.isRoundVideo()) { + boolean isVideo = messageObject.isVideo(); + if (messageObject.isRoundVideo() || isVideo) { + if (isVideo) { + FileLoader.getInstance(messageObject.currentAccount).setLoadingVideoForPlayer(messageObject.getDocument(), true); + } + playerWasReady = false; + boolean destroyAtEnd = !isVideo || messageObject.messageOwner.to_id.channel_id == 0 && messageObject.audioProgress <= 0.1f; + int[] playCount = isVideo && messageObject.getDuration() <= 30 ? new int[]{1} : null; playlist.clear(); shuffledPlaylist.clear(); videoPlayer = new VideoPlayer(); @@ -2144,12 +2187,29 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, } } if (playbackState == ExoPlayer.STATE_READY) { + playerWasReady = true; + if (playingMessageObject != null && playingMessageObject.isVideo()) { + AndroidUtilities.cancelRunOnUIThread(setLoadingRunnable); + FileLoader.getInstance(messageObject.currentAccount).removeLoadingVideo(playingMessageObject.getDocument(), true, false); + } currentAspectRatioFrameLayoutReady = true; - if (currentTextureViewContainer != null && currentTextureViewContainer.getVisibility() != View.VISIBLE) { - currentTextureViewContainer.setVisibility(View.VISIBLE); + } else if (playbackState == ExoPlayer.STATE_BUFFERING) { + if (playWhenReady && playingMessageObject != null && playingMessageObject.isVideo()) { + if (playerWasReady) { + setLoadingRunnable.run(); + } else { + AndroidUtilities.runOnUIThread(setLoadingRunnable, 1000); + } } } else if (videoPlayer.isPlaying() && playbackState == ExoPlayer.STATE_ENDED) { - cleanupPlayer(true, true, true); + if (playingMessageObject.isVideo() && !destroyAtEnd && (playCount == null || playCount[0] < 4)) { + videoPlayer.seekTo(0); + if (playCount != null) { + playCount[0]++; + } + } else { + cleanupPlayer(true, true, true, false); + } } } @@ -2178,9 +2238,10 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, if (currentAspectRatioFrameLayout != null && !currentAspectRatioFrameLayout.isDrawingReady()) { isDrawingWasReady = true; currentAspectRatioFrameLayout.setDrawingReady(true); - if (currentTextureViewContainer != null && currentTextureViewContainer.getVisibility() != View.VISIBLE) { - currentTextureViewContainer.setVisibility(View.VISIBLE); - } + currentTextureViewContainer.setTag(1); + //if (currentTextureViewContainer != null && currentTextureViewContainer.getVisibility() != View.VISIBLE) { + // currentTextureViewContainer.setVisibility(View.VISIBLE); + //} } } @@ -2223,6 +2284,9 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, } pipSwitchingState = 0; return true; + } else if (PhotoViewer.hasInstance() && PhotoViewer.getInstance().isInjectingVideoPlayer()) { + PhotoViewer.getInstance().injectVideoPlayerSurface(surfaceTexture); + return true; } return false; } @@ -2248,12 +2312,39 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, } else if (currentTextureView != null) { videoPlayer.setTextureView(currentTextureView); } - videoPlayer.preparePlayer(Uri.fromFile(cacheFile), "other"); - videoPlayer.setStreamType(useFrontSpeaker ? AudioManager.STREAM_VOICE_CALL : AudioManager.STREAM_MUSIC); - if (currentPlaybackSpeed > 1.0f) { - videoPlayer.setPlaybackSpeed(currentPlaybackSpeed); + + if (exists) { + if (!messageObject.mediaExists && cacheFile != file) { + AndroidUtilities.runOnUIThread(() -> NotificationCenter.getInstance(messageObject.currentAccount).postNotificationName(NotificationCenter.fileDidLoad, FileLoader.getAttachFileName(messageObject.getDocument()))); + } + videoPlayer.preparePlayer(Uri.fromFile(cacheFile), "other"); + } else { + try { + int reference = FileLoader.getInstance(messageObject.currentAccount).getFileReference(messageObject); + TLRPC.Document document = messageObject.getDocument(); + String params = "?account=" + messageObject.currentAccount + + "&id=" + document.id + + "&hash=" + document.access_hash + + "&dc=" + document.dc_id + + "&size=" + document.size + + "&mime=" + URLEncoder.encode(document.mime_type, "UTF-8") + + "&rid=" + reference + + "&name=" + URLEncoder.encode(FileLoader.getDocumentFileName(document), "UTF-8") + + "&reference=" + Utilities.bytesToHex(document.file_reference != null ? document.file_reference : new byte[0]); + Uri uri = Uri.parse("tg://" + messageObject.getFileName() + params); + videoPlayer.preparePlayer(uri, "other"); + } catch (Exception e) { + FileLog.e(e); + } + } + if (messageObject.isRoundVideo()) { + videoPlayer.setStreamType(useFrontSpeaker ? AudioManager.STREAM_VOICE_CALL : AudioManager.STREAM_MUSIC); + if (currentPlaybackSpeed > 1.0f) { + videoPlayer.setPlaybackSpeed(currentPlaybackSpeed); + } + } else { + videoPlayer.setStreamType(AudioManager.STREAM_MUSIC); } - videoPlayer.play(); } else { if (pipRoundVideoView != null) { pipRoundVideoView.close(true); @@ -2268,7 +2359,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, if (!playlist.isEmpty() && playlist.size() > 1) { playNextMessageWithoutOrder(true); } else { - cleanupPlayer(true, true, messageObject != null && messageObject.isVoice()); + cleanupPlayer(true, true, messageObject != null && messageObject.isVoice(), false); } } else if (seekToProgressPending != 0 && (playbackState == ExoPlayer.STATE_READY || playbackState == ExoPlayer.STATE_IDLE)) { int seekTo = (int) (audioPlayer.getDuration() * seekToProgressPending); @@ -2343,7 +2434,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, FileLog.e(e); NotificationCenter.getInstance(messageObject.currentAccount).postNotificationName(NotificationCenter.messagePlayingPlayStateChanged, playingMessageObject != null ? playingMessageObject.getId() : 0); if (audioPlayer != null) { - audioPlayer.releasePlayer(); + audioPlayer.releasePlayer(true); audioPlayer = null; isPaused = false; playingMessageObject = null; @@ -2367,11 +2458,15 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, if (videoPlayer != null) { try { if (playingMessageObject.audioProgress != 0) { - long duration = audioPlayer.getDuration(); + long duration = videoPlayer.getDuration(); if (duration == C.TIME_UNSET) { duration = (long) playingMessageObject.getDuration() * 1000; } int seekTo = (int) (duration * playingMessageObject.audioProgress); + if (playingMessageObject.audioProgressMs != 0) { + seekTo = playingMessageObject.audioProgressMs; + playingMessageObject.audioProgressMs = 0; + } videoPlayer.seekTo(seekTo); } } catch (Exception e2) { @@ -2380,6 +2475,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, NotificationCenter.getInstance(messageObject.currentAccount).postNotificationName(NotificationCenter.messagePlayingProgressDidChanged, playingMessageObject.getId(), 0); FileLog.e(e2); } + videoPlayer.play(); } else if (audioPlayer != null) { try { if (playingMessageObject.audioProgress != 0) { @@ -2397,7 +2493,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, } } - if (playingMessageObject.isMusic()) { + if (playingMessageObject != null && playingMessageObject.isMusic()) { Intent intent = new Intent(ApplicationLoader.applicationContext, MusicPlayerService.class); try { /*if (Build.VERSION.SDK_INT >= 26) { @@ -2416,48 +2512,6 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, return true; } - public void stopAudio() { - if (audioPlayer == null && videoPlayer == null || playingMessageObject == null) { - return; - } - try { - if (audioPlayer != null) { - audioPlayer.pause(); - } else if (videoPlayer != null) { - videoPlayer.pause(); - } - } catch (Exception e) { - FileLog.e(e); - } - try { - if (audioPlayer != null) { - audioPlayer.releasePlayer(); - audioPlayer = null; - } else if (videoPlayer != null) { - currentAspectRatioFrameLayout = null; - currentTextureViewContainer = null; - currentAspectRatioFrameLayoutReady = false; - currentTextureView = null; - videoPlayer.releasePlayer(); - videoPlayer = null; - try { - baseActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - } catch (Exception e) { - FileLog.e(e); - } - } - } catch (Exception e) { - FileLog.e(e); - } - stopProgressTimer(); - playingMessageObject = null; - downloadingCurrentMessage = false; - isPaused = false; - - Intent intent = new Intent(ApplicationLoader.applicationContext, MusicPlayerService.class); - ApplicationLoader.applicationContext.stopService(intent); - } - public AudioInfo getAudioInfo() { return audioInfo; } @@ -2529,7 +2583,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, return true; } - public boolean isRoundVideoDrawingReady() { + public boolean isVideoDrawingReady() { return currentAspectRatioFrameLayout != null && currentAspectRatioFrameLayout.isDrawingReady(); } @@ -2551,6 +2605,10 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, return false; } + public boolean isPlayingMessageAndReadyToDraw(MessageObject messageObject) { + return isDrawingWasReady && isPlayingMessage(messageObject); + } + public boolean isMessagePaused() { return isPaused || downloadingCurrentMessage; } @@ -3370,7 +3428,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, return -5; } - private void didWriteData(final MessageObject messageObject, final File file, final boolean last, final boolean error) { + private void didWriteData(final MessageObject messageObject, final File file, final boolean last, long availableSize, final boolean error) { final boolean firstWrite = videoConvertFirstWrite; if (firstWrite) { videoConvertFirstWrite = false; @@ -3389,7 +3447,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, if (firstWrite) { NotificationCenter.getInstance(messageObject.currentAccount).postNotificationName(NotificationCenter.filePreparingStarted, messageObject, file.toString()); } - NotificationCenter.getInstance(messageObject.currentAccount).postNotificationName(NotificationCenter.fileNewChunkAvailable, messageObject, file.toString(), file.length(), last ? file.length() : 0); + NotificationCenter.getInstance(messageObject.currentAccount).postNotificationName(NotificationCenter.fileNewChunkAvailable, messageObject, file.toString(), availableSize, last ? file.length() : 0); } }); } @@ -3481,8 +3539,9 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, if (end < 0 || info.presentationTimeUs < end) { info.offset = 0; info.flags = extractor.getSampleFlags(); - if (mediaMuxer.writeSampleData(muxerTrackIndex, buffer, info, false)) { - didWriteData(messageObject, file, false, false); + long availableSize = mediaMuxer.writeSampleData(muxerTrackIndex, buffer, info, false); + if (availableSize != 0) { + didWriteData(messageObject, file, false, availableSize, false); } } else { eof = true; @@ -3600,7 +3659,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, boolean isPreviousOk = preferences.getBoolean("isPreviousOk", true); preferences.edit().putBoolean("isPreviousOk", false).commit(); if (!inputFile.canRead() || !isPreviousOk) { - didWriteData(messageObject, cacheFile, true, true); + didWriteData(messageObject, cacheFile, true, 0, true); preferences.edit().putBoolean("isPreviousOk", true).commit(); return false; } @@ -3814,8 +3873,9 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, if (info.size > 0 && (endTime < 0 || info.presentationTimeUs < endTime)) { info.offset = 0; info.flags = extractor.getSampleFlags(); - if (mediaMuxer.writeSampleData(audioTrackIndex, audioBuffer, info, false)) { - didWriteData(messageObject, cacheFile, false, false); + long availableSize = mediaMuxer.writeSampleData(audioTrackIndex, audioBuffer, info, false); + if (availableSize != 0) { + didWriteData(messageObject, cacheFile, false, availableSize, false); } } } else if (index == -1) { @@ -3860,8 +3920,9 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, } if (info.size > 1) { if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) { - if (mediaMuxer.writeSampleData(videoTrackIndex, encodedData, info, true)) { - didWriteData(messageObject, cacheFile, false, false); + long availableSize = mediaMuxer.writeSampleData(videoTrackIndex, encodedData, info, true); + if (availableSize != 0) { + didWriteData(messageObject, cacheFile, false, availableSize, false); } } else if (videoTrackIndex == -5) { byte[] csd = new byte[info.size]; @@ -4031,11 +4092,11 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, } } else { preferences.edit().putBoolean("isPreviousOk", true).commit(); - didWriteData(messageObject, cacheFile, true, true); + didWriteData(messageObject, cacheFile, true, 0, true); return false; } preferences.edit().putBoolean("isPreviousOk", true).commit(); - didWriteData(messageObject, cacheFile, true, error); + didWriteData(messageObject, cacheFile, true, 0, error); return true; } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java index 73af5e6bf..f55d90144 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java @@ -81,6 +81,7 @@ public class MessageObject { public String monthKey; public boolean deleted; public float audioProgress; + public int audioProgressMs; public float bufferedProgress; public float gifState; public int audioProgressSec; @@ -100,6 +101,8 @@ public class MessageObject { public boolean useCustomPhoto; public StringBuilder botButtonsLayout; + public boolean hadAnimationNotReadyLoading; + public boolean cancelEditing; public CharSequence editingMessage; @@ -422,6 +425,8 @@ public class MessageObject { float maxAspectRatio = maxSizeWidth / maxSizeHeight; averageAspectRatio = averageAspectRatio / count; + float minH = AndroidUtilities.dp(100) / maxSizeHeight; + if (!forceCalc && (count == 2 || count == 3 || count == 4)) { if (count == 2) { GroupedMessagePosition position1 = posArray.get(0); @@ -484,6 +489,9 @@ public class MessageObject { int width = maxSizeWidth / 2; float secondHeight = Math.min(maxSizeHeight - firstHeight, Math.round(Math.min(width / position2.aspectRatio, width / position3.aspectRatio))) / maxSizeHeight; + if (secondHeight < minH) { + secondHeight = minH; + } position2.set(0, 0, 1, 1, width, secondHeight, POSITION_FLAG_LEFT | POSITION_FLAG_BOTTOM); position3.set(1, 1, 1, 1, width, secondHeight, POSITION_FLAG_RIGHT | POSITION_FLAG_BOTTOM); maxX = 1; @@ -509,6 +517,9 @@ public class MessageObject { } h = Math.min(maxSizeHeight - h0, h); h /= maxSizeHeight; + if (h < minH) { + h = minH; + } position2.set(0, 0, 1, 1, w0, h, POSITION_FLAG_LEFT | POSITION_FLAG_BOTTOM); position3.set(1, 1, 1, 1, w1, h, POSITION_FLAG_BOTTOM); position4.set(2, 2, 1, 1, w2, h, POSITION_FLAG_RIGHT | POSITION_FLAG_BOTTOM); @@ -655,7 +666,7 @@ public class MessageObject { posToFix = pos; } } - pos.set(k, k, i, i, width, lineHeight / maxSizeHeight, flags); + pos.set(k, k, i, i, width, Math.max(minH, lineHeight / maxSizeHeight), flags); index++; } posToFix.pw += spanLeft; @@ -2107,6 +2118,7 @@ public class MessageObject { message.media.document = getDocumentWithId(webPage, pageBlockVideo.video_id); } message.message = ""; + message.realId = getId(); message.id = Utilities.random.nextInt(); message.date = messageOwner.date; message.to_id = messageOwner.to_id; @@ -2381,7 +2393,7 @@ public class MessageObject { } public static boolean isRoundVideoDocument(TLRPC.Document document) { - if (document != null && document.mime_type != null && document.mime_type.equals("video/mp4")) { + if (document != null && "video/mp4".equals(document.mime_type)) { int width = 0; int height = 0; boolean round = false; @@ -2401,7 +2413,7 @@ public class MessageObject { } public static boolean isNewGifDocument(WebFile document) { - if (document != null && document.mime_type != null && document.mime_type.equals("video/mp4")) { + if (document != null && "video/mp4".equals(document.mime_type)) { int width = 0; int height = 0; boolean animated = false; @@ -2422,7 +2434,7 @@ public class MessageObject { } public static boolean isNewGifDocument(TLRPC.Document document) { - if (document != null && document.mime_type != null && document.mime_type.equals("video/mp4")) { + if (document != null && "video/mp4".equals(document.mime_type)) { int width = 0; int height = 0; boolean animated = false; @@ -3390,7 +3402,7 @@ public class MessageObject { } int selfUserId = UserConfig.getInstance(currentAccount).getClientUserId(); if (getDialogId() == selfUserId) { - return messageOwner.fwd_from.from_id == selfUserId || messageOwner.fwd_from.saved_from_peer != null && messageOwner.fwd_from.saved_from_peer.user_id == selfUserId; + return messageOwner.fwd_from.from_id == selfUserId && (messageOwner.fwd_from.saved_from_peer == null || messageOwner.fwd_from.saved_from_peer.user_id == selfUserId) || messageOwner.fwd_from.saved_from_peer != null && messageOwner.fwd_from.saved_from_peer.user_id == selfUserId; } return messageOwner.fwd_from.saved_from_peer == null || messageOwner.fwd_from.saved_from_peer.user_id == selfUserId; } @@ -3453,9 +3465,19 @@ public class MessageObject { return messageOwner.id; } + public int getRealId() { + return messageOwner.realId != 0 ? messageOwner.realId : messageOwner.id; + } + public static int getMessageSize(TLRPC.Message message) { - if (message.media != null && message.media.document != null) { - return message.media.document.size; + TLRPC.Document document; + if (message.media instanceof TLRPC.TL_messageMediaWebPage) { + document = message.media.webpage.document; + } else { + document = message.media != null ? message.media.document : null; + } + if (document != null) { + return document.size; } return 0; } @@ -3570,9 +3592,9 @@ public class MessageObject { return attribute.supports_streaming; } } - /*if ("video/x-matroska".equals(document.mime_type)) { + if (SharedConfig.streamMkv && "video/x-matroska".equals(document.mime_type)) { return true; - }*/ + } return false; } @@ -3733,9 +3755,9 @@ public class MessageObject { if (isAnimated && (width > 1280 || height > 1280)) { isAnimated = false; } - /*if (!isVideo && "video/x-matroska".equals(document.mime_type)) { + if (SharedConfig.streamMkv && !isVideo && "video/x-matroska".equals(document.mime_type)) { isVideo = true; - }*/ + } return isVideo && !isAnimated; } return false; @@ -3744,6 +3766,8 @@ public class MessageObject { public TLRPC.Document getDocument() { if (messageOwner.media instanceof TLRPC.TL_messageMediaWebPage) { return messageOwner.media.webpage.document; + } else if (messageOwner.media instanceof TLRPC.TL_messageMediaGame) { + return messageOwner.media.game.document; } return messageOwner.media != null ? messageOwner.media.document : null; } @@ -3780,7 +3804,7 @@ public class MessageObject { public static boolean isPhoto(TLRPC.Message message) { if (message.media instanceof TLRPC.TL_messageMediaWebPage) { - return message.media.webpage.photo instanceof TLRPC.TL_photo; + return message.media.webpage.photo instanceof TLRPC.TL_photo && !(message.media.webpage.document instanceof TLRPC.TL_document); } return message.media instanceof TLRPC.TL_messageMediaPhoto; } @@ -4068,15 +4092,13 @@ public class MessageObject { } public int getDuration() { - TLRPC.Document document; - if (type == 0) { - document = messageOwner.media.webpage.document; - } else { - document = messageOwner.media.document; - } + TLRPC.Document document = getDocument(); if (document == null) { return 0; } + if (audioPlayerDuration > 0) { + return audioPlayerDuration; + } for (int a = 0; a < document.attributes.size(); a++) { TLRPC.DocumentAttribute attribute = document.attributes.get(a); if (attribute instanceof TLRPC.TL_documentAttributeAudio) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java index 69d3a7f90..3f53876b9 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java @@ -48,6 +48,8 @@ import java.util.Locale; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; +import android.support.v4.app.NotificationManagerCompat; + public class MessagesController implements NotificationCenter.NotificationCenterDelegate { private ConcurrentHashMap chats = new ConcurrentHashMap<>(100, 1.0f, 2); @@ -63,9 +65,10 @@ public class MessagesController implements NotificationCenter.NotificationCenter public ArrayList dialogs = new ArrayList<>(); public ArrayList dialogsForward = new ArrayList<>(); public ArrayList dialogsServerOnly = new ArrayList<>(); - public ArrayList dialogsGroupsOnly = new ArrayList<>(); + public ArrayList dialogsCanAddUsers = new ArrayList<>(); public ArrayList dialogsChannelsOnly = new ArrayList<>(); public ArrayList dialogsUsersOnly = new ArrayList<>(); + public ArrayList dialogsGroupsOnly = new ArrayList<>(); public int unreadUnmutedDialogs; public int nextDialogsCacheOffset; public ConcurrentHashMap dialogs_read_inbox_max = new ConcurrentHashMap<>(100, 1.0f, 2); @@ -167,6 +170,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter private TLRPC.TL_dialog proxyDialog; private boolean isLeftProxyChannel; private long proxyDialogId; + private String proxyDialogAddress; private boolean checkingTosUpdate; private int nextTosCheckTime; @@ -414,6 +418,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter canRevokePmInbox = mainPreferences.getBoolean("canRevokePmInbox", canRevokePmInbox); preloadFeaturedStickers = mainPreferences.getBoolean("preloadFeaturedStickers", false); proxyDialogId = mainPreferences.getLong("proxy_dialog", 0); + proxyDialogAddress = mainPreferences.getString("proxyDialogAddress", null); nextTosCheckTime = notificationsPreferences.getInt("nextTosCheckTime", 0); venueSearchBot = mainPreferences.getString("venueSearchBot", "foursquare"); gifSearchBot = mainPreferences.getString("gifSearchBot", "gif"); @@ -426,6 +431,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter public void updateConfig(final TLRPC.TL_config config) { AndroidUtilities.runOnUIThread(() -> { + DownloadController.getInstance(currentAccount).loadAutoDownloadConfig(false); LocaleController.getInstance().loadRemoteLanguages(currentAccount); maxMegagroupCount = config.megagroup_size_max; maxGroupCount = config.chat_size_max; @@ -790,7 +796,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter editor = emojiPreferences.edit(); editor.putLong("lastGifLoadTime", 0).putLong("lastStickersLoadTime", 0).putLong("lastStickersLoadTimeMask", 0).putLong("lastStickersLoadTimeFavs", 0).commit(); editor = mainPreferences.edit(); - editor.remove("gifhint").remove("dcDomainName").remove("webFileDatacenterId").commit(); + editor.remove("gifhint").remove("soundHint").remove("dcDomainName").remove("webFileDatacenterId").commit(); reloadingWebpages.clear(); reloadingWebpagesPending.clear(); @@ -808,8 +814,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter pollsToCheckSize = 0; dialogsServerOnly.clear(); dialogsForward.clear(); - dialogsGroupsOnly.clear(); + dialogsCanAddUsers.clear(); dialogsChannelsOnly.clear(); + dialogsGroupsOnly.clear(); dialogsUsersOnly.clear(); dialogMessagesByIds.clear(); dialogMessagesByRandomIds.clear(); @@ -2507,8 +2514,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter needShortPollOnlines.delete(-(int) did); }); } - dialogsGroupsOnly.remove(dialog); + dialogsCanAddUsers.remove(dialog); dialogsChannelsOnly.remove(dialog); + dialogsGroupsOnly.remove(dialog); dialogsUsersOnly.remove(dialog); dialogsForward.remove(dialog); dialogs_dict.remove(did); @@ -3003,10 +3011,18 @@ public class MessagesController implements NotificationCenter.NotificationCenter if (!reset && nextProxyInfoCheckTime > ConnectionsManager.getInstance(currentAccount).getCurrentTime() || checkingProxyInfo) { return; } + if (checkingProxyInfoRequestId != 0) { + ConnectionsManager.getInstance(currentAccount).cancelRequest(checkingProxyInfoRequestId, true); + checkingProxyInfoRequestId = 0; + } SharedPreferences preferences = getGlobalMainSettings(); boolean enabled = preferences.getBoolean("proxy_enabled", false); String proxyAddress = preferences.getString("proxy_ip", ""); String proxySecret = preferences.getString("proxy_secret", ""); + int removeCurrent = 0; + if (proxyDialogId != 0 && proxyDialogAddress != null && !proxyDialogAddress.equals(proxyAddress + proxySecret)) { + removeCurrent = 1; + } if (enabled && !TextUtils.isEmpty(proxyAddress) && !TextUtils.isEmpty(proxySecret)) { checkingProxyInfo = true; TLRPC.TL_help_getProxyData req = new TLRPC.TL_help_getProxyData(); @@ -3049,7 +3065,8 @@ public class MessagesController implements NotificationCenter.NotificationCenter } } proxyDialogId = did; - getGlobalMainSettings().edit().putLong("proxy_dialog", proxyDialogId).commit(); + proxyDialogAddress = proxyAddress + proxySecret; + getGlobalMainSettings().edit().putLong("proxy_dialog", proxyDialogId).putString("proxyDialogAddress", proxyDialogAddress).commit(); nextProxyInfoCheckTime = res.expires; if (!noDialog) { AndroidUtilities.runOnUIThread(() -> { @@ -3180,7 +3197,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter } if (noDialog) { proxyDialogId = 0; - getGlobalMainSettings().edit().putLong("proxy_dialog", proxyDialogId).commit(); + getGlobalMainSettings().edit().putLong("proxy_dialog", proxyDialogId).remove("proxyDialogAddress").commit(); checkingProxyInfoRequestId = 0; checkingProxyInfo = false; AndroidUtilities.runOnUIThread(() -> { @@ -3200,13 +3217,19 @@ public class MessagesController implements NotificationCenter.NotificationCenter } }); } else { + removeCurrent = 2; + } + if (removeCurrent != 0) { proxyDialogId = 0; - getGlobalMainSettings().edit().putLong("proxy_dialog", proxyDialogId).commit(); + proxyDialogAddress = null; + getGlobalMainSettings().edit().putLong("proxy_dialog", proxyDialogId).remove("proxyDialogAddress").commit(); nextProxyInfoCheckTime = ConnectionsManager.getInstance(currentAccount).getCurrentTime() + 60 * 60; - checkingProxyInfo = false; - if (checkingProxyInfoRequestId != 0) { - ConnectionsManager.getInstance(currentAccount).cancelRequest(checkingProxyInfoRequestId, true); - checkingProxyInfoRequestId = 0; + if (removeCurrent == 2) { + checkingProxyInfo = false; + if (checkingProxyInfoRequestId != 0) { + ConnectionsManager.getInstance(currentAccount).cancelRequest(checkingProxyInfoRequestId, true); + checkingProxyInfoRequestId = 0; + } } AndroidUtilities.runOnUIThread(() -> { if (proxyDialog != null) { @@ -6462,18 +6485,18 @@ public class MessagesController implements NotificationCenter.NotificationCenter getChannelDifference(channelId, 0, 0, null); } - public static boolean isSupportId(int id) { - return id / 1000 == 777 || id == 333000 || - id == 4240000 || id == 4240000 || id == 4244000 || - id == 4245000 || id == 4246000 || id == 410000 || - id == 420000 || id == 431000 || id == 431415000 || - id == 434000 || id == 4243000 || id == 439000 || - id == 449000 || id == 450000 || id == 452000 || - id == 454000 || id == 4254000 || id == 455000 || - id == 460000 || id == 470000 || id == 479000 || - id == 796000 || id == 482000 || id == 490000 || - id == 496000 || id == 497000 || id == 498000 || - id == 4298000; + public static boolean isSupportUser(TLRPC.User user) { + return user != null && (user.support || user.id / 1000 == 777 || user.id == 333000 || + user.id == 4240000 || user.id == 4240000 || user.id == 4244000 || + user.id == 4245000 || user.id == 4246000 || user.id == 410000 || + user.id == 420000 || user.id == 431000 || user.id == 431415000 || + user.id == 434000 || user.id == 4243000 || user.id == 439000 || + user.id == 449000 || user.id == 450000 || user.id == 452000 || + user.id == 454000 || user.id == 4254000 || user.id == 455000 || + user.id == 460000 || user.id == 470000 || user.id == 479000 || + user.id == 796000 || user.id == 482000 || user.id == 490000 || + user.id == 496000 || user.id == 497000 || user.id == 498000 || + user.id == 4298000); } protected void getChannelDifference(final int channelId, final int newDialogType, final long taskId, TLRPC.InputChannel inputChannel) { @@ -9298,8 +9321,13 @@ public class MessagesController implements NotificationCenter.NotificationCenter } continue; } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !NotificationManagerCompat.from(ApplicationLoader.applicationContext).areNotificationsEnabled()) { + if (BuildVars.LOGS_ENABLED) + FileLog.d("Ignoring incoming call because notifications are disabled in system"); + continue; + } TelephonyManager tm = (TelephonyManager) ApplicationLoader.applicationContext.getSystemService(Context.TELEPHONY_SERVICE); - if (svc != null || VoIPService.callIShouldHavePutIntoIntent!=null || tm.getCallState() != TelephonyManager.CALL_STATE_IDLE) { + if (svc != null || VoIPService.callIShouldHavePutIntoIntent != null || tm.getCallState() != TelephonyManager.CALL_STATE_IDLE) { if (BuildVars.LOGS_ENABLED) { FileLog.d("Auto-declining call " + call.id + " because there's already active one"); } @@ -9325,9 +9353,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter intent.putExtra("user_id", call.participant_id == UserConfig.getInstance(currentAccount).getClientUserId() ? call.admin_id : call.participant_id); intent.putExtra("account", currentAccount); try { - if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { ApplicationLoader.applicationContext.startForegroundService(intent); - }else{ + } else { ApplicationLoader.applicationContext.startService(intent); } } catch (Throwable e) { @@ -9738,8 +9766,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter if (dialog != null) { dialogs.remove(dialog); dialogsServerOnly.remove(dialog); - dialogsGroupsOnly.remove(dialog); + dialogsCanAddUsers.remove(dialog); dialogsChannelsOnly.remove(dialog); + dialogsGroupsOnly.remove(dialog); dialogsUsersOnly.remove(dialog); dialogsForward.remove(dialog); dialogs_dict.remove(dialog.id); @@ -9858,8 +9887,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter public void sortDialogs(SparseArray chatsDict) { dialogsServerOnly.clear(); - dialogsGroupsOnly.clear(); + dialogsCanAddUsers.clear(); dialogsChannelsOnly.clear(); + dialogsGroupsOnly.clear(); dialogsUsersOnly.clear(); dialogsForward.clear(); unreadUnmutedDialogs = 0; @@ -9888,10 +9918,10 @@ public class MessagesController implements NotificationCenter.NotificationCenter if (DialogObject.isChannel(d)) { TLRPC.Chat chat = getChat(-lower_id); if (chat != null && (chat.megagroup && (chat.admin_rights != null && (chat.admin_rights.post_messages || chat.admin_rights.add_admins)) || chat.creator)) { - dialogsGroupsOnly.add(d); + dialogsCanAddUsers.add(d); } if (chat != null && chat.megagroup) { - + dialogsGroupsOnly.add(d); } else { dialogsChannelsOnly.add(d); } @@ -9904,6 +9934,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter continue; } } + dialogsCanAddUsers.add(d); dialogsGroupsOnly.add(d); } else if (lower_id > 0) { dialogsUsersOnly.add(d); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java index 5397ecc74..86349b165 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java @@ -5500,47 +5500,41 @@ public class MessagesStorage { data.reuse(); - if (downloadMask != 0 && (message.to_id.channel_id == 0 || message.post) && message.date >= ConnectionsManager.getInstance(currentAccount).getCurrentTime() - 60 * 60 && DownloadController.getInstance(currentAccount).canDownloadMedia(message)) { + if (downloadMask != 0 && (message.to_id.channel_id == 0 || message.post) && message.date >= ConnectionsManager.getInstance(currentAccount).getCurrentTime() - 60 * 60 && DownloadController.getInstance(currentAccount).canDownloadMedia(message) == 1) { if (message.media instanceof TLRPC.TL_messageMediaPhoto || message.media instanceof TLRPC.TL_messageMediaDocument) { int type = 0; long id = 0; TLRPC.MessageMedia object = null; if (MessageObject.isVoiceMessage(message)) { id = message.media.document.id; - type = DownloadController.AUTODOWNLOAD_MASK_AUDIO; - object = new TLRPC.TL_messageMediaDocument(); - object.document = message.media.document; - object.flags |= 1; - } else if (MessageObject.isRoundVideoMessage(message)) { - id = message.media.document.id; - type = DownloadController.AUTODOWNLOAD_MASK_VIDEOMESSAGE; + type = DownloadController.AUTODOWNLOAD_TYPE_AUDIO; object = new TLRPC.TL_messageMediaDocument(); object.document = message.media.document; object.flags |= 1; } else if (MessageObject.isStickerMessage(message)) { id = message.media.document.id; - type = DownloadController.AUTODOWNLOAD_MASK_PHOTO; + type = DownloadController.AUTODOWNLOAD_TYPE_PHOTO; object = new TLRPC.TL_messageMediaDocument(); object.document = message.media.document; object.flags |= 1; - }else if (message.media instanceof TLRPC.TL_messageMediaPhoto) { + } else if (message.media instanceof TLRPC.TL_messageMediaPhoto) { TLRPC.PhotoSize photoSize = FileLoader.getClosestPhotoSizeWithSize(message.media.photo.sizes, AndroidUtilities.getPhotoSize()); if (photoSize != null) { id = message.media.photo.id; - type = DownloadController.AUTODOWNLOAD_MASK_PHOTO; + type = DownloadController.AUTODOWNLOAD_TYPE_PHOTO; object = new TLRPC.TL_messageMediaPhoto(); object.photo = message.media.photo; object.flags |= 1; } - } else if (MessageObject.isVideoMessage(message)) { + } else if (MessageObject.isVideoMessage(message) || MessageObject.isRoundVideoMessage(message)) { id = message.media.document.id; - type = DownloadController.AUTODOWNLOAD_MASK_VIDEO; + type = DownloadController.AUTODOWNLOAD_TYPE_VIDEO; object = new TLRPC.TL_messageMediaDocument(); object.document = message.media.document; object.flags |= 1; } else if (message.media instanceof TLRPC.TL_messageMediaDocument && !MessageObject.isMusicMessage(message) && !MessageObject.isGifDocument(message.media.document)) { id = message.media.document.id; - type = DownloadController.AUTODOWNLOAD_MASK_DOCUMENT; + type = DownloadController.AUTODOWNLOAD_TYPE_DOCUMENT; object = new TLRPC.TL_messageMediaDocument(); object.document = message.media.document; object.flags |= 1; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/NativeLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/NativeLoader.java index e386935b9..a2166eaa7 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NativeLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NativeLoader.java @@ -24,7 +24,7 @@ import java.util.zip.ZipFile; public class NativeLoader { - private final static int LIB_VERSION = 29; + private final static int LIB_VERSION = 30; private final static String LIB_NAME = "tmessages." + LIB_VERSION; private final static String LIB_SO_NAME = "lib" + LIB_NAME + ".so"; private final static String LOCALE_LIB_SO_NAME = "lib" + LIB_NAME + "loc.so"; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java index 8b0e000bd..fdec7152d 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java @@ -87,6 +87,7 @@ public class NotificationCenter { public static final int updateMentionsCount = totalEvents++; public static final int didUpdatePollResults = totalEvents++; public static final int chatOnlineCountDidLoad = totalEvents++; + public static final int videoLoadingStateChanged = totalEvents++; public static final int httpFileDidLoad = totalEvents++; public static final int httpFileDidFailedLoad = totalEvents++; @@ -110,6 +111,7 @@ public class NotificationCenter { public static final int messagePlayingPlayStateChanged = totalEvents++; public static final int messagePlayingDidStart = totalEvents++; public static final int messagePlayingDidSeek = totalEvents++; + public static final int messagePlayingGoingToStop = totalEvents++; public static final int recordProgressChanged = totalEvents++; public static final int recordStarted = totalEvents++; public static final int recordStartError = totalEvents++; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java index d684e8ae2..3656f5c65 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java @@ -699,7 +699,11 @@ public class NotificationsController { if (popupFinal == 3 || popupFinal == 1 && ApplicationLoader.isScreenOn || popupFinal == 2 && !ApplicationLoader.isScreenOn) { Intent popupIntent = new Intent(ApplicationLoader.applicationContext, PopupNotificationActivity.class); popupIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_FROM_BACKGROUND); - ApplicationLoader.applicationContext.startActivity(popupIntent); + try { + ApplicationLoader.applicationContext.startActivity(popupIntent); + } catch (Throwable ignore) { + + } } } }); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java b/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java index 8c61c5b6f..e6bf13056 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java @@ -4890,7 +4890,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter bitmap = ImageLoader.loadBitmap(finalPath, null, side, side, true); } if (bitmap != null) { - TLRPC.PhotoSize thumb = ImageLoader.scaleAndSaveImage(bitmap, side, side, 55, false); + TLRPC.PhotoSize thumb = ImageLoader.scaleAndSaveImage(bitmap, side, side, side > 90 ? 70 : 55, false); if (thumb != null) { document.thumbs.add(thumb); document.flags |= 1; @@ -5174,7 +5174,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter thumb = ThumbnailUtils.createVideoThumbnail(path, MediaStore.Video.Thumbnails.MINI_KIND); } int side = isEncrypted ? 90 : 320; - document.thumbs.set(0, ImageLoader.scaleAndSaveImage(photoSize, thumb, side, side, 55, false)); + document.thumbs.set(0, ImageLoader.scaleAndSaveImage(photoSize, thumb, side, side, side > 90 ? 70 : 55, false)); } } } @@ -5335,7 +5335,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter } if (thumbFile != null) { try { - int side = isEncrypted ? 90 : 320; + int side = isEncrypted || info.ttl != 0 ? 90 : 320; Bitmap bitmap; if (thumbFile.getAbsolutePath().endsWith("mp4")) { bitmap = ThumbnailUtils.createVideoThumbnail(thumbFile.getAbsolutePath(), MediaStore.Video.Thumbnails.MINI_KIND); @@ -5343,7 +5343,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter bitmap = ImageLoader.loadBitmap(thumbFile.getAbsolutePath(), null, side, side, true); } if (bitmap != null) { - TLRPC.PhotoSize thumb = ImageLoader.scaleAndSaveImage(bitmap, side, side, 55, isEncrypted); + TLRPC.PhotoSize thumb = ImageLoader.scaleAndSaveImage(bitmap, side, side, side > 90 ? 70 : 55, isEncrypted); if (thumb != null) { document.thumbs.add(thumb); document.flags |= 1; @@ -5492,8 +5492,8 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter if (thumb == null) { thumb = ThumbnailUtils.createVideoThumbnail(info.path, MediaStore.Video.Thumbnails.MINI_KIND); } - int side = isEncrypted ? 90 : 320; - TLRPC.PhotoSize size = ImageLoader.scaleAndSaveImage(thumb, side, side, 55, isEncrypted); + int side = isEncrypted || info.ttl != 0 ? 90 : 320; + TLRPC.PhotoSize size = ImageLoader.scaleAndSaveImage(thumb, side, side, side > 90 ? 70 : 55, isEncrypted); if (thumb != null && size != null) { thumb = null; } @@ -5707,7 +5707,11 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter if (AndroidUtilities.isTablet()) { maxPhotoWidth = photoWidth = (int) (AndroidUtilities.getMinTabletSide() * 0.7f); } else { - maxPhotoWidth = photoWidth = (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.7f); + if (currentPhotoObject.w >= currentPhotoObject.h) { + maxPhotoWidth = photoWidth = Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) - AndroidUtilities.dp(64); + } else { + maxPhotoWidth = photoWidth = (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.7f); + } } photoHeight = photoWidth + AndroidUtilities.dp(100); @@ -5988,8 +5992,36 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter for (int a = 0; a < sizes.length; a++) { sampleSizes += sizes[a]; } - videoDuration = (float) mediaHeaderBox.getDuration() / (float) mediaHeaderBox.getTimescale(); - trackBitrate = (int) (sampleSizes * 8 / videoDuration); + if (videoDuration == 0) { + videoDuration = (float) mediaHeaderBox.getDuration() / (float) mediaHeaderBox.getTimescale(); + if (videoDuration == 0) { + MediaPlayer player = null; + try { + player = new MediaPlayer(); + player.setDataSource(videoPath); + player.prepare(); + videoDuration = player.getDuration() / 1000.0f; + if (videoDuration < 0) { + videoDuration = 0; + } + } catch (Throwable ignore) { + + } finally { + try { + if (player != null) { + player.release(); + } + } catch (Throwable ignore) { + + } + } + } + } + if (videoDuration != 0) { + trackBitrate = (int) (sampleSizes * 8 / videoDuration); + } else { + trackBitrate = 400000; + } } catch (Exception e) { FileLog.e(e); } @@ -6149,6 +6181,9 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter videoEditedInfo.estimatedSize = (int) (audioFramesSize + videoFramesSize); videoEditedInfo.estimatedSize += videoEditedInfo.estimatedSize / (32 * 1024) * 16; } + if (videoEditedInfo.estimatedSize == 0) { + videoEditedInfo.estimatedSize = 1; + } return videoEditedInfo; } @@ -6200,8 +6235,8 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter if (thumb == null) { thumb = ThumbnailUtils.createVideoThumbnail(videoPath, MediaStore.Video.Thumbnails.MINI_KIND); } - int side = isEncrypted ? 90 : 320; - TLRPC.PhotoSize size = ImageLoader.scaleAndSaveImage(thumb, side, side, 55, isEncrypted); + int side = isEncrypted || ttl != 0 ? 90 : 320; + TLRPC.PhotoSize size = ImageLoader.scaleAndSaveImage(thumb, side, side, side > 90 ? 70 : 55, isEncrypted); if (thumb != null && size != null) { if (isRound) { if (isEncrypted) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java b/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java index 13fcfd585..86c52685a 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java @@ -60,14 +60,17 @@ public class SharedConfig { public static boolean saveToGallery; public static int mapPreviewType = 2; public static boolean autoplayGifs = true; + public static boolean autoplayVideo = true; public static boolean raiseToSpeak = true; public static boolean customTabs = true; public static boolean directShare = true; public static boolean inappCamera = true; public static boolean roundCamera16to9 = true; public static boolean groupPhotosEnabled = true; + public static boolean noSoundHintShowed = false; public static boolean streamMedia = true; public static boolean streamAllVideo = false; + public static boolean streamMkv = false; public static boolean saveStreamMedia = true; public static boolean sortContactsByName; public static boolean shuffleMusic; @@ -204,6 +207,7 @@ public class SharedConfig { preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); saveToGallery = preferences.getBoolean("save_gallery", false); autoplayGifs = preferences.getBoolean("autoplay_gif", true); + autoplayVideo = preferences.getBoolean("autoplay_video", true); mapPreviewType = preferences.getInt("mapPreviewType", 2); raiseToSpeak = preferences.getBoolean("raise_to_speak", true); customTabs = preferences.getBoolean("custom_tabs", true); @@ -221,8 +225,10 @@ public class SharedConfig { streamMedia = preferences.getBoolean("streamMedia", true); saveStreamMedia = preferences.getBoolean("saveStreamMedia", true); streamAllVideo = preferences.getBoolean("streamAllVideo", BuildVars.DEBUG_VERSION); + streamMkv = preferences.getBoolean("streamMkv", false); suggestStickers = preferences.getInt("suggestStickers", 0); sortContactsByName = preferences.getBoolean("sortContactsByName", false); + noSoundHintShowed = preferences.getBoolean("noSoundHintShowed", false); configLoaded = true; } @@ -388,6 +394,14 @@ public class SharedConfig { editor.commit(); } + public static void toggleAutoplayVideo() { + autoplayVideo = !autoplayVideo; + SharedPreferences preferences = MessagesController.getGlobalMainSettings(); + SharedPreferences.Editor editor = preferences.edit(); + editor.putBoolean("autoplay_video", autoplayVideo); + editor.commit(); + } + public static boolean isSecretMapPreviewSet() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); return preferences.contains("mapPreviewType"); @@ -401,6 +415,17 @@ public class SharedConfig { editor.commit(); } + public static void setNoSoundHintShowed(boolean value) { + if (noSoundHintShowed == value) { + return; + } + noSoundHintShowed = value; + SharedPreferences preferences = MessagesController.getGlobalMainSettings(); + SharedPreferences.Editor editor = preferences.edit(); + editor.putBoolean("noSoundHintShowed", noSoundHintShowed); + editor.commit(); + } + public static void toogleRaiseToSpeak() { raiseToSpeak = !raiseToSpeak; SharedPreferences preferences = MessagesController.getGlobalMainSettings(); @@ -449,6 +474,14 @@ public class SharedConfig { editor.commit(); } + public static void toggleStreamMkv() { + streamMkv = !streamMkv; + SharedPreferences preferences = MessagesController.getGlobalMainSettings(); + SharedPreferences.Editor editor = preferences.edit(); + editor.putBoolean("streamMkv", streamMkv); + editor.commit(); + } + public static void toggleSaveStreamMedia() { saveStreamMedia = !saveStreamMedia; SharedPreferences preferences = MessagesController.getGlobalMainSettings(); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java b/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java index dbfd4c836..1a6297b67 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java @@ -66,6 +66,7 @@ public class UserConfig { public int pendingAppUpdateBuildVersion; public long pendingAppUpdateInstallTime; public long lastUpdateCheckTime; + public long autoDownloadConfigLoadTime; public volatile byte[] savedPasswordHash; public volatile byte[] savedSaltedPassword; @@ -145,6 +146,7 @@ public class UserConfig { editor.putBoolean("hasSecureData", hasSecureData); editor.putBoolean("notificationsSettingsLoaded3", notificationsSettingsLoaded); editor.putBoolean("notificationsSignUpSettingsLoaded", notificationsSignUpSettingsLoaded); + editor.putLong("autoDownloadConfigLoadTime", autoDownloadConfigLoadTime); editor.putInt("3migrateOffsetId", migrateOffsetId); if (migrateOffsetId != -1) { @@ -292,6 +294,7 @@ public class UserConfig { hasSecureData = preferences.getBoolean("hasSecureData", false); notificationsSettingsLoaded = preferences.getBoolean("notificationsSettingsLoaded3", false); notificationsSignUpSettingsLoaded = preferences.getBoolean("notificationsSignUpSettingsLoaded", false); + autoDownloadConfigLoadTime = preferences.getLong("autoDownloadConfigLoadTime", 0); try { String terms = preferences.getString("terms", null); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java b/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java index 1ad27e7a3..f588de6c2 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java @@ -31,7 +31,7 @@ public class Utilities { public static volatile DispatchQueue stageQueue = new DispatchQueue("stageQueue"); public static volatile DispatchQueue globalQueue = new DispatchQueue("globalQueue"); public static volatile DispatchQueue searchQueue = new DispatchQueue("searchQueue"); - public static volatile DispatchQueue phoneBookQueue = new DispatchQueue("photoBookQueue"); + public static volatile DispatchQueue phoneBookQueue = new DispatchQueue("phoneBookQueue"); final protected static char[] hexArray = "0123456789ABCDEF".toCharArray(); @@ -358,6 +358,10 @@ public class Utilities { + (((long) bytes[3] & 0xFF) << 24) + (((long) bytes[2] & 0xFF) << 16) + (((long) bytes[1] & 0xFF) << 8) + ((long) bytes[0] & 0xFF); } + public static int bytesToInt(byte[] bytes) { + return (((int) bytes[3] & 0xFF) << 24) + (((int) bytes[2] & 0xFF) << 16) + (((int) bytes[1] & 0xFF) << 8) + ((int) bytes[0] & 0xFF); + } + public static String MD5(String md5) { if (md5 == null) { return null; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/support/widget/LinearSmoothScrollerMiddle.java b/TMessagesProj/src/main/java/org/telegram/messenger/support/widget/LinearSmoothScrollerMiddle.java index 3e2d4cbfd..f47846ca6 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/support/widget/LinearSmoothScrollerMiddle.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/support/widget/LinearSmoothScrollerMiddle.java @@ -124,7 +124,11 @@ public class LinearSmoothScrollerMiddle extends RecyclerView.SmoothScroller { int boxSize = end - start; int viewSize = bottom - top; - start = (boxSize - viewSize) / 2; + if (viewSize > boxSize) { + start = 0; + } else { + start = (boxSize - viewSize) / 2; + } end = start + viewSize; final int dtStart = start - top; if (dtStart > 0) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/MP4Builder.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/MP4Builder.java index 349974ed8..4487bbe8d 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/video/MP4Builder.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/MP4Builder.java @@ -56,7 +56,7 @@ public class MP4Builder { private FileOutputStream fos = null; private FileChannel fc = null; private long dataOffset = 0; - private long writedSinceLastMdat = 0; + private long wroteSinceLastMdat = 0; private boolean writeNewMdat = true; private HashMap track2SampleSizes = new HashMap<>(); private ByteBuffer sizeBuffer = null; @@ -71,7 +71,7 @@ public class MP4Builder { FileTypeBox fileTypeBox = createFileTypeBox(); fileTypeBox.getBox(fc); dataOffset += fileTypeBox.getSize(); - writedSinceLastMdat += dataOffset; + wroteSinceLastMdat += dataOffset; splitMdat = split; mdat = new InterleaveChunkMdat(); @@ -92,27 +92,27 @@ public class MP4Builder { fos.getFD().sync(); } - public boolean writeSampleData(int trackIndex, ByteBuffer byteBuf, MediaCodec.BufferInfo bufferInfo, boolean writeLength) throws Exception { + public long writeSampleData(int trackIndex, ByteBuffer byteBuf, MediaCodec.BufferInfo bufferInfo, boolean writeLength) throws Exception { if (writeNewMdat) { mdat.setContentSize(0); mdat.getBox(fc); mdat.setDataOffset(dataOffset); dataOffset += 16; - writedSinceLastMdat += 16; + wroteSinceLastMdat += 16; writeNewMdat = false; } mdat.setContentSize(mdat.getContentSize() + bufferInfo.size); - writedSinceLastMdat += bufferInfo.size; + wroteSinceLastMdat += bufferInfo.size; boolean flush = false; - if (writedSinceLastMdat >= 32 * 1024) { + if (wroteSinceLastMdat >= 32 * 1024) { if (splitMdat) { flushCurrentMdat(); writeNewMdat = true; } flush = true; - writedSinceLastMdat = 0; + wroteSinceLastMdat = 0; } currentMp4Movie.addSample(trackIndex, dataOffset, bufferInfo); @@ -132,8 +132,9 @@ public class MP4Builder { if (flush) { fos.flush(); fos.getFD().sync(); + return fc.position(); } - return flush; + return 0; } public int addTrack(MediaFormat mediaFormat, boolean isAudio) { @@ -454,6 +455,12 @@ public class MP4Builder { stbl.addBox(stsz); } + protected void createSidx(Track track, SampleTableBox stbl) { + //SampleSizeBox stsz = new SampleSizeBox(); + //stsz.setSampleSizes(track2SampleSizes.get(track)); + //stbl.addBox(stsz); + } + protected void createStco(Track track, SampleTableBox stbl) { ArrayList chunksOffsets = new ArrayList<>(); long lastOffset = -1; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/voip/CallNotificationSoundProvider.java b/TMessagesProj/src/main/java/org/telegram/messenger/voip/CallNotificationSoundProvider.java new file mode 100644 index 000000000..e2940fdb4 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/voip/CallNotificationSoundProvider.java @@ -0,0 +1,85 @@ +package org.telegram.messenger.voip; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; +import android.os.ParcelFileDescriptor; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import org.telegram.messenger.ApplicationLoader; + +import java.io.FileNotFoundException; +import java.io.IOException; + +/** + * This is a very dirty hack to allow Telegram calls to respect user's DND settings. + * URIs to this content provider are specified as the sound for the incoming call notifications. + * We then assume that the system will only try opening these if it actually wants to make a sound + * for this particular call - that's how we know whether the call went through DND and start + * ringing for it. To avoid any potential issues, this serves a wav file containing 5 samples + * of silence. + */ +public class CallNotificationSoundProvider extends ContentProvider{ + + @Override + public boolean onCreate(){ + return true; + } + + @Nullable + @Override + public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder){ + return null; + } + + + @Nullable + @Override + public String getType(@NonNull Uri uri){ + return null; + } + + + @Nullable + @Override + public Uri insert(@NonNull Uri uri, @Nullable ContentValues values){ + return null; + } + + @Override + public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs){ + return 0; + } + + @Override + public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs){ + return 0; + } + + @Nullable + @Override + public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException{ + if(!"r".equals(mode)) + throw new SecurityException("Unexpected file mode "+mode); + if(ApplicationLoader.applicationContext==null) + throw new FileNotFoundException("Unexpected application state"); + + VoIPBaseService srv=VoIPBaseService.getSharedInstance(); + if(srv!=null){ + srv.startRingtoneAndVibration(); + } + + try{ + ParcelFileDescriptor[] pipe=ParcelFileDescriptor.createPipe(); + ParcelFileDescriptor.AutoCloseOutputStream outputStream = new ParcelFileDescriptor.AutoCloseOutputStream(pipe[1]); + byte[] silentWav={82,73,70,70,41,0,0,0,87,65,86,69,102,109,116,32,16,0,0,0,1,0,1,0,68,(byte)172,0,0,16,(byte)177,2,0,2,0,16,0,100,97,116,97,10,0,0,0,0,0,0,0,0,0,0,0,0,0}; + outputStream.write(silentWav); + outputStream.close(); + return pipe[0]; + }catch(IOException x){ + throw new FileNotFoundException(x.getMessage()); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/voip/JNIUtilities.java b/TMessagesProj/src/main/java/org/telegram/messenger/voip/JNIUtilities.java index 93da4c62e..2cf0fb1f8 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/voip/JNIUtilities.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/voip/JNIUtilities.java @@ -6,6 +6,8 @@ import android.net.ConnectivityManager; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.Network; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; import android.os.Build; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; @@ -110,4 +112,13 @@ public class JNIUtilities{ } return null; } + + public static int[] getWifiInfo(){ + try{ + WifiManager wmgr=(WifiManager) ApplicationLoader.applicationContext.getSystemService(Context.WIFI_SERVICE); + WifiInfo info=wmgr.getConnectionInfo(); + return new int[]{info.getRssi(), info.getLinkSpeed()}; + }catch(Exception ignore){} + return null; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPBaseService.java b/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPBaseService.java index 2ca6542bc..58dfdb691 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPBaseService.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPBaseService.java @@ -33,6 +33,7 @@ import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; +import android.media.AudioAttributes; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioTrack; @@ -64,6 +65,7 @@ import android.widget.RemoteViews; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.BuildConfig; import org.telegram.messenger.BuildVars; import org.telegram.messenger.ContactsController; import org.telegram.messenger.FileLoader; @@ -505,13 +507,13 @@ public abstract class VoIPBaseService extends Service implements SensorEventList SharedPreferences prefs = MessagesController.getNotificationsSettings(currentAccount); AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); boolean needRing=am.getRingerMode()!=AudioManager.RINGER_MODE_SILENT; - if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP){ + /*if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP){ try{ int mode=Settings.Global.getInt(getContentResolver(), "zen_mode"); if(needRing) needRing=mode==0; }catch(Exception ignore){} - } + }*/ if(needRing){ if(!USE_CONNECTION_SERVICE){ am.requestAudioFocus(this, AudioManager.STREAM_RING, AudioManager.AUDIOFOCUS_GAIN); @@ -567,6 +569,7 @@ public abstract class VoIPBaseService extends Service implements SensorEventList protected abstract Class getUIActivityClass(); public abstract CallConnection getConnectionAndStartCall(); protected abstract void startRinging(); + public abstract void startRingtoneAndVibration(); @Override public void onDestroy() { @@ -1032,16 +1035,21 @@ public abstract class VoIPBaseService extends Service implements SensorEventList .setSmallIcon(R.drawable.notification) .setSubText(subText) .setContentIntent(PendingIntent.getActivity(this, 0, intent, 0)); + Uri soundProviderUri=Uri.parse("content://"+BuildConfig.APPLICATION_ID+".call_sound_provider/start_ringing"); if (Build.VERSION.SDK_INT >= 26) { SharedPreferences nprefs=MessagesController.getGlobalNotificationsSettings(); int chanIndex=nprefs.getInt("calls_notification_channel", 0); NotificationManager nm=(NotificationManager) getSystemService(NOTIFICATION_SERVICE); - NotificationChannel existingChannel=nm.getNotificationChannel("incoming_calls"+chanIndex); + NotificationChannel oldChannel=nm.getNotificationChannel("incoming_calls"+chanIndex); + if(oldChannel!=null) + nm.deleteNotificationChannel(oldChannel.getId()); + NotificationChannel existingChannel=nm.getNotificationChannel("incoming_calls2"+chanIndex); boolean needCreate=true; if(existingChannel!=null){ - if(existingChannel.getImportance()=Build.VERSION_CODES.LOLLIPOP){ + builder.setSound(soundProviderUri, AudioManager.STREAM_RING); } Intent endIntent = new Intent(this, VoIPActionsReceiver.class); endIntent.setAction(getPackageName() + ".DECLINE_CALL"); @@ -1086,6 +1099,12 @@ public abstract class VoIPBaseService extends Service implements SensorEventList builder.setVibrate(new long[0]); builder.setCategory(Notification.CATEGORY_CALL); builder.setFullScreenIntent(PendingIntent.getActivity(this, 0, intent, 0), true); + if(userOrChat instanceof TLRPC.User){ + TLRPC.User user=(TLRPC.User) userOrChat; + if(!TextUtils.isEmpty(user.phone)){ + builder.addPerson("tel:"+user.phone); + } + } } /*Bitmap photoBitmap=null; if (photo != null) { @@ -1130,11 +1149,13 @@ public abstract class VoIPBaseService extends Service implements SensorEventList } customView.setTextViewText(R.id.title, subText); } + Bitmap avatar=getRoundAvatarBitmap(userOrChat); customView.setTextViewText(R.id.answer_text, LocaleController.getString("VoipAnswerCall", R.string.VoipAnswerCall)); customView.setTextViewText(R.id.decline_text, LocaleController.getString("VoipDeclineCall", R.string.VoipDeclineCall)); - customView.setImageViewBitmap(R.id.photo, getRoundAvatarBitmap(userOrChat)); + customView.setImageViewBitmap(R.id.photo, avatar); customView.setOnClickPendingIntent(R.id.answer_btn, answerPendingIntent); customView.setOnClickPendingIntent(R.id.decline_btn, endPendingIntent); + builder.setLargeIcon(avatar); /*if(groupUsers==null || groupUsers.size()==0){ customView.setViewVisibility(R.id.group_photos, View.GONE); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPController.java b/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPController.java index 205088e3b..62a645bb2 100755 --- a/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPController.java @@ -51,6 +51,7 @@ public class VoIPController{ public static final int DATA_SAVING_NEVER=0; public static final int DATA_SAVING_MOBILE=1; public static final int DATA_SAVING_ALWAYS=2; + public static final int DATA_SAVING_ROAMING=3; public static final int ERROR_CONNECTION_SERVICE=-5; public static final int ERROR_INSECURE_UPGRADE=-4; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPService.java b/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPService.java index f287f5b24..b9cb105df 100755 --- a/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPService.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPService.java @@ -103,6 +103,9 @@ public class VoIPService extends VoIPBaseService{ private List groupUsersToAdd=new ArrayList<>(); private boolean upgrading; private boolean joiningGroupCall; + private String debugLog; + + private boolean startedRinging=false; @Nullable @Override @@ -209,7 +212,7 @@ public class VoIPService extends VoIPBaseService{ @Override protected void onControllerPreRelease(){ - if(BuildConfig.DEBUG){ + /*if(BuildConfig.DEBUG){ String debugLog=controller.getDebugLog(); TLRPC.TL_phone_saveCallDebug req=new TLRPC.TL_phone_saveCallDebug(); req.debug=new TLRPC.TL_dataJSON(); @@ -225,7 +228,9 @@ public class VoIPService extends VoIPBaseService{ } } }); - } + }*/ + if(debugLog==null) + debugLog=controller.getDebugLog(); } public static VoIPService getSharedInstance() { @@ -450,13 +455,13 @@ public class VoIPService extends VoIPBaseService{ FileLog.d("starting ringing for call " + call.id); } dispatchStateChanged(STATE_WAITING_INCOMING); - startRingtoneAndVibration(user.id); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !((KeyguardManager) getSystemService(KEYGUARD_SERVICE)).inKeyguardRestrictedInputMode() && NotificationManagerCompat.from(this).areNotificationsEnabled()) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){ showIncomingNotification(ContactsController.formatName(user.first_name, user.last_name), null, user, null, 0, VoIPActivity.class); if (BuildVars.LOGS_ENABLED) { FileLog.d("Showing incoming call notification"); } } else { + startRingtoneAndVibration(user.id); if (BuildVars.LOGS_ENABLED) { FileLog.d("Starting incall activity for incoming call"); } @@ -467,9 +472,14 @@ public class VoIPService extends VoIPBaseService{ FileLog.e("Error starting incall activity", x); } } - if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){ - showNotification(); - } + } + } + + @Override + public void startRingtoneAndVibration(){ + if(!startedRinging){ + startRingtoneAndVibration(user.id); + startedRinging=true; } } @@ -748,6 +758,25 @@ public class VoIPService extends VoIPBaseService{ if (call.need_rating || forceRating || (controller!=null && VoIPServerConfig.getBoolean("bad_call_rating", true) && controller.needRate())) { startRatingActivity(); } + if(debugLog==null && controller!=null){ + debugLog=controller.getDebugLog(); + } + if(needSendDebugLog && debugLog!=null){ + TLRPC.TL_phone_saveCallDebug req=new TLRPC.TL_phone_saveCallDebug(); + req.debug=new TLRPC.TL_dataJSON(); + req.debug.data=debugLog; + req.peer=new TLRPC.TL_inputPhoneCall(); + req.peer.access_hash=call.access_hash; + req.peer.id=call.id; + ConnectionsManager.getInstance(currentAccount).sendRequest(req, new RequestDelegate(){ + @Override + public void run(TLObject response, TLRPC.TL_error error){ + if (BuildVars.LOGS_ENABLED) { + FileLog.d("Sent debug logs, response=" + response); + } + } + }); + } } else if (call instanceof TLRPC.TL_phoneCall && authKey == null){ if(call.g_a_or_b==null){ if (BuildVars.LOGS_ENABLED) { @@ -916,6 +945,12 @@ public class VoIPService extends VoIPBaseService{ }); } + private int convertDataSavingMode(int mode){ + if(mode!=VoIPController.DATA_SAVING_ROAMING) + return mode; + return ApplicationLoader.isRoaming() ? VoIPController.DATA_SAVING_MOBILE : VoIPController.DATA_SAVING_NEVER; + } + private void initiateActualEncryptedCall() { if (timeoutRunnable != null) { AndroidUtilities.cancelRunOnUIThread(timeoutRunnable); @@ -955,7 +990,7 @@ public class VoIPService extends VoIPBaseService{ nprefs.edit().putStringSet("calls_access_hashes", hashes).commit(); final SharedPreferences preferences = MessagesController.getGlobalMainSettings(); controller.setConfig(MessagesController.getInstance(currentAccount).callPacketTimeout / 1000.0, MessagesController.getInstance(currentAccount).callConnectTimeout / 1000.0, - preferences.getInt("VoipDataSaving", VoIPController.DATA_SAVING_NEVER), call.id); + convertDataSavingMode(preferences.getInt("VoipDataSaving", VoIPHelper.getDataSavingDefault())), call.id); controller.setEncryptionKey(authKey, isOutgoing); TLRPC.TL_phoneConnection[] endpoints = new TLRPC.TL_phoneConnection[1 + call.alternative_connections.size()]; endpoints[0] = call.connection; @@ -1062,6 +1097,9 @@ public class VoIPService extends VoIPBaseService{ } public void onUIForegroundStateChanged(boolean isForeground) { + if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP) + return; + if (currentState == STATE_WAITING_INCOMING) { if (isForeground) { stopForeground(true); diff --git a/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java b/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java index 931656302..76f7ca7d8 100644 --- a/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java +++ b/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java @@ -59,7 +59,7 @@ public class TLRPC { public static final int MESSAGE_FLAG_EDITED = 0x00008000; public static final int MESSAGE_FLAG_MEGAGROUP = 0x80000000; - public static final int LAYER = 95; + public static final int LAYER = 96; public static class TL_chatBannedRights extends TLObject { public static int constructor = 0x9f120418; @@ -11431,6 +11431,7 @@ public class TLRPC { public boolean restricted; public boolean min; public boolean bot_inline_geo; + public boolean support; public int bot_info_version; public String restriction_reason; public String bot_inline_placeholder; @@ -11551,109 +11552,111 @@ public class TLRPC { } } - public static class TL_user extends User { - public static int constructor = 0x2e13f4c3; + public static class TL_user extends User { + public static int constructor = 0x2e13f4c3; - public void readParams(AbstractSerializedData stream, boolean exception) { - flags = stream.readInt32(exception); - self = (flags & 1024) != 0; - contact = (flags & 2048) != 0; - mutual_contact = (flags & 4096) != 0; - deleted = (flags & 8192) != 0; - bot = (flags & 16384) != 0; - bot_chat_history = (flags & 32768) != 0; - bot_nochats = (flags & 65536) != 0; - verified = (flags & 131072) != 0; - restricted = (flags & 262144) != 0; - min = (flags & 1048576) != 0; - bot_inline_geo = (flags & 2097152) != 0; - id = stream.readInt32(exception); - if ((flags & 1) != 0) { - access_hash = stream.readInt64(exception); - } - if ((flags & 2) != 0) { - first_name = stream.readString(exception); - } - if ((flags & 4) != 0) { - last_name = stream.readString(exception); - } - if ((flags & 8) != 0) { - username = stream.readString(exception); - } - if ((flags & 16) != 0) { - phone = stream.readString(exception); - } - if ((flags & 32) != 0) { - photo = UserProfilePhoto.TLdeserialize(stream, stream.readInt32(exception), exception); - } - if ((flags & 64) != 0) { - status = UserStatus.TLdeserialize(stream, stream.readInt32(exception), exception); - } - if ((flags & 16384) != 0) { - bot_info_version = stream.readInt32(exception); - } - if ((flags & 262144) != 0) { - restriction_reason = stream.readString(exception); - } - if ((flags & 524288) != 0) { - bot_inline_placeholder = stream.readString(exception); - } - if ((flags & 4194304) != 0) { - lang_code = stream.readString(exception); - } - } + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + self = (flags & 1024) != 0; + contact = (flags & 2048) != 0; + mutual_contact = (flags & 4096) != 0; + deleted = (flags & 8192) != 0; + bot = (flags & 16384) != 0; + bot_chat_history = (flags & 32768) != 0; + bot_nochats = (flags & 65536) != 0; + verified = (flags & 131072) != 0; + restricted = (flags & 262144) != 0; + min = (flags & 1048576) != 0; + bot_inline_geo = (flags & 2097152) != 0; + support = (flags & 8388608) != 0; + id = stream.readInt32(exception); + if ((flags & 1) != 0) { + access_hash = stream.readInt64(exception); + } + if ((flags & 2) != 0) { + first_name = stream.readString(exception); + } + if ((flags & 4) != 0) { + last_name = stream.readString(exception); + } + if ((flags & 8) != 0) { + username = stream.readString(exception); + } + if ((flags & 16) != 0) { + phone = stream.readString(exception); + } + if ((flags & 32) != 0) { + photo = UserProfilePhoto.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 64) != 0) { + status = UserStatus.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 16384) != 0) { + bot_info_version = stream.readInt32(exception); + } + if ((flags & 262144) != 0) { + restriction_reason = stream.readString(exception); + } + if ((flags & 524288) != 0) { + bot_inline_placeholder = stream.readString(exception); + } + if ((flags & 4194304) != 0) { + lang_code = stream.readString(exception); + } + } - public void serializeToStream(AbstractSerializedData stream) { - stream.writeInt32(constructor); - flags = self ? (flags | 1024) : (flags &~ 1024); - flags = contact ? (flags | 2048) : (flags &~ 2048); - flags = mutual_contact ? (flags | 4096) : (flags &~ 4096); - flags = deleted ? (flags | 8192) : (flags &~ 8192); - flags = bot ? (flags | 16384) : (flags &~ 16384); - flags = bot_chat_history ? (flags | 32768) : (flags &~ 32768); - flags = bot_nochats ? (flags | 65536) : (flags &~ 65536); - flags = verified ? (flags | 131072) : (flags &~ 131072); - flags = restricted ? (flags | 262144) : (flags &~ 262144); - flags = min ? (flags | 1048576) : (flags &~ 1048576); - flags = bot_inline_geo ? (flags | 2097152) : (flags &~ 2097152); - stream.writeInt32(flags); - stream.writeInt32(id); - if ((flags & 1) != 0) { - stream.writeInt64(access_hash); - } - if ((flags & 2) != 0) { - stream.writeString(first_name); - } - if ((flags & 4) != 0) { - stream.writeString(last_name); - } - if ((flags & 8) != 0) { - stream.writeString(username); - } - if ((flags & 16) != 0) { - stream.writeString(phone); - } - if ((flags & 32) != 0) { - photo.serializeToStream(stream); - } - if ((flags & 64) != 0) { - status.serializeToStream(stream); - } - if ((flags & 16384) != 0) { - stream.writeInt32(bot_info_version); - } - if ((flags & 262144) != 0) { - stream.writeString(restriction_reason); - } - if ((flags & 524288) != 0) { - stream.writeString(bot_inline_placeholder); - } - if ((flags & 4194304) != 0) { - stream.writeString(lang_code); - } - } - } + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = self ? (flags | 1024) : (flags &~ 1024); + flags = contact ? (flags | 2048) : (flags &~ 2048); + flags = mutual_contact ? (flags | 4096) : (flags &~ 4096); + flags = deleted ? (flags | 8192) : (flags &~ 8192); + flags = bot ? (flags | 16384) : (flags &~ 16384); + flags = bot_chat_history ? (flags | 32768) : (flags &~ 32768); + flags = bot_nochats ? (flags | 65536) : (flags &~ 65536); + flags = verified ? (flags | 131072) : (flags &~ 131072); + flags = restricted ? (flags | 262144) : (flags &~ 262144); + flags = min ? (flags | 1048576) : (flags &~ 1048576); + flags = bot_inline_geo ? (flags | 2097152) : (flags &~ 2097152); + flags = support ? (flags | 8388608) : (flags &~ 8388608); + stream.writeInt32(flags); + stream.writeInt32(id); + if ((flags & 1) != 0) { + stream.writeInt64(access_hash); + } + if ((flags & 2) != 0) { + stream.writeString(first_name); + } + if ((flags & 4) != 0) { + stream.writeString(last_name); + } + if ((flags & 8) != 0) { + stream.writeString(username); + } + if ((flags & 16) != 0) { + stream.writeString(phone); + } + if ((flags & 32) != 0) { + photo.serializeToStream(stream); + } + if ((flags & 64) != 0) { + status.serializeToStream(stream); + } + if ((flags & 16384) != 0) { + stream.writeInt32(bot_info_version); + } + if ((flags & 262144) != 0) { + stream.writeString(restriction_reason); + } + if ((flags & 524288) != 0) { + stream.writeString(bot_inline_placeholder); + } + if ((flags & 4194304) != 0) { + stream.writeString(lang_code); + } + } + } public static class TL_userSelf_old extends TL_userSelf_old3 { public static int constructor = 0x720535ec; @@ -17280,7 +17283,7 @@ public class TLRPC { case 0x6e5f8c22: result = new TL_updateChatParticipantDelete(); break; - case 0x22893b26: + case 0xe10db349: result = new TL_updateChatPinnedMessage(); break; case 0xe40370a3: @@ -17763,20 +17766,23 @@ public class TLRPC { } public static class TL_updateChatPinnedMessage extends Update { - public static int constructor = 0x22893b26; + public static int constructor = 0xe10db349; public int chat_id; public int id; + public int version; public void readParams(AbstractSerializedData stream, boolean exception) { chat_id = stream.readInt32(exception); id = stream.readInt32(exception); + version = stream.readInt32(exception); } public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); stream.writeInt32(chat_id); stream.writeInt32(id); + stream.writeInt32(version); } } @@ -23472,6 +23478,40 @@ public class TLRPC { } } + public static class TL_account_autoDownloadSettings extends TLObject { + public static int constructor = 0x63cacf26; + + public TL_autoDownloadSettings low; + public TL_autoDownloadSettings medium; + public TL_autoDownloadSettings high; + + public static TL_account_autoDownloadSettings TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_account_autoDownloadSettings.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_account_autoDownloadSettings", constructor)); + } else { + return null; + } + } + TL_account_autoDownloadSettings result = new TL_account_autoDownloadSettings(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + low = TL_autoDownloadSettings.TLdeserialize(stream, stream.readInt32(exception), exception); + medium = TL_autoDownloadSettings.TLdeserialize(stream, stream.readInt32(exception), exception); + high = TL_autoDownloadSettings.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + low.serializeToStream(stream); + medium.serializeToStream(stream); + high.serializeToStream(stream); + } + } + public static abstract class ChatParticipant extends TLObject { public int user_id; public int inviter_id; @@ -24131,6 +24171,55 @@ public class TLRPC { } } + public static class TL_autoDownloadSettings extends TLObject { + public static int constructor = 0xd246fd47; + + public int flags; + public boolean disabled; + public boolean video_preload_large; + public boolean audio_preload_next; + public boolean phonecalls_less_data; + public int photo_size_max; + public int video_size_max; + public int file_size_max; + + public static TL_autoDownloadSettings TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_autoDownloadSettings.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_autoDownloadSettings", constructor)); + } else { + return null; + } + } + TL_autoDownloadSettings result = new TL_autoDownloadSettings(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + disabled = (flags & 1) != 0; + video_preload_large = (flags & 2) != 0; + audio_preload_next = (flags & 4) != 0; + phonecalls_less_data = (flags & 8) != 0; + photo_size_max = stream.readInt32(exception); + video_size_max = stream.readInt32(exception); + file_size_max = stream.readInt32(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = disabled ? (flags | 1) : (flags &~ 1); + flags = video_preload_large ? (flags | 2) : (flags &~ 2); + flags = audio_preload_next ? (flags | 4) : (flags &~ 4); + flags = phonecalls_less_data ? (flags | 8) : (flags &~ 8); + stream.writeInt32(flags); + stream.writeInt32(photo_size_max); + stream.writeInt32(video_size_max); + stream.writeInt32(file_size_max); + } + } + public static class TL_account_sentEmailCode extends TLObject { public static int constructor = 0x811f854f; @@ -31092,6 +31181,40 @@ public class TLRPC { } } + public static class TL_account_getAutoDownloadSettings extends TLObject { + public static int constructor = 0x56da0b3f; + + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return TL_account_autoDownloadSettings.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_account_saveAutoDownloadSettings extends TLObject { + public static int constructor = 0x76f36233; + + public int flags; + public boolean low; + public boolean high; + public TL_autoDownloadSettings settings; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = low ? (flags | 1) : (flags &~ 1); + flags = high ? (flags | 2) : (flags &~ 2); + stream.writeInt32(flags); + settings.serializeToStream(stream); + } + } + public static class TL_messages_getAllStickers extends TLObject { public static int constructor = 0x1c9618b1; @@ -32386,9 +32509,10 @@ public class TLRPC { } public static class TL_messages_getStatsURL extends TLObject { - public static int constructor = 0x83f6c0cd; + public static int constructor = 0xebd58cb3; public InputPeer peer; + public String params; public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { return TL_statsURL.TLdeserialize(stream, constructor, exception); @@ -32397,6 +32521,7 @@ public class TLRPC { public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); + stream.writeString(params); } } @@ -34390,6 +34515,7 @@ public class TLRPC { public boolean with_my_score; public TLRPC.Message replyMessage; //custom public int reqId; //custom + public int realId; //custom public static Message TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { Message result = null; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java index 7d9b3e056..ebd5203aa 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java @@ -40,6 +40,7 @@ import android.widget.FrameLayout; import android.widget.LinearLayout; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.FileLog; import org.telegram.messenger.MessagesController; import org.telegram.messenger.R; import org.telegram.ui.Components.CubicBezierInterpolator; @@ -126,8 +127,8 @@ public class ActionBarLayout extends FrameLayout { // try { return (!inPreviewMode || this != containerView) && super.dispatchTouchEvent(ev); - } catch (Throwable ignore) { - + } catch (Throwable e) { + FileLog.e(e); } return false; } @@ -1058,7 +1059,11 @@ public class ActionBarLayout extends FrameLayout { ViewGroup parent = (ViewGroup) fragmentView.getParent(); if (parent != null) { previousFragment.onRemoveFromParent(); - parent.removeView(fragmentView); + try { + parent.removeView(fragmentView); + } catch (Exception ignore) { + + } } containerView.addView(fragmentView); LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) fragmentView.getLayoutParams(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java index bd50a3b2d..a4e553d93 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java @@ -404,6 +404,7 @@ public class Theme { public static TextPaint chat_contextResult_titleTextPaint; public static TextPaint chat_contextResult_descriptionTextPaint; + public static Drawable chat_msgNoSoundDrawable; public static Drawable chat_composeShadowDrawable; public static Drawable chat_roundVideoShadow; public static Drawable chat_msgInDrawable; @@ -532,6 +533,9 @@ public class Theme { public static final String key_windowBackgroundWhite = "windowBackgroundWhite"; + public static final String key_windowBackgroundUnchecked = "windowBackgroundUnchecked"; + public static final String key_windowBackgroundChecked = "windowBackgroundChecked"; + public static final String key_windowBackgroundCheckText = "windowBackgroundCheckText"; public static final String key_progressCircle = "progressCircle"; public static final String key_listSelector = "listSelectorSDK21"; public static final String key_windowBackgroundWhiteInputField = "windowBackgroundWhiteInputField"; @@ -571,6 +575,12 @@ public class Theme { public static final String key_windowBackgroundWhiteBlueHeader = "windowBackgroundWhiteBlueHeader"; public static final String key_switchTrack = "switchTrack"; public static final String key_switchTrackChecked = "switchTrackChecked"; + public static final String key_switchTrackBlue = "switchTrackBlue"; + public static final String key_switchTrackBlueChecked = "switchTrackBlueChecked"; + public static final String key_switchTrackBlueThumb = "switchTrackBlueThumb"; + public static final String key_switchTrackBlueThumbChecked = "switchTrackBlueThumbChecked"; + public static final String key_switchTrackBlueSelector = "switchTrackBlueSelector"; + public static final String key_switchTrackBlueSelectorChecked = "switchTrackBlueSelectorChecked"; public static final String key_switch2Track = "switch2Track"; public static final String key_switch2TrackChecked = "switch2TrackChecked"; public static final String key_checkboxSquareBackground = "checkboxSquareBackground"; @@ -606,6 +616,8 @@ public class Theme { public static final String key_contextProgressOuter2 = "contextProgressOuter2"; public static final String key_contextProgressInner3 = "contextProgressInner3"; public static final String key_contextProgressOuter3 = "contextProgressOuter3"; + public static final String key_contextProgressInner4 = "contextProgressInner4"; + public static final String key_contextProgressOuter4 = "contextProgressOuter4"; public static final String key_avatar_text = "avatar_text"; public static final String key_avatar_backgroundSaved = "avatar_backgroundSaved"; @@ -825,10 +837,10 @@ public class Theme { public static final String key_chat_outTimeText = "chat_outTimeText"; public static final String key_chat_inTimeSelectedText = "chat_inTimeSelectedText"; public static final String key_chat_outTimeSelectedText = "chat_outTimeSelectedText"; - public static final String key_chat_inAudioPerfomerText = "chat_inAudioPerfomerText"; - public static final String key_chat_inAudioPerfomerSelectedText = "chat_inAudioPerfomerSelectedText"; - public static final String key_chat_outAudioPerfomerText = "chat_outAudioPerfomerText"; - public static final String key_chat_outAudioPerfomerSelectedText = "chat_outAudioPerfomerSelectedText"; + public static final String key_chat_inAudioPerformerText = "chat_inAudioPerfomerText"; + public static final String key_chat_inAudioPerformerSelectedText = "chat_inAudioPerfomerSelectedText"; + public static final String key_chat_outAudioPerformerText = "chat_outAudioPerfomerText"; + public static final String key_chat_outAudioPerformerSelectedText = "chat_outAudioPerfomerSelectedText"; public static final String key_chat_inAudioTitleText = "chat_inAudioTitleText"; public static final String key_chat_outAudioTitleText = "chat_outAudioTitleText"; public static final String key_chat_inAudioDurationText = "chat_inAudioDurationText"; @@ -1124,6 +1136,9 @@ public class Theme { defaultColors.put(key_dialog_inlineProgress, 0xff6b7378); defaultColors.put(key_windowBackgroundWhite, 0xffffffff); + defaultColors.put(key_windowBackgroundUnchecked, 0xff9da7b1); + defaultColors.put(key_windowBackgroundChecked, 0xff579ed9); + defaultColors.put(key_windowBackgroundCheckText, 0xffffffff); defaultColors.put(key_progressCircle, 0xff527da3); defaultColors.put(key_windowBackgroundWhiteGrayIcon, 0xff81868b); defaultColors.put(key_windowBackgroundWhiteBlueText, 0xff4092cd); @@ -1162,6 +1177,12 @@ public class Theme { defaultColors.put(key_windowBackgroundWhiteInputFieldActivated, 0xff37a9f0); defaultColors.put(key_switchTrack, 0xffb0b5ba); defaultColors.put(key_switchTrackChecked, 0xff52ade9); + defaultColors.put(key_switchTrackBlue, 0xff828e99); + defaultColors.put(key_switchTrackBlueChecked, 0xff3c88c7); + defaultColors.put(key_switchTrackBlueThumb, 0xffffffff); + defaultColors.put(key_switchTrackBlueThumbChecked, 0xffffffff); + defaultColors.put(key_switchTrackBlueSelector, 0x17404a53); + defaultColors.put(key_switchTrackBlueSelectorChecked, 0x21024781); defaultColors.put(key_switch2Track, 0xfff57e7e); defaultColors.put(key_switch2TrackChecked, 0xff52ade9); defaultColors.put(key_checkboxSquareBackground, 0xff43a0df); @@ -1183,6 +1204,8 @@ public class Theme { defaultColors.put(key_contextProgressOuter2, 0xffffffff); defaultColors.put(key_contextProgressInner3, 0xffb3b3b3); defaultColors.put(key_contextProgressOuter3, 0xffffffff); + defaultColors.put(key_contextProgressInner4, 0xffcacdd0); + defaultColors.put(key_contextProgressOuter4, 0xff2f3438); defaultColors.put(key_fastScrollActive, 0xff52a3db); defaultColors.put(key_fastScrollInactive, 0xffc9cdd1); defaultColors.put(key_fastScrollText, 0xffffffff); @@ -1401,10 +1424,10 @@ public class Theme { defaultColors.put(key_chat_adminSelectedText, 0xff89b4c1); defaultColors.put(key_chat_inTimeSelectedText, 0xff89b4c1); defaultColors.put(key_chat_outTimeSelectedText, 0xff70b15c); - defaultColors.put(key_chat_inAudioPerfomerText, 0xff2f3438); - defaultColors.put(key_chat_inAudioPerfomerSelectedText, 0xff2f3438); - defaultColors.put(key_chat_outAudioPerfomerText, 0xff354234); - defaultColors.put(key_chat_outAudioPerfomerSelectedText, 0xff354234); + defaultColors.put(key_chat_inAudioPerformerText, 0xff2f3438); + defaultColors.put(key_chat_inAudioPerformerSelectedText, 0xff2f3438); + defaultColors.put(key_chat_outAudioPerformerText, 0xff354234); + defaultColors.put(key_chat_outAudioPerformerSelectedText, 0xff354234); defaultColors.put(key_chat_inAudioTitleText, 0xff4e9ad4); defaultColors.put(key_chat_outAudioTitleText, 0xff55ab4f); defaultColors.put(key_chat_inAudioDurationText, 0xffa1aab3); @@ -1689,6 +1712,17 @@ public class Theme { fallbackKeys.put(key_undo_background, key_chat_gifSaveHintBackground); fallbackKeys.put(key_undo_cancelColor, key_chat_gifSaveHintText); fallbackKeys.put(key_undo_infoColor, key_chat_gifSaveHintText); + fallbackKeys.put(key_windowBackgroundUnchecked, key_windowBackgroundWhite); + fallbackKeys.put(key_windowBackgroundChecked, key_windowBackgroundWhite); + fallbackKeys.put(key_switchTrackBlue, key_switchTrack); + fallbackKeys.put(key_switchTrackBlueChecked, key_switchTrackChecked); + fallbackKeys.put(key_switchTrackBlueThumb, key_windowBackgroundWhite); + fallbackKeys.put(key_switchTrackBlueThumbChecked, key_windowBackgroundWhite); + fallbackKeys.put(key_windowBackgroundCheckText, key_windowBackgroundWhiteBlackText); + fallbackKeys.put(key_contextProgressInner4, key_contextProgressInner1); + fallbackKeys.put(key_contextProgressOuter4, key_contextProgressOuter1); + fallbackKeys.put(key_switchTrackBlueSelector, key_listSelector); + fallbackKeys.put(key_switchTrackBlueSelectorChecked, key_listSelector); themes = new ArrayList<>(); otherThemes = new ArrayList<>(); @@ -2936,6 +2970,8 @@ public class Theme { chat_msgInDrawable = resources.getDrawable(R.drawable.msg_in).mutate(); chat_msgInSelectedDrawable = resources.getDrawable(R.drawable.msg_in).mutate(); + chat_msgNoSoundDrawable = resources.getDrawable(R.drawable.video_muted); + chat_msgOutDrawable = resources.getDrawable(R.drawable.msg_out).mutate(); chat_msgOutSelectedDrawable = resources.getDrawable(R.drawable.msg_out).mutate(); @@ -3199,6 +3235,7 @@ public class Theme { chat_composeBackgroundPaint.setColor(getColor(key_chat_messagePanelBackground)); chat_timeBackgroundPaint.setColor(getColor(key_chat_mediaTimeBackground)); + setDrawableColorByKey(chat_msgNoSoundDrawable, key_chat_mediaTimeText); setDrawableColorByKey(chat_msgInDrawable, key_chat_inBubble); setDrawableColorByKey(chat_msgInSelectedDrawable, key_chat_inBubbleSelected); setDrawableColorByKey(chat_msgInShadowDrawable, key_chat_inBubbleShadow); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsAdapter.java index 92ce33f70..8383bc542 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsAdapter.java @@ -39,6 +39,7 @@ import org.telegram.ui.Cells.UserCell; import org.telegram.ui.Components.CombinedDrawable; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.DialogsActivity; import java.util.ArrayList; @@ -95,27 +96,10 @@ public class DialogsAdapter extends RecyclerListView.SelectionAdapter { return current != getItemCount() || current == 1; } - private ArrayList getDialogsArray() { - if (dialogsType == 0) { - return MessagesController.getInstance(currentAccount).dialogs; - } else if (dialogsType == 1) { - return MessagesController.getInstance(currentAccount).dialogsServerOnly; - } else if (dialogsType == 2) { - return MessagesController.getInstance(currentAccount).dialogsGroupsOnly; - } else if (dialogsType == 3) { - return MessagesController.getInstance(currentAccount).dialogsForward; - } else if (dialogsType == 4) { - return MessagesController.getInstance(currentAccount).dialogsUsersOnly; - } else if (dialogsType == 5) { - return MessagesController.getInstance(currentAccount).dialogsChannelsOnly; - } - return null; - } - @Override public int getItemCount() { showContacts = false; - ArrayList array = getDialogsArray(); + ArrayList array = DialogsActivity.getDialogsArray(dialogsType, currentAccount); int dialogsCount = array.size(); if (dialogsCount == 0 && MessagesController.getInstance(currentAccount).loadingDialogs) { return 0; @@ -147,7 +131,7 @@ public class DialogsAdapter extends RecyclerListView.SelectionAdapter { } return MessagesController.getInstance(currentAccount).getUser(ContactsController.getInstance(currentAccount).contacts.get(i).user_id); } - ArrayList arrayList = getDialogsArray(); + ArrayList arrayList = DialogsActivity.getDialogsArray(dialogsType, currentAccount); if (hasHints) { int count = MessagesController.getInstance(currentAccount).hintDialogs.size(); if (i < 2 + count) { @@ -319,7 +303,7 @@ public class DialogsAdapter extends RecyclerListView.SelectionAdapter { i -= 2 + count; } } - if (i == getDialogsArray().size()) { + if (i == DialogsActivity.getDialogsArray(dialogsType, currentAccount).size()) { if (!MessagesController.getInstance(currentAccount).dialogsEndReached) { return 1; } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java index 1a14c7f32..67a8e85b0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java @@ -5563,6 +5563,8 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg setWillNotDraw(false); imageView = new ImageReceiver(this); + imageView.setNeedsQualityThumb(true); + imageView.setShouldGenerateQualityThumb(true); currentType = type; radialProgress = new RadialProgress(this); radialProgress.setAlphaForPrevious(true); @@ -5704,14 +5706,16 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg photoHeight -= AndroidUtilities.dp(2); } } + imageView.setQualityThumbDocument(currentDocument); imageView.setImageCoords(photoX, (isFirst || currentType == 1 || currentType == 2 || currentBlock.level > 0) ? 0 : AndroidUtilities.dp(8), photoWidth, photoHeight); + if (isGif) { String filter = String.format(Locale.US, "%d_%d", photoWidth, photoHeight); - imageView.setImage(currentDocument, filter, thumb, "80_80_b", currentDocument.size, null, currentPage, 1); + imageView.setImage(currentDocument, null, null, null, null, thumb, "80_80_b", currentDocument.size, null, currentPage, 1); } else { imageView.setImage(null, null, thumb, "80_80_b", 0, null, currentPage, 1); } - imageView.setAspectFit(isGif); + imageView.setAspectFit(true); buttonX = (int) (imageView.getImageX() + (imageView.getImageWidth() - size) / 2.0f); buttonY = (int) (imageView.getImageY() + (imageView.getImageHeight() - size) / 2.0f); radialProgress.setProgressRect(buttonX, buttonY, buttonX + size, buttonY + size); @@ -6323,7 +6327,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg avatarImageView.setImage(image, String.format(Locale.US, "%d_%d", 40, 40), avatarDrawable, 0, null, currentPage, 1); } } - nameLayout = createLayoutForText(this, currentBlock.author, null, width - AndroidUtilities.dp(36 + 14 + (avatarVisible ? 40 + 14 : 0)), currentBlock, parentAdapter); + nameLayout = createLayoutForText(this, currentBlock.author, null, width - AndroidUtilities.dp(36 + 14 + (avatarVisible ? 40 + 14 : 0)), 0, currentBlock, Layout.Alignment.ALIGN_NORMAL, 1, parentAdapter); if (currentBlock.date != 0) { dateLayout = createLayoutForText(this, LocaleController.getInstance().chatFullDate.format((long) currentBlock.date * 1000), null, width - AndroidUtilities.dp(36 + 14 + (avatarVisible ? 40 + 14 : 0)), currentBlock, parentAdapter); } else { @@ -6489,6 +6493,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg private int creditOffset; private int listX; private int exactWebViewHeight; + private boolean wasUserInteraction; private TLRPC.TL_pageBlockEmbed currentBlock; @@ -6503,6 +6508,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg @Override public boolean onTouchEvent(MotionEvent event) { + wasUserInteraction = true; if (currentBlock != null) { if (currentBlock.allow_scrolling) { requestDisallowInterceptTouchEvent(true); @@ -6683,8 +6689,11 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { - Browser.openUrl(parentActivity, url); - return true; + if (wasUserInteraction) { + Browser.openUrl(parentActivity, url); + return true; + } + return false; } }); addView(webView); @@ -6708,6 +6717,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg TLRPC.TL_pageBlockEmbed previousBlock = currentBlock; currentBlock = block; if (previousBlock != currentBlock) { + wasUserInteraction = false; if (currentBlock.allow_scrolling) { webView.setVerticalScrollBarEnabled(true); webView.setHorizontalScrollBarEnabled(true); @@ -10146,7 +10156,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg } }; - private float animationValues[][] = new float[2][8]; + private float animationValues[][] = new float[2][10]; private int photoAnimationInProgress; private long photoTransitionAnimationStartTime; @@ -10573,7 +10583,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg private void releasePlayer() { if (videoPlayer != null) { - videoPlayer.releasePlayer(); + videoPlayer.releasePlayer(true); videoPlayer = null; } try { @@ -11221,6 +11231,8 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg animationValues[0][5] = clipTop * object.scale; animationValues[0][6] = clipBottom * object.scale; animationValues[0][7] = animatingImageView.getRadius(); + animationValues[0][8] = clipVertical * object.scale; + animationValues[0][9] = clipHorizontal * object.scale; animationValues[1][0] = scale; animationValues[1][1] = scale; @@ -11230,6 +11242,8 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg animationValues[1][5] = 0; animationValues[1][6] = 0; animationValues[1][7] = 0; + animationValues[1][8] = 0; + animationValues[1][9] = 0; photoContainerView.setVisibility(View.VISIBLE); photoContainerBackground.setVisibility(View.VISIBLE); @@ -11394,6 +11408,8 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg animationValues[0][5] = 0; animationValues[0][6] = 0; animationValues[0][7] = 0; + animationValues[0][8] = 0; + animationValues[0][9] = 0; animationValues[1][0] = object.scale; animationValues[1][1] = object.scale; @@ -11403,6 +11419,8 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg animationValues[1][5] = clipTop * object.scale; animationValues[1][6] = clipBottom * object.scale; animationValues[1][7] = object.radius; + animationValues[1][8] = clipVertical * object.scale; + animationValues[1][9] = clipHorizontal * object.scale; animatorSet.playTogether( ObjectAnimator.ofFloat(animatingImageView, "animationProgress", 0.0f, 1.0f), @@ -11609,7 +11627,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg moveStartX = ev.getX(); moveStartY = ev.getY(); updateMinMax(scale); - if (translationX < minX && (!rightImage.hasImage()) || translationX > maxX && !leftImage.hasImage()) { + if (translationX < minX && (!rightImage.hasImageSet()) || translationX > maxX && !leftImage.hasImageSet()) { moveDx /= 3.0f; } if (maxY == 0 && minY == 0) { @@ -11683,11 +11701,11 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg velocity = velocityTracker.getXVelocity(); } - if ((translationX < minX - getContainerViewWidth() / 3 || velocity < -AndroidUtilities.dp(650)) && rightImage.hasImage()) { + if ((translationX < minX - getContainerViewWidth() / 3 || velocity < -AndroidUtilities.dp(650)) && rightImage.hasImageSet()) { goToNext(); return true; } - if ((translationX > maxX + getContainerViewWidth() / 3 || velocity > AndroidUtilities.dp(650)) && leftImage.hasImage()) { + if ((translationX > maxX + getContainerViewWidth() / 3 || velocity > AndroidUtilities.dp(650)) && leftImage.hasImageSet()) { goToPrev(); return true; } 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 daa0c573d..da3b52ade 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java @@ -26,6 +26,7 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.RippleDrawable; import android.net.Uri; import android.os.Build; +import android.os.SystemClock; import android.text.Layout; import android.text.Spannable; import android.text.SpannableStringBuilder; @@ -68,6 +69,7 @@ import org.telegram.messenger.WebFile; import org.telegram.messenger.browser.Browser; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; +import org.telegram.ui.Components.AnimatedFileDrawable; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.LinkPath; import org.telegram.ui.Components.MediaActionDrawable; @@ -147,6 +149,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate default boolean canPerformActions() { return false; } + + default void videoTimerReached() { + } } private final static int DOCUMENT_ATTACH_TYPE_NONE = 0; @@ -215,6 +220,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate private boolean attachedToWindow; private RadialProgress2 radialProgress; + private RadialProgress2 videoRadialProgress; private boolean drawRadialCheckBackground; private ImageReceiver photoImage; private AvatarDrawable contactAvatarDrawable; @@ -223,6 +229,16 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate private boolean isSmallImage; private boolean drawImageButton; + private boolean drawVideoImageButton; + private boolean drawVideoSize; + private boolean canStreamVideo; + private int animatingDrawVideoImageButton; + private float animatingDrawVideoImageButtonProgress; + private boolean animatingNoSoundPlaying; + private int animatingNoSound; + private float animatingNoSoundProgress; + private int noSoundCenterX; + private long lastAnimationTime; private int documentAttachType; private TLRPC.Document documentAttach; private boolean drawPhotoImage; @@ -266,6 +282,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate private RoundVideoPlayingDrawable roundVideoPlayingDrawable; private StaticLayout docTitleLayout; + private int docTitleWidth; private int docTitleOffsetX; private boolean locationExpired; @@ -288,11 +305,15 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate private boolean hasEmbed; + private boolean wasSending; private boolean checkOnlyButtonPressed; private int buttonX; private int buttonY; + private int videoButtonX; + private int videoButtonY; private int buttonState; private int buttonPressed; + private int videoButtonPressed; private int miniButtonPressed; private int otherX; private int otherY; @@ -363,6 +384,8 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate private ArrayList lastPollResults; private int lastPollResultsVoters; + private boolean autoPlayingVideo; + private ArrayList botButtons = new ArrayList<>(); private HashMap botButtonsByData = new HashMap<>(); private HashMap botButtonsByPosition = new HashMap<>(); @@ -370,6 +393,12 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate private int widthForButtons; private int pressedBotButton; + private MessageObject currentMessageObject; + private MessageObject messageObjectToSet; + private MessageObject.GroupedMessages groupedMessagesToSet; + private boolean topNearToSet; + private boolean bottomNearToSet; + // private int TAG; private int currentAccount = UserConfig.selectedAccount; @@ -391,7 +420,6 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate private Drawable currentBackgroundDrawable; private int backgroundDrawableLeft; private int backgroundDrawableRight; - private MessageObject currentMessageObject; private int viaWidth; private int viaNameWidth; private int availableTimeWidth; @@ -508,6 +536,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate photoImage = new ImageReceiver(this); photoImage.setDelegate(this); radialProgress = new RadialProgress2(this); + videoRadialProgress = new RadialProgress2(this); + videoRadialProgress.setDrawBackground(false); + videoRadialProgress.setCircleRadius(AndroidUtilities.dp(15)); seekBar = new SeekBar(context); seekBar.setDelegate(this); seekBarWaveform = new SeekBarWaveform(context); @@ -742,7 +773,11 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate int y = (int) event.getY(); if (event.getAction() == MotionEvent.ACTION_DOWN) { - if (drawPhotoImage && photoImage.isInsideImage(x, y)) { + if (drawPhotoImage && drawImageButton && buttonState != -1 && x >= buttonX && x <= buttonX + AndroidUtilities.dp(48) && y >= buttonY && y <= buttonY + AndroidUtilities.dp(48) && radialProgress.getIcon() != MediaActionDrawable.ICON_NONE) { + buttonPressed = 1; + invalidate(); + return true; + } else if (drawPhotoImage && photoImage.isInsideImage(x, y)) { gamePreviewPressed = true; return true; } else if (descriptionLayout != null && y >= descriptionY) { @@ -782,8 +817,13 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } } } else if (event.getAction() == MotionEvent.ACTION_UP) { - if (pressedLinkType == 2 || gamePreviewPressed) { - if (pressedLink != null) { + if (pressedLinkType == 2 || gamePreviewPressed || buttonPressed != 0) { + if (buttonPressed != 0) { + buttonPressed = 0; + playSoundEffect(SoundEffectConstants.CLICK); + didPressButton(true, false); + invalidate(); + } else if (pressedLink != null) { if (pressedLink instanceof URLSpan) { Browser.openUrl(getContext(), ((URLSpan) pressedLink).getURL()); } else if (pressedLink instanceof ClickableSpan) { @@ -869,8 +909,13 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate miniButtonPressed = 1; invalidate(); return true; - } else if (drawPhotoImage && drawImageButton && buttonState != -1 && (!checkOnlyButtonPressed && photoImage.isInsideImage(x, y) || x >= buttonX && x <= buttonX + AndroidUtilities.dp(48) && y >= buttonY && y <= buttonY + AndroidUtilities.dp(48))) { + } else if (drawVideoImageButton && buttonState != -1 && x >= videoButtonX && x <= videoButtonX + AndroidUtilities.dp(26 + 8) + Math.max(infoWidth, docTitleWidth) && y >= videoButtonY && y <= videoButtonY + AndroidUtilities.dp(30)) { + videoButtonPressed = 1; + invalidate(); + return true; + } else if (drawPhotoImage && drawImageButton && buttonState != -1 && (!checkOnlyButtonPressed && photoImage.isInsideImage(x, y) || x >= buttonX && x <= buttonX + AndroidUtilities.dp(48) && y >= buttonY && y <= buttonY + AndroidUtilities.dp(48) && radialProgress.getIcon() != MediaActionDrawable.ICON_NONE)) { buttonPressed = 1; + invalidate(); return true; } else if (drawInstantView) { instantPressed = true; @@ -904,11 +949,20 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } instantPressed = instantButtonPressed = false; invalidate(); - } else if (pressedLinkType == 2 || buttonPressed != 0 || miniButtonPressed != 0 || linkPreviewPressed) { - if (buttonPressed != 0) { + } else if (pressedLinkType == 2 || buttonPressed != 0 || miniButtonPressed != 0 || videoButtonPressed != 0 || linkPreviewPressed) { + if (videoButtonPressed == 1) { + videoButtonPressed = 0; + playSoundEffect(SoundEffectConstants.CLICK); + didPressButton(true, true); + invalidate(); + } else if (buttonPressed != 0) { buttonPressed = 0; playSoundEffect(SoundEffectConstants.CLICK); - didPressButton(true); + if (drawVideoImageButton) { + didClickedImage(); + } else { + didPressButton(true, false); + } invalidate(); } else if (miniButtonPressed != 0) { miniButtonPressed = 0; @@ -943,7 +997,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate playSoundEffect(SoundEffectConstants.CLICK); } } else if (buttonState == 2 || buttonState == 0) { - didPressButton(true); + didPressButton(true, false); playSoundEffect(SoundEffectConstants.CLICK); } } else { @@ -1116,6 +1170,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (event.getAction() == MotionEvent.ACTION_DOWN) { boolean area2 = false; int side = AndroidUtilities.dp(48); + if (miniButtonState >= 0) { int offset = AndroidUtilities.dp(27); area2 = x >= buttonX + offset && x <= buttonX + offset + side && y >= buttonY + offset && y <= buttonY + offset + side; @@ -1124,10 +1179,14 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate miniButtonPressed = 1; invalidate(); result = true; - } else if (buttonState != -1 && x >= buttonX && x <= buttonX + side && y >= buttonY && y <= buttonY + side) { + } else if (buttonState != -1 && radialProgress.getIcon() != MediaActionDrawable.ICON_NONE && x >= buttonX && x <= buttonX + side && y >= buttonY && y <= buttonY + side) { buttonPressed = 1; invalidate(); result = true; + } else if (drawVideoImageButton && buttonState != -1 && x >= videoButtonX && x <= videoButtonX + AndroidUtilities.dp(26 + 8) + Math.max(infoWidth, docTitleWidth) && y >= videoButtonY && y <= videoButtonY + AndroidUtilities.dp(30)) { + videoButtonPressed = 1; + invalidate(); + result = true; } else { if (documentAttachType == DOCUMENT_ATTACH_TYPE_DOCUMENT) { if (x >= photoImage.getImageX() && x <= photoImage.getImageX() + backgroundWidth - AndroidUtilities.dp(50) && y >= photoImage.getImageY() && y <= photoImage.getImageY() + photoImage.getImageHeight()) { @@ -1162,10 +1221,19 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } } else { if (event.getAction() == MotionEvent.ACTION_UP) { - if (buttonPressed == 1) { + if (videoButtonPressed == 1) { + videoButtonPressed = 0; + playSoundEffect(SoundEffectConstants.CLICK); + didPressButton(true, true); + invalidate(); + } else if (buttonPressed == 1) { buttonPressed = 0; playSoundEffect(SoundEffectConstants.CLICK); - didPressButton(true); + if (drawVideoImageButton) { + didClickedImage(); + } else { + didPressButton(true, false); + } invalidate(); } else if (miniButtonPressed == 1) { miniButtonPressed = 0; @@ -1174,12 +1242,12 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate invalidate(); } else if (imagePressed) { imagePressed = false; - if (buttonState == -1 || buttonState == 2 || buttonState == 3) { + if (buttonState == -1 || buttonState == 2 || buttonState == 3 || drawVideoImageButton) { playSoundEffect(SoundEffectConstants.CLICK); didClickedImage(); } else if (buttonState == 0 && documentAttachType == DOCUMENT_ATTACH_TYPE_DOCUMENT) { playSoundEffect(SoundEffectConstants.CLICK); - didPressButton(true); + didPressButton(true, false); } invalidate(); } @@ -1205,7 +1273,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (!useSeekBarWaweform && event.getAction() == MotionEvent.ACTION_DOWN) { getParent().requestDisallowInterceptTouchEvent(true); } else if (useSeekBarWaweform && !seekBarWaveform.isStartDraging() && event.getAction() == MotionEvent.ACTION_UP) { - didPressButton(true); + didPressButton(true, false); } disallowLongPress = true; invalidate(); @@ -1238,7 +1306,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (event.getAction() == MotionEvent.ACTION_UP) { buttonPressed = 0; playSoundEffect(SoundEffectConstants.CLICK); - didPressButton(true); + didPressButton(true, false); invalidate(); } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { buttonPressed = 0; @@ -1514,7 +1582,37 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate return; } - if (currentMessageObject.isRoundVideo()) { + if (documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO) { + if (PhotoViewer.isPlayingMessage(currentMessageObject) || MediaController.getInstance().isGoingToShowMessageObject(currentMessageObject)) { + return; + } + int duration = 0; + AnimatedFileDrawable animation = photoImage.getAnimation(); + if (animation != null) { + duration = currentMessageObject.audioPlayerDuration = animation.getDurationMs() / 1000; + } + if (duration == 0) { + duration = currentMessageObject.getDuration(); + } + if (MediaController.getInstance().isPlayingMessage(currentMessageObject)) { + duration -= duration * currentMessageObject.audioProgress; + } else if (animation != null) { + if (duration != 0) { + duration -= animation.getCurrentProgressMs() / 1000; + } + if (delegate != null && animation.getCurrentProgressMs() >= 3000) { + delegate.videoTimerReached(); + } + } + int minutes = duration / 60; + int seconds = duration - minutes * 60; + if (lastTime != duration) { + String str = String.format("%d:%02d", minutes, seconds); + infoWidth = (int) Math.ceil(Theme.chat_infoPaint.measureText(str)); + infoLayout = new StaticLayout(str, Theme.chat_infoPaint, infoWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + lastTime = duration; + } + } else if (currentMessageObject.isRoundVideo()) { int duration = 0; TLRPC.Document document = currentMessageObject.getDocument(); for (int a = 0; a < document.attributes.size(); a++) { @@ -1592,17 +1690,6 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } } - public void downloadAudioIfNeed() { - if (documentAttachType != DOCUMENT_ATTACH_TYPE_AUDIO) { - return; - } - if (buttonState == 2) { - FileLoader.getInstance(currentAccount).loadFile(documentAttach, currentMessageObject, 1, 0); - buttonState = 4; - radialProgress.setIcon(getIconForCurrentState(), false, false); - } - } - public void setFullyDraw(boolean draw) { fullyDraw = draw; } @@ -1676,7 +1763,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate break; } } - return StaticLayoutEx.createStaticLayout(stringBuilder, paint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, AndroidUtilities.dp(1), false, TextUtils.TruncateAt.END, maxWidth, maxLines); + return StaticLayoutEx.createStaticLayout(stringBuilder, paint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, AndroidUtilities.dp(1), false, TextUtils.TruncateAt.END, maxWidth, maxLines, true); } private void didClickedImage() { @@ -1684,7 +1771,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (buttonState == -1) { delegate.didPressImage(this); } else if (buttonState == 0) { - didPressButton(true); + didPressButton(true, false); } } else if (currentMessageObject.type == 12) { TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(currentMessageObject.messageOwner.media.user_id); @@ -1708,13 +1795,13 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate invalidate(); } } else if (buttonState == 2 || buttonState == 0) { - didPressButton(true); + didPressButton(true, false); } } else if (documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO) { - if (buttonState == -1) { + if (buttonState == -1 || drawVideoImageButton) { delegate.didPressImage(this); } else if (buttonState == 0 || buttonState == 3) { - didPressButton(true); + didPressButton(true, false); } } else if (currentMessageObject.type == 4) { delegate.didPressImage(this); @@ -1866,11 +1953,16 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate return photoImage; } + public int getNoSoundIconCenterX() { + return noSoundCenterX; + } + @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); attachedToWindow = false; radialProgress.onDetachedFromWindow(); + videoRadialProgress.onDetachedFromWindow(); avatarImage.onDetachedFromWindow(); replyImageReceiver.onDetachedFromWindow(); locationImageReceiver.onDetachedFromWindow(); @@ -1885,9 +1977,17 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); + + if (messageObjectToSet != null) { + setMessageContent(messageObjectToSet, groupedMessagesToSet, bottomNearToSet, topNearToSet); + messageObjectToSet = null; + groupedMessagesToSet = null; + } + attachedToWindow = true; setTranslationX(0); radialProgress.onAttachedToWindow(); + videoRadialProgress.onAttachedToWindow(); avatarImage.onAttachedToWindow(); avatarImage.setParentView((View) getParent()); replyImageReceiver.onAttachedToWindow(); @@ -1899,392 +1999,21 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } else { updateButtonState(false, false, false); } - if (currentMessageObject != null && currentMessageObject.isRoundVideo()) { - checkRoundVideoPlayback(true); + if (currentMessageObject != null && (currentMessageObject.isRoundVideo() || currentMessageObject.isVideo())) { + checkVideoPlayback(true); } - } - - public void checkRoundVideoPlayback(boolean allowStart) { - if (allowStart) { - allowStart = MediaController.getInstance().getPlayingMessageObject() == null; - } - photoImage.setAllowStartAnimation(allowStart); - if (allowStart) { - photoImage.startAnimation(); + if (documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO && autoPlayingVideo) { + animatingNoSoundPlaying = MediaController.getInstance().isPlayingMessage(currentMessageObject); + animatingNoSound = 0; + animatingNoSoundProgress = animatingNoSoundPlaying ? 0.0f : 1.0f; } else { - photoImage.stopAnimation(); + animatingNoSoundPlaying = false; + animatingNoSoundProgress = 0; + animatingDrawVideoImageButtonProgress = documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO && drawVideoSize ? 1.0f : 0.0f; } } - @Override - protected void onLongPress() { - if (pressedLink instanceof URLSpanMono) { - delegate.didPressUrl(currentMessageObject, pressedLink, true); - return; - } else if (pressedLink instanceof URLSpanNoUnderline) { - URLSpanNoUnderline url = (URLSpanNoUnderline) pressedLink; - if (url.getURL().startsWith("/")) { - delegate.didPressUrl(currentMessageObject, pressedLink, true); - return; - } - } else if (pressedLink instanceof URLSpan) { - delegate.didPressUrl(currentMessageObject, pressedLink, true); - return; - } - resetPressedLink(-1); - if (buttonPressed != 0 || miniButtonPressed != 0 || pressedBotButton != -1) { - buttonPressed = 0; - miniButtonState = 0; - pressedBotButton = -1; - invalidate(); - } - if (instantPressed) { - instantPressed = instantButtonPressed = false; - if (Build.VERSION.SDK_INT >= 21 && selectorDrawable != null) { - selectorDrawable.setState(StateSet.NOTHING); - } - invalidate(); - } - if (pressedVoteButton != -1) { - pressedVoteButton = -1; - if (Build.VERSION.SDK_INT >= 21 && selectorDrawable != null) { - selectorDrawable.setState(StateSet.NOTHING); - } - invalidate(); - } - if (delegate != null) { - delegate.didLongPress(this); - } - } - - public void setCheckPressed(boolean value, boolean pressed) { - isCheckPressed = value; - isPressed = pressed; - updateRadialProgressBackground(); - if (useSeekBarWaweform) { - seekBarWaveform.setSelected(isDrawSelectedBackground()); - } else { - seekBar.setSelected(isDrawSelectedBackground()); - } - invalidate(); - } - - public void setHighlightedAnimated() { - isHighlightedAnimated = true; - highlightProgress = 1000; - lastHighlightProgressTime = System.currentTimeMillis(); - invalidate(); - } - - public boolean isHighlighted() { - return isHighlighted; - } - - public void setHighlighted(boolean value) { - if (isHighlighted == value) { - return; - } - isHighlighted = value; - if (!isHighlighted) { - lastHighlightProgressTime = System.currentTimeMillis(); - isHighlightedAnimated = true; - highlightProgress = 300; - } else { - isHighlightedAnimated = false; - highlightProgress = 0; - } - - updateRadialProgressBackground(); - if (useSeekBarWaweform) { - seekBarWaveform.setSelected(isDrawSelectedBackground()); - } else { - seekBar.setSelected(isDrawSelectedBackground()); - } - invalidate(); - } - - @Override - public void setPressed(boolean pressed) { - super.setPressed(pressed); - updateRadialProgressBackground(); - if (useSeekBarWaweform) { - seekBarWaveform.setSelected(isDrawSelectedBackground()); - } else { - seekBar.setSelected(isDrawSelectedBackground()); - } - invalidate(); - } - - private void updateRadialProgressBackground() { - if (drawRadialCheckBackground) { - return; - } - radialProgress.setPressed(isHighlighted || isPressed || buttonPressed != 0 || isPressed(), false); - if (hasMiniProgress != 0) { - radialProgress.setPressed(isHighlighted || isPressed || miniButtonPressed != 0 || isPressed(), true); - } - } - - @Override - public void onSeekBarDrag(float progress) { - if (currentMessageObject == null) { - return; - } - currentMessageObject.audioProgress = progress; - MediaController.getInstance().seekToProgress(currentMessageObject, progress); - } - - private void updateWaveform() { - if (currentMessageObject == null || documentAttachType != DOCUMENT_ATTACH_TYPE_AUDIO) { - return; - } - for (int a = 0; a < documentAttach.attributes.size(); a++) { - TLRPC.DocumentAttribute attribute = documentAttach.attributes.get(a); - if (attribute instanceof TLRPC.TL_documentAttributeAudio) { - if (attribute.waveform == null || attribute.waveform.length == 0) { - MediaController.getInstance().generateWaveform(currentMessageObject); - } - useSeekBarWaweform = attribute.waveform != null; - seekBarWaveform.setWaveform(attribute.waveform); - break; - } - } - } - - private int createDocumentLayout(int maxWidth, MessageObject messageObject) { - if (messageObject.type == 0) { - documentAttach = messageObject.messageOwner.media.webpage.document; - } else { - documentAttach = messageObject.messageOwner.media.document; - } - if (documentAttach == null) { - return 0; - } - if (MessageObject.isVoiceDocument(documentAttach)) { - documentAttachType = DOCUMENT_ATTACH_TYPE_AUDIO; - int duration = 0; - for (int a = 0; a < documentAttach.attributes.size(); a++) { - TLRPC.DocumentAttribute attribute = documentAttach.attributes.get(a); - if (attribute instanceof TLRPC.TL_documentAttributeAudio) { - duration = attribute.duration; - break; - } - } - widthBeforeNewTimeLine = maxWidth - AndroidUtilities.dp(76 + 18) - (int) Math.ceil(Theme.chat_audioTimePaint.measureText("00:00")); - availableTimeWidth = maxWidth - AndroidUtilities.dp(18); - measureTime(messageObject); - int minSize = AndroidUtilities.dp(40 + 14 + 20 + 90 + 10) + timeWidth; - if (!hasLinkPreview) { - backgroundWidth = Math.min(maxWidth, minSize + duration * AndroidUtilities.dp(10)); - } - seekBarWaveform.setMessageObject(messageObject); - return 0; - } else if (MessageObject.isMusicDocument(documentAttach)) { - documentAttachType = DOCUMENT_ATTACH_TYPE_MUSIC; - - maxWidth = maxWidth - AndroidUtilities.dp(86); - if (maxWidth < 0) { - maxWidth = AndroidUtilities.dp(100); - } - - CharSequence stringFinal = TextUtils.ellipsize(messageObject.getMusicTitle().replace('\n', ' '), Theme.chat_audioTitlePaint, maxWidth - AndroidUtilities.dp(12), TextUtils.TruncateAt.END); - songLayout = new StaticLayout(stringFinal, Theme.chat_audioTitlePaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); - if (songLayout.getLineCount() > 0) { - songX = -(int) Math.ceil(songLayout.getLineLeft(0)); - } - - stringFinal = TextUtils.ellipsize(messageObject.getMusicAuthor().replace('\n', ' '), Theme.chat_audioPerformerPaint, maxWidth, TextUtils.TruncateAt.END); - performerLayout = new StaticLayout(stringFinal, Theme.chat_audioPerformerPaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); - if (performerLayout.getLineCount() > 0) { - performerX = -(int) Math.ceil(performerLayout.getLineLeft(0)); - } - - int duration = 0; - for (int a = 0; a < documentAttach.attributes.size(); a++) { - TLRPC.DocumentAttribute attribute = documentAttach.attributes.get(a); - if (attribute instanceof TLRPC.TL_documentAttributeAudio) { - duration = attribute.duration; - break; - } - } - int durationWidth = (int) Math.ceil(Theme.chat_audioTimePaint.measureText(String.format("%d:%02d / %d:%02d", duration / 60, duration % 60, duration / 60, duration % 60))); - widthBeforeNewTimeLine = backgroundWidth - AndroidUtilities.dp(10 + 76) - durationWidth; - availableTimeWidth = backgroundWidth - AndroidUtilities.dp(28); - return durationWidth; - } else if (MessageObject.isVideoDocument(documentAttach)) { - documentAttachType = DOCUMENT_ATTACH_TYPE_VIDEO; - if (!messageObject.needDrawBluredPreview()) { - int duration = 0; - for (int a = 0; a < documentAttach.attributes.size(); a++) { - TLRPC.DocumentAttribute attribute = documentAttach.attributes.get(a); - if (attribute instanceof TLRPC.TL_documentAttributeVideo) { - duration = attribute.duration; - break; - } - } - int minutes = duration / 60; - int seconds = duration - minutes * 60; - String str = String.format("%d:%02d, %s", minutes, seconds, AndroidUtilities.formatFileSize(documentAttach.size)); - infoWidth = (int) Math.ceil(Theme.chat_infoPaint.measureText(str)); - infoLayout = new StaticLayout(str, Theme.chat_infoPaint, infoWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); - } - return 0; - } else { - drawPhotoImage = documentAttach.mime_type != null && documentAttach.mime_type.toLowerCase().startsWith("image/") || MessageObject.isDocumentHasThumb(documentAttach); - if (!drawPhotoImage) { - maxWidth += AndroidUtilities.dp(30); - } - documentAttachType = DOCUMENT_ATTACH_TYPE_DOCUMENT; - String name = FileLoader.getDocumentFileName(documentAttach); - if (name == null || name.length() == 0) { - name = LocaleController.getString("AttachDocument", R.string.AttachDocument); - } - docTitleLayout = StaticLayoutEx.createStaticLayout(name, Theme.chat_docNamePaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false, TextUtils.TruncateAt.MIDDLE, maxWidth, 2, false); - docTitleOffsetX = Integer.MIN_VALUE; - int width; - if (docTitleLayout != null && docTitleLayout.getLineCount() > 0) { - int maxLineWidth = 0; - for (int a = 0; a < docTitleLayout.getLineCount(); a++) { - maxLineWidth = Math.max(maxLineWidth, (int) Math.ceil(docTitleLayout.getLineWidth(a))); - docTitleOffsetX = Math.max(docTitleOffsetX, (int) Math.ceil(-docTitleLayout.getLineLeft(a))); - } - width = Math.min(maxWidth, maxLineWidth); - } else { - width = maxWidth; - docTitleOffsetX = 0; - } - - String str = AndroidUtilities.formatFileSize(documentAttach.size) + " " + FileLoader.getDocumentExtension(documentAttach); - infoWidth = Math.min(maxWidth - AndroidUtilities.dp(30), (int) Math.ceil(Theme.chat_infoPaint.measureText(str))); - CharSequence str2 = TextUtils.ellipsize(str, Theme.chat_infoPaint, infoWidth, TextUtils.TruncateAt.END); - try { - if (infoWidth < 0) { - infoWidth = AndroidUtilities.dp(10); - } - infoLayout = new StaticLayout(str2, Theme.chat_infoPaint, infoWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); - } catch (Exception e) { - FileLog.e(e); - } - - if (drawPhotoImage) { - currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, 320); - currentPhotoObjectThumb = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, 40); - - if ((DownloadController.getInstance(currentAccount).getAutodownloadMask() & DownloadController.AUTODOWNLOAD_MASK_PHOTO) == 0) { - currentPhotoObject = null; - } - if (currentPhotoObject == null || currentPhotoObject == currentPhotoObjectThumb) { - currentPhotoObject = null; - photoImage.setNeedsQualityThumb(true); - photoImage.setShouldGenerateQualityThumb(true); - } - currentPhotoFilter = "86_86_b"; - photoImage.setImage(currentPhotoObject, "86_86", null, currentPhotoObjectThumb, currentPhotoFilter, 0, null, messageObject, 1); - } - return width; - } - } - - private void calcBackgroundWidth(int maxWidth, int timeMore, int maxChildWidth) { - if (hasLinkPreview || hasOldCaptionPreview || hasGamePreview || hasInvoicePreview || maxWidth - currentMessageObject.lastLineWidth < timeMore || currentMessageObject.hasRtl) { - totalHeight += AndroidUtilities.dp(14); - hasNewLineForTime = true; - backgroundWidth = Math.max(maxChildWidth, currentMessageObject.lastLineWidth) + AndroidUtilities.dp(31); - backgroundWidth = Math.max(backgroundWidth, (currentMessageObject.isOutOwner() ? timeWidth + AndroidUtilities.dp(17) : timeWidth)+ AndroidUtilities.dp(31)); - } else { - int diff = maxChildWidth - currentMessageObject.lastLineWidth; - if (diff >= 0 && diff <= timeMore) { - backgroundWidth = maxChildWidth + timeMore - diff + AndroidUtilities.dp(31); - } else { - backgroundWidth = Math.max(maxChildWidth, currentMessageObject.lastLineWidth + timeMore) + AndroidUtilities.dp(31); - } - } - } - - public void setHighlightedText(String text) { - if (currentMessageObject.messageOwner.message == null || currentMessageObject == null || currentMessageObject.type != 0 || TextUtils.isEmpty(currentMessageObject.messageText) || text == null) { - if (!urlPathSelection.isEmpty()) { - linkSelectionBlockNum = -1; - resetUrlPaths(true); - invalidate(); - } - return; - } - int start = TextUtils.indexOf(currentMessageObject.messageOwner.message.toLowerCase(), text.toLowerCase()); - if (start == -1) { - if (!urlPathSelection.isEmpty()) { - linkSelectionBlockNum = -1; - resetUrlPaths(true); - invalidate(); - } - return; - } - int end = start + text.length(); - for (int c = 0; c < currentMessageObject.textLayoutBlocks.size(); c++) { - MessageObject.TextLayoutBlock block = currentMessageObject.textLayoutBlocks.get(c); - if (start >= block.charactersOffset && start < block.charactersOffset + block.textLayout.getText().length()) { - linkSelectionBlockNum = c; - resetUrlPaths(true); - try { - LinkPath path = obtainNewUrlPath(true); - int length = block.textLayout.getText().length(); - path.setCurrentLayout(block.textLayout, start, 0); - block.textLayout.getSelectionPath(start, end - block.charactersOffset, path); - if (end >= block.charactersOffset + length) { - for (int a = c + 1; a < currentMessageObject.textLayoutBlocks.size(); a++) { - MessageObject.TextLayoutBlock nextBlock = currentMessageObject.textLayoutBlocks.get(a); - length = nextBlock.textLayout.getText().length(); - path = obtainNewUrlPath(true); - path.setCurrentLayout(nextBlock.textLayout, 0, nextBlock.height); - nextBlock.textLayout.getSelectionPath(0, end - nextBlock.charactersOffset, path); - if (end < block.charactersOffset + length - 1) { - break; - } - } - } - } catch (Exception e) { - FileLog.e(e); - } - invalidate(); - break; - } - } - } - - @Override - protected boolean verifyDrawable(Drawable who) { - return super.verifyDrawable(who) || who == selectorDrawable; - } - - private boolean isCurrentLocationTimeExpired(MessageObject messageObject) { - if (currentMessageObject.messageOwner.media.period % 60 == 0) { - return Math.abs(ConnectionsManager.getInstance(currentAccount).getCurrentTime() - messageObject.messageOwner.date) > messageObject.messageOwner.media.period; - } else { - return Math.abs(ConnectionsManager.getInstance(currentAccount).getCurrentTime() - messageObject.messageOwner.date) > messageObject.messageOwner.media.period - 5; - } - } - - private void checkLocationExpired() { - if (currentMessageObject == null) { - return; - } - boolean newExpired = isCurrentLocationTimeExpired(currentMessageObject); - if (newExpired != locationExpired) { - locationExpired = newExpired; - if (!locationExpired) { - AndroidUtilities.runOnUIThread(invalidateRunnable, 1000); - scheduledInvalidate = true; - int maxWidth = backgroundWidth - AndroidUtilities.dp(37 + 54); - docTitleLayout = new StaticLayout(TextUtils.ellipsize(LocaleController.getString("AttachLiveLocation", R.string.AttachLiveLocation), Theme.chat_locationTitlePaint, maxWidth, TextUtils.TruncateAt.END), Theme.chat_locationTitlePaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); - } else { - MessageObject messageObject = currentMessageObject; - currentMessageObject = null; - setMessageObject(messageObject, currentMessagesGroup, pinnedBottom, pinnedTop); - } - } - } - - public void setMessageObject(MessageObject messageObject, MessageObject.GroupedMessages groupedMessages, boolean bottomNear, boolean topNear) { + private void setMessageContent(MessageObject messageObject, MessageObject.GroupedMessages groupedMessages, boolean bottomNear, boolean topNear) { if (messageObject.checkLayout() || currentPosition != null && lastHeight != AndroidUtilities.displaySize.y) { currentMessageObject = null; } @@ -2332,11 +2061,11 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (messageChanged || dataChanged || groupChanged || pollChanged || isPhotoDataChanged(messageObject) || pinnedBottom != bottomNear || pinnedTop != topNear) { pinnedBottom = bottomNear; pinnedTop = topNear; + currentMessageObject = messageObject; + currentMessagesGroup = groupedMessages; lastTime = -2; isHighlightedAnimated = false; widthBeforeNewTimeLine = -1; - currentMessageObject = messageObject; - currentMessagesGroup = groupedMessages; if (currentMessagesGroup != null && currentMessagesGroup.posArray.size() > 1) { currentPosition = currentMessagesGroup.positions.get(currentMessageObject); if (currentPosition == null) { @@ -2360,12 +2089,17 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate wasLayout = false; drwaShareGoIcon = false; groupPhotoInvisible = false; + animatingDrawVideoImageButton = 0; + drawVideoSize = false; + canStreamVideo = false; + animatingNoSound = 0; drawShareButton = checkNeedDrawShareButton(messageObject); replyNameLayout = null; adminLayout = null; checkOnlyButtonPressed = false; replyTextLayout = null; hasEmbed = false; + autoPlayingVideo = false; replyNameWidth = 0; replyTextWidth = 0; viaWidth = 0; @@ -2414,6 +2148,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate currentCaption = null; docTitleLayout = null; drawImageButton = false; + drawVideoImageButton = false; currentPhotoObject = null; currentPhotoObjectThumb = null; currentPhotoFilter = null; @@ -2448,7 +2183,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate photoImage.setNeedsQualityThumb(false); photoImage.setShouldGenerateQualityThumb(false); photoImage.setAllowDecodeSingleFrame(false); - photoImage.setRoundRadius(AndroidUtilities.dp(3)); + photoImage.setRoundRadius(AndroidUtilities.dp(4)); photoImage.setColorFilter(null); if (messageChanged) { @@ -2881,16 +2616,32 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } else if (MessageObject.isVideoDocument(document)) { if (photo != null) { currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, AndroidUtilities.getPhotoSize(), true); + currentPhotoObjectThumb = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, 40); } if (currentPhotoObject == null) { - currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 90); + currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 320); + currentPhotoObjectThumb = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 40); } - if (currentPhotoObject != null && (currentPhotoObject.w == 0 || currentPhotoObject.h == 0)) { + if (currentPhotoObject == currentPhotoObjectThumb) { + currentPhotoObjectThumb = null; + } + if (currentPhotoObject == null) { + currentPhotoObject = new TLRPC.TL_photoSize(); + currentPhotoObject.type = "s"; + currentPhotoObject.location = new TLRPC.TL_fileLocationUnavailable(); + } + if (currentPhotoObject != null && (currentPhotoObject.w == 0 || currentPhotoObject.h == 0 || currentPhotoObject instanceof TLRPC.TL_photoStrippedSize)) { for (int a = 0; a < document.attributes.size(); a++) { TLRPC.DocumentAttribute attribute = document.attributes.get(a); if (attribute instanceof TLRPC.TL_documentAttributeVideo) { - currentPhotoObject.w = attribute.w; - currentPhotoObject.h = attribute.h; + if (currentPhotoObject instanceof TLRPC.TL_photoStrippedSize) { + float scale = Math.max(attribute.w, attribute.w) / 50.0f; + currentPhotoObject.w = (int) (attribute.w / scale); + currentPhotoObject.h = (int) (attribute.h / scale); + } else { + currentPhotoObject.w = attribute.w; + currentPhotoObject.h = attribute.h; + } break; } } @@ -3108,7 +2859,22 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } else if (documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO) { photoImage.setNeedsQualityThumb(true); photoImage.setShouldGenerateQualityThumb(true); - photoImage.setImage(null, null, currentPhotoObject, currentPhotoFilter, 0, null, messageObject, 0); + if (SharedConfig.autoplayVideo && ( + currentMessageObject.mediaExists || + messageObject.canStreamVideo() && DownloadController.getInstance(currentAccount).canDownloadMedia(currentMessageObject) + )) { + photoImage.setAllowDecodeSingleFrame(true); + photoImage.setAllowStartAnimation(true); + photoImage.startAnimation(); + photoImage.setImage(documentAttach, ImageLoader.VIDEO_FILTER, currentPhotoObject, currentPhotoFilter, null, currentPhotoObjectThumb, currentPhotoFilterThumb, documentAttach.size, null, messageObject, 0); + autoPlayingVideo = true; + } else { + if (currentPhotoObjectThumb != null) { + photoImage.setImage(currentPhotoObject, currentPhotoFilter, currentPhotoObjectThumb, currentPhotoFilterThumb, 0, null, messageObject, 0); + } else { + photoImage.setImage(null, null, currentPhotoObject, currentPhotoObject instanceof TLRPC.TL_photoStrippedSize || "s".equals(currentPhotoObject.type) ? currentPhotoFilterThumb : currentPhotoFilter, 0, null, messageObject, 0); + } + } } else if (documentAttachType == DOCUMENT_ATTACH_TYPE_GIF || documentAttachType == DOCUMENT_ATTACH_TYPE_ROUND) { String fileName = FileLoader.getAttachFileName(document); boolean autoDownload = false; @@ -3392,7 +3158,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate int maxVote = 0; if (!animatePollAnswer && pollVoteInProgress) { - performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); } animatePollAnswerAlpha = animatePollAnswer = attachedToWindow && (pollVoteInProgress || pollUnvoteInProgress); ArrayList previousPollButtons = null; @@ -3513,9 +3279,13 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate messageObject.gifState = 1; } - if (messageObject.isRoundVideo()) { + if (messageObject.isVideo()) { photoImage.setAllowDecodeSingleFrame(true); - photoImage.setAllowStartAnimation(MediaController.getInstance().getPlayingMessageObject() == null); + photoImage.setAllowStartAnimation(true); + } else if (messageObject.isRoundVideo()) { + photoImage.setAllowDecodeSingleFrame(true); + MessageObject playingMessage = MediaController.getInstance().getPlayingMessageObject(); + photoImage.setAllowStartAnimation(playingMessage == null || !playingMessage.isRoundVideo()); } else { photoImage.setAllowStartAnimation(messageObject.gifState == 0); } @@ -3586,11 +3356,14 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } } availableTimeWidth = maxWidth; - if (!drawPhotoImage && TextUtils.isEmpty(messageObject.caption) && infoLayout.getLineCount() > 0) { + if (!drawPhotoImage && TextUtils.isEmpty(messageObject.caption) && infoLayout != null) { + int lineCount = infoLayout.getLineCount(); measureTime(messageObject); int timeLeft = backgroundWidth - AndroidUtilities.dp(40 + 18 + 56 + 8) - (int) Math.ceil(infoLayout.getLineWidth(0)); - if (timeLeft < timeWidth) { + if (timeLeft < timeWidth && lineCount > 1) { photoHeight += AndroidUtilities.dp(8); + } else if (lineCount == 1) { + photoHeight += AndroidUtilities.dp(4); } } } else if (messageObject.type == 4) { //geo @@ -3604,6 +3377,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } else { backgroundWidth = Math.min(AndroidUtilities.displaySize.x - AndroidUtilities.dp(isChat && messageObject.needDrawAvatar() && !messageObject.isOutOwner() ? 102 : 50), AndroidUtilities.dp(252 + 37)); } + backgroundWidth -= AndroidUtilities.dp(4); if (checkNeedDrawShareButton(messageObject)) { backgroundWidth -= AndroidUtilities.dp(20); } @@ -3611,7 +3385,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate availableTimeWidth = maxWidth; maxWidth -= AndroidUtilities.dp(54); - photoWidth = backgroundWidth - AndroidUtilities.dp(21); + photoWidth = backgroundWidth - AndroidUtilities.dp(17); photoHeight = AndroidUtilities.dp(195); int offset = 268435456; @@ -3657,13 +3431,14 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } else { backgroundWidth = Math.min(AndroidUtilities.displaySize.x - AndroidUtilities.dp(isChat && messageObject.needDrawAvatar() && !messageObject.isOutOwner() ? 102 : 50), AndroidUtilities.dp(252 + 37)); } + backgroundWidth -= AndroidUtilities.dp(4); if (checkNeedDrawShareButton(messageObject)) { backgroundWidth -= AndroidUtilities.dp(20); } int maxWidth = backgroundWidth - AndroidUtilities.dp(34); availableTimeWidth = maxWidth; - photoWidth = backgroundWidth - AndroidUtilities.dp(21); + photoWidth = backgroundWidth - AndroidUtilities.dp(17); photoHeight = AndroidUtilities.dp(195); mediaBackground = false; @@ -3689,12 +3464,13 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } else { backgroundWidth = Math.min(AndroidUtilities.displaySize.x - AndroidUtilities.dp(isChat && messageObject.needDrawAvatar() && !messageObject.isOutOwner() ? 102 : 50), AndroidUtilities.dp(252 + 37)); } + backgroundWidth -= AndroidUtilities.dp(4); if (checkNeedDrawShareButton(messageObject)) { backgroundWidth -= AndroidUtilities.dp(20); } availableTimeWidth = backgroundWidth - AndroidUtilities.dp(34); - photoWidth = backgroundWidth - AndroidUtilities.dp(12); + photoWidth = backgroundWidth - AndroidUtilities.dp(8); photoHeight = AndroidUtilities.dp(195); currentUrl = AndroidUtilities.formapMapUrl(currentAccount, lat, lon, (int) (photoWidth / AndroidUtilities.density), (int) (photoHeight / AndroidUtilities.density), true, 15); @@ -3774,31 +3550,41 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate messageObject.messageOwner.media.document.size, "webp", messageObject, 1); } } else { + currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, AndroidUtilities.getPhotoSize()); int maxPhotoWidth; + boolean useFullWidth = false; if (messageObject.type == 5) { maxPhotoWidth = photoWidth = AndroidUtilities.roundMessageSize; } else { if (AndroidUtilities.isTablet()) { maxPhotoWidth = photoWidth = (int) (AndroidUtilities.getMinTabletSide() * 0.7f); } else { - maxPhotoWidth = photoWidth = (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.7f); + if (currentPhotoObject != null && (messageObject.type == 1 || messageObject.type == 3 || messageObject.type == 8) && currentPhotoObject.w >= currentPhotoObject.h) { + maxPhotoWidth = photoWidth = Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) - AndroidUtilities.dp(64); + useFullWidth = true; + } else { + maxPhotoWidth = photoWidth = (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.7f); + } } } photoHeight = photoWidth + AndroidUtilities.dp(100); - if (messageObject.type != 5 && checkNeedDrawShareButton(messageObject)) { - maxPhotoWidth -= AndroidUtilities.dp(20); - photoWidth -= AndroidUtilities.dp(20); - } - - if (photoWidth > AndroidUtilities.getPhotoSize()) { - photoWidth = AndroidUtilities.getPhotoSize(); - } - if (photoHeight > AndroidUtilities.getPhotoSize()) { - photoHeight = AndroidUtilities.getPhotoSize(); + if (!useFullWidth) { + if (messageObject.type != 5 && checkNeedDrawShareButton(messageObject)) { + maxPhotoWidth -= AndroidUtilities.dp(20); + photoWidth -= AndroidUtilities.dp(20); + } + if (photoWidth > AndroidUtilities.getPhotoSize()) { + photoWidth = AndroidUtilities.getPhotoSize(); + } + if (photoHeight > AndroidUtilities.getPhotoSize()) { + photoHeight = AndroidUtilities.getPhotoSize(); + } + } else if (isChat && messageObject.needDrawAvatar() && !messageObject.isOutOwner()) { + photoWidth -= AndroidUtilities.dp(52); } boolean needQualityPreview = false; - currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, AndroidUtilities.getPhotoSize()); + if (messageObject.type == 1) { //photo updateSecretTimeText(messageObject); currentPhotoObjectThumb = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, 40); @@ -3817,35 +3603,47 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate infoLayout = new StaticLayout(str, Theme.chat_infoPaint, infoWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); needQualityPreview = true; } - int w = 0; - int h = 0; + int w; + int h; if (messageObject.type == 5) { w = h = AndroidUtilities.roundMessageSize; } else { TLRPC.PhotoSize size = currentPhotoObject != null ? currentPhotoObject : currentPhotoObjectThumb; + int imageW = 0; + int imageH = 0; if (size != null) { - float scale = (float) size.w / (float) photoWidth; - w = (int) (size.w / scale); - h = (int) (size.h / scale); - if (w == 0) { - w = AndroidUtilities.dp(150); - } - if (h == 0) { - h = AndroidUtilities.dp(150); - } - if (h > photoHeight) { - float scale2 = h; - h = photoHeight; - scale2 /= h; - w = (int) (w / scale2); - } else if (h < AndroidUtilities.dp(120)) { - h = AndroidUtilities.dp(120); - float hScale = (float) size.h / h; - if (size.w / hScale < photoWidth) { - w = (int) (size.w / hScale); + imageW = size.w; + imageH = size.h; + } else if (documentAttach != null) { + for (int a = 0, N = documentAttach.attributes.size(); a < N; a++) { + TLRPC.DocumentAttribute attribute = documentAttach.attributes.get(a); + if (attribute instanceof TLRPC.TL_documentAttributeVideo) { + imageW = attribute.w; + imageH = attribute.h; } } } + float scale = (float) imageW / (float) photoWidth; + w = (int) (imageW / scale); + h = (int) (imageH / scale); + if (w == 0) { + w = AndroidUtilities.dp(150); + } + if (h == 0) { + h = AndroidUtilities.dp(150); + } + if (h > photoHeight) { + float scale2 = h; + h = photoHeight; + scale2 /= h; + w = (int) (w / scale2); + } else if (h < AndroidUtilities.dp(120)) { + h = AndroidUtilities.dp(120); + float hScale = (float) imageH / h; + if (imageW / hScale < photoWidth) { + w = (int) (imageW / hScale); + } + } } if (currentPhotoObject != null && "s".equals(currentPhotoObject.type)) { currentPhotoObject = null; @@ -3860,9 +3658,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } if (needQualityPreview) { - if ((DownloadController.getInstance(currentAccount).getAutodownloadMask() & DownloadController.AUTODOWNLOAD_MASK_PHOTO) == 0) { + /*if ((DownloadController.getInstance(currentAccount).getAutodownloadMask() & DownloadController.AUTODOWNLOAD_TYPE_PHOTO) == 0) { currentPhotoObject = null; - } + }*/ if (!messageObject.needDrawBluredPreview() && (currentPhotoObject == null || currentPhotoObject == currentPhotoObjectThumb) && (currentPhotoObjectThumb == null || !"m".equals(currentPhotoObjectThumb.type))) { photoImage.setNeedsQualityThumb(true); photoImage.setShouldGenerateQualityThumb(true); @@ -3972,12 +3770,20 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate for (int a = 0; a < currentPosition.siblingHeights.length; a++) { h += (int) Math.ceil(maxHeight * currentPosition.siblingHeights[a]); } - h += (currentPosition.maxY - currentPosition.minY) * AndroidUtilities.dp(11); //TODO fix + h += (currentPosition.maxY - currentPosition.minY) * AndroidUtilities.dp(7); //TODO fix } else { h = (int) Math.ceil(maxHeight * currentPosition.ph); } backgroundWidth = w; - w -= AndroidUtilities.dp(12); + if ((currentPosition.flags & MessageObject.POSITION_FLAG_RIGHT) != 0 && (currentPosition.flags & MessageObject.POSITION_FLAG_LEFT) != 0) { + w -= AndroidUtilities.dp(8); + } else if ((currentPosition.flags & MessageObject.POSITION_FLAG_RIGHT) == 0 && (currentPosition.flags & MessageObject.POSITION_FLAG_LEFT) == 0) { + w -= AndroidUtilities.dp(11); + } else if ((currentPosition.flags & MessageObject.POSITION_FLAG_RIGHT) != 0) { + w -= AndroidUtilities.dp(10); + } else { + w -= AndroidUtilities.dp(9); + } photoWidth = w; if (!currentPosition.edge) { photoWidth += AndroidUtilities.dp(10); @@ -4007,7 +3813,16 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } w += firstLineWidth - currentLineWidth; } - w -= AndroidUtilities.dp(18); + w -= AndroidUtilities.dp(9); + if ((rowPosition.flags & MessageObject.POSITION_FLAG_RIGHT) != 0 && (rowPosition.flags & MessageObject.POSITION_FLAG_LEFT) != 0) { + w -= AndroidUtilities.dp(8); + } else if ((rowPosition.flags & MessageObject.POSITION_FLAG_RIGHT) == 0 && (rowPosition.flags & MessageObject.POSITION_FLAG_LEFT) == 0) { + w -= AndroidUtilities.dp(11); + } else if ((rowPosition.flags & MessageObject.POSITION_FLAG_RIGHT) != 0) { + w -= AndroidUtilities.dp(10); + } else { + w -= AndroidUtilities.dp(9); + } if (isChat && !m.isOutOwner() && m.needDrawAvatar() && (rowPosition == null || rowPosition.edge)) { w -= AndroidUtilities.dp(48); } @@ -4048,7 +3863,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate widthForCaption = photoWidth - AndroidUtilities.dp(10); } - backgroundWidth = photoWidth + AndroidUtilities.dp(12); + backgroundWidth = photoWidth + AndroidUtilities.dp(8); if (!mediaBackground) { backgroundWidth += AndroidUtilities.dp(9); } @@ -4104,7 +3919,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (fixPhotoWidth && photoWidth < captionWidth + AndroidUtilities.dp(10)) { photoWidth = captionWidth + AndroidUtilities.dp(10); - backgroundWidth = photoWidth + AndroidUtilities.dp(12); + backgroundWidth = photoWidth + AndroidUtilities.dp(8); if (!mediaBackground) { backgroundWidth += AndroidUtilities.dp(9); } @@ -4131,7 +3946,22 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate currentPhotoObjectThumb.size = -1; } - if (messageObject.type == 1) { + if (SharedConfig.autoplayVideo && messageObject.type == 3 && !messageObject.needDrawBluredPreview() && ( + currentMessageObject.mediaExists || + messageObject.canStreamVideo() && DownloadController.getInstance(currentAccount).canDownloadMedia(currentMessageObject) + )) { + if (currentPosition != null) { + autoPlayingVideo = (currentPosition.flags & MessageObject.POSITION_FLAG_LEFT) != 0 && (currentPosition.flags & MessageObject.POSITION_FLAG_RIGHT) != 0; + } else { + autoPlayingVideo = true; + } + } + + if (autoPlayingVideo) { + photoImage.setAllowStartAnimation(true); + photoImage.startAnimation(); + photoImage.setImage(messageObject.messageOwner.media.document, ImageLoader.VIDEO_FILTER, currentPhotoObject, currentPhotoFilter, null, currentPhotoObjectThumb, currentPhotoFilterThumb, messageObject.messageOwner.media.document.size, null, messageObject, 0); + } else if (messageObject.type == 1) { if (messageObject.useCustomPhoto) { photoImage.setImageBitmap(getResources().getDrawable(R.drawable.theme_preview_image)); } else { @@ -4208,7 +4038,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate additionalTop -= AndroidUtilities.dp(4); } if ((currentPosition.flags & MessageObject.POSITION_FLAG_BOTTOM) == 0) { - photoHeight += AndroidUtilities.dp(4); + photoHeight += AndroidUtilities.dp(1); } } @@ -4216,7 +4046,25 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate namesOffset -= AndroidUtilities.dp(1); } - photoImage.setImageCoords(0, AndroidUtilities.dp(7) + namesOffset + additionalTop, photoWidth, photoHeight); + int y; + if (currentPosition != null) { + if (namesOffset > 0) { + y = AndroidUtilities.dp(7); + totalHeight -= AndroidUtilities.dp(2); + } else { + y = AndroidUtilities.dp(5); + totalHeight -= AndroidUtilities.dp(4); + } + } else { + if (namesOffset > 0) { + y = AndroidUtilities.dp(7); + totalHeight -= AndroidUtilities.dp(2); + } else { + y = AndroidUtilities.dp(5); + totalHeight -= AndroidUtilities.dp(4); + } + } + photoImage.setImageCoords(0, y + namesOffset + additionalTop, photoWidth, photoHeight); invalidate(); } @@ -4430,6 +4278,409 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } updateWaveform(); updateButtonState(false, dataChanged && !messageObject.cancelEditing, true); + + if (buttonState == 2 && documentAttachType == DOCUMENT_ATTACH_TYPE_AUDIO && DownloadController.getInstance(currentAccount).canDownloadMedia(messageObject)) { + FileLoader.getInstance(currentAccount).loadFile(documentAttach, currentMessageObject, 1, 0); + buttonState = 4; + radialProgress.setIcon(getIconForCurrentState(), false, false); + } + } + + public void checkVideoPlayback(boolean allowStart) { + if (currentMessageObject.isVideo()) { + if (MediaController.getInstance().isPlayingMessage(currentMessageObject)) { + photoImage.setAllowStartAnimation(false); + photoImage.stopAnimation(); + } else { + photoImage.setAllowStartAnimation(true); + photoImage.startAnimation(); + } + } else { + if (allowStart) { + MessageObject playingMessage = MediaController.getInstance().getPlayingMessageObject(); + allowStart = playingMessage == null || !playingMessage.isRoundVideo(); + } + photoImage.setAllowStartAnimation(allowStart); + if (allowStart) { + photoImage.startAnimation(); + } else { + photoImage.stopAnimation(); + } + } + } + + @Override + protected void onLongPress() { + if (pressedLink instanceof URLSpanMono) { + delegate.didPressUrl(currentMessageObject, pressedLink, true); + return; + } else if (pressedLink instanceof URLSpanNoUnderline) { + URLSpanNoUnderline url = (URLSpanNoUnderline) pressedLink; + if (url.getURL().startsWith("/")) { + delegate.didPressUrl(currentMessageObject, pressedLink, true); + return; + } + } else if (pressedLink instanceof URLSpan) { + delegate.didPressUrl(currentMessageObject, pressedLink, true); + return; + } + resetPressedLink(-1); + if (buttonPressed != 0 || miniButtonPressed != 0 || videoButtonPressed != 0 || pressedBotButton != -1) { + buttonPressed = 0; + miniButtonState = 0; + videoButtonPressed = 0; + pressedBotButton = -1; + invalidate(); + } + if (instantPressed) { + instantPressed = instantButtonPressed = false; + if (Build.VERSION.SDK_INT >= 21 && selectorDrawable != null) { + selectorDrawable.setState(StateSet.NOTHING); + } + invalidate(); + } + if (pressedVoteButton != -1) { + pressedVoteButton = -1; + if (Build.VERSION.SDK_INT >= 21 && selectorDrawable != null) { + selectorDrawable.setState(StateSet.NOTHING); + } + invalidate(); + } + if (delegate != null) { + delegate.didLongPress(this); + } + } + + public void setCheckPressed(boolean value, boolean pressed) { + isCheckPressed = value; + isPressed = pressed; + updateRadialProgressBackground(); + if (useSeekBarWaweform) { + seekBarWaveform.setSelected(isDrawSelectedBackground()); + } else { + seekBar.setSelected(isDrawSelectedBackground()); + } + invalidate(); + } + + public void setHighlightedAnimated() { + isHighlightedAnimated = true; + highlightProgress = 1000; + lastHighlightProgressTime = System.currentTimeMillis(); + invalidate(); + } + + public boolean isHighlighted() { + return isHighlighted; + } + + public void setHighlighted(boolean value) { + if (isHighlighted == value) { + return; + } + isHighlighted = value; + if (!isHighlighted) { + lastHighlightProgressTime = System.currentTimeMillis(); + isHighlightedAnimated = true; + highlightProgress = 300; + } else { + isHighlightedAnimated = false; + highlightProgress = 0; + } + + updateRadialProgressBackground(); + if (useSeekBarWaweform) { + seekBarWaveform.setSelected(isDrawSelectedBackground()); + } else { + seekBar.setSelected(isDrawSelectedBackground()); + } + invalidate(); + } + + @Override + public void setPressed(boolean pressed) { + super.setPressed(pressed); + updateRadialProgressBackground(); + if (useSeekBarWaweform) { + seekBarWaveform.setSelected(isDrawSelectedBackground()); + } else { + seekBar.setSelected(isDrawSelectedBackground()); + } + invalidate(); + } + + private void updateRadialProgressBackground() { + if (drawRadialCheckBackground) { + return; + } + radialProgress.setPressed(isHighlighted || isPressed || buttonPressed != 0 || isPressed(), false); + if (hasMiniProgress != 0) { + radialProgress.setPressed(isHighlighted || isPressed || miniButtonPressed != 0 || isPressed(), true); + } + videoRadialProgress.setPressed(isHighlighted || isPressed || videoButtonPressed != 0 || isPressed(), false); + } + + @Override + public void onSeekBarDrag(float progress) { + if (currentMessageObject == null) { + return; + } + currentMessageObject.audioProgress = progress; + MediaController.getInstance().seekToProgress(currentMessageObject, progress); + } + + private void updateWaveform() { + if (currentMessageObject == null || documentAttachType != DOCUMENT_ATTACH_TYPE_AUDIO) { + return; + } + for (int a = 0; a < documentAttach.attributes.size(); a++) { + TLRPC.DocumentAttribute attribute = documentAttach.attributes.get(a); + if (attribute instanceof TLRPC.TL_documentAttributeAudio) { + if (attribute.waveform == null || attribute.waveform.length == 0) { + MediaController.getInstance().generateWaveform(currentMessageObject); + } + useSeekBarWaweform = attribute.waveform != null; + seekBarWaveform.setWaveform(attribute.waveform); + break; + } + } + } + + private int createDocumentLayout(int maxWidth, MessageObject messageObject) { + if (messageObject.type == 0) { + documentAttach = messageObject.messageOwner.media.webpage.document; + } else { + documentAttach = messageObject.messageOwner.media.document; + } + if (documentAttach == null) { + return 0; + } + if (MessageObject.isVoiceDocument(documentAttach)) { + documentAttachType = DOCUMENT_ATTACH_TYPE_AUDIO; + int duration = 0; + for (int a = 0; a < documentAttach.attributes.size(); a++) { + TLRPC.DocumentAttribute attribute = documentAttach.attributes.get(a); + if (attribute instanceof TLRPC.TL_documentAttributeAudio) { + duration = attribute.duration; + break; + } + } + widthBeforeNewTimeLine = maxWidth - AndroidUtilities.dp(76 + 18) - (int) Math.ceil(Theme.chat_audioTimePaint.measureText("00:00")); + availableTimeWidth = maxWidth - AndroidUtilities.dp(18); + measureTime(messageObject); + int minSize = AndroidUtilities.dp(40 + 14 + 20 + 90 + 10) + timeWidth; + if (!hasLinkPreview) { + backgroundWidth = Math.min(maxWidth, minSize + duration * AndroidUtilities.dp(10)); + } + seekBarWaveform.setMessageObject(messageObject); + return 0; + } else if (MessageObject.isMusicDocument(documentAttach)) { + documentAttachType = DOCUMENT_ATTACH_TYPE_MUSIC; + + maxWidth = maxWidth - AndroidUtilities.dp(86); + if (maxWidth < 0) { + maxWidth = AndroidUtilities.dp(100); + } + + CharSequence stringFinal = TextUtils.ellipsize(messageObject.getMusicTitle().replace('\n', ' '), Theme.chat_audioTitlePaint, maxWidth - AndroidUtilities.dp(12), TextUtils.TruncateAt.END); + songLayout = new StaticLayout(stringFinal, Theme.chat_audioTitlePaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + if (songLayout.getLineCount() > 0) { + songX = -(int) Math.ceil(songLayout.getLineLeft(0)); + } + + stringFinal = TextUtils.ellipsize(messageObject.getMusicAuthor().replace('\n', ' '), Theme.chat_audioPerformerPaint, maxWidth, TextUtils.TruncateAt.END); + performerLayout = new StaticLayout(stringFinal, Theme.chat_audioPerformerPaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + if (performerLayout.getLineCount() > 0) { + performerX = -(int) Math.ceil(performerLayout.getLineLeft(0)); + } + + int duration = 0; + for (int a = 0; a < documentAttach.attributes.size(); a++) { + TLRPC.DocumentAttribute attribute = documentAttach.attributes.get(a); + if (attribute instanceof TLRPC.TL_documentAttributeAudio) { + duration = attribute.duration; + break; + } + } + int durationWidth = (int) Math.ceil(Theme.chat_audioTimePaint.measureText(String.format("%d:%02d / %d:%02d", duration / 60, duration % 60, duration / 60, duration % 60))); + widthBeforeNewTimeLine = backgroundWidth - AndroidUtilities.dp(10 + 76) - durationWidth; + availableTimeWidth = backgroundWidth - AndroidUtilities.dp(28); + return durationWidth; + } else if (MessageObject.isVideoDocument(documentAttach)) { + documentAttachType = DOCUMENT_ATTACH_TYPE_VIDEO; + if (!messageObject.needDrawBluredPreview()) { + updatePlayingMessageProgress(); + String str = String.format("%s", AndroidUtilities.formatFileSize(documentAttach.size)); + docTitleWidth = (int) Math.ceil(Theme.chat_infoPaint.measureText(str)); + docTitleLayout = new StaticLayout(str, Theme.chat_infoPaint, docTitleWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + } + return 0; + } else { + drawPhotoImage = documentAttach.mime_type != null && documentAttach.mime_type.toLowerCase().startsWith("image/") || MessageObject.isDocumentHasThumb(documentAttach); + if (!drawPhotoImage) { + maxWidth += AndroidUtilities.dp(30); + } + documentAttachType = DOCUMENT_ATTACH_TYPE_DOCUMENT; + String name = FileLoader.getDocumentFileName(documentAttach); + if (name == null || name.length() == 0) { + name = LocaleController.getString("AttachDocument", R.string.AttachDocument); + } + docTitleLayout = StaticLayoutEx.createStaticLayout(name, Theme.chat_docNamePaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false, TextUtils.TruncateAt.MIDDLE, maxWidth, 2, false); + docTitleOffsetX = Integer.MIN_VALUE; + int width; + if (docTitleLayout != null && docTitleLayout.getLineCount() > 0) { + int maxLineWidth = 0; + for (int a = 0; a < docTitleLayout.getLineCount(); a++) { + maxLineWidth = Math.max(maxLineWidth, (int) Math.ceil(docTitleLayout.getLineWidth(a))); + docTitleOffsetX = Math.max(docTitleOffsetX, (int) Math.ceil(-docTitleLayout.getLineLeft(a))); + } + width = Math.min(maxWidth, maxLineWidth); + } else { + width = maxWidth; + docTitleOffsetX = 0; + } + + String str = AndroidUtilities.formatFileSize(documentAttach.size) + " " + FileLoader.getDocumentExtension(documentAttach); + infoWidth = Math.min(maxWidth - AndroidUtilities.dp(30), (int) Math.ceil(Theme.chat_infoPaint.measureText(str))); + CharSequence str2 = TextUtils.ellipsize(str, Theme.chat_infoPaint, infoWidth, TextUtils.TruncateAt.END); + try { + if (infoWidth < 0) { + infoWidth = AndroidUtilities.dp(10); + } + infoLayout = new StaticLayout(str2, Theme.chat_infoPaint, infoWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + } catch (Exception e) { + FileLog.e(e); + } + + if (drawPhotoImage) { + currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, 320); + currentPhotoObjectThumb = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, 40); + + if ((DownloadController.getInstance(currentAccount).getAutodownloadMask() & DownloadController.AUTODOWNLOAD_TYPE_PHOTO) == 0) { + currentPhotoObject = null; + } + if (currentPhotoObject == null || currentPhotoObject == currentPhotoObjectThumb) { + currentPhotoObject = null; + photoImage.setNeedsQualityThumb(true); + photoImage.setShouldGenerateQualityThumb(true); + } + currentPhotoFilter = "86_86_b"; + photoImage.setImage(currentPhotoObject, "86_86", null, currentPhotoObjectThumb, currentPhotoFilter, 0, null, messageObject, 1); + } + return width; + } + } + + private void calcBackgroundWidth(int maxWidth, int timeMore, int maxChildWidth) { + if (hasLinkPreview || hasOldCaptionPreview || hasGamePreview || hasInvoicePreview || maxWidth - currentMessageObject.lastLineWidth < timeMore || currentMessageObject.hasRtl) { + totalHeight += AndroidUtilities.dp(14); + hasNewLineForTime = true; + backgroundWidth = Math.max(maxChildWidth, currentMessageObject.lastLineWidth) + AndroidUtilities.dp(31); + backgroundWidth = Math.max(backgroundWidth, (currentMessageObject.isOutOwner() ? timeWidth + AndroidUtilities.dp(17) : timeWidth)+ AndroidUtilities.dp(31)); + } else { + int diff = maxChildWidth - currentMessageObject.lastLineWidth; + if (diff >= 0 && diff <= timeMore) { + backgroundWidth = maxChildWidth + timeMore - diff + AndroidUtilities.dp(31); + } else { + backgroundWidth = Math.max(maxChildWidth, currentMessageObject.lastLineWidth + timeMore) + AndroidUtilities.dp(31); + } + } + } + + public void setHighlightedText(String text) { + MessageObject messageObject = messageObjectToSet != null ? messageObjectToSet : currentMessageObject; + + if (messageObject == null || messageObject.messageOwner.message == null || messageObject.type != 0 || TextUtils.isEmpty(messageObject.messageText) || text == null) { + if (!urlPathSelection.isEmpty()) { + linkSelectionBlockNum = -1; + resetUrlPaths(true); + invalidate(); + } + return; + } + int start = TextUtils.indexOf(messageObject.messageOwner.message.toLowerCase(), text.toLowerCase()); + if (start == -1) { + if (!urlPathSelection.isEmpty()) { + linkSelectionBlockNum = -1; + resetUrlPaths(true); + invalidate(); + } + return; + } + int end = start + text.length(); + for (int c = 0; c < messageObject.textLayoutBlocks.size(); c++) { + MessageObject.TextLayoutBlock block = messageObject.textLayoutBlocks.get(c); + if (start >= block.charactersOffset && start < block.charactersOffset + block.textLayout.getText().length()) { + linkSelectionBlockNum = c; + resetUrlPaths(true); + try { + LinkPath path = obtainNewUrlPath(true); + int length = block.textLayout.getText().length(); + path.setCurrentLayout(block.textLayout, start, 0); + block.textLayout.getSelectionPath(start, end - block.charactersOffset, path); + if (end >= block.charactersOffset + length) { + for (int a = c + 1; a < messageObject.textLayoutBlocks.size(); a++) { + MessageObject.TextLayoutBlock nextBlock = messageObject.textLayoutBlocks.get(a); + length = nextBlock.textLayout.getText().length(); + path = obtainNewUrlPath(true); + path.setCurrentLayout(nextBlock.textLayout, 0, nextBlock.height); + nextBlock.textLayout.getSelectionPath(0, end - nextBlock.charactersOffset, path); + if (end < block.charactersOffset + length - 1) { + break; + } + } + } + } catch (Exception e) { + FileLog.e(e); + } + invalidate(); + break; + } + } + } + + @Override + protected boolean verifyDrawable(Drawable who) { + return super.verifyDrawable(who) || who == selectorDrawable; + } + + private boolean isCurrentLocationTimeExpired(MessageObject messageObject) { + if (currentMessageObject.messageOwner.media.period % 60 == 0) { + return Math.abs(ConnectionsManager.getInstance(currentAccount).getCurrentTime() - messageObject.messageOwner.date) > messageObject.messageOwner.media.period; + } else { + return Math.abs(ConnectionsManager.getInstance(currentAccount).getCurrentTime() - messageObject.messageOwner.date) > messageObject.messageOwner.media.period - 5; + } + } + + private void checkLocationExpired() { + if (currentMessageObject == null) { + return; + } + boolean newExpired = isCurrentLocationTimeExpired(currentMessageObject); + if (newExpired != locationExpired) { + locationExpired = newExpired; + if (!locationExpired) { + AndroidUtilities.runOnUIThread(invalidateRunnable, 1000); + scheduledInvalidate = true; + int maxWidth = backgroundWidth - AndroidUtilities.dp(37 + 54); + docTitleLayout = new StaticLayout(TextUtils.ellipsize(LocaleController.getString("AttachLiveLocation", R.string.AttachLiveLocation), Theme.chat_locationTitlePaint, maxWidth, TextUtils.TruncateAt.END), Theme.chat_locationTitlePaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + } else { + MessageObject messageObject = currentMessageObject; + currentMessageObject = null; + setMessageObject(messageObject, currentMessagesGroup, pinnedBottom, pinnedTop); + } + } + } + + public void setMessageObject(MessageObject messageObject, MessageObject.GroupedMessages groupedMessages, boolean bottomNear, boolean topNear) { + if (attachedToWindow) { + setMessageContent(messageObject, groupedMessages, bottomNear, topNear); + } else { + messageObjectToSet = messageObject; + groupedMessagesToSet = groupedMessages; + bottomNearToSet = bottomNear; + topNearToSet = topNear; + } } private int getAdditionalWidthForPosition(MessageObject.GroupedMessagePosition position) { @@ -4734,22 +4985,30 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } if (currentPosition != null) { if ((currentPosition.flags & MessageObject.POSITION_FLAG_LEFT) == 0) { - x -= AndroidUtilities.dp(4); + x -= AndroidUtilities.dp(2); } if (currentPosition.leftSpanOffset != 0) { x += (int) Math.ceil(currentPosition.leftSpanOffset / 1000.0f * getGroupPhotosWidth()); } } + if (currentMessageObject.type != 0) { + x -= AndroidUtilities.dp(2); + } photoImage.setImageCoords(x, photoImage.getImageY(), photoImage.getImageWidth(), photoImage.getImageHeight()); buttonX = (int) (x + (photoImage.getImageWidth() - AndroidUtilities.dp(48)) / 2.0f); buttonY = photoImage.getImageY() + (photoImage.getImageHeight() - AndroidUtilities.dp(48)) / 2; radialProgress.setProgressRect(buttonX, buttonY, buttonX + AndroidUtilities.dp(48), buttonY + AndroidUtilities.dp(48)); deleteProgressRect.set(buttonX + AndroidUtilities.dp(5), buttonY + AndroidUtilities.dp(5), buttonX + AndroidUtilities.dp(43), buttonY + AndroidUtilities.dp(43)); + if (documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO) { + videoButtonX = photoImage.getImageX() + AndroidUtilities.dp(8); + videoButtonY = photoImage.getImageY() + AndroidUtilities.dp(8); + videoRadialProgress.setProgressRect(videoButtonX, videoButtonY, videoButtonX + AndroidUtilities.dp(24), videoButtonY + AndroidUtilities.dp(24)); + } } } public boolean needDelayRoundProgressDraw() { - return documentAttachType == DOCUMENT_ATTACH_TYPE_ROUND && currentMessageObject.type != 5 && MediaController.getInstance().isPlayingMessage(currentMessageObject); + return (documentAttachType == DOCUMENT_ATTACH_TYPE_ROUND || documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO) && currentMessageObject.type != 5 && MediaController.getInstance().isPlayingMessage(currentMessageObject); } public void drawRoundProgress(Canvas canvas) { @@ -4820,6 +5079,13 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (!photoImage.getVisible()) { mediaWasInvisible = true; timeWasInvisible = true; + if (animatingNoSound == 1) { + animatingNoSoundProgress = 0.0f; + animatingNoSound = 0; + } else if (animatingNoSound == 2) { + animatingNoSoundProgress = 1.0f; + animatingNoSound = 0; + } } else if (groupPhotoInvisible) { timeWasInvisible = true; } else if (mediaWasInvisible || timeWasInvisible) { @@ -4835,6 +5101,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate totalChangeTime = 0; } radialProgress.setProgressColor(Theme.getColor(Theme.key_chat_mediaProgress)); + videoRadialProgress.setProgressColor(Theme.getColor(Theme.key_chat_mediaProgress)); boolean imageDrawn = false; if (currentMessageObject.type == 0) { @@ -5043,13 +5310,18 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate radialProgress.setProgressRect(buttonX, buttonY, buttonX + size, buttonY + size); } } - if (currentMessageObject.isRoundVideo() && MediaController.getInstance().isPlayingMessage(currentMessageObject) && MediaController.getInstance().isRoundVideoDrawingReady()) { + if (currentMessageObject.isRoundVideo() && MediaController.getInstance().isPlayingMessage(currentMessageObject) && MediaController.getInstance().isVideoDrawingReady()) { imageDrawn = true; drawTime = true; } else { imageDrawn = photoImage.draw(canvas); } } + if (documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO) { + videoButtonX = photoImage.getImageX() + AndroidUtilities.dp(8); + videoButtonY = photoImage.getImageY() + AndroidUtilities.dp(8); + videoRadialProgress.setProgressRect(videoButtonX, videoButtonY, videoButtonX + AndroidUtilities.dp(24), videoButtonY + AndroidUtilities.dp(24)); + } if (photosCountLayout != null && photoImage.getVisible()) { int x = photoImage.getImageX() + photoImage.getImageWidth() - AndroidUtilities.dp(8) - photosCountWidth; int y = photoImage.getImageY() + photoImage.getImageHeight() - AndroidUtilities.dp(19); @@ -5137,7 +5409,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } drawTime = true; } else if (drawPhotoImage) { - if (currentMessageObject.isRoundVideo() && MediaController.getInstance().isPlayingMessage(currentMessageObject) && MediaController.getInstance().isRoundVideoDrawingReady()) { + if (currentMessageObject.isRoundVideo() && MediaController.getInstance().isPlayingMessage(currentMessageObject) && MediaController.getInstance().isVideoDrawingReady()) { imageDrawn = true; drawTime = true; } else { @@ -5242,12 +5514,12 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } else if (documentAttachType == DOCUMENT_ATTACH_TYPE_MUSIC) { if (currentMessageObject.isOutOwner()) { Theme.chat_audioTitlePaint.setColor(Theme.getColor(Theme.key_chat_outAudioTitleText)); - Theme.chat_audioPerformerPaint.setColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_outAudioPerfomerSelectedText : Theme.key_chat_outAudioPerfomerText)); + Theme.chat_audioPerformerPaint.setColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_outAudioPerformerSelectedText : Theme.key_chat_outAudioPerformerText)); Theme.chat_audioTimePaint.setColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_outAudioDurationSelectedText : Theme.key_chat_outAudioDurationText)); radialProgress.setProgressColor(Theme.getColor(isDrawSelectedBackground() || buttonPressed != 0 ? Theme.key_chat_outAudioSelectedProgress : Theme.key_chat_outAudioProgress)); } else { Theme.chat_audioTitlePaint.setColor(Theme.getColor(Theme.key_chat_inAudioTitleText)); - Theme.chat_audioPerformerPaint.setColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_inAudioPerfomerSelectedText : Theme.key_chat_inAudioPerfomerText)); + Theme.chat_audioPerformerPaint.setColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_inAudioPerformerSelectedText : Theme.key_chat_inAudioPerformerText)); Theme.chat_audioTimePaint.setColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_inAudioDurationSelectedText : Theme.key_chat_inAudioDurationText)); radialProgress.setProgressColor(Theme.getColor(isDrawSelectedBackground() || buttonPressed != 0 ? Theme.key_chat_inAudioSelectedProgress : Theme.key_chat_inAudioProgress)); } @@ -5313,323 +5585,6 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } } - if (currentMessageObject.type == 1 || documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO) { - if (photoImage.getVisible()) { - if (!currentMessageObject.needDrawBluredPreview()) { - if (documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO) { - int oldAlpha = ((BitmapDrawable) Theme.chat_msgMediaMenuDrawable).getPaint().getAlpha(); - Theme.chat_msgMediaMenuDrawable.setAlpha((int) (oldAlpha * controlsAlpha)); - setDrawableBounds(Theme.chat_msgMediaMenuDrawable, otherX = photoImage.getImageX() + photoImage.getImageWidth() - AndroidUtilities.dp(14), otherY = photoImage.getImageY() + AndroidUtilities.dp(8.1f)); - Theme.chat_msgMediaMenuDrawable.draw(canvas); - Theme.chat_msgMediaMenuDrawable.setAlpha(oldAlpha); - } - } - - if (!forceNotDrawTime && infoLayout != null && (buttonState == 1 || buttonState == 0 || buttonState == 3 || currentMessageObject.needDrawBluredPreview())) { - Theme.chat_infoPaint.setColor(Theme.getColor(Theme.key_chat_mediaInfoText)); - int x1 = photoImage.getImageX() + AndroidUtilities.dp(4); - int y1 = photoImage.getImageY() + AndroidUtilities.dp(4); - rect.set(x1, y1, x1 + infoWidth + AndroidUtilities.dp(8), y1 + AndroidUtilities.dp(16.5f)); - int oldAlpha = Theme.chat_timeBackgroundPaint.getAlpha(); - Theme.chat_timeBackgroundPaint.setAlpha((int) (oldAlpha * controlsAlpha)); - canvas.drawRoundRect(rect, AndroidUtilities.dp(4), AndroidUtilities.dp(4), Theme.chat_timeBackgroundPaint); - Theme.chat_timeBackgroundPaint.setAlpha(oldAlpha); - - canvas.save(); - canvas.translate(photoImage.getImageX() + AndroidUtilities.dp(8), photoImage.getImageY() + AndroidUtilities.dp(5.5f)); - Theme.chat_infoPaint.setAlpha((int) (255 * controlsAlpha)); - infoLayout.draw(canvas); - canvas.restore(); - Theme.chat_infoPaint.setAlpha(255); - } - } - } else { - if (currentMessageObject.type == 4) { - if (docTitleLayout != null) { - if (currentMessageObject.isOutOwner()) { - Theme.chat_locationTitlePaint.setColor(Theme.getColor(Theme.key_chat_messageTextOut)); - Theme.chat_locationAddressPaint.setColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_outVenueInfoSelectedText : Theme.key_chat_outVenueInfoText)); - } else { - Theme.chat_locationTitlePaint.setColor(Theme.getColor(Theme.key_chat_messageTextIn)); - Theme.chat_locationAddressPaint.setColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_inVenueInfoSelectedText : Theme.key_chat_inVenueInfoText)); - } - - if (currentMessageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeoLive) { - int cy = photoImage.getImageY2() + AndroidUtilities.dp(30); - if (!locationExpired) { - forceNotDrawTime = true; - float progress = 1.0f - Math.abs(ConnectionsManager.getInstance(currentAccount).getCurrentTime() - currentMessageObject.messageOwner.date) / (float) currentMessageObject.messageOwner.media.period; - rect.set(photoImage.getImageX2() - AndroidUtilities.dp(43), cy - AndroidUtilities.dp(15), photoImage.getImageX2() - AndroidUtilities.dp(13), cy + AndroidUtilities.dp(15)); - if (currentMessageObject.isOutOwner()) { - Theme.chat_radialProgress2Paint.setColor(Theme.getColor(Theme.key_chat_outInstant)); - Theme.chat_livePaint.setColor(Theme.getColor(Theme.key_chat_outInstant)); - } else { - Theme.chat_radialProgress2Paint.setColor(Theme.getColor(Theme.key_chat_inInstant)); - Theme.chat_livePaint.setColor(Theme.getColor(Theme.key_chat_inInstant)); - } - - Theme.chat_radialProgress2Paint.setAlpha(50); - canvas.drawCircle(rect.centerX(), rect.centerY(), AndroidUtilities.dp(15), Theme.chat_radialProgress2Paint); - Theme.chat_radialProgress2Paint.setAlpha(255); - canvas.drawArc(rect, -90, -360 * progress, false, Theme.chat_radialProgress2Paint); - - String text = LocaleController.formatLocationLeftTime(Math.abs(currentMessageObject.messageOwner.media.period - (ConnectionsManager.getInstance(currentAccount).getCurrentTime() - currentMessageObject.messageOwner.date))); - float w = Theme.chat_livePaint.measureText(text); - - canvas.drawText(text, rect.centerX() - w / 2, cy + AndroidUtilities.dp(4), Theme.chat_livePaint); - - canvas.save(); - canvas.translate(photoImage.getImageX() + AndroidUtilities.dp(10), photoImage.getImageY2() + AndroidUtilities.dp(10)); - docTitleLayout.draw(canvas); - canvas.translate(0, AndroidUtilities.dp(23)); - infoLayout.draw(canvas); - canvas.restore(); - } - - int cx = photoImage.getImageX() + photoImage.getImageWidth() / 2 - AndroidUtilities.dp(31); - cy = photoImage.getImageY() + photoImage.getImageHeight() / 2 - AndroidUtilities.dp(38); - setDrawableBounds(Theme.chat_msgAvatarLiveLocationDrawable, cx, cy); - Theme.chat_msgAvatarLiveLocationDrawable.draw(canvas); - - locationImageReceiver.setImageCoords(cx + AndroidUtilities.dp(5.0f), cy + AndroidUtilities.dp(5.0f), AndroidUtilities.dp(52), AndroidUtilities.dp(52)); - locationImageReceiver.draw(canvas); - } else { - canvas.save(); - canvas.translate(photoImage.getImageX() + AndroidUtilities.dp(6), photoImage.getImageY2() + AndroidUtilities.dp(8)); - docTitleLayout.draw(canvas); - if (infoLayout != null) { - canvas.translate(0, AndroidUtilities.dp(21)); - infoLayout.draw(canvas); - } - canvas.restore(); - } - } - } else if (currentMessageObject.type == 16) { - if (currentMessageObject.isOutOwner()) { - Theme.chat_audioTitlePaint.setColor(Theme.getColor(Theme.key_chat_messageTextOut)); - Theme.chat_contactPhonePaint.setColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_outTimeSelectedText : Theme.key_chat_outTimeText)); - } else { - Theme.chat_audioTitlePaint.setColor(Theme.getColor(Theme.key_chat_messageTextIn)); - Theme.chat_contactPhonePaint.setColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_inTimeSelectedText : Theme.key_chat_inTimeText)); - } - forceNotDrawTime = true; - int x; - if (currentMessageObject.isOutOwner()) { - x = layoutWidth - backgroundWidth + AndroidUtilities.dp(16); - } else { - if (isChat && currentMessageObject.needDrawAvatar()) { - x = AndroidUtilities.dp(74); - } else { - x = AndroidUtilities.dp(25); - } - } - otherX = x; - if (titleLayout != null) { - canvas.save(); - canvas.translate(x, AndroidUtilities.dp(12) + namesOffset); - titleLayout.draw(canvas); - canvas.restore(); - } - if (docTitleLayout != null) { - canvas.save(); - canvas.translate(x + AndroidUtilities.dp(19), AndroidUtilities.dp(37) + namesOffset); - docTitleLayout.draw(canvas); - canvas.restore(); - } - Drawable icon; - Drawable phone; - if (currentMessageObject.isOutOwner()) { - icon = Theme.chat_msgCallUpGreenDrawable; - phone = isDrawSelectedBackground() || otherPressed ? Theme.chat_msgOutCallSelectedDrawable : Theme.chat_msgOutCallDrawable; - } else { - TLRPC.PhoneCallDiscardReason reason = currentMessageObject.messageOwner.action.reason; - if (reason instanceof TLRPC.TL_phoneCallDiscardReasonMissed || reason instanceof TLRPC.TL_phoneCallDiscardReasonBusy) { - icon = Theme.chat_msgCallDownRedDrawable; - } else { - icon = Theme.chat_msgCallDownGreenDrawable; - } - phone = isDrawSelectedBackground() || otherPressed ? Theme.chat_msgInCallSelectedDrawable : Theme.chat_msgInCallDrawable; - } - setDrawableBounds(icon, x - AndroidUtilities.dp(3), AndroidUtilities.dp(36) + namesOffset); - icon.draw(canvas); - - setDrawableBounds(phone, x + AndroidUtilities.dp(205), otherY = AndroidUtilities.dp(22)); - phone.draw(canvas); - } else if (currentMessageObject.type == MessageObject.TYPE_POLL) { - if (currentMessageObject.isOutOwner()) { - int color = Theme.getColor(Theme.key_chat_messageTextOut); - Theme.chat_audioTitlePaint.setColor(color); - Theme.chat_audioPerformerPaint.setColor(color); - Theme.chat_instantViewPaint.setColor(color); - color = Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_outTimeSelectedText : Theme.key_chat_outTimeText); - Theme.chat_timePaint.setColor(color); - Theme.chat_livePaint.setColor(color); - } else { - int color = Theme.getColor(Theme.key_chat_messageTextIn); - Theme.chat_audioTitlePaint.setColor(color); - Theme.chat_audioPerformerPaint.setColor(color); - Theme.chat_instantViewPaint.setColor(color); - color = Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_inTimeSelectedText : Theme.key_chat_inTimeText); - Theme.chat_timePaint.setColor(color); - Theme.chat_livePaint.setColor(color); - } - - int x; - if (currentMessageObject.isOutOwner()) { - x = layoutWidth - backgroundWidth + AndroidUtilities.dp(11); - } else { - if (isChat && currentMessageObject.needDrawAvatar()) { - x = AndroidUtilities.dp(68); - } else { - x = AndroidUtilities.dp(20); - } - } - if (titleLayout != null) { - canvas.save(); - canvas.translate(x, AndroidUtilities.dp(15) + namesOffset); - titleLayout.draw(canvas); - canvas.restore(); - } - if (docTitleLayout != null) { - canvas.save(); - canvas.translate(x + docTitleOffsetX, (titleLayout != null ? titleLayout.getHeight() : 0) + AndroidUtilities.dp(20) + namesOffset); - docTitleLayout.draw(canvas); - canvas.restore(); - } - if (Build.VERSION.SDK_INT >= 21 && selectorDrawable != null) { - selectorDrawable.draw(canvas); - } - int lastVoteY = 0; - for (int a = 0, N = pollButtons.size(); a < N; a++) { - PollButton button = pollButtons.get(a); - button.x = x; - canvas.save(); - canvas.translate(x + AndroidUtilities.dp(34), button.y + namesOffset); - button.title.draw(canvas); - int alpha = (int) (animatePollAnswerAlpha ? 255 * Math.min((pollUnvoteInProgress ? 1.0f - pollAnimationProgress : pollAnimationProgress) / 0.3f, 1.0f) : 255); - if (pollVoted || pollClosed || animatePollAnswerAlpha) { - Theme.chat_docBackPaint.setColor(Theme.getColor(currentMessageObject.isOutOwner() ? Theme.key_chat_outAudioSeekbarFill : Theme.key_chat_inAudioSeekbarFill)); - if (animatePollAnswerAlpha) { - float oldAlpha = Theme.chat_instantViewPaint.getAlpha() / 255.0f; - Theme.chat_instantViewPaint.setAlpha((int) (alpha * oldAlpha)); - oldAlpha = Theme.chat_docBackPaint.getAlpha() / 255.0f; - Theme.chat_docBackPaint.setAlpha((int) (alpha * oldAlpha)); - } - - int currentPercent = (int) Math.ceil(button.prevPercent + (button.percent - button.prevPercent) * pollAnimationProgress); - String text = String.format("%d%%", currentPercent); - int width = (int) Math.ceil(Theme.chat_instantViewPaint.measureText(text)); - canvas.drawText(text, -AndroidUtilities.dp(7) - width, AndroidUtilities.dp(14), Theme.chat_instantViewPaint); - - width = backgroundWidth - AndroidUtilities.dp(76); - float currentPercentProgress = button.prevPercentProgress + (button.percentProgress - button.prevPercentProgress) * pollAnimationProgress; - instantButtonRect.set(0, button.height + AndroidUtilities.dp(6), width * currentPercentProgress, button.height + AndroidUtilities.dp(11)); - canvas.drawRoundRect(instantButtonRect, AndroidUtilities.dp(2), AndroidUtilities.dp(2), Theme.chat_docBackPaint); - } - - if (!pollVoted && !pollClosed || animatePollAnswerAlpha) { - if (isDrawSelectedBackground()) { - Theme.chat_replyLinePaint.setColor(Theme.getColor(currentMessageObject.isOutOwner() ? Theme.key_chat_outVoiceSeekbarSelected : Theme.key_chat_inVoiceSeekbarSelected)); - } else { - Theme.chat_replyLinePaint.setColor(Theme.getColor(currentMessageObject.isOutOwner() ? Theme.key_chat_outVoiceSeekbar : Theme.key_chat_inVoiceSeekbar)); - } - if (animatePollAnswerAlpha) { - float oldAlpha = Theme.chat_replyLinePaint.getAlpha() / 255.0f; - Theme.chat_replyLinePaint.setAlpha((int) ((255 - alpha) * oldAlpha)); - } - canvas.drawLine(-AndroidUtilities.dp(2), button.height + AndroidUtilities.dp(13), backgroundWidth - AndroidUtilities.dp(56), button.height + AndroidUtilities.dp(13), Theme.chat_replyLinePaint); - if (pollVoteInProgress && a == pollVoteInProgressNum) { - Theme.chat_instantViewRectPaint.setColor(Theme.getColor(currentMessageObject.isOutOwner() ? Theme.key_chat_outAudioSeekbarFill : Theme.key_chat_inAudioSeekbarFill)); - if (animatePollAnswerAlpha) { - float oldAlpha = Theme.chat_instantViewRectPaint.getAlpha() / 255.0f; - Theme.chat_instantViewRectPaint.setAlpha((int) ((255 - alpha) * oldAlpha)); - } - instantButtonRect.set(-AndroidUtilities.dp(23) - AndroidUtilities.dp(8.5f), AndroidUtilities.dp(9) - AndroidUtilities.dp(8.5f), -AndroidUtilities.dp(23) + AndroidUtilities.dp(8.5f), AndroidUtilities.dp(9) + AndroidUtilities.dp(8.5f)); - canvas.drawArc(instantButtonRect, voteRadOffset, voteCurrentCircleLength, false, Theme.chat_instantViewRectPaint); - } else { - if (currentMessageObject.isOutOwner()) { - Theme.chat_instantViewRectPaint.setColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_outMenuSelected : Theme.key_chat_outMenu)); - } else { - Theme.chat_instantViewRectPaint.setColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_inMenuSelected : Theme.key_chat_inMenu)); - } - if (animatePollAnswerAlpha) { - float oldAlpha = Theme.chat_instantViewRectPaint.getAlpha() / 255.0f; - Theme.chat_instantViewRectPaint.setAlpha((int) ((255 - alpha) * oldAlpha)); - } - canvas.drawCircle(-AndroidUtilities.dp(23), AndroidUtilities.dp(9), AndroidUtilities.dp(8.5f), Theme.chat_instantViewRectPaint); - } - } - canvas.restore(); - if (a == N - 1) { - lastVoteY = button.y + namesOffset + button.height; - } - } - if (infoLayout != null) { - canvas.save(); - canvas.translate(x + infoX, lastVoteY + AndroidUtilities.dp(22)); - infoLayout.draw(canvas); - canvas.restore(); - } - updatePollAnimations(); - } else if (currentMessageObject.type == 12) { - if (currentMessageObject.isOutOwner()) { - Theme.chat_contactNamePaint.setColor(Theme.getColor(Theme.key_chat_outContactNameText)); - Theme.chat_contactPhonePaint.setColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_outContactPhoneSelectedText : Theme.key_chat_outContactPhoneText)); - } else { - Theme.chat_contactNamePaint.setColor(Theme.getColor(Theme.key_chat_inContactNameText)); - Theme.chat_contactPhonePaint.setColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_inContactPhoneSelectedText : Theme.key_chat_inContactPhoneText)); - } - if (titleLayout != null) { - canvas.save(); - canvas.translate(photoImage.getImageX() + photoImage.getImageWidth() + AndroidUtilities.dp(9), AndroidUtilities.dp(16) + namesOffset); - titleLayout.draw(canvas); - canvas.restore(); - } - if (docTitleLayout != null) { - canvas.save(); - canvas.translate(photoImage.getImageX() + photoImage.getImageWidth() + AndroidUtilities.dp(9), AndroidUtilities.dp(39) + namesOffset); - docTitleLayout.draw(canvas); - canvas.restore(); - } - - Drawable menuDrawable; - if (currentMessageObject.isOutOwner()) { - menuDrawable = isDrawSelectedBackground() ? Theme.chat_msgOutMenuSelectedDrawable : Theme.chat_msgOutMenuDrawable; - } else { - menuDrawable = isDrawSelectedBackground() ? Theme.chat_msgInMenuSelectedDrawable : Theme.chat_msgInMenuDrawable; - } - setDrawableBounds(menuDrawable, otherX = photoImage.getImageX() + backgroundWidth - AndroidUtilities.dp(48), otherY = photoImage.getImageY() - AndroidUtilities.dp(5)); - menuDrawable.draw(canvas); - - if (drawInstantView) { - int textX = photoImage.getImageX() - AndroidUtilities.dp(2); - Drawable instantDrawable; - int instantY = getMeasuredHeight() - AndroidUtilities.dp(36 + 28); - Paint backPaint = Theme.chat_instantViewRectPaint; - if (currentMessageObject.isOutOwner()) { - Theme.chat_instantViewPaint.setColor(Theme.getColor(Theme.key_chat_outPreviewInstantText)); - backPaint.setColor(Theme.getColor(Theme.key_chat_outPreviewInstantText)); - } else { - Theme.chat_instantViewPaint.setColor(Theme.getColor(Theme.key_chat_inPreviewInstantText)); - backPaint.setColor(Theme.getColor(Theme.key_chat_inPreviewInstantText)); - } - - if (Build.VERSION.SDK_INT >= 21) { - selectorDrawable.setBounds(textX, instantY, textX + instantWidth, instantY + AndroidUtilities.dp(36)); - selectorDrawable.draw(canvas); - } - instantButtonRect.set(textX, instantY, textX + instantWidth, instantY + AndroidUtilities.dp(36)); - canvas.drawRoundRect(instantButtonRect, AndroidUtilities.dp(6), AndroidUtilities.dp(6), backPaint); - if (instantViewLayout != null) { - canvas.save(); - canvas.translate(textX + instantTextX, instantY + AndroidUtilities.dp(10.5f)); - instantViewLayout.draw(canvas); - canvas.restore(); - } - } - } - } - if (captionLayout != null) { if (currentMessageObject.type == 1 || documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO || currentMessageObject.type == 8) { captionX = photoImage.getImageX() + AndroidUtilities.dp(5) + captionOffsetX; @@ -5723,26 +5678,27 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (!imageDrawn) { if (currentMessageObject.isOutOwner()) { radialProgress.setColors(Theme.key_chat_outLoader, Theme.key_chat_outLoaderSelected, Theme.key_chat_outMediaIcon, Theme.key_chat_outMediaIconSelected); + radialProgress.setProgressColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_outFileProgressSelected : Theme.key_chat_outFileProgress)); + videoRadialProgress.setColors(Theme.key_chat_outLoader, Theme.key_chat_outLoaderSelected, Theme.key_chat_outMediaIcon, Theme.key_chat_outMediaIconSelected); + videoRadialProgress.setProgressColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_outFileProgressSelected : Theme.key_chat_outFileProgress)); } else { radialProgress.setColors(Theme.key_chat_inLoader, Theme.key_chat_inLoaderSelected, Theme.key_chat_inMediaIcon, Theme.key_chat_inMediaIconSelected); + radialProgress.setProgressColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_inFileProgressSelected : Theme.key_chat_inFileProgress)); + videoRadialProgress.setColors(Theme.key_chat_inLoader, Theme.key_chat_inLoaderSelected, Theme.key_chat_inMediaIcon, Theme.key_chat_inMediaIconSelected); + videoRadialProgress.setProgressColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_inFileProgressSelected : Theme.key_chat_inFileProgress)); } - } else { - radialProgress.setColors(Theme.key_chat_mediaLoaderPhoto, Theme.key_chat_mediaLoaderPhotoSelected, Theme.key_chat_mediaLoaderPhotoIcon, Theme.key_chat_mediaLoaderPhotoIconSelected); - } - if (!imageDrawn) { rect.set(photoImage.getImageX(), photoImage.getImageY(), photoImage.getImageX() + photoImage.getImageWidth(), photoImage.getImageY() + photoImage.getImageHeight()); canvas.drawRoundRect(rect, AndroidUtilities.dp(3), AndroidUtilities.dp(3), Theme.chat_docBackPaint); - if (currentMessageObject.isOutOwner()) { - radialProgress.setProgressColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_outFileProgressSelected : Theme.key_chat_outFileProgress)); - } else { - radialProgress.setProgressColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_inFileProgressSelected : Theme.key_chat_inFileProgress)); - } } else { + radialProgress.setColors(Theme.key_chat_mediaLoaderPhoto, Theme.key_chat_mediaLoaderPhotoSelected, Theme.key_chat_mediaLoaderPhotoIcon, Theme.key_chat_mediaLoaderPhotoIconSelected); + radialProgress.setProgressColor(Theme.getColor(Theme.key_chat_mediaProgress)); + videoRadialProgress.setColors(Theme.key_chat_mediaLoaderPhoto, Theme.key_chat_mediaLoaderPhotoSelected, Theme.key_chat_mediaLoaderPhotoIcon, Theme.key_chat_mediaLoaderPhotoIconSelected); + videoRadialProgress.setProgressColor(Theme.getColor(Theme.key_chat_mediaProgress)); + if (buttonState == -1 && radialProgress.getIcon() != MediaActionDrawable.ICON_NONE) { radialProgress.setIcon(MediaActionDrawable.ICON_NONE, true, true); } - radialProgress.setProgressColor(Theme.getColor(Theme.key_chat_mediaProgress)); } } else { setDrawableBounds(menuDrawable, otherX = buttonX + backgroundWidth - AndroidUtilities.dp(currentMessageObject.type == 0 ? 58 : 48), otherY = buttonY - AndroidUtilities.dp(5)); @@ -5754,8 +5710,10 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } if (currentMessageObject.isOutOwner()) { radialProgress.setProgressColor(Theme.getColor(isDrawSelectedBackground() || buttonPressed != 0 ? Theme.key_chat_outAudioSelectedProgress : Theme.key_chat_outAudioProgress)); + videoRadialProgress.setProgressColor(Theme.getColor(isDrawSelectedBackground() || videoButtonPressed != 0 ? Theme.key_chat_outAudioSelectedProgress : Theme.key_chat_outAudioProgress)); } else { radialProgress.setProgressColor(Theme.getColor(isDrawSelectedBackground() || buttonPressed != 0 ? Theme.key_chat_inAudioSelectedProgress : Theme.key_chat_inAudioProgress)); + videoRadialProgress.setProgressColor(Theme.getColor(isDrawSelectedBackground() || videoButtonPressed != 0 ? Theme.key_chat_inAudioSelectedProgress : Theme.key_chat_inAudioProgress)); } } menuDrawable.draw(canvas); @@ -5782,12 +5740,6 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate FileLog.e(e); } } - if (drawImageButton && photoImage.getVisible()) { - if (controlsAlpha != 1.0f) { - radialProgress.setOverrideAlpha(controlsAlpha); - } - radialProgress.draw(canvas); - } if (buttonState == -1 && currentMessageObject.needDrawBluredPreview() && !MediaController.getInstance().isPlayingMessage(currentMessageObject) && photoImage.getVisible()) { if (currentMessageObject.messageOwner.destroyTime != 0) { if (!currentMessageObject.isOutOwner()) { @@ -5918,6 +5870,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } } else { radialProgress.setColors(Theme.key_chat_mediaLoaderPhoto, Theme.key_chat_mediaLoaderPhotoSelected, Theme.key_chat_mediaLoaderPhotoIcon, Theme.key_chat_mediaLoaderPhotoIconSelected); + videoRadialProgress.setColors(Theme.key_chat_mediaLoaderPhoto, Theme.key_chat_mediaLoaderPhotoSelected, Theme.key_chat_mediaLoaderPhotoIcon, Theme.key_chat_mediaLoaderPhotoIconSelected); if (buttonState >= 0 && buttonState < 4) { if (buttonState == 0) { return MediaActionDrawable.ICON_DOWNLOAD; @@ -5926,7 +5879,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } else if (buttonState == 2) { return MediaActionDrawable.ICON_GIF; } else if (buttonState == 3) { - return MediaActionDrawable.ICON_PLAY; + return autoPlayingVideo ? MediaActionDrawable.ICON_NONE : MediaActionDrawable.ICON_PLAY; } } else if (buttonState == -1) { if (documentAttachType == DOCUMENT_ATTACH_TYPE_DOCUMENT) { @@ -6023,13 +5976,18 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate fileName = FileLoader.getAttachFileName(currentPhotoObject); fileExists = currentMessageObject.mediaExists; } - if (SharedConfig.streamMedia && (int) currentMessageObject.getDialogId() != 0 && !currentMessageObject.isSecretMedia() && (documentAttachType == DOCUMENT_ATTACH_TYPE_MUSIC || documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO && currentMessageObject.canStreamVideo())) { + canStreamVideo = currentMessageObject.isSent() && documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO && currentMessageObject.canStreamVideo() && !currentMessageObject.needDrawBluredPreview(); + if (SharedConfig.streamMedia && (int) currentMessageObject.getDialogId() != 0 && !currentMessageObject.isSecretMedia() && + (documentAttachType == DOCUMENT_ATTACH_TYPE_MUSIC || + canStreamVideo && currentPosition != null && ((currentPosition.flags & MessageObject.POSITION_FLAG_LEFT) == 0 || (currentPosition.flags & MessageObject.POSITION_FLAG_RIGHT) == 0))) { hasMiniProgress = fileExists ? 1 : 2; fileExists = true; } if (currentMessageObject.isSendError() || TextUtils.isEmpty(fileName) && !currentMessageObject.isSending() && !currentMessageObject.isEditing()) { radialProgress.setIcon(MediaActionDrawable.ICON_NONE, ifSame, false); radialProgress.setMiniIcon(MediaActionDrawable.ICON_NONE, ifSame, false); + videoRadialProgress.setIcon(MediaActionDrawable.ICON_NONE, ifSame, false); + videoRadialProgress.setMiniIcon(MediaActionDrawable.ICON_NONE, ifSame, false); return; } boolean fromBot = currentMessageObject.messageOwner.params != null && currentMessageObject.messageOwner.params.containsKey("query_id"); @@ -6038,6 +5996,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (currentMessageObject.isOut() && (currentMessageObject.isSending() || currentMessageObject.isEditing()) || currentMessageObject.isSendError() && fromBot) { if (!TextUtils.isEmpty(currentMessageObject.messageOwner.attachPath)) { DownloadController.getInstance(currentAccount).addLoadingFileObserver(currentMessageObject.messageOwner.attachPath, currentMessageObject, this); + wasSending = true; buttonState = 4; radialProgress.setIcon(getIconForCurrentState(), ifSame, animated); if (!fromBot) { @@ -6147,6 +6106,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (currentMessageObject.isOut() && (currentMessageObject.isSending() || currentMessageObject.isEditing())) { if (!TextUtils.isEmpty(currentMessageObject.messageOwner.attachPath)) { DownloadController.getInstance(currentAccount).addLoadingFileObserver(currentMessageObject.messageOwner.attachPath, currentMessageObject, this); + wasSending = true; boolean needProgress = currentMessageObject.messageOwner.attachPath == null || !currentMessageObject.messageOwner.attachPath.startsWith("http"); HashMap params = currentMessageObject.messageOwner.params; if (currentMessageObject.messageOwner.message != null && params != null && (params.containsKey("url") || params.containsKey("bot"))) { @@ -6158,6 +6118,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate boolean sending = SendMessagesHelper.getInstance(currentAccount).isSendingMessage(currentMessageObject.getId()); if (currentPosition != null && sending && buttonState == 1) { drawRadialCheckBackground = true; + getIconForCurrentState(); radialProgress.setIcon(MediaActionDrawable.ICON_CHECK, ifSame, animated); } else { radialProgress.setIcon(getIconForCurrentState(), ifSame, animated); @@ -6178,10 +6139,40 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate radialProgress.setIcon(currentMessageObject.isSticker() || currentMessageObject.isLocation() ? MediaActionDrawable.ICON_NONE : MediaActionDrawable.ICON_CANCEL_NOPROFRESS, ifSame, false); radialProgress.setProgress(0, false); } + videoRadialProgress.setIcon(MediaActionDrawable.ICON_NONE, ifSame, false); } else { - if (!TextUtils.isEmpty(currentMessageObject.messageOwner.attachPath)) { + if (wasSending && !TextUtils.isEmpty(currentMessageObject.messageOwner.attachPath)) { DownloadController.getInstance(currentAccount).removeLoadingFileObserver(this); } + boolean playingCurrentMessage = MediaController.getInstance().isPlayingMessage(currentMessageObject); + /*boolean playingCurrentMessageAndDrawing = MediaController.getInstance().isPlayingMessageAndReadyToDraw(currentMessageObject); + if (autoPlayingVideo && documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO) { + photoImage.setAllowStartAnimation(!playingCurrentMessageAndDrawing); + if (playingCurrentMessageAndDrawing) { + photoImage.stopAnimation(); + AnimatedFileDrawable animation = photoImage.getAnimation(); + if (animation != null) { + //animation.seekTo(0, true); + } + } else if (!playingCurrentMessage && !PhotoViewer.isPlayingMessage(currentMessageObject) && !MediaController.getInstance().isGoingToShowMessageObject(currentMessageObject)) { + photoImage.startAnimation(); + } + }*/ + + boolean isLoadingVideo = false; + if (documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO && autoPlayingVideo) { + isLoadingVideo = FileLoader.getInstance(currentAccount).isLoadingVideo(documentAttach, playingCurrentMessage); + AnimatedFileDrawable animation = photoImage.getAnimation(); + if (animation != null) { + if (currentMessageObject.hadAnimationNotReadyLoading) { + if (animation.hasBitmap()) { + currentMessageObject.hadAnimationNotReadyLoading = false; + } + } else { + currentMessageObject.hadAnimationNotReadyLoading = isLoadingVideo && !animation.hasBitmap(); + } + } + } if (hasMiniProgress != 0) { radialProgress.setMiniProgressBackgroundColor(Theme.getColor(Theme.key_chat_inLoaderPhoto)); buttonState = 3; @@ -6204,8 +6195,20 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } } radialProgress.setMiniIcon(getMiniIconForCurrentState(), ifSame, animated); - } else if (fileExists) { + } else if (fileExists || documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO && autoPlayingVideo && !currentMessageObject.hadAnimationNotReadyLoading && !isLoadingVideo) { DownloadController.getInstance(currentAccount).removeLoadingFileObserver(this); + if (drawVideoImageButton && animated) { + if (animatingDrawVideoImageButton != 1 && animatingDrawVideoImageButtonProgress > 0) { + if (animatingDrawVideoImageButton == 0) { + animatingDrawVideoImageButtonProgress = 1.0f; + } + animatingDrawVideoImageButton = 1; + } + } else if (animatingDrawVideoImageButton == 0) { + animatingDrawVideoImageButtonProgress = 0.0f; + } + drawVideoImageButton = false; + drawVideoSize = false; if (currentMessageObject.needDrawBluredPreview()) { buttonState = -1; } else { @@ -6217,12 +6220,24 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate buttonState = -1; } } + videoRadialProgress.setIcon(MediaActionDrawable.ICON_NONE, ifSame, animatingDrawVideoImageButton != 0); radialProgress.setIcon(getIconForCurrentState(), ifSame, animated); if (!fromSet && photoNotSet) { setMessageObject(currentMessageObject, currentMessagesGroup, pinnedBottom, pinnedTop); } invalidate(); } else { + drawVideoSize = documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO; + if (documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO && canStreamVideo && !drawVideoImageButton && animated) { + if (animatingDrawVideoImageButton != 2 && animatingDrawVideoImageButtonProgress < 1.0f) { + if (animatingDrawVideoImageButton == 0) { + animatingDrawVideoImageButtonProgress = 0.0f; + } + animatingDrawVideoImageButton = 2; + } + } else if (animatingDrawVideoImageButton == 0) { + animatingDrawVideoImageButtonProgress = 1.0f; + } DownloadController.getInstance(currentAccount).addLoadingFileObserver(fileName, currentMessageObject, this); boolean progressVisible = false; if (!FileLoader.getInstance(currentAccount).isLoadingFile(fileName)) { @@ -6239,12 +6254,37 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } else { buttonState = 0; } - radialProgress.setIcon(getIconForCurrentState(), ifSame, animated); + if (documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO && canStreamVideo) { + drawVideoImageButton = true; + getIconForCurrentState(); + radialProgress.setIcon(autoPlayingVideo ? MediaActionDrawable.ICON_NONE : MediaActionDrawable.ICON_PLAY, ifSame, animated); + videoRadialProgress.setIcon(MediaActionDrawable.ICON_DOWNLOAD, ifSame, animated); + } else { + drawVideoImageButton = false; + radialProgress.setIcon(getIconForCurrentState(), ifSame, animated); + videoRadialProgress.setIcon(MediaActionDrawable.ICON_NONE, ifSame, false); + if (!drawVideoSize && animatingDrawVideoImageButton == 0) { + animatingDrawVideoImageButtonProgress = 0.0f; + } + } } else { buttonState = 1; Float progress = ImageLoader.getInstance().getFileProgress(fileName); - radialProgress.setProgress(progress != null ? progress : 0, false); - radialProgress.setIcon(getIconForCurrentState(), ifSame, animated); + if (documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO && canStreamVideo) { + drawVideoImageButton = true; + getIconForCurrentState(); + radialProgress.setIcon(autoPlayingVideo ? MediaActionDrawable.ICON_NONE : MediaActionDrawable.ICON_PLAY, ifSame, animated); + videoRadialProgress.setProgress(progress != null ? progress : 0, false); + videoRadialProgress.setIcon(MediaActionDrawable.ICON_CANCEL_FILL, ifSame, animated); + } else { + drawVideoImageButton = false; + radialProgress.setProgress(progress != null ? progress : 0, false); + radialProgress.setIcon(getIconForCurrentState(), ifSame, animated); + videoRadialProgress.setIcon(MediaActionDrawable.ICON_NONE, ifSame, false); + if (!drawVideoSize && animatingDrawVideoImageButton == 0) { + animatingDrawVideoImageButtonProgress = 0.0f; + } + } } invalidate(); } @@ -6279,8 +6319,8 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } } - private void didPressButton(boolean animated) { - if (buttonState == 0) { + private void didPressButton(boolean animated, boolean video) { + if (buttonState == 0 && (!drawVideoImageButton && !video || video)) { if (documentAttachType == DOCUMENT_ATTACH_TYPE_AUDIO || documentAttachType == DOCUMENT_ATTACH_TYPE_MUSIC) { if (miniButtonState == 0) { FileLoader.getInstance(currentAccount).loadFile(documentAttach, currentMessageObject, 1, 0); @@ -6298,14 +6338,27 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } } else { cancelLoading = false; - radialProgress.setProgress(0, false); + if (video) { + videoRadialProgress.setProgress(0, false); + } else { + radialProgress.setProgress(0, false); + } + TLRPC.PhotoSize thumb; + String thumbFilter; + if (currentPhotoObject != null && (photoImage.hasNotThumb() || currentPhotoObjectThumb == null)) { + thumb = currentPhotoObject; + thumbFilter = thumb instanceof TLRPC.TL_photoStrippedSize || "s".equals(thumb.type) ? currentPhotoFilterThumb : currentPhotoFilter; + } else { + thumb = currentPhotoObjectThumb; + thumbFilter = currentPhotoFilterThumb; + } if (currentMessageObject.type == 1) { photoImage.setForceLoading(true); photoImage.setImage(currentPhotoObject, currentPhotoFilter, currentPhotoObjectThumb, currentPhotoFilterThumb, currentPhotoObject.size, null, currentMessageObject, currentMessageObject.shouldEncryptPhotoOrVideo() ? 2 : 0); } else if (currentMessageObject.type == 8) { currentMessageObject.gifState = 2; photoImage.setForceLoading(true); - photoImage.setImage(currentMessageObject.messageOwner.media.document, null, currentPhotoObjectThumb, currentPhotoFilterThumb, currentMessageObject.messageOwner.media.document.size, null, currentMessageObject, 0); + photoImage.setImage(currentMessageObject.messageOwner.media.document, null, thumb, thumbFilter, currentMessageObject.messageOwner.media.document.size, null, currentMessageObject, 0); } else if (currentMessageObject.isRoundVideo()) { if (currentMessageObject.isSecretMedia()) { FileLoader.getInstance(currentAccount).loadFile(currentMessageObject.getDocument(), currentMessageObject, 1, 1); @@ -6313,19 +6366,20 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate currentMessageObject.gifState = 2; TLRPC.Document document = currentMessageObject.getDocument(); photoImage.setForceLoading(true); - photoImage.setImage(document, null, currentPhotoObjectThumb, currentPhotoFilterThumb, document.size, null, currentMessageObject, 0); + + photoImage.setImage(document, null, thumb, thumbFilter, document.size, null, currentMessageObject, 0); } } else if (currentMessageObject.type == 9) { - FileLoader.getInstance(currentAccount).loadFile(currentMessageObject.messageOwner.media.document, currentMessageObject, 0, 0); + FileLoader.getInstance(currentAccount).loadFile(documentAttach, currentMessageObject, 0, 0); } else if (documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO) { FileLoader.getInstance(currentAccount).loadFile(documentAttach, currentMessageObject, 1, currentMessageObject.shouldEncryptPhotoOrVideo() ? 2 : 0); } else if (currentMessageObject.type == 0 && documentAttachType != DOCUMENT_ATTACH_TYPE_NONE) { if (documentAttachType == DOCUMENT_ATTACH_TYPE_GIF) { photoImage.setForceLoading(true); - photoImage.setImage(currentMessageObject.messageOwner.media.webpage.document, null, currentPhotoObject, currentPhotoFilterThumb, currentMessageObject.messageOwner.media.webpage.document.size, null, currentMessageObject, 0); + photoImage.setImage(documentAttach, null, currentPhotoObject, currentPhotoFilterThumb, documentAttach.size, null, currentMessageObject, 0); currentMessageObject.gifState = 2; } else if (documentAttachType == DOCUMENT_ATTACH_TYPE_DOCUMENT) { - FileLoader.getInstance(currentAccount).loadFile(currentMessageObject.messageOwner.media.webpage.document, currentMessageObject, 0, 0); + FileLoader.getInstance(currentAccount).loadFile(documentAttach, currentMessageObject, 0, 0); } else if (documentAttachType == DOCUMENT_ATTACH_TYPE_WALLPAPER) { photoImage.setImage(documentAttach, currentPhotoFilter, null, currentPhotoObject, "b1", 0, "jpg", currentMessageObject, 1); } @@ -6334,10 +6388,15 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate photoImage.setImage(currentPhotoObject, currentPhotoFilter, currentPhotoObjectThumb, currentPhotoFilterThumb, 0, null, currentMessageObject, 0); } buttonState = 1; - radialProgress.setIcon(getIconForCurrentState(), false, animated); + if (video) { + videoRadialProgress.setIcon(MediaActionDrawable.ICON_CANCEL_FILL, false, animated); + } else { + radialProgress.setIcon(getIconForCurrentState(), false, animated); + } invalidate(); } - } else if (buttonState == 1) { + } else if (buttonState == 1 && (!drawVideoImageButton && !video || video)) { + photoImage.setForceLoading(false); if (documentAttachType == DOCUMENT_ATTACH_TYPE_AUDIO || documentAttachType == DOCUMENT_ATTACH_TYPE_MUSIC) { boolean result = MediaController.getInstance().pauseMessage(currentMessageObject); if (result) { @@ -6361,7 +6420,11 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate FileLoader.getInstance(currentAccount).cancelLoadFile(currentMessageObject.messageOwner.media.document); } buttonState = 0; - radialProgress.setIcon(getIconForCurrentState(), false, animated); + if (video) { + videoRadialProgress.setIcon(MediaActionDrawable.ICON_DOWNLOAD, false, animated); + } else { + radialProgress.setIcon(getIconForCurrentState(), false, animated); + } invalidate(); } } @@ -6373,13 +6436,21 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate radialProgress.setIcon(getIconForCurrentState(), true, animated); invalidate(); } else { - photoImage.setAllowStartAnimation(true); - photoImage.startAnimation(); + if (currentMessageObject.isRoundVideo()) { + MessageObject playingMessage = MediaController.getInstance().getPlayingMessageObject(); + if (playingMessage == null || !playingMessage.isRoundVideo()) { + photoImage.setAllowStartAnimation(true); + photoImage.startAnimation(); + } + } else { + photoImage.setAllowStartAnimation(true); + photoImage.startAnimation(); + } currentMessageObject.gifState = 0; buttonState = -1; radialProgress.setIcon(getIconForCurrentState(), false, animated); } - } else if (buttonState == 3) { + } else if (buttonState == 3 || buttonState == 0 && drawVideoImageButton) { if (hasMiniProgress == 2 && miniButtonState != 1) { miniButtonState = 1; radialProgress.setProgress(0, false); @@ -6413,11 +6484,26 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate updateButtonState(false, true, false); updateWaveform(); } else { - radialProgress.setProgress(1, true); + if (drawVideoImageButton) { + videoRadialProgress.setProgress(1, true); + } else { + radialProgress.setProgress(1, true); + } + if (SharedConfig.autoplayVideo && !currentMessageObject.needDrawBluredPreview() && !autoPlayingVideo && documentAttach != null && documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO && (currentPosition == null || (currentPosition.flags & MessageObject.POSITION_FLAG_LEFT) != 0 && (currentPosition.flags & MessageObject.POSITION_FLAG_RIGHT) != 0)) { + animatingNoSound = 2; + photoImage.setImage(documentAttach, ImageLoader.VIDEO_FILTER, currentPhotoObject, currentPhotoObject instanceof TLRPC.TL_photoStrippedSize || currentPhotoObject != null && "s".equals(currentPhotoObject.type) ? currentPhotoFilterThumb : currentPhotoFilter, null, currentPhotoObjectThumb, currentPhotoFilterThumb, documentAttach.size, null, currentMessageObject, 0); + if (!PhotoViewer.isPlayingMessage(currentMessageObject)) { + photoImage.setAllowStartAnimation(true); + photoImage.startAnimation(); + } else { + photoImage.setAllowStartAnimation(false); + } + autoPlayingVideo = true; + } if (currentMessageObject.type == 0) { if (documentAttachType == DOCUMENT_ATTACH_TYPE_GIF && currentMessageObject.gifState != 1) { buttonState = 2; - didPressButton(true); + didPressButton(true, false); } else if (!photoNotSet) { updateButtonState(false, true, false); } else { @@ -6428,7 +6514,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if ((currentMessageObject.type == 8 || currentMessageObject.type == 5) && currentMessageObject.gifState != 1) { photoNotSet = false; buttonState = 2; - didPressButton(true); + didPressButton(true, false); } else { updateButtonState(false, true, false); } @@ -6442,7 +6528,10 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate @Override public void didSetImage(ImageReceiver imageReceiver, boolean set, boolean thumb) { - if (currentMessageObject != null && (currentMessageObject.type == 0 || currentMessageObject.type == 1 || currentMessageObject.type == 5 || currentMessageObject.type == 8) && set && !thumb && !currentMessageObject.mediaExists && !currentMessageObject.attachPathExists) { + boolean animationLoaded = !thumb && imageReceiver.getAnimation() != null; + if (currentMessageObject != null && set && !thumb && !currentMessageObject.mediaExists && !currentMessageObject.attachPathExists && + (currentMessageObject.type == 0 && (documentAttachType == DOCUMENT_ATTACH_TYPE_WALLPAPER || documentAttachType == DOCUMENT_ATTACH_TYPE_NONE || documentAttachType == DOCUMENT_ATTACH_TYPE_STICKER || animationLoaded && (documentAttachType == DOCUMENT_ATTACH_TYPE_GIF || documentAttachType == DOCUMENT_ATTACH_TYPE_ROUND)) || + currentMessageObject.type == 1 || animationLoaded && (currentMessageObject.type == 5 || currentMessageObject.type == 8))) { currentMessageObject.mediaExists = true; updateButtonState(false, true, false); } @@ -6450,7 +6539,11 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate @Override public void onProgressDownload(String fileName, float progress) { - radialProgress.setProgress(progress, true); + if (drawVideoImageButton) { + videoRadialProgress.setProgress(progress, true); + } else { + radialProgress.setProgress(progress, true); + } if (documentAttachType == DOCUMENT_ATTACH_TYPE_AUDIO || documentAttachType == DOCUMENT_ATTACH_TYPE_MUSIC) { if (hasMiniProgress != 0) { if (miniButtonState != 1) { @@ -6481,6 +6574,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate boolean sending = SendMessagesHelper.getInstance(currentAccount).isSendingMessage(currentMessageObject.getId()); if (sending && buttonState == 1) { drawRadialCheckBackground = true; + getIconForCurrentState(); radialProgress.setIcon(MediaActionDrawable.ICON_CHECK, false, true); } } @@ -7230,8 +7324,11 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate drawNamesLayout(canvas); } + if (!autoPlayingVideo || !MediaController.getInstance().isPlayingMessageAndReadyToDraw(currentMessageObject)) { + drawOverlays(canvas); + } if ((drawTime || !mediaBackground) && !forceNotDrawTime) { - drawTimeLayout(canvas); + drawTime(canvas); } if ((controlsAlpha != 1.0f || timeAlpha != 1.0f) && currentMessageObject.type != 5) { @@ -7440,7 +7537,11 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate canvas.restore(); } - public void drawTimeLayout(Canvas canvas) { + public boolean needDrawTime() { + return !forceNotDrawTime; + } + + public void drawTime(Canvas canvas) { if ((!drawTime || groupPhotoInvisible) && mediaBackground && captionLayout == null || timeLayout == null) { return; } @@ -7693,6 +7794,424 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } } + public void drawOverlays(Canvas canvas) { + long newAnimationTime = SystemClock.uptimeMillis(); + long animationDt = newAnimationTime - lastAnimationTime; + if (animationDt > 17) { + animationDt = 17; + } + lastAnimationTime = newAnimationTime; + + if (currentMessageObject.type == 1 || documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO) { + if (photoImage.getVisible()) { + if (!currentMessageObject.needDrawBluredPreview()) { + if (documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO) { + if (currentMessageObject.hadAnimationNotReadyLoading) { + AnimatedFileDrawable animation = photoImage.getAnimation(); + if (animation != null && animation.hasBitmap()) { + currentMessageObject.hadAnimationNotReadyLoading = false; + updateButtonState(false, true, false); + } + } + int oldAlpha = ((BitmapDrawable) Theme.chat_msgMediaMenuDrawable).getPaint().getAlpha(); + Theme.chat_msgMediaMenuDrawable.setAlpha((int) (oldAlpha * controlsAlpha)); + setDrawableBounds(Theme.chat_msgMediaMenuDrawable, otherX = photoImage.getImageX() + photoImage.getImageWidth() - AndroidUtilities.dp(14), otherY = photoImage.getImageY() + AndroidUtilities.dp(8.1f)); + Theme.chat_msgMediaMenuDrawable.draw(canvas); + Theme.chat_msgMediaMenuDrawable.setAlpha(oldAlpha); + } + } + + boolean playing = MediaController.getInstance().isPlayingMessage(currentMessageObject); + if (animatingNoSoundPlaying != playing) { + animatingNoSoundPlaying = playing; + animatingNoSound = playing ? 1 : 2; + animatingNoSoundProgress = playing ? 1.0f : 0.0f; + } + if (buttonState == 1 || buttonState == 0 || buttonState == 3 || currentMessageObject.needDrawBluredPreview()) { + if (autoPlayingVideo) { + updatePlayingMessageProgress(); + } + if (infoLayout != null && (!forceNotDrawTime || autoPlayingVideo || drawVideoImageButton)) { + float alpha = currentMessageObject.needDrawBluredPreview() && docTitleLayout == null ? 0 : animatingDrawVideoImageButtonProgress; + + Theme.chat_infoPaint.setColor(Theme.getColor(Theme.key_chat_mediaInfoText)); + int x1 = photoImage.getImageX() + AndroidUtilities.dp(4); + int y1 = photoImage.getImageY() + AndroidUtilities.dp(4); + + int imageW; + if (autoPlayingVideo && (!playing || animatingNoSound != 0)) { + imageW = (int) ((Theme.chat_msgNoSoundDrawable.getIntrinsicWidth() + AndroidUtilities.dp(4)) * animatingNoSoundProgress); + } else { + imageW = 0; + } + int w = (int) Math.ceil(infoWidth + AndroidUtilities.dp(8) + imageW + (Math.max(infoWidth + imageW, docTitleWidth) + (canStreamVideo ? AndroidUtilities.dp(32) : 0) - infoWidth - imageW) * alpha); + if (alpha != 0 && docTitleLayout == null) { + alpha = 0; + } + rect.set(x1, y1, x1 + w, y1 + AndroidUtilities.dp(16.5f + 15.5f * alpha)); + + int oldAlpha = Theme.chat_timeBackgroundPaint.getAlpha(); + Theme.chat_timeBackgroundPaint.setAlpha((int) (oldAlpha * controlsAlpha)); + canvas.drawRoundRect(rect, AndroidUtilities.dp(4), AndroidUtilities.dp(4), Theme.chat_timeBackgroundPaint); + Theme.chat_timeBackgroundPaint.setAlpha(oldAlpha); + + Theme.chat_infoPaint.setAlpha((int) (255 * controlsAlpha)); + + canvas.save(); + canvas.translate(noSoundCenterX = photoImage.getImageX() + AndroidUtilities.dp(8 + (canStreamVideo ? 30 * alpha : 0)), photoImage.getImageY() + AndroidUtilities.dp(5.5f + 0.2f * alpha)); + if (infoLayout != null) { + infoLayout.draw(canvas); + } + if (alpha > 0 && docTitleLayout != null) { + canvas.save(); + Theme.chat_infoPaint.setAlpha((int) (255 * controlsAlpha * alpha)); + canvas.translate(0, AndroidUtilities.dp(14.3f * alpha)); + docTitleLayout.draw(canvas); + canvas.restore(); + } + if (imageW != 0) { + Theme.chat_msgNoSoundDrawable.setAlpha((int) (255 * animatingNoSoundProgress * animatingNoSoundProgress * controlsAlpha)); + canvas.translate(infoWidth + AndroidUtilities.dp(4), 0); + int size = AndroidUtilities.dp(14 * animatingNoSoundProgress); + int y = (AndroidUtilities.dp(14) - size) / 2; + Theme.chat_msgNoSoundDrawable.setBounds(0, y, size, y + size); + Theme.chat_msgNoSoundDrawable.draw(canvas); + noSoundCenterX += infoWidth + AndroidUtilities.dp(4) + size / 2; + } + canvas.restore(); + Theme.chat_infoPaint.setAlpha(255); + } + } + if (animatingDrawVideoImageButton == 1) { + animatingDrawVideoImageButtonProgress -= animationDt / 160.0f; + if (animatingDrawVideoImageButtonProgress <= 0) { + animatingDrawVideoImageButtonProgress = 0; + animatingDrawVideoImageButton = 0; + } + invalidate(); + } else if (animatingDrawVideoImageButton == 2) { + animatingDrawVideoImageButtonProgress += animationDt / 160.0f; + if (animatingDrawVideoImageButtonProgress >= 1) { + animatingDrawVideoImageButtonProgress = 1; + animatingDrawVideoImageButton = 0; + } + invalidate(); + } + if (animatingNoSound == 1) { + animatingNoSoundProgress -= animationDt / 180.0f; + if (animatingNoSoundProgress <= 0.0f) { + animatingNoSoundProgress = 0.0f; + animatingNoSound = 0; + } + invalidate(); + } else if (animatingNoSound == 2) { + animatingNoSoundProgress += animationDt / 180.0f; + if (animatingNoSoundProgress >= 1.0f) { + animatingNoSoundProgress = 1.0f; + animatingNoSound = 0; + } + invalidate(); + } + } + } else if (currentMessageObject.type == 4) { + if (docTitleLayout != null) { + if (currentMessageObject.isOutOwner()) { + Theme.chat_locationTitlePaint.setColor(Theme.getColor(Theme.key_chat_messageTextOut)); + Theme.chat_locationAddressPaint.setColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_outVenueInfoSelectedText : Theme.key_chat_outVenueInfoText)); + } else { + Theme.chat_locationTitlePaint.setColor(Theme.getColor(Theme.key_chat_messageTextIn)); + Theme.chat_locationAddressPaint.setColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_inVenueInfoSelectedText : Theme.key_chat_inVenueInfoText)); + } + + if (currentMessageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeoLive) { + int cy = photoImage.getImageY2() + AndroidUtilities.dp(30); + if (!locationExpired) { + forceNotDrawTime = true; + float progress = 1.0f - Math.abs(ConnectionsManager.getInstance(currentAccount).getCurrentTime() - currentMessageObject.messageOwner.date) / (float) currentMessageObject.messageOwner.media.period; + rect.set(photoImage.getImageX2() - AndroidUtilities.dp(43), cy - AndroidUtilities.dp(15), photoImage.getImageX2() - AndroidUtilities.dp(13), cy + AndroidUtilities.dp(15)); + if (currentMessageObject.isOutOwner()) { + Theme.chat_radialProgress2Paint.setColor(Theme.getColor(Theme.key_chat_outInstant)); + Theme.chat_livePaint.setColor(Theme.getColor(Theme.key_chat_outInstant)); + } else { + Theme.chat_radialProgress2Paint.setColor(Theme.getColor(Theme.key_chat_inInstant)); + Theme.chat_livePaint.setColor(Theme.getColor(Theme.key_chat_inInstant)); + } + + Theme.chat_radialProgress2Paint.setAlpha(50); + canvas.drawCircle(rect.centerX(), rect.centerY(), AndroidUtilities.dp(15), Theme.chat_radialProgress2Paint); + Theme.chat_radialProgress2Paint.setAlpha(255); + canvas.drawArc(rect, -90, -360 * progress, false, Theme.chat_radialProgress2Paint); + + String text = LocaleController.formatLocationLeftTime(Math.abs(currentMessageObject.messageOwner.media.period - (ConnectionsManager.getInstance(currentAccount).getCurrentTime() - currentMessageObject.messageOwner.date))); + float w = Theme.chat_livePaint.measureText(text); + + canvas.drawText(text, rect.centerX() - w / 2, cy + AndroidUtilities.dp(4), Theme.chat_livePaint); + + canvas.save(); + canvas.translate(photoImage.getImageX() + AndroidUtilities.dp(10), photoImage.getImageY2() + AndroidUtilities.dp(10)); + docTitleLayout.draw(canvas); + canvas.translate(0, AndroidUtilities.dp(23)); + infoLayout.draw(canvas); + canvas.restore(); + } + + int cx = photoImage.getImageX() + photoImage.getImageWidth() / 2 - AndroidUtilities.dp(31); + cy = photoImage.getImageY() + photoImage.getImageHeight() / 2 - AndroidUtilities.dp(38); + setDrawableBounds(Theme.chat_msgAvatarLiveLocationDrawable, cx, cy); + Theme.chat_msgAvatarLiveLocationDrawable.draw(canvas); + + locationImageReceiver.setImageCoords(cx + AndroidUtilities.dp(5.0f), cy + AndroidUtilities.dp(5.0f), AndroidUtilities.dp(52), AndroidUtilities.dp(52)); + locationImageReceiver.draw(canvas); + } else { + canvas.save(); + canvas.translate(photoImage.getImageX() + AndroidUtilities.dp(6), photoImage.getImageY2() + AndroidUtilities.dp(8)); + docTitleLayout.draw(canvas); + if (infoLayout != null) { + canvas.translate(0, AndroidUtilities.dp(21)); + infoLayout.draw(canvas); + } + canvas.restore(); + } + } + } else if (currentMessageObject.type == 16) { + if (currentMessageObject.isOutOwner()) { + Theme.chat_audioTitlePaint.setColor(Theme.getColor(Theme.key_chat_messageTextOut)); + Theme.chat_contactPhonePaint.setColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_outTimeSelectedText : Theme.key_chat_outTimeText)); + } else { + Theme.chat_audioTitlePaint.setColor(Theme.getColor(Theme.key_chat_messageTextIn)); + Theme.chat_contactPhonePaint.setColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_inTimeSelectedText : Theme.key_chat_inTimeText)); + } + forceNotDrawTime = true; + int x; + if (currentMessageObject.isOutOwner()) { + x = layoutWidth - backgroundWidth + AndroidUtilities.dp(16); + } else { + if (isChat && currentMessageObject.needDrawAvatar()) { + x = AndroidUtilities.dp(74); + } else { + x = AndroidUtilities.dp(25); + } + } + otherX = x; + if (titleLayout != null) { + canvas.save(); + canvas.translate(x, AndroidUtilities.dp(12) + namesOffset); + titleLayout.draw(canvas); + canvas.restore(); + } + if (docTitleLayout != null) { + canvas.save(); + canvas.translate(x + AndroidUtilities.dp(19), AndroidUtilities.dp(37) + namesOffset); + docTitleLayout.draw(canvas); + canvas.restore(); + } + Drawable icon; + Drawable phone; + if (currentMessageObject.isOutOwner()) { + icon = Theme.chat_msgCallUpGreenDrawable; + phone = isDrawSelectedBackground() || otherPressed ? Theme.chat_msgOutCallSelectedDrawable : Theme.chat_msgOutCallDrawable; + } else { + TLRPC.PhoneCallDiscardReason reason = currentMessageObject.messageOwner.action.reason; + if (reason instanceof TLRPC.TL_phoneCallDiscardReasonMissed || reason instanceof TLRPC.TL_phoneCallDiscardReasonBusy) { + icon = Theme.chat_msgCallDownRedDrawable; + } else { + icon = Theme.chat_msgCallDownGreenDrawable; + } + phone = isDrawSelectedBackground() || otherPressed ? Theme.chat_msgInCallSelectedDrawable : Theme.chat_msgInCallDrawable; + } + setDrawableBounds(icon, x - AndroidUtilities.dp(3), AndroidUtilities.dp(36) + namesOffset); + icon.draw(canvas); + + setDrawableBounds(phone, x + AndroidUtilities.dp(205), otherY = AndroidUtilities.dp(22)); + phone.draw(canvas); + } else if (currentMessageObject.type == MessageObject.TYPE_POLL) { + if (currentMessageObject.isOutOwner()) { + int color = Theme.getColor(Theme.key_chat_messageTextOut); + Theme.chat_audioTitlePaint.setColor(color); + Theme.chat_audioPerformerPaint.setColor(color); + Theme.chat_instantViewPaint.setColor(color); + color = Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_outTimeSelectedText : Theme.key_chat_outTimeText); + Theme.chat_timePaint.setColor(color); + Theme.chat_livePaint.setColor(color); + } else { + int color = Theme.getColor(Theme.key_chat_messageTextIn); + Theme.chat_audioTitlePaint.setColor(color); + Theme.chat_audioPerformerPaint.setColor(color); + Theme.chat_instantViewPaint.setColor(color); + color = Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_inTimeSelectedText : Theme.key_chat_inTimeText); + Theme.chat_timePaint.setColor(color); + Theme.chat_livePaint.setColor(color); + } + + int x; + if (currentMessageObject.isOutOwner()) { + x = layoutWidth - backgroundWidth + AndroidUtilities.dp(11); + } else { + if (isChat && currentMessageObject.needDrawAvatar()) { + x = AndroidUtilities.dp(68); + } else { + x = AndroidUtilities.dp(20); + } + } + if (titleLayout != null) { + canvas.save(); + canvas.translate(x, AndroidUtilities.dp(15) + namesOffset); + titleLayout.draw(canvas); + canvas.restore(); + } + if (docTitleLayout != null) { + canvas.save(); + canvas.translate(x + docTitleOffsetX, (titleLayout != null ? titleLayout.getHeight() : 0) + AndroidUtilities.dp(20) + namesOffset); + docTitleLayout.draw(canvas); + canvas.restore(); + } + if (Build.VERSION.SDK_INT >= 21 && selectorDrawable != null) { + selectorDrawable.draw(canvas); + } + int lastVoteY = 0; + for (int a = 0, N = pollButtons.size(); a < N; a++) { + PollButton button = pollButtons.get(a); + button.x = x; + canvas.save(); + canvas.translate(x + AndroidUtilities.dp(34), button.y + namesOffset); + button.title.draw(canvas); + int alpha = (int) (animatePollAnswerAlpha ? 255 * Math.min((pollUnvoteInProgress ? 1.0f - pollAnimationProgress : pollAnimationProgress) / 0.3f, 1.0f) : 255); + if (pollVoted || pollClosed || animatePollAnswerAlpha) { + Theme.chat_docBackPaint.setColor(Theme.getColor(currentMessageObject.isOutOwner() ? Theme.key_chat_outAudioSeekbarFill : Theme.key_chat_inAudioSeekbarFill)); + if (animatePollAnswerAlpha) { + float oldAlpha = Theme.chat_instantViewPaint.getAlpha() / 255.0f; + Theme.chat_instantViewPaint.setAlpha((int) (alpha * oldAlpha)); + oldAlpha = Theme.chat_docBackPaint.getAlpha() / 255.0f; + Theme.chat_docBackPaint.setAlpha((int) (alpha * oldAlpha)); + } + + int currentPercent = (int) Math.ceil(button.prevPercent + (button.percent - button.prevPercent) * pollAnimationProgress); + String text = String.format("%d%%", currentPercent); + int width = (int) Math.ceil(Theme.chat_instantViewPaint.measureText(text)); + canvas.drawText(text, -AndroidUtilities.dp(7) - width, AndroidUtilities.dp(14), Theme.chat_instantViewPaint); + + width = backgroundWidth - AndroidUtilities.dp(76); + float currentPercentProgress = button.prevPercentProgress + (button.percentProgress - button.prevPercentProgress) * pollAnimationProgress; + instantButtonRect.set(0, button.height + AndroidUtilities.dp(6), width * currentPercentProgress, button.height + AndroidUtilities.dp(11)); + canvas.drawRoundRect(instantButtonRect, AndroidUtilities.dp(2), AndroidUtilities.dp(2), Theme.chat_docBackPaint); + } + + if (!pollVoted && !pollClosed || animatePollAnswerAlpha) { + if (isDrawSelectedBackground()) { + Theme.chat_replyLinePaint.setColor(Theme.getColor(currentMessageObject.isOutOwner() ? Theme.key_chat_outVoiceSeekbarSelected : Theme.key_chat_inVoiceSeekbarSelected)); + } else { + Theme.chat_replyLinePaint.setColor(Theme.getColor(currentMessageObject.isOutOwner() ? Theme.key_chat_outVoiceSeekbar : Theme.key_chat_inVoiceSeekbar)); + } + if (animatePollAnswerAlpha) { + float oldAlpha = Theme.chat_replyLinePaint.getAlpha() / 255.0f; + Theme.chat_replyLinePaint.setAlpha((int) ((255 - alpha) * oldAlpha)); + } + canvas.drawLine(-AndroidUtilities.dp(2), button.height + AndroidUtilities.dp(13), backgroundWidth - AndroidUtilities.dp(56), button.height + AndroidUtilities.dp(13), Theme.chat_replyLinePaint); + if (pollVoteInProgress && a == pollVoteInProgressNum) { + Theme.chat_instantViewRectPaint.setColor(Theme.getColor(currentMessageObject.isOutOwner() ? Theme.key_chat_outAudioSeekbarFill : Theme.key_chat_inAudioSeekbarFill)); + if (animatePollAnswerAlpha) { + float oldAlpha = Theme.chat_instantViewRectPaint.getAlpha() / 255.0f; + Theme.chat_instantViewRectPaint.setAlpha((int) ((255 - alpha) * oldAlpha)); + } + instantButtonRect.set(-AndroidUtilities.dp(23) - AndroidUtilities.dp(8.5f), AndroidUtilities.dp(9) - AndroidUtilities.dp(8.5f), -AndroidUtilities.dp(23) + AndroidUtilities.dp(8.5f), AndroidUtilities.dp(9) + AndroidUtilities.dp(8.5f)); + canvas.drawArc(instantButtonRect, voteRadOffset, voteCurrentCircleLength, false, Theme.chat_instantViewRectPaint); + } else { + if (currentMessageObject.isOutOwner()) { + Theme.chat_instantViewRectPaint.setColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_outMenuSelected : Theme.key_chat_outMenu)); + } else { + Theme.chat_instantViewRectPaint.setColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_inMenuSelected : Theme.key_chat_inMenu)); + } + if (animatePollAnswerAlpha) { + float oldAlpha = Theme.chat_instantViewRectPaint.getAlpha() / 255.0f; + Theme.chat_instantViewRectPaint.setAlpha((int) ((255 - alpha) * oldAlpha)); + } + canvas.drawCircle(-AndroidUtilities.dp(23), AndroidUtilities.dp(9), AndroidUtilities.dp(8.5f), Theme.chat_instantViewRectPaint); + } + } + canvas.restore(); + if (a == N - 1) { + lastVoteY = button.y + namesOffset + button.height; + } + } + if (infoLayout != null) { + canvas.save(); + canvas.translate(x + infoX, lastVoteY + AndroidUtilities.dp(22)); + infoLayout.draw(canvas); + canvas.restore(); + } + updatePollAnimations(); + } else if (currentMessageObject.type == 12) { + if (currentMessageObject.isOutOwner()) { + Theme.chat_contactNamePaint.setColor(Theme.getColor(Theme.key_chat_outContactNameText)); + Theme.chat_contactPhonePaint.setColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_outContactPhoneSelectedText : Theme.key_chat_outContactPhoneText)); + } else { + Theme.chat_contactNamePaint.setColor(Theme.getColor(Theme.key_chat_inContactNameText)); + Theme.chat_contactPhonePaint.setColor(Theme.getColor(isDrawSelectedBackground() ? Theme.key_chat_inContactPhoneSelectedText : Theme.key_chat_inContactPhoneText)); + } + if (titleLayout != null) { + canvas.save(); + canvas.translate(photoImage.getImageX() + photoImage.getImageWidth() + AndroidUtilities.dp(9), AndroidUtilities.dp(16) + namesOffset); + titleLayout.draw(canvas); + canvas.restore(); + } + if (docTitleLayout != null) { + canvas.save(); + canvas.translate(photoImage.getImageX() + photoImage.getImageWidth() + AndroidUtilities.dp(9), AndroidUtilities.dp(39) + namesOffset); + docTitleLayout.draw(canvas); + canvas.restore(); + } + + Drawable menuDrawable; + if (currentMessageObject.isOutOwner()) { + menuDrawable = isDrawSelectedBackground() ? Theme.chat_msgOutMenuSelectedDrawable : Theme.chat_msgOutMenuDrawable; + } else { + menuDrawable = isDrawSelectedBackground() ? Theme.chat_msgInMenuSelectedDrawable : Theme.chat_msgInMenuDrawable; + } + setDrawableBounds(menuDrawable, otherX = photoImage.getImageX() + backgroundWidth - AndroidUtilities.dp(48), otherY = photoImage.getImageY() - AndroidUtilities.dp(5)); + menuDrawable.draw(canvas); + + if (drawInstantView) { + int textX = photoImage.getImageX() - AndroidUtilities.dp(2); + Drawable instantDrawable; + int instantY = getMeasuredHeight() - AndroidUtilities.dp(36 + 28); + Paint backPaint = Theme.chat_instantViewRectPaint; + if (currentMessageObject.isOutOwner()) { + Theme.chat_instantViewPaint.setColor(Theme.getColor(Theme.key_chat_outPreviewInstantText)); + backPaint.setColor(Theme.getColor(Theme.key_chat_outPreviewInstantText)); + } else { + Theme.chat_instantViewPaint.setColor(Theme.getColor(Theme.key_chat_inPreviewInstantText)); + backPaint.setColor(Theme.getColor(Theme.key_chat_inPreviewInstantText)); + } + + if (Build.VERSION.SDK_INT >= 21) { + selectorDrawable.setBounds(textX, instantY, textX + instantWidth, instantY + AndroidUtilities.dp(36)); + selectorDrawable.draw(canvas); + } + instantButtonRect.set(textX, instantY, textX + instantWidth, instantY + AndroidUtilities.dp(36)); + canvas.drawRoundRect(instantButtonRect, AndroidUtilities.dp(6), AndroidUtilities.dp(6), backPaint); + if (instantViewLayout != null) { + canvas.save(); + canvas.translate(textX + instantTextX, instantY + AndroidUtilities.dp(10.5f)); + instantViewLayout.draw(canvas); + canvas.restore(); + } + } + } + + if (drawImageButton && photoImage.getVisible()) { + if (controlsAlpha != 1.0f) { + radialProgress.setOverrideAlpha(controlsAlpha); + } + radialProgress.draw(canvas); + } + if ((drawVideoImageButton || animatingDrawVideoImageButton != 0) && photoImage.getVisible()) { + if (controlsAlpha != 1.0f) { + videoRadialProgress.setOverrideAlpha(controlsAlpha); + } + videoRadialProgress.draw(canvas); + } + } + @Override public int getObserverTag() { return TAG; @@ -7702,6 +8221,10 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate return currentMessageObject; } + public TLRPC.Document getStreamingVideo() { + return documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO ? documentAttach : null; + } + public boolean isPinnedBottom() { return pinnedBottom; } 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 81dc4bd58..638545ccd 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java @@ -38,6 +38,7 @@ import org.telegram.messenger.ImageReceiver; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.GroupCreateCheckBox; +import org.telegram.ui.DialogsActivity; import java.util.ArrayList; @@ -901,26 +902,9 @@ public class DialogCell extends BaseCell { isSelected = value; } - private ArrayList getDialogsArray() { - if (dialogsType == 0) { - return MessagesController.getInstance(currentAccount).dialogs; - } else if (dialogsType == 1) { - return MessagesController.getInstance(currentAccount).dialogsServerOnly; - } else if (dialogsType == 2) { - return MessagesController.getInstance(currentAccount).dialogsGroupsOnly; - } else if (dialogsType == 3) { - return MessagesController.getInstance(currentAccount).dialogsForward; - } else if (dialogsType == 4) { - return MessagesController.getInstance(currentAccount).dialogsUsersOnly; - } else if (dialogsType == 5) { - return MessagesController.getInstance(currentAccount).dialogsChannelsOnly; - } - return null; - } - public void checkCurrentDialogIndex() { - if (index < getDialogsArray().size()) { - ArrayList dialogsArray = getDialogsArray(); + ArrayList dialogsArray = DialogsActivity.getDialogsArray(dialogsType, currentAccount); + if (index < dialogsArray.size()) { TLRPC.TL_dialog dialog = dialogsArray.get(index); TLRPC.TL_dialog nextDialog = index + 1 < dialogsArray.size() ? dialogsArray.get(index + 1) : null; TLRPC.DraftMessage newDraftMessage = DataQuery.getInstance(currentAccount).getDraft(currentDialogId); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/MaxFileSizeCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/MaxFileSizeCell.java index f7876a658..1f2577caf 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/MaxFileSizeCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/MaxFileSizeCell.java @@ -8,7 +8,10 @@ package org.telegram.ui.Cells; +import android.animation.Animator; +import android.animation.ObjectAnimator; import android.content.Context; +import android.graphics.Canvas; import android.text.TextUtils; import android.util.TypedValue; import android.view.Gravity; @@ -23,30 +26,33 @@ import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.SeekBarView; +import java.util.ArrayList; + public class MaxFileSizeCell extends FrameLayout { private TextView textView; private TextView sizeTextView; private SeekBarView seekBarView; - private long maxSize; + private long currentSize; public MaxFileSizeCell(Context context) { super(context); + setWillNotDraw(false); + textView = new TextView(context); - textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + textView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); textView.setLines(1); textView.setMaxLines(1); textView.setSingleLine(true); - textView.setText(LocaleController.getString("AutodownloadSizeLimit", R.string.AutodownloadSizeLimit)); textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); textView.setEllipsize(TextUtils.TruncateAt.END); addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 21, 13, 21, 0)); sizeTextView = new TextView(context); - sizeTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueText6)); + sizeTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlue2)); sizeTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); sizeTextView.setLines(1); sizeTextView.setMaxLines(1); @@ -65,27 +71,48 @@ public class MaxFileSizeCell extends FrameLayout { }; seekBarView.setReportChanges(true); seekBarView.setDelegate(progress -> { - int size; - if (maxSize > 1024 * 1024 * 10) { - int min = 1024 * 1024 * 100; - if (progress <= 0.8f) { - size = (int) (min * (progress / 0.8f)); - } else { - size = (int) (min + (maxSize - min) * (progress - 0.8f) / 0.2f); - } + int size = 500 * 1024; + if (progress <= 0.25f) { + size += 524 * 1024 * (progress / 0.25f); } else { - size = (int) (maxSize * progress); + progress -= 0.25f; + size += 524 * 1024; + + if (progress < 0.25f) { + size += 9 * 1024 * 1024 * (progress / 0.25f); + } else { + progress -= 0.25f; + size += 9 * 1024 * 1024; + + if (progress <= 0.25f) { + size += 90 * 1024 * 1024 * (progress / 0.25f); + } else { + progress -= 0.25f; + size += 90 * 1024 * 1024; + + size += 1436 * 1024 * 1024 * (progress / 0.25f); + } + } } sizeTextView.setText(LocaleController.formatString("AutodownloadSizeLimitUpTo", R.string.AutodownloadSizeLimitUpTo, AndroidUtilities.formatFileSize(size))); + currentSize = size; didChangedSizeValue(size); }); - addView(seekBarView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 30, Gravity.TOP | Gravity.LEFT, 4, 40, 4, 0)); + addView(seekBarView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 30, Gravity.TOP | Gravity.LEFT, 10, 40, 10, 0)); } protected void didChangedSizeValue(int value) { } + public void setText(String text) { + textView.setText(text); + } + + public long getSize() { + return currentSize; + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(80), MeasureSpec.EXACTLY)); @@ -99,23 +126,79 @@ public class MaxFileSizeCell extends FrameLayout { textView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(30), MeasureSpec.EXACTLY)); - seekBarView.measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth() - AndroidUtilities.dp(8), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(30), MeasureSpec.EXACTLY)); + seekBarView.measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth() - AndroidUtilities.dp(20), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(30), MeasureSpec.EXACTLY)); } - public void setSize(long size, long max) { - maxSize = max; - float progress; - if (maxSize > 1024 * 1024 * 10) { - int min = 1024 * 1024 * 100; - if (size <= min) { - progress = size / (float) min * 0.8f; - } else { - progress = 0.8f + (size - min) / (float) (maxSize - min) * 0.2f; - } + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (!isEnabled()) { + return true; + } + return super.onInterceptTouchEvent(ev); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (!isEnabled()) { + return true; + } + return super.dispatchTouchEvent(ev); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (!isEnabled()) { + return true; + } + return super.onTouchEvent(event); + } + + public void setSize(long size) { + currentSize = size; + sizeTextView.setText(LocaleController.formatString("AutodownloadSizeLimitUpTo", R.string.AutodownloadSizeLimitUpTo, AndroidUtilities.formatFileSize(size))); + + float progress = 0.0f; + size -= 500 * 1024; + if (size < 524 * 1024) { + progress = Math.max(0, size / (float) (524 * 1024)) * 0.25f; } else { - progress = size / (float) maxSize; + progress += 0.25f; + size -= 524 * 1024; + + if (size < 1024 * 1024 * 9) { + progress += Math.max(0, size / (float) (9 * 1024 * 1024)) * 0.25f; + } else { + progress += 0.25f; + size -= 9 * 1024 * 1024; + + if (size < 1024 * 1024 * 90) { + progress += Math.max(0, size / (float) (90 * 1024 * 1024)) * 0.25f; + } else { + progress += 0.25f; + size -= 90 * 1024 * 1024; + + progress += Math.max(0, size / (float) (1436 * 1024 * 1024)) * 0.25f; + } + } } seekBarView.setProgress(progress); - sizeTextView.setText(LocaleController.formatString("AutodownloadSizeLimitUpTo", R.string.AutodownloadSizeLimitUpTo, AndroidUtilities.formatFileSize(size))); + } + + public void setEnabled(boolean value, ArrayList animators) { + super.setEnabled(value); + if (animators != null) { + animators.add(ObjectAnimator.ofFloat(textView, "alpha", value ? 1.0f : 0.5f)); + animators.add(ObjectAnimator.ofFloat(seekBarView, "alpha", value ? 1.0f : 0.5f)); + animators.add(ObjectAnimator.ofFloat(sizeTextView, "alpha", value ? 1.0f : 0.5f)); + } else { + textView.setAlpha(value ? 1.0f : 0.5f); + seekBarView.setAlpha(value ? 1.0f : 0.5f); + sizeTextView.setAlpha(value ? 1.0f : 0.5f); + } + } + + @Override + protected void onDraw(Canvas canvas) { + canvas.drawLine(LocaleController.isRTL ? 0 : AndroidUtilities.dp(20), getMeasuredHeight() - 1, getMeasuredWidth() - (LocaleController.isRTL ? AndroidUtilities.dp(20) : 0), getMeasuredHeight() - 1, Theme.dividerPaint); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/NotificationsCheckCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/NotificationsCheckCell.java index ee22369dc..fa3bd4c24 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/NotificationsCheckCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/NotificationsCheckCell.java @@ -29,6 +29,7 @@ public class NotificationsCheckCell extends FrameLayout { private Switch checkBox; private boolean needDivider; private boolean drawLine = true; + private boolean isMultiline; public NotificationsCheckCell(Context context) { this(context, 21); @@ -66,19 +67,42 @@ public class NotificationsCheckCell extends FrameLayout { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(70), MeasureSpec.EXACTLY)); + if (isMultiline) { + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); + } else { + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(70), MeasureSpec.EXACTLY)); + } } public void setTextAndValueAndCheck(String text, CharSequence value, boolean checked, boolean divider) { - setTextAndValueAndCheck(text, value, checked, 0, divider); + setTextAndValueAndCheck(text, value, checked, 0, false, divider); } public void setTextAndValueAndCheck(String text, CharSequence value, boolean checked, int iconType, boolean divider) { + setTextAndValueAndCheck(text, value, checked, iconType, false, divider); + } + + public void setTextAndValueAndCheck(String text, CharSequence value, boolean checked, int iconType, boolean multiline, boolean divider) { textView.setText(text); valueTextView.setText(value); checkBox.setChecked(checked, iconType, false); valueTextView.setVisibility(VISIBLE); needDivider = divider; + isMultiline = multiline; + + if (multiline) { + valueTextView.setLines(0); + valueTextView.setMaxLines(0); + valueTextView.setSingleLine(false); + valueTextView.setEllipsize(null); + valueTextView.setPadding(0, 0, 0, AndroidUtilities.dp(14)); + } else { + valueTextView.setLines(1); + valueTextView.setMaxLines(1); + valueTextView.setSingleLine(true); + valueTextView.setEllipsize(TextUtils.TruncateAt.END); + valueTextView.setPadding(0, 0, 0, 0); + } } public void setDrawLine(boolean value) { @@ -104,7 +128,8 @@ public class NotificationsCheckCell extends FrameLayout { } if (drawLine) { int x = LocaleController.isRTL ? AndroidUtilities.dp(76) : getMeasuredWidth() - AndroidUtilities.dp(76) - 1; - canvas.drawRect(x, AndroidUtilities.dp(24), x + 2, AndroidUtilities.dp(24 + 22), Theme.dividerPaint); + int y = (getMeasuredHeight() - AndroidUtilities.dp(22)) / 2; + canvas.drawRect(x, y, x + 2, y + AndroidUtilities.dp(22), Theme.dividerPaint); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell.java index 8386af1e7..06944a871 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell.java @@ -173,16 +173,20 @@ public class SharedPhotoVideoCell extends FrameLayout { int seconds = duration - minutes * 60; videoTextView.setText(String.format("%d:%02d", minutes, seconds)); TLRPC.Document document = messageObject.getDocument(); - TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 90); + TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 50); + TLRPC.PhotoSize qualityThumb = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 320); + if (thumb == qualityThumb) { + qualityThumb = null; + } if (thumb != null) { - imageView.setImage(null, null, ApplicationLoader.applicationContext.getResources().getDrawable(R.drawable.photo_placeholder_in), null, thumb, "b", null, 0, messageObject); + imageView.setImage(qualityThumb, "100_100", ApplicationLoader.applicationContext.getResources().getDrawable(R.drawable.photo_placeholder_in), null, thumb, "b", null, 0, messageObject); } else { imageView.setImageResource(R.drawable.photo_placeholder_in); } } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaPhoto && messageObject.messageOwner.media.photo != null && !messageObject.photoThumbs.isEmpty()) { videoInfoContainer.setVisibility(INVISIBLE); TLRPC.PhotoSize currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, 320); - TLRPC.PhotoSize currentPhotoObjectThumb = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, 80); + TLRPC.PhotoSize currentPhotoObjectThumb = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, 50); if (messageObject.mediaExists || DownloadController.getInstance(currentAccount).canDownloadMedia(messageObject)) { if (currentPhotoObject == currentPhotoObjectThumb) { currentPhotoObjectThumb = null; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckBoxCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckBoxCell.java index 995c73591..426473822 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckBoxCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckBoxCell.java @@ -29,10 +29,14 @@ public class TextCheckBoxCell extends FrameLayout { private boolean needDivider; public TextCheckBoxCell(Context context) { + this(context, false); + } + + public TextCheckBoxCell(Context context, boolean dialog) { super(context); textView = new TextView(context); - textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + textView.setTextColor(Theme.getColor(dialog ? Theme.key_dialogTextBlack : Theme.key_windowBackgroundWhiteBlackText)); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); textView.setLines(1); textView.setMaxLines(1); @@ -41,7 +45,7 @@ public class TextCheckBoxCell extends FrameLayout { textView.setEllipsize(TextUtils.TruncateAt.END); addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 66 : 21, 0, LocaleController.isRTL ? 21 : 66, 0)); - checkBox = new CheckBoxSquare(context, false); + checkBox = new CheckBoxSquare(context, dialog); checkBox.setDuplicateParentStateEnabled(false); checkBox.setFocusable(false); checkBox.setFocusableInTouchMode(false); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckCell.java index f15736d2a..7b7e5cb03 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckCell.java @@ -9,18 +9,25 @@ package org.telegram.ui.Cells; import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Typeface; import android.text.TextUtils; +import android.util.Property; import android.util.TypedValue; import android.view.Gravity; +import android.view.MotionEvent; import android.widget.FrameLayout; import android.widget.TextView; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.LocaleController; import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimationProperties; +import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.Switch; @@ -33,16 +40,40 @@ public class TextCheckCell extends FrameLayout { private Switch checkBox; private boolean needDivider; private boolean isMultiline; + private int height = 50; + private int animatedColorBackground; + private float animationProgress; + private Paint animationPaint; + private float lastTouchX; + private ObjectAnimator animator; + private boolean drawCheckRipple; + + public static final Property ANIMATION_PROGRESS = new AnimationProperties.FloatProperty("animationProgress") { + @Override + public void setValue(TextCheckCell object, float value) { + object.setAnimationProgress(value); + object.invalidate(); + } + + @Override + public Float get(TextCheckCell object) { + return object.animationProgress; + } + }; public TextCheckCell(Context context) { this(context, 21); } public TextCheckCell(Context context, int padding) { + this(context, padding, false); + } + + public TextCheckCell(Context context, int padding, boolean dialog) { super(context); textView = new TextView(context); - textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + textView.setTextColor(Theme.getColor(dialog ? Theme.key_dialogTextBlack : Theme.key_windowBackgroundWhiteBlackText)); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); textView.setLines(1); textView.setMaxLines(1); @@ -52,7 +83,7 @@ public class TextCheckCell extends FrameLayout { addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 70 : padding, 0, LocaleController.isRTL ? padding : 70, 0)); valueTextView = new TextView(context); - valueTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2)); + valueTextView.setTextColor(Theme.getColor(dialog ? Theme.key_dialogIcon : Theme.key_windowBackgroundWhiteGrayText2)); valueTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); valueTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); valueTextView.setLines(1); @@ -64,7 +95,9 @@ public class TextCheckCell extends FrameLayout { checkBox = new Switch(context); checkBox.setColors(Theme.key_switchTrack, Theme.key_switchTrackChecked, Theme.key_windowBackgroundWhite, Theme.key_windowBackgroundWhite); - addView(checkBox, LayoutHelper.createFrame(37, 40, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL, 22, 0, 22, 0)); + addView(checkBox, LayoutHelper.createFrame(37, 20, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL, 22, 0, 22, 0)); + + setClipChildren(false); } @Override @@ -72,10 +105,16 @@ public class TextCheckCell extends FrameLayout { if (isMultiline) { super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); } else { - super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(valueTextView.getVisibility() == VISIBLE ? 64 : 50) + (needDivider ? 1 : 0), MeasureSpec.EXACTLY)); + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(valueTextView.getVisibility() == VISIBLE ? 64 : height) + (needDivider ? 1 : 0), MeasureSpec.EXACTLY)); } } + @Override + public boolean onTouchEvent(MotionEvent event) { + lastTouchX = event.getX(); + return super.onTouchEvent(event); + } + public void setTextAndCheck(String text, boolean checked, boolean divider) { textView.setText(text); isMultiline = false; @@ -89,6 +128,32 @@ public class TextCheckCell extends FrameLayout { setWillNotDraw(!divider); } + public void setColors(String key, String switchKey, String switchKeyChecked, String switchThumb, String switchThumbChecked) { + textView.setTextColor(Theme.getColor(key)); + checkBox.setColors(switchKey, switchKeyChecked, switchThumb, switchThumbChecked); + textView.setTag(key); + } + + public void setTypeface(Typeface typeface) { + textView.setTypeface(typeface); + } + + public void setHeight(int value) { + height = value; + } + + public void setDrawCheckRipple(boolean value) { + drawCheckRipple = value; + } + + @Override + public void setPressed(boolean pressed) { + if (drawCheckRipple) { + checkBox.setDrawRipple(pressed); + } + super.setPressed(pressed); + } + public void setTextAndValueAndCheck(String text, String value, boolean checked, boolean multiline, boolean divider) { textView.setText(text); valueTextView.setText(value); @@ -141,8 +206,59 @@ public class TextCheckCell extends FrameLayout { return checkBox.isChecked(); } + @Override + public void setBackgroundColor(int color) { + clearAnimation(); + animatedColorBackground = 0; + super.setBackgroundColor(color); + } + + public void setBackgroundColorAnimated(boolean checked, int color) { + if (animator != null) { + animator.cancel(); + animator = null; + } + if (animatedColorBackground != 0) { + setBackgroundColor(animatedColorBackground); + } + if (animationPaint == null) { + animationPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + } + checkBox.setOverrideColor(checked ? 1 : 2); + animatedColorBackground = color; + animationPaint.setColor(animatedColorBackground); + animationProgress = 0.0f; + animator = ObjectAnimator.ofFloat(this, ANIMATION_PROGRESS, 0.0f, 1.0f); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + setBackgroundColor(animatedColorBackground); + animatedColorBackground = 0; + invalidate(); + } + }); + animator.setInterpolator(CubicBezierInterpolator.EASE_OUT); + animator.setDuration(240).start(); + } + + private void setAnimationProgress(float value) { + animationProgress = value; + float rad = Math.max(lastTouchX, getMeasuredWidth() - lastTouchX) + AndroidUtilities.dp(40); + float cx = lastTouchX; + int cy = getMeasuredHeight() / 2; + float animatedRad = rad * animationProgress; + checkBox.setOverrideColorProgress(cx, cy, animatedRad); + } + @Override protected void onDraw(Canvas canvas) { + if (animatedColorBackground != 0) { + float rad = Math.max(lastTouchX, getMeasuredWidth() - lastTouchX) + AndroidUtilities.dp(40); + float cx = lastTouchX; + int cy = getMeasuredHeight() / 2; + float animatedRad = rad * animationProgress; + canvas.drawCircle(cx, cy, animatedRad, animationPaint); + } if (needDivider) { canvas.drawLine(LocaleController.isRTL ? 0 : AndroidUtilities.dp(20), getMeasuredHeight() - 1, getMeasuredWidth() - (LocaleController.isRTL ? AndroidUtilities.dp(20) : 0), getMeasuredHeight() - 1, Theme.dividerPaint); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextDetailSettingsCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextDetailSettingsCell.java index 7d09c8b06..69153ad23 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextDetailSettingsCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextDetailSettingsCell.java @@ -10,10 +10,13 @@ package org.telegram.ui.Cells; import android.content.Context; import android.graphics.Canvas; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; import android.text.TextUtils; import android.util.TypedValue; import android.view.Gravity; import android.widget.FrameLayout; +import android.widget.ImageView; import android.widget.TextView; import org.telegram.messenger.AndroidUtilities; @@ -26,6 +29,7 @@ public class TextDetailSettingsCell extends FrameLayout { private TextView textView; private TextView valueTextView; + private ImageView imageView; private boolean needDivider; private boolean multiline; @@ -51,6 +55,12 @@ public class TextDetailSettingsCell extends FrameLayout { valueTextView.setSingleLine(true); valueTextView.setPadding(0, 0, 0, 0); addView(valueTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 21, 35, 21, 0)); + + imageView = new ImageView(context); + imageView.setScaleType(ImageView.ScaleType.CENTER); + imageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteGrayIcon), PorterDuff.Mode.MULTIPLY)); + imageView.setVisibility(GONE); + addView(imageView, LayoutHelper.createFrame(52, 52, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 8, 6, 8, 0)); } @Override @@ -89,6 +99,18 @@ public class TextDetailSettingsCell extends FrameLayout { textView.setText(text); valueTextView.setText(value); needDivider = divider; + imageView.setVisibility(GONE); + setWillNotDraw(!divider); + } + + public void setTextAndValueAndIcon(String text, CharSequence value, int resId, boolean divider) { + textView.setText(text); + valueTextView.setText(value); + imageView.setImageResource(resId); + imageView.setVisibility(VISIBLE); + textView.setPadding(LocaleController.isRTL ? 0 : AndroidUtilities.dp(50), 0, LocaleController.isRTL ? AndroidUtilities.dp(50) : 0, 0); + valueTextView.setPadding(LocaleController.isRTL ? 0 : AndroidUtilities.dp(50), 0, LocaleController.isRTL ? AndroidUtilities.dp(50) : 0, multiline ? AndroidUtilities.dp(12) : 0); + needDivider = divider; setWillNotDraw(!divider); } @@ -96,7 +118,7 @@ public class TextDetailSettingsCell extends FrameLayout { valueTextView.setText(value); } - public void setTextWithEmojiAndValue(String text, CharSequence value, boolean divider) { + public void setTextWithEmojiAnd21Value(String text, CharSequence value, boolean divider) { textView.setText(Emoji.replaceEmoji(text, textView.getPaint().getFontMetricsInt(), AndroidUtilities.dp(14), false)); valueTextView.setText(value); needDivider = divider; @@ -112,7 +134,7 @@ public class TextDetailSettingsCell extends FrameLayout { @Override protected void onDraw(Canvas canvas) { if (needDivider) { - canvas.drawLine(LocaleController.isRTL ? 0 : AndroidUtilities.dp(20), getMeasuredHeight() - 1, getMeasuredWidth() - (LocaleController.isRTL ? AndroidUtilities.dp(20) : 0), getMeasuredHeight() - 1, Theme.dividerPaint); + canvas.drawLine(LocaleController.isRTL ? 0 : AndroidUtilities.dp(imageView.getVisibility() == VISIBLE ? 71 : 20), getMeasuredHeight() - 1, getMeasuredWidth() - (LocaleController.isRTL ? AndroidUtilities.dp(imageView.getVisibility() == VISIBLE ? 71 : 20) : 0), getMeasuredHeight() - 1, Theme.dividerPaint); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextInfoPrivacyCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextInfoPrivacyCell.java index 34169e4c8..c7dfd5500 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextInfoPrivacyCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextInfoPrivacyCell.java @@ -29,6 +29,7 @@ public class TextInfoPrivacyCell extends FrameLayout { private TextView textView; private String linkTextColorKey = Theme.key_windowBackgroundWhiteLinkText; private int bottomPadding = 17; + private int fixedSize; public TextInfoPrivacyCell(Context context) { this(context, 21); @@ -53,13 +54,21 @@ public class TextInfoPrivacyCell extends FrameLayout { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); + if (fixedSize != 0) { + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(fixedSize), MeasureSpec.EXACTLY)); + } else { + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); + } } public void setBottomPadding(int value) { bottomPadding = value; } + public void setFixedSize(int size) { + fixedSize = size; + } + public void setText(CharSequence text) { if (text == null) { textView.setPadding(0, AndroidUtilities.dp(2), 0, 0); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSettingsCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSettingsCell.java index 5a32dfa2a..50e638f45 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSettingsCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSettingsCell.java @@ -34,6 +34,7 @@ public class TextSettingsCell extends FrameLayout { private TextView valueTextView; private ImageView valueImageView; private boolean needDivider; + private boolean canDisable; public TextSettingsCell(Context context) { this(context, 21); @@ -91,6 +92,10 @@ public class TextSettingsCell extends FrameLayout { return textView; } + public void setCanDisable(boolean value) { + canDisable = value; + } + public TextView getValueTextView() { return valueTextView; } @@ -159,6 +164,18 @@ public class TextSettingsCell extends FrameLayout { } } + @Override + public void setEnabled(boolean value) { + super.setEnabled(value); + textView.setAlpha(value || !canDisable ? 1.0f : 0.5f); + if (valueTextView.getVisibility() == VISIBLE) { + valueTextView.setAlpha(value || !canDisable ? 1.0f : 0.5f); + } + if (valueImageView.getVisibility() == VISIBLE) { + valueImageView.setAlpha(value || !canDisable ? 1.0f : 0.5f); + } + } + @Override protected void onDraw(Canvas canvas) { if (needDivider) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells2/UserCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells2/UserCell.java index 407569d55..e767ce13a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells2/UserCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells2/UserCell.java @@ -12,11 +12,13 @@ import android.content.Context; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.Typeface; +import android.text.TextUtils; import android.view.Gravity; import android.widget.FrameLayout; import android.widget.ImageView; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ChatObject; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MessagesController; import org.telegram.messenger.R; @@ -256,6 +258,25 @@ public class UserCell extends FrameLayout { statusTextView.setText(LocaleController.formatUserStatus(currentAccount, currentUser)); } } + } else if (currentChat != null) { + statusTextView.setTextColor(statusColor); + if (ChatObject.isChannel(currentChat) && !currentChat.megagroup) { + if (currentChat.participants_count != 0) { + statusTextView.setText(LocaleController.formatPluralString("Subscribers", currentChat.participants_count)); + } else if (TextUtils.isEmpty(currentChat.username)) { + statusTextView.setText(LocaleController.getString("ChannelPrivate", R.string.ChannelPrivate)); + } else { + statusTextView.setText(LocaleController.getString("ChannelPublic", R.string.ChannelPublic)); + } + } else { + if (currentChat.participants_count != 0) { + statusTextView.setText(LocaleController.formatPluralString("Members", currentChat.participants_count)); + } else if (TextUtils.isEmpty(currentChat.username)) { + statusTextView.setText(LocaleController.getString("MegaPrivate", R.string.MegaPrivate)); + } else { + statusTextView.setText(LocaleController.getString("MegaPublic", R.string.MegaPublic)); + } + } } if (imageView.getVisibility() == VISIBLE && currentDrawable == 0 || imageView.getVisibility() == GONE && currentDrawable != 0) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChannelAdminLogActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChannelAdminLogActivity.java index 7054ed7fe..e2a4c8aae 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChannelAdminLogActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChannelAdminLogActivity.java @@ -59,7 +59,6 @@ import org.telegram.messenger.BuildVars; import org.telegram.messenger.ChatObject; import org.telegram.messenger.ContactsController; import org.telegram.messenger.DataQuery; -import org.telegram.messenger.DownloadController; import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; import org.telegram.messenger.ImageReceiver; @@ -185,7 +184,7 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio private PhotoViewer.PhotoViewerProvider provider = new PhotoViewer.EmptyPhotoViewerProvider() { @Override - public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index) { + public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index, boolean needPreview) { int count = chatListView.getChildCount(); for (int a = 0; a < count; a++) { @@ -404,7 +403,7 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio if (messageObject1.isVoice() || messageObject1.isMusic()) { cell.updateButtonState(false, true, false); } else if (messageObject1.isRoundVideo()) { - cell.checkRoundVideoPlayback(false); + cell.checkVideoPlayback(false); if (!MediaController.getInstance().isPlayingMessage(messageObject1)) { if (messageObject1.audioProgress != 0) { messageObject1.resetPlayingProgress(); @@ -429,7 +428,7 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio cell.updateButtonState(false, true, false); } else if (messageObject.isRoundVideo()) { if (!MediaController.getInstance().isPlayingMessage(messageObject)) { - cell.checkRoundVideoPlayback(true); + cell.checkVideoPlayback(true); } } } @@ -1605,11 +1604,11 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio fragmentView.invalidate(); if (messageObject != null && messageObject.isRoundVideo()) { if (checkTextureViewPosition || PipRoundVideoView.getInstance() != null) { - MediaController.getInstance().setCurrentRoundVisible(false); + MediaController.getInstance().setCurrentVideoVisible(false); } } } else { - MediaController.getInstance().setCurrentRoundVisible(true); + MediaController.getInstance().setCurrentVideoVisible(true); } } } @@ -1676,10 +1675,10 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio fragmentView.invalidate(); MessageObject messageObject = MediaController.getInstance().getPlayingMessageObject(); if (messageObject != null && messageObject.isRoundVideo() && checkTextureViewPosition) { - MediaController.getInstance().setCurrentRoundVisible(false); + MediaController.getInstance().setCurrentVideoVisible(false); } } else { - MediaController.getInstance().setCurrentRoundVisible(true); + MediaController.getInstance().setCurrentVideoVisible(true); } } if (minMessageChild != null) { @@ -2274,9 +2273,6 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio pinnedTop = false; } messageCell.setMessageObject(message, null, pinnedBotton, pinnedTop); - if (view instanceof ChatMessageCell && DownloadController.getInstance(currentAccount).canDownloadMedia(message)) { - ((ChatMessageCell) view).downloadAudioIfNeed(); - } messageCell.setHighlighted(false); messageCell.setHighlightedText(null); } else if (view instanceof ChatActionCell) { @@ -2541,8 +2537,8 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_outTimeText), new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_inTimeSelectedText), new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_outTimeSelectedText), - new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_inAudioPerfomerText), - new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_outAudioPerfomerText), + new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_inAudioPerformerText), + new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_outAudioPerformerText), new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_inAudioTitleText), new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_outAudioTitleText), new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_inAudioDurationText), diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java index 7302df7ee..db4d52aa5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java @@ -33,6 +33,8 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.SurfaceTexture; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.media.ThumbnailUtils; @@ -40,6 +42,7 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.provider.MediaStore; +import android.support.annotation.NonNull; import android.support.v4.content.FileProvider; import android.text.Spannable; import android.text.SpannableString; @@ -134,6 +137,7 @@ import org.telegram.ui.Cells.ContextLinkCell; import org.telegram.ui.Cells.MentionCell; import org.telegram.ui.Cells.StickerCell; import org.telegram.ui.Components.AlertsCreator; +import org.telegram.ui.Components.AnimatedFileDrawable; import org.telegram.ui.Components.AnimationProperties; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.ActionBar.BaseFragment; @@ -151,6 +155,7 @@ import org.telegram.ui.Components.ExtendedGridLayoutManager; import org.telegram.ui.Components.FragmentContextView; import org.telegram.ui.Components.InstantCameraView; import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.NoSoundHintView; import org.telegram.ui.Components.NumberTextView; import org.telegram.ui.Components.PipRoundVideoView; import org.telegram.ui.Components.RadialProgressView; @@ -254,6 +259,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private TextView gifHintTextView; private TextView mediaBanTooltip; private TextView voiceHintTextView; + private NoSoundHintView noSoundHintView; private Runnable voiceHintHideRunnable; private AnimatorSet voiceHintAnimation; private View emojiButtonRed; @@ -282,6 +288,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private boolean currentFloatingTopIsNotMessage; private AnimatorSet floatingDateAnimation; private boolean scrollingFloatingDate; + private boolean scrollingChatListView; private boolean checkTextureViewPosition; private boolean searchingForUser; private TLRPC.User searchingUserMessages; @@ -429,17 +436,18 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private String startVideoEdit; - private FrameLayout roundVideoContainer; + private FrameLayout videoPlayerContainer; private ChatMessageCell drawLaterRoundProgressCell; private AspectRatioFrameLayout aspectRatioFrameLayout; private TextureView videoTextureView; + private boolean scrollToVideo; private Path aspectPath; private Paint aspectPaint; private PhotoViewer.PhotoViewerProvider photoViewerProvider = new PhotoViewer.EmptyPhotoViewerProvider() { @Override - public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index) { + public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index, boolean needPreview) { int count = chatListView.getChildCount(); for (int a = 0; a < count; a++) { @@ -481,7 +489,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not object.viewY = coords[1] - (Build.VERSION.SDK_INT >= 21 ? 0 : AndroidUtilities.statusBarHeight); object.parentView = chatListView; object.imageReceiver = imageReceiver; - object.thumb = imageReceiver.getBitmapSafe(); + if (needPreview) { + object.thumb = imageReceiver.getBitmapSafe(); + } object.radius = imageReceiver.getRoundRadius(); if (view instanceof ChatActionCell && currentChat != null) { object.dialogId = -currentChat.id; @@ -500,7 +510,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private PhotoViewer.PhotoViewerProvider botContextProvider = new PhotoViewer.EmptyPhotoViewerProvider() { @Override - public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index) { + public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index, boolean needPreview) { if (index < 0 || index >= botContextResults.size()) { return null; } @@ -743,6 +753,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.messagesDidLoad); NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.emojiDidLoad); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.didUpdateConnectionState); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.updateInterfaces); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.didReceiveNewMessages); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.closeChats); @@ -759,6 +770,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.removeAllMessagesFromDialog); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.messagePlayingProgressDidChanged); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.messagePlayingDidReset); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.messagePlayingGoingToStop); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.messagePlayingPlayStateChanged); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.screenshotTook); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.blockedUsersDidLoad); @@ -788,6 +800,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.audioRecordTooShort); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.didUpdatePollResults); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.chatOnlineCountDidLoad); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.videoLoadingStateChanged); super.onFragmentCreate(); @@ -887,6 +900,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not MessagesController.getInstance(currentAccount).setLastCreatedDialogId(dialog_id, false); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.messagesDidLoad); NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.emojiDidLoad); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.didUpdateConnectionState); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.updateInterfaces); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.didReceiveNewMessages); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.closeChats); @@ -908,6 +922,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.fileNewChunkAvailable); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.didCreatedNewDeleteTask); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.messagePlayingDidStart); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.messagePlayingGoingToStop); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.updateMessageMedia); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.replaceMessagesObjects); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.notificationsSettingsUpdated); @@ -932,6 +947,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.audioRecordTooShort); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.didUpdatePollResults); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.chatOnlineCountDidLoad); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.videoLoadingStateChanged); if (AndroidUtilities.isTablet()) { NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.openedChatChanged, dialog_id, true); @@ -978,7 +994,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } cantDeleteMessagesCount = 0; canEditMessagesCount = 0; - roundVideoContainer = null; + videoPlayerContainer = null; hasOwnBackground = true; if (chatAttachAlert != null) { @@ -1500,8 +1516,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not protected void onAttachedToWindow() { super.onAttachedToWindow(); MessageObject messageObject = MediaController.getInstance().getPlayingMessageObject(); - if (messageObject != null && messageObject.isRoundVideo() && messageObject.eventId == 0 && messageObject.getDialogId() == dialog_id) { - MediaController.getInstance().setTextureView(createTextureView(false), aspectRatioFrameLayout, roundVideoContainer, true); + if (messageObject != null && (messageObject.isRoundVideo() || messageObject.isVideo()) && messageObject.eventId == 0 && messageObject.getDialogId() == dialog_id) { + MediaController.getInstance().setTextureView(createTextureView(false), aspectRatioFrameLayout, videoPlayerContainer, true); } } @@ -1509,9 +1525,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not protected boolean drawChild(Canvas canvas, View child, long drawingTime) { boolean result; MessageObject messageObject = MediaController.getInstance().getPlayingMessageObject(); - boolean isRoundVideo = messageObject != null && messageObject.eventId == 0 && messageObject.isRoundVideo(); - if (isRoundVideo && child == roundVideoContainer) { - if (messageObject.type == 5) { + boolean isRoundVideo = false; + boolean isVideo = messageObject != null && messageObject.eventId == 0 && ((isRoundVideo = messageObject.isRoundVideo()) || messageObject.isVideo()); + if (child == videoPlayerContainer) { + if (messageObject != null && messageObject.type == 5) { if (Theme.chat_roundVideoShadow != null && aspectRatioFrameLayout.isDrawingReady()) { int x = (int) child.getX() - AndroidUtilities.dp(3); int y = (int) child.getY() - AndroidUtilities.dp(2); @@ -1521,16 +1538,30 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } result = super.drawChild(canvas, child, drawingTime); } else { - result = false; + if (child.getTag() == null) { + float oldTranslation = child.getTranslationY(); + child.setTranslationY(-AndroidUtilities.dp(1000)); + result = super.drawChild(canvas, child, drawingTime); + child.setTranslationY(oldTranslation); + } else { + result = false; + } } } else { result = super.drawChild(canvas, child, drawingTime); - if (isRoundVideo && child == chatListView && messageObject.type != 5 && roundVideoContainer != null) { - super.drawChild(canvas, roundVideoContainer, drawingTime); + if (isVideo && child == chatListView && messageObject.type != 5 && videoPlayerContainer != null && videoPlayerContainer.getTag() != null) { + super.drawChild(canvas, videoPlayerContainer, drawingTime); if (drawLaterRoundProgressCell != null) { canvas.save(); canvas.translate(drawLaterRoundProgressCell.getX(), drawLaterRoundProgressCell.getTop() + chatListView.getTop()); - drawLaterRoundProgressCell.drawRoundProgress(canvas); + if (isRoundVideo) { + drawLaterRoundProgressCell.drawRoundProgress(canvas); + } else { + drawLaterRoundProgressCell.drawOverlays(canvas); + if (drawLaterRoundProgressCell.needDrawTime()) { + drawLaterRoundProgressCell.drawTime(canvas); + } + } canvas.restore(); } } @@ -1692,7 +1723,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not for (int i = 0; i < count; i++) { final View child = getChildAt(i); - if (child.getVisibility() == GONE) { + if (child == null || child.getVisibility() == GONE) { continue; } final LayoutParams lp = (LayoutParams) child.getLayoutParams(); @@ -1772,7 +1803,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not childTop += AndroidUtilities.statusBarHeight; } childTop -= getPaddingTop(); - } else if (child == roundVideoContainer) { + } else if (child == videoPlayerContainer) { childTop = actionBar.getMeasuredHeight(); } else if (child == instantCameraView || child == overlayView) { childTop = 0; @@ -1781,6 +1812,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } updateMessagesVisiblePart(true); + checkAutoDownloadMessages(); notifyHeightChanged(); } }; @@ -1803,7 +1835,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } else { emptyView = new TextView(context); - if (currentUser != null && currentUser.id != 777000 && currentUser.id != 429000 && currentUser.id != 4244000 && MessagesController.isSupportId(currentUser.id)) { + if (currentUser != null && currentUser.id != 777000 && currentUser.id != 429000 && currentUser.id != 4244000 && MessagesController.isSupportUser(currentUser)) { emptyView.setText(LocaleController.getString("GotAQuestion", R.string.GotAQuestion)); } else { emptyView.setText(LocaleController.getString("NoMessages", R.string.NoMessages)); @@ -1844,6 +1876,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not chatListView = new RecyclerListView(context) { + private int lastWidth; + ArrayList drawTimeAfter = new ArrayList<>(); ArrayList drawNamesAfter = new ArrayList<>(); ArrayList drawCaptionAfter = new ArrayList<>(); @@ -1865,6 +1899,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); + if (lastWidth != r - l) { + lastWidth = r - l; + if (noSoundHintView != null) { + noSoundHintView.hide(); + } + } forceScrollToTop = false; if (chatAdapter.isBot) { int childCount = getChildCount(); @@ -2020,7 +2060,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not wasTrackingVibrate = false; } slidingView.setTranslationX(dx); - if (slidingView.getMessageObject().isRoundVideo()) { + MessageObject messageObject = slidingView.getMessageObject(); + if (messageObject.isRoundVideo() || messageObject.isVideo()) { updateTextureViewPosition(); } setGroupTranslationX(slidingView, dx); @@ -2104,7 +2145,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } setGroupTranslationX(slidingView, translationX); slidingView.setTranslationX(translationX); - if (slidingView.getMessageObject().isRoundVideo()) { + MessageObject messageObject = slidingView.getMessageObject(); + if (messageObject.isRoundVideo() || messageObject.isVideo()) { updateTextureViewPosition(); } invalidate(); @@ -2164,7 +2206,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not ChatMessageCell cell = drawTimeAfter.get(a); canvas.save(); canvas.translate(cell.getLeft() + cell.getTranslationX(), cell.getTop()); - cell.drawTimeLayout(canvas); + cell.drawTime(canvas); canvas.restore(); } drawTimeAfter.clear(); @@ -2205,7 +2247,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not canvas.save(); canvas.translate(chatMessageCell.getLeft() + chatMessageCell.getTranslationX(), chatMessageCell.getTop()); if (position.last) { - chatMessageCell.drawTimeLayout(canvas); + chatMessageCell.drawTime(canvas); } if (position.minX == 0 && position.minY == 0) { chatMessageCell.drawNamesLayout(canvas); @@ -2234,16 +2276,16 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } MessageObject message = chatMessageCell.getMessageObject(); - if (roundVideoContainer != null && message.isRoundVideo() && MediaController.getInstance().isPlayingMessage(message)) { + if (videoPlayerContainer != null && (message.isRoundVideo() || message.isVideo()) && MediaController.getInstance().isPlayingMessage(message)) { ImageReceiver imageReceiver = chatMessageCell.getPhotoImage(); int additionalTop = chatActivityEnterView.isTopViewVisible() ? AndroidUtilities.dp(48) : 0; - float newX = imageReceiver.getImageX() + chatMessageCell.getTranslationX(); + float newX = imageReceiver.getImageX() + chatMessageCell.getX(); float newY = fragmentView.getPaddingTop() + chatMessageCell.getTop() + imageReceiver.getImageY() - additionalTop + (inPreviewMode ? AndroidUtilities.statusBarHeight : 0); - if (roundVideoContainer.getTranslationX() != newX || roundVideoContainer.getTranslationY() != newY) { - roundVideoContainer.setTranslationX(newX); - roundVideoContainer.setTranslationY(newY); + if (videoPlayerContainer.getTranslationX() != newX || videoPlayerContainer.getTranslationY() != newY) { + videoPlayerContainer.setTranslationX(newX); + videoPlayerContainer.setTranslationY(newY); fragmentView.invalidate(); - roundVideoContainer.invalidate(); + videoPlayerContainer.invalidate(); } } ImageReceiver imageReceiver = chatMessageCell.getAvatarImage(); @@ -2446,7 +2488,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not for (int a = 0; a < position.siblingHeights.length; a++) { h += (int) Math.ceil(maxHeight * position.siblingHeights[a]); } - h += (position.maxY - position.minY) * AndroidUtilities.dp2(11); + h += (position.maxY - position.minY) * AndroidUtilities.dp2(7); int count = group.posArray.size(); for (int a = 0; a < count; a++) { MessageObject.GroupedMessagePosition pos = group.posArray.get(a); @@ -2476,14 +2518,18 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not public void onScrollStateChanged(RecyclerView recyclerView, int newState) { if (newState == RecyclerView.SCROLL_STATE_SETTLING) { wasManualScroll = true; + scrollingChatListView = true; } else if (newState == RecyclerView.SCROLL_STATE_DRAGGING) { wasManualScroll = true; scrollingFloatingDate = true; checkTextureViewPosition = true; + scrollingChatListView = true; } else if (newState == RecyclerView.SCROLL_STATE_IDLE) { scrollingFloatingDate = false; + scrollingChatListView = false; checkTextureViewPosition = false; hideFloatingDateView(true); + checkAutoDownloadMessages(); } } @@ -2493,6 +2539,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (!wasManualScroll && dy != 0) { wasManualScroll = true; } + if (dy != 0 && noSoundHintView != null) { + noSoundHintView.hide(); + } if (dy != 0 && scrollingFloatingDate && !currentFloatingTopIsNotMessage) { if (highlightMessageId != Integer.MAX_VALUE) { highlightMessageId = Integer.MAX_VALUE; @@ -4076,30 +4125,42 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (parentLayout == null) { return null; } - if (roundVideoContainer == null) { + if (videoPlayerContainer == null) { if (Build.VERSION.SDK_INT >= 21) { - roundVideoContainer = new FrameLayout(getParentActivity()) { + videoPlayerContainer = new FrameLayout(getParentActivity()) { @Override public void setTranslationY(float translationY) { super.setTranslationY(translationY); contentView.invalidate(); } }; - roundVideoContainer.setOutlineProvider(new ViewOutlineProvider() { + videoPlayerContainer.setOutlineProvider(new ViewOutlineProvider() { @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public void getOutline(View view, Outline outline) { - outline.setOval(0, 0, AndroidUtilities.roundMessageSize, AndroidUtilities.roundMessageSize); + if (view.getTag(R.id.parent_tag) != null) { + outline.setRoundRect(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight(), AndroidUtilities.dp(4)); + } else { + outline.setOval(0, 0, AndroidUtilities.roundMessageSize, AndroidUtilities.roundMessageSize); + } } }); - roundVideoContainer.setClipToOutline(true); + videoPlayerContainer.setClipToOutline(true); } else { - roundVideoContainer = new FrameLayout(getParentActivity()) { + videoPlayerContainer = new FrameLayout(getParentActivity()) { + + RectF rect = new RectF(); + @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); aspectPath.reset(); - aspectPath.addCircle(w / 2, h / 2, w / 2, Path.Direction.CW); + if (getTag(R.id.parent_tag) != null) { + rect.set(0, 0, w, h); + aspectPath.addRoundRect(rect, AndroidUtilities.dp(4), AndroidUtilities.dp(4), Path.Direction.CW); + } else { + aspectPath.addCircle(w / 2, h / 2, w / 2, Path.Direction.CW); + } aspectPath.toggleInverseFillType(); } @@ -4120,7 +4181,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); - canvas.drawPath(aspectPath, aspectPaint); + if (getTag() == null) { + canvas.drawPath(aspectPath, aspectPaint); + } } }; aspectPath = new Path(); @@ -4128,41 +4191,40 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not aspectPaint.setColor(0xff000000); aspectPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); } - roundVideoContainer.setWillNotDraw(false); - roundVideoContainer.setVisibility(View.INVISIBLE); + videoPlayerContainer.setWillNotDraw(false); aspectRatioFrameLayout = new AspectRatioFrameLayout(getParentActivity()); aspectRatioFrameLayout.setBackgroundColor(0); if (add) { - roundVideoContainer.addView(aspectRatioFrameLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + videoPlayerContainer.addView(aspectRatioFrameLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.CENTER)); } videoTextureView = new TextureView(getParentActivity()); videoTextureView.setOpaque(false); aspectRatioFrameLayout.addView(videoTextureView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); } - ViewGroup parent = (ViewGroup) roundVideoContainer.getParent(); + ViewGroup parent = (ViewGroup) videoPlayerContainer.getParent(); if (parent != null && parent != contentView) { - parent.removeView(roundVideoContainer); + parent.removeView(videoPlayerContainer); parent = null; } if (parent == null) { - contentView.addView(roundVideoContainer, 1, new FrameLayout.LayoutParams(AndroidUtilities.roundMessageSize, AndroidUtilities.roundMessageSize)); + contentView.addView(videoPlayerContainer, 1, new FrameLayout.LayoutParams(AndroidUtilities.roundMessageSize, AndroidUtilities.roundMessageSize)); } - roundVideoContainer.setVisibility(View.INVISIBLE); + videoPlayerContainer.setTag(null); aspectRatioFrameLayout.setDrawingReady(false); return videoTextureView; } private void destroyTextureView() { - if (roundVideoContainer == null || roundVideoContainer.getParent() == null) { + if (videoPlayerContainer == null || videoPlayerContainer.getParent() == null) { return; } - contentView.removeView(roundVideoContainer); + contentView.removeView(videoPlayerContainer); aspectRatioFrameLayout.setDrawingReady(false); - roundVideoContainer.setVisibility(View.INVISIBLE); + videoPlayerContainer.setTag(null); if (Build.VERSION.SDK_INT < 21) { - roundVideoContainer.setLayerType(View.LAYER_TYPE_NONE, null); + videoPlayerContainer.setLayerType(View.LAYER_TYPE_NONE, null); } } @@ -4733,6 +4795,46 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not AnimatorSet.start(); } + private void showNoSoundHint() { + if (scrollingChatListView || SharedConfig.noSoundHintShowed || chatListView == null || getParentActivity() == null || fragmentView == null || noSoundHintView != null && noSoundHintView.getTag() != null) { + return; + } + + if (noSoundHintView == null) { + SizeNotifierFrameLayout frameLayout = (SizeNotifierFrameLayout) fragmentView; + int index = frameLayout.indexOfChild(chatActivityEnterView); + if (index == -1) { + return; + } + noSoundHintView = new NoSoundHintView(getParentActivity()); + frameLayout.addView(noSoundHintView, index + 1, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 19, 0, 19, 0)); + noSoundHintView.setAlpha(0.0f); + noSoundHintView.setVisibility(View.INVISIBLE); + } + + int count = chatListView.getChildCount(); + for (int a = 0; a < count; a++) { + View child = chatListView.getChildAt(a); + if (!(child instanceof ChatMessageCell)) { + continue; + } + ChatMessageCell messageCell = (ChatMessageCell) child; + MessageObject messageObject = messageCell.getMessageObject(); + if (messageObject == null || !messageObject.isVideo()) { + continue; + } + ImageReceiver imageReceiver = messageCell.getPhotoImage(); + AnimatedFileDrawable animation = imageReceiver.getAnimation(); + if (animation == null || animation.getCurrentProgressMs() < 3000) { + continue; + } + if (noSoundHintView.showForMessageCell(messageCell)) { + SharedConfig.setNoSoundHintShowed(true); + break; + } + } + } + private void showGifHint() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); if (preferences.getBoolean("gifhint", false)) { @@ -4879,6 +4981,41 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } + private void checkAutoDownloadMessages() { + if (chatListView == null) { + return; + } + int count = chatListView.getChildCount(); + for (int a = 0; a < count; a++) { + View child = chatListView.getChildAt(a); + if (!(child instanceof ChatMessageCell)) { + continue; + } + ChatMessageCell cell = (ChatMessageCell) child; + MessageObject object = cell.getMessageObject(); + if (object == null || object.mediaExists || !object.isSent()) { + continue; + } + TLRPC.Document document = object.getDocument(); + if (document == null) { + continue; + } + int canDownload; + if (!MessageObject.isStickerDocument(document) && !MessageObject.isGifDocument(document) && !MessageObject.isRoundVideoDocument(document) + && (canDownload = DownloadController.getInstance(currentAccount).canDownloadMedia(object.messageOwner)) != 0) { + if (canDownload == 2) { + if (currentEncryptedChat == null && !object.shouldEncryptPhotoOrVideo() && object.canStreamVideo()) { + FileLoader.getInstance(currentAccount).loadFile(document, object, 0, 10); + } + } else { + FileLoader.getInstance(currentAccount).loadFile(document, object, 0, MessageObject.isVideoDocument(document) && object.shouldEncryptPhotoOrVideo() ? 2 : 0); + cell.updateButtonState(false, true, false); + } + } + } + showNoSoundHint(); + } + private void hideFloatingDateView(boolean animated) { if (floatingDateView.getTag() != null && !currentFloatingDateOnScreen && (!scrollingFloatingDate || currentFloatingTopIsNotMessage)) { floatingDateView.setTag(null); @@ -4908,7 +5045,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not @Override protected void onRemoveFromParent() { - MediaController.getInstance().setTextureView(videoTextureView, null, null, false); + MessageObject messageObject = MediaController.getInstance().getPlayingMessageObject(); + if (messageObject != null && messageObject.isVideo()) { + MediaController.getInstance().cleanupPlayer(true, true); + } else { + MediaController.getInstance().setTextureView(videoTextureView, null, null, false); + } } protected void setIgnoreAttachOnPause(boolean value) { @@ -5763,33 +5905,58 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (view instanceof ChatMessageCell) { ChatMessageCell messageCell = (ChatMessageCell) view; MessageObject messageObject = messageCell.getMessageObject(); - if (roundVideoContainer != null && messageObject.isRoundVideo() && MediaController.getInstance().isPlayingMessage(messageObject)) { + if (videoPlayerContainer != null && (messageObject.isRoundVideo() || messageObject.isVideo()) && MediaController.getInstance().isPlayingMessage(messageObject)) { ImageReceiver imageReceiver = messageCell.getPhotoImage(); - roundVideoContainer.setTranslationX(imageReceiver.getImageX() + messageCell.getTranslationX()); - roundVideoContainer.setTranslationY(fragmentView.getPaddingTop() + messageCell.getTop() + imageReceiver.getImageY() - additionalTop + (inPreviewMode ? AndroidUtilities.statusBarHeight : 0)); + videoPlayerContainer.setTranslationX(imageReceiver.getImageX() + messageCell.getX()); + videoPlayerContainer.setTranslationY(fragmentView.getPaddingTop() + messageCell.getTop() + imageReceiver.getImageY() - additionalTop + (inPreviewMode ? AndroidUtilities.statusBarHeight : 0)); + FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) videoPlayerContainer.getLayoutParams(); + if (messageObject.isRoundVideo()) { + videoPlayerContainer.setTag(R.id.parent_tag, null); + if (layoutParams.width != AndroidUtilities.roundMessageSize || layoutParams.height != AndroidUtilities.roundMessageSize) { + layoutParams.width = layoutParams.height = AndroidUtilities.roundMessageSize; + aspectRatioFrameLayout.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FIT); + videoPlayerContainer.setLayoutParams(layoutParams); + } + } else { + videoPlayerContainer.setTag(R.id.parent_tag, 1); + if (layoutParams.width != imageReceiver.getImageWidth() || layoutParams.height != imageReceiver.getImageHeight()) { + aspectRatioFrameLayout.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FILL); + layoutParams.width = imageReceiver.getImageWidth(); + layoutParams.height = imageReceiver.getImageHeight(); + videoPlayerContainer.setLayoutParams(layoutParams); + } + } fragmentView.invalidate(); - roundVideoContainer.invalidate(); + videoPlayerContainer.invalidate(); foundTextureViewMessage = true; break; } } } - if (roundVideoContainer != null) { + if (videoPlayerContainer != null) { MessageObject messageObject = MediaController.getInstance().getPlayingMessageObject(); if (messageObject != null && messageObject.eventId == 0) { if (!foundTextureViewMessage) { - roundVideoContainer.setTranslationY(-AndroidUtilities.roundMessageSize - 100); - fragmentView.invalidate(); - if (messageObject != null && messageObject.isRoundVideo()) { - if (checkTextureViewPosition || PipRoundVideoView.getInstance() != null) { - MediaController.getInstance().setCurrentRoundVisible(false); - } else { - scrollToMessageId(messageObject.getId(), 0, false, 0, true); + if (checkTextureViewPosition && messageObject.isVideo()) { + MediaController.getInstance().cleanupPlayer(true, true); + } else { + videoPlayerContainer.setTranslationY(-AndroidUtilities.roundMessageSize - 100); + fragmentView.invalidate(); + if (messageObject != null && (messageObject.isRoundVideo() || messageObject.isVideo())) { + if (checkTextureViewPosition || PipRoundVideoView.getInstance() != null) { + MediaController.getInstance().setCurrentVideoVisible(false); + } else { + scrollToMessageId(messageObject.getId(), 0, false, 0, true); + } } } } else { - MediaController.getInstance().setCurrentRoundVisible(true); - scrollToMessageId(messageObject.getId(), 0, false, 0, true); + MediaController.getInstance().setCurrentVideoVisible(true); + if (messageObject.isRoundVideo() || scrollToVideo) { + scrollToMessageId(messageObject.getId(), 0, false, 0, true); + } else { + chatListView.invalidate(); + } } } } @@ -5829,13 +5996,18 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not messageCell.setVisiblePart(viewTop, viewBottom - viewTop); messageObject = messageCell.getMessageObject(); - if (roundVideoContainer != null && messageObject.isRoundVideo() && MediaController.getInstance().isPlayingMessage(messageObject)) { + boolean isVideo; + if (videoPlayerContainer != null && (isVideo = messageObject.isVideo() || messageObject.isRoundVideo()) && MediaController.getInstance().isPlayingMessage(messageObject)) { ImageReceiver imageReceiver = messageCell.getPhotoImage(); - roundVideoContainer.setTranslationX(imageReceiver.getImageX() + messageCell.getTranslationX()); - roundVideoContainer.setTranslationY(fragmentView.getPaddingTop() + top + imageReceiver.getImageY() - additionalTop + (inPreviewMode ? AndroidUtilities.statusBarHeight : 0)); - fragmentView.invalidate(); - roundVideoContainer.invalidate(); - foundTextureViewMessage = true; + if (isVideo && top + imageReceiver.getImageY2() < 0) { + foundTextureViewMessage = false; + } else { + videoPlayerContainer.setTranslationX(imageReceiver.getImageX() + messageCell.getX()); + videoPlayerContainer.setTranslationY(fragmentView.getPaddingTop() + top + imageReceiver.getImageY() - additionalTop + (inPreviewMode ? AndroidUtilities.statusBarHeight : 0)); + fragmentView.invalidate(); + videoPlayerContainer.invalidate(); + foundTextureViewMessage = true; + } } } else if (view instanceof ChatActionCell) { messageObject = ((ChatActionCell) view).getMessageObject(); @@ -5877,16 +6049,22 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } MessagesController.getInstance(currentAccount).addToPollsQueue(dialog_id, pollsToCheck); - if (roundVideoContainer != null) { + if (videoPlayerContainer != null) { if (!foundTextureViewMessage) { - roundVideoContainer.setTranslationY(-AndroidUtilities.roundMessageSize - 100); - fragmentView.invalidate(); MessageObject messageObject = MediaController.getInstance().getPlayingMessageObject(); - if (messageObject != null && messageObject.isRoundVideo() && messageObject.eventId == 0 &&checkTextureViewPosition) { - MediaController.getInstance().setCurrentRoundVisible(false); + if (messageObject != null) { + if (checkTextureViewPosition && messageObject.isVideo()) { + MediaController.getInstance().cleanupPlayer(true, true); + } else { + videoPlayerContainer.setTranslationY(-AndroidUtilities.roundMessageSize - 100); + fragmentView.invalidate(); + if ((messageObject.isRoundVideo() || messageObject.isVideo()) && messageObject.eventId == 0 && checkTextureViewPosition) { + MediaController.getInstance().setCurrentVideoVisible(false); + } + } } } else { - MediaController.getInstance().setCurrentRoundVisible(true); + MediaController.getInstance().setCurrentVideoVisible(true); } } if (minMessageChild != null) { @@ -6763,7 +6941,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } else if (currentUser != null) { if (currentUser.self) { avatarContainer.setTitle(LocaleController.getString("SavedMessages", R.string.SavedMessages)); - } else if (!MessagesController.isSupportId(currentUser.id) && ContactsController.getInstance(currentAccount).contactsDict.get(currentUser.id) == null && (ContactsController.getInstance(currentAccount).contactsDict.size() != 0 || !ContactsController.getInstance(currentAccount).isLoadingContacts())) { + } else if (!MessagesController.isSupportUser(currentUser) && ContactsController.getInstance(currentAccount).contactsDict.get(currentUser.id) == null && (ContactsController.getInstance(currentAccount).contactsDict.size() != 0 || !ContactsController.getInstance(currentAccount).isLoadingContacts())) { if (!TextUtils.isEmpty(currentUser.phone)) { avatarContainer.setTitle(PhoneFormat.getInstance().format("+" + currentUser.phone)); } else { @@ -7657,6 +7835,11 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (mentionListView != null) { mentionListView.invalidateViews(); } + } else if (id == NotificationCenter.didUpdateConnectionState) { + int state = ConnectionsManager.getInstance(account).getConnectionState(); + if (state == ConnectionsManager.ConnectionStateConnected) { + checkAutoDownloadMessages(); + } } else if (id == NotificationCenter.chatOnlineCountDidLoad) { Integer chatId = (Integer) args[0]; if (chatInfo == null || currentChat == null || currentChat.id != chatId) { @@ -8825,8 +9008,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } sendSecretMessageRead(messageObject); - if (messageObject.isRoundVideo() && fragmentView != null && fragmentView.getParent() != null) { - MediaController.getInstance().setTextureView(createTextureView(true), aspectRatioFrameLayout, roundVideoContainer, true); + if ((messageObject.isRoundVideo() || messageObject.isVideo()) && fragmentView != null && fragmentView.getParent() != null) { + MediaController.getInstance().setTextureView(createTextureView(true), aspectRatioFrameLayout, videoPlayerContainer, true); updateTextureViewPosition(); } @@ -8838,16 +9021,25 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not ChatMessageCell cell = (ChatMessageCell) view; MessageObject messageObject1 = cell.getMessageObject(); if (messageObject1 != null) { - if (messageObject1.isVoice() || messageObject1.isMusic()) { - cell.updateButtonState(false, true, false); - } else if (messageObject1.isRoundVideo()) { - cell.checkRoundVideoPlayback(false); + boolean isVideo = messageObject1.isVideo(); + if (messageObject1.isRoundVideo() || isVideo) { + cell.checkVideoPlayback(false); if (!MediaController.getInstance().isPlayingMessage(messageObject1)) { + if (isVideo && !MediaController.getInstance().isGoingToShowMessageObject(messageObject1)) { + AnimatedFileDrawable animation = cell.getPhotoImage().getAnimation(); + if (animation != null) { + animation.start(); + } + } if (messageObject1.audioProgress != 0) { messageObject1.resetPlayingProgress(); cell.invalidate(); } + } else if (isVideo) { + cell.updateButtonState(false, true, false); } + } else if (messageObject1.isVoice() || messageObject1.isMusic()) { + cell.updateButtonState(false, true, false); } } } @@ -8864,6 +9056,44 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } } + } else if (id == NotificationCenter.messagePlayingGoingToStop) { + boolean injecting = (Boolean) args[1]; + if (injecting) { + contentView.removeView(videoPlayerContainer); + videoPlayerContainer = null; + videoTextureView = null; + aspectRatioFrameLayout = null; + } else { + if (chatListView != null && videoPlayerContainer != null && videoPlayerContainer.getTag() != null) { + MessageObject messageObject = (MessageObject) args[0]; + int count = chatListView.getChildCount(); + for (int a = 0; a < count; a++) { + View view = chatListView.getChildAt(a); + if (view instanceof ChatMessageCell) { + ChatMessageCell cell = (ChatMessageCell) view; + MessageObject messageObject1 = cell.getMessageObject(); + if (messageObject == messageObject1) { + AnimatedFileDrawable animation = cell.getPhotoImage().getAnimation(); + if (animation != null) { + Bitmap bitmap = animation.getAnimatedBitmap(); + if (bitmap != null) { + try { + Bitmap src = videoTextureView.getBitmap(bitmap.getWidth(), bitmap.getHeight()); + Canvas canvas = new Canvas(bitmap); + canvas.drawBitmap(src, 0, 0, null); + src.recycle(); + } catch (Throwable e) { + FileLog.e(e); + } + } + animation.seekTo(messageObject.audioProgressMs, !FileLoader.getInstance(currentAccount).isLoadingVideo(messageObject.getDocument(), true)); + } + break; + } + } + } + } + } } else if (id == NotificationCenter.messagePlayingDidReset || id == NotificationCenter.messagePlayingPlayStateChanged) { if (id == NotificationCenter.messagePlayingDidReset) { destroyTextureView(); @@ -8878,9 +9108,17 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (messageObject != null) { if (messageObject.isVoice() || messageObject.isMusic()) { cell.updateButtonState(false, true, false); + } else if (messageObject.isVideo()) { + cell.updateButtonState(false, true, false); + if (!MediaController.getInstance().isPlayingMessage(messageObject) && !MediaController.getInstance().isGoingToShowMessageObject(messageObject)) { + AnimatedFileDrawable animation = cell.getPhotoImage().getAnimation(); + if (animation != null) { + animation.start(); + } + } } else if (messageObject.isRoundVideo()) { if (!MediaController.getInstance().isPlayingMessage(messageObject)) { - cell.checkRoundVideoPlayback(true); + cell.checkVideoPlayback(true); } } } @@ -9340,6 +9578,25 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } else if (id == NotificationCenter.audioRecordTooShort) { showVoiceHint(false, (Boolean) args[0]); + } else if (id == NotificationCenter.videoLoadingStateChanged) { + if (chatListView != null) { + String fileName = (String) args[0]; + int count = chatListView.getChildCount(); + for (int a = 0; a < count; a++) { + View child = chatListView.getChildAt(a); + if (!(child instanceof ChatMessageCell)) { + continue; + } + ChatMessageCell cell = (ChatMessageCell) child; + TLRPC.Document document = cell.getStreamingVideo(); + if (document == null) { + continue; + } + if (FileLoader.getAttachFileName(document).equals(fileName)) { + cell.updateButtonState(false, true, false); + } + } + } } } @@ -9904,7 +10161,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not pinnedMessageTextView.setText(String.format("%s - %s", pinnedMessageObject.getMusicAuthor(), pinnedMessageObject.getMusicTitle())); } else if (pinnedMessageObject.type == MessageObject.TYPE_POLL) { TLRPC.TL_messageMediaPoll poll = (TLRPC.TL_messageMediaPoll) pinnedMessageObject.messageOwner.media; - pinnedMessageTextView.setText(poll.poll.question); + String mess = poll.poll.question; + if (mess.length() > 150) { + mess = mess.substring(0, 150); + } + mess = mess.replace('\n', ' '); + pinnedMessageTextView.setText(mess); } else if (pinnedMessageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGame) { pinnedMessageTextView.setText(Emoji.replaceEmoji(pinnedMessageObject.messageOwner.media.game.title, pinnedMessageTextView.getPaint().getFontMetricsInt(), AndroidUtilities.dp(14), false)); } else if (pinnedMessageObject.messageText != null) { @@ -10021,7 +10283,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not currentUser = user; } if (currentEncryptedChat != null && !(currentEncryptedChat instanceof TLRPC.TL_encryptedChat) - || MessagesController.isSupportId(currentUser.id) + || MessagesController.isSupportUser(currentUser) || UserObject.isDeleted(currentUser) || ContactsController.getInstance(currentAccount).isLoadingContacts() || (!TextUtils.isEmpty(currentUser.phone) && ContactsController.getInstance(currentAccount).contactsDict.get(currentUser.id) != null && (ContactsController.getInstance(currentAccount).contactsDict.size() != 0 || !ContactsController.getInstance(currentAccount).isLoadingContacts()))) { @@ -10549,6 +10811,84 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } + public boolean maybePlayVisibleVideo() { + if (chatListView == null) { + return false; + } + MessageObject playingMessage = MediaController.getInstance().getPlayingMessageObject(); + if (playingMessage != null && !playingMessage.isVideo()) { + return false; + } + MessageObject visibleMessage = null; + AnimatedFileDrawable visibleAnimation = null; + if (noSoundHintView != null && noSoundHintView.getTag() != null) { + ChatMessageCell cell = noSoundHintView.getMessageCell(); + ImageReceiver imageReceiver = cell.getPhotoImage(); + visibleAnimation = imageReceiver.getAnimation(); + if (visibleAnimation != null) { + visibleMessage = cell.getMessageObject(); + scrollToVideo = cell.getTop() + imageReceiver.getImageY2() > chatListView.getMeasuredHeight(); + } + } + if (visibleMessage == null) { + int count = chatListView.getChildCount(); + for (int a = 0; a < count; a++) { + View child = chatListView.getChildAt(a); + if (!(child instanceof ChatMessageCell)) { + continue; + } + ChatMessageCell messageCell = (ChatMessageCell) child; + MessageObject messageObject = messageCell.getMessageObject(); + boolean isRoundVideo = messageObject.isRoundVideo(); + if (!messageObject.isVideo() && !isRoundVideo) { + continue; + } + ImageReceiver imageReceiver = messageCell.getPhotoImage(); + AnimatedFileDrawable animation = imageReceiver.getAnimation(); + if (animation == null) { + continue; + } + int top = child.getTop() + imageReceiver.getImageY(); + int bottom = top + imageReceiver.getImageHeight(); + if (bottom < 0 || top > chatListView.getMeasuredHeight()) { + continue; + } + if (visibleMessage != null && top < 0) { + break; + } + visibleMessage = messageObject; + visibleAnimation = animation; + scrollToVideo = top < 0 || bottom > chatListView.getMeasuredHeight(); + if (top >= 0 && bottom <= chatListView.getMeasuredHeight()) { + break; + } + } + } + if (visibleMessage != null) { + if (MediaController.getInstance().isPlayingMessage(visibleMessage)) { + return false; + } + if (noSoundHintView != null) { + noSoundHintView.hide(); + } + if (visibleMessage.isRoundVideo()) { + boolean result = MediaController.getInstance().playMessage(visibleMessage); + MediaController.getInstance().setVoiceMessagesPlaylist(result ? createVoiceMessagesPlaylist(visibleMessage, false) : null, false); + return result; + } else { + SharedConfig.setNoSoundHintShowed(true); + visibleMessage.audioProgress = visibleAnimation.getCurrentProgress(); + visibleMessage.audioProgressMs = visibleAnimation.getCurrentProgressMs(); + visibleAnimation.stop(); + if (PhotoViewer.isPlayingMessageInPip(visibleMessage)) { + PhotoViewer.getPipInstance().destroyPhotoViewer(); + } + return MediaController.getInstance().playMessage(visibleMessage); + } + } + return false; + } + @Override public void onConfigurationChanged(android.content.res.Configuration newConfig) { fixLayout(); @@ -12242,7 +12582,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not messagesStartRow = -1; messagesEndRow = -1; - if (currentUser != null && currentUser.bot) { + if (currentUser != null && currentUser.bot && !MessagesController.isSupportUser(currentUser)) { botInfoRow = rowCount++; } else { botInfoRow = -1; @@ -12322,6 +12662,11 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not return false; } + @Override + public void videoTimerReached() { + showNoSoundHint(); + } + @Override public void didPressChannelAvatar(ChatMessageCell cell, TLRPC.Chat chat, int postId) { if (actionBar.isActionModeShowed()) { @@ -12540,9 +12885,34 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not sendSecretMessageRead(message); } PhotoViewer.getInstance().setParentActivity(getParentActivity()); + MessageObject playingObject = MediaController.getInstance().getPlayingMessageObject(); + if (playingObject != null && playingObject.isVideo()) { + FileLoader.getInstance(currentAccount).setLoadingVideoForPlayer(playingObject.getDocument(), false); + if (playingObject == message) { + AnimatedFileDrawable animation = cell.getPhotoImage().getAnimation(); + if (animation != null && videoTextureView != null && videoPlayerContainer.getTag() != null) { + Bitmap bitmap = animation.getAnimatedBitmap(); + if (bitmap != null) { + try { + Bitmap src = videoTextureView.getBitmap(bitmap.getWidth(), bitmap.getHeight()); + Canvas canvas = new Canvas(bitmap); + canvas.drawBitmap(src, 0, 0, null); + src.recycle(); + } catch (Throwable e) { + FileLog.e(e); + } + } + } + } + MediaController.getInstance().cleanupPlayer(true, true, false, playingObject == message); + } if (PhotoViewer.getInstance().openPhoto(message, message.type != 0 ? dialog_id : 0, message.type != 0 ? mergeDialogId : 0, photoViewerProvider)) { PhotoViewer.getInstance().setParentChatActivity(ChatActivity.this); } + if (noSoundHintView != null) { + noSoundHintView.hide(); + } + MediaController.getInstance().resetGoingToShowMessageObject(); } else if (message.type == 3) { sendSecretMessageRead(message); try { @@ -12814,9 +13184,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } messageCell.setMessageObject(message, groupedMessages, pinnedBottom, pinnedTop); - if (view instanceof ChatMessageCell && DownloadController.getInstance(currentAccount).canDownloadMedia(message)) { - ((ChatMessageCell) view).downloadAudioIfNeed(); - } + messageCell.setHighlighted(highlightMessageId != Integer.MAX_VALUE && message.getId() == highlightMessageId); if (searchContainer != null && searchContainer.getVisibility() == View.VISIBLE && DataQuery.getInstance(currentAccount).isMessageFound(message.getId(), message.getDialogId() == mergeDialogId) && DataQuery.getInstance(currentAccount).getLastSearchQuery() != null) { messageCell.setHighlightedText(DataQuery.getInstance(currentAccount).getLastSearchQuery()); @@ -12910,29 +13278,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not createUnreadMessageAfterId = 0; } } - if (message != null && message.messageOwner != null && message.messageOwner.media_unread && message.messageOwner.mentioned) { - if (!inPreviewMode) { - if (!message.isVoice() && !message.isRoundVideo()) { - newMentionsCount--; - if (newMentionsCount <= 0) { - newMentionsCount = 0; - hasAllMentionsLocal = true; - showMentiondownButton(false, true); - } else { - mentiondownButtonCounter.setText(String.format("%d", newMentionsCount)); - } - MessagesController.getInstance(currentAccount).markMentionMessageAsRead(message.getId(), ChatObject.isChannel(currentChat) ? currentChat.id : 0, dialog_id); - message.setContentIsRead(); - } - } - if (view instanceof ChatMessageCell) { - if (inPreviewMode) { - ((ChatMessageCell) view).setHighlighted(true); - } else { - ((ChatMessageCell) view).setHighlightedAnimated(); - } - } - } } } @@ -12991,6 +13336,35 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not messageCell.setHighlighted(highlightMessageId != Integer.MAX_VALUE && messageCell.getMessageObject().getId() == highlightMessageId); } } + + int position = holder.getAdapterPosition(); + if (position >= messagesStartRow && position < messagesEndRow) { + MessageObject message = messages.get(position - messagesStartRow); + View view = holder.itemView; + if (message != null && message.messageOwner != null && message.messageOwner.media_unread && message.messageOwner.mentioned) { + if (!inPreviewMode) { + if (!message.isVoice() && !message.isRoundVideo()) { + newMentionsCount--; + if (newMentionsCount <= 0) { + newMentionsCount = 0; + hasAllMentionsLocal = true; + showMentiondownButton(false, true); + } else { + mentiondownButtonCounter.setText(String.format("%d", newMentionsCount)); + } + MessagesController.getInstance(currentAccount).markMentionMessageAsRead(message.getId(), ChatObject.isChannel(currentChat) ? currentChat.id : 0, dialog_id); + message.setContentIsRead(); + } + } + if (view instanceof ChatMessageCell) { + if (inPreviewMode) { + ((ChatMessageCell) view).setHighlighted(true); + } else { + ((ChatMessageCell) view).setHighlightedAnimated(); + } + } + } + } } public void updateRowAtPosition(int index) { @@ -13186,6 +13560,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_messageTextOut), new ThemeDescription(chatListView, ThemeDescription.FLAG_LINKCOLOR, new Class[]{ChatMessageCell.class, BotHelpCell.class}, null, null, null, Theme.key_chat_messageLinkIn, null), new ThemeDescription(chatListView, ThemeDescription.FLAG_LINKCOLOR, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_messageLinkOut, null), + new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgNoSoundDrawable}, null, Theme.key_chat_mediaTimeText), new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutCheckDrawable, Theme.chat_msgOutHalfCheckDrawable}, null, Theme.key_chat_outSentCheck), new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutCheckSelectedDrawable, Theme.chat_msgOutHalfCheckSelectedDrawable}, null, Theme.key_chat_outSentCheckSelected), new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutClockDrawable}, null, Theme.key_chat_outSentClock), @@ -13265,10 +13640,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_adminText), new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_adminSelectedText), new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_outTimeSelectedText), - new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_inAudioPerfomerText), - new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_inAudioPerfomerSelectedText), - new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_outAudioPerfomerText), - new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_outAudioPerfomerSelectedText), + new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_inAudioPerformerText), + new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_inAudioPerformerSelectedText), + new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_outAudioPerformerText), + new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_outAudioPerformerSelectedText), new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_inAudioTitleText), new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_outAudioTitleText), new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, null, null, Theme.key_chat_inAudioDurationText), @@ -13494,6 +13869,11 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not new ThemeDescription(gifHintTextView, ThemeDescription.FLAG_BACKGROUNDFILTER, null, null, null, null, Theme.key_chat_gifSaveHintBackground), new ThemeDescription(gifHintTextView, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_chat_gifSaveHintText), + new ThemeDescription(noSoundHintView, ThemeDescription.FLAG_TEXTCOLOR, new Class[]{NoSoundHintView.class}, new String[]{"usernameTextView"}, null, null, null, Theme.key_chat_gifSaveHintText), + new ThemeDescription(noSoundHintView, ThemeDescription.FLAG_IMAGECOLOR, new Class[]{NoSoundHintView.class}, new String[]{"imageView"}, null, null, null, Theme.key_chat_gifSaveHintText), + new ThemeDescription(noSoundHintView, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{NoSoundHintView.class}, new String[]{"usernameTextView"}, null, null, null, Theme.key_chat_gifSaveHintBackground), + new ThemeDescription(noSoundHintView, ThemeDescription.FLAG_TEXTCOLOR, new Class[]{NoSoundHintView.class}, new String[]{"arrowImageView"}, null, null, null, Theme.key_chat_gifSaveHintBackground), + new ThemeDescription(pagedownButtonCounter, ThemeDescription.FLAG_BACKGROUNDFILTER, null, null, null, null, Theme.key_chat_goDownButtonCounterBackground), new ThemeDescription(pagedownButtonCounter, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_chat_goDownButtonCounter), new ThemeDescription(pagedownButtonImage, ThemeDescription.FLAG_BACKGROUNDFILTER, null, null, null, null, Theme.key_chat_goDownButton), diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatUsersActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatUsersActivity.java index f96e197da..50e4e990c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatUsersActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatUsersActivity.java @@ -258,7 +258,7 @@ public class ChatUsersActivity extends BaseFragment implements NotificationCente participantsInfoRow = rowCount++; } else if (type == TYPE_USERS) { if (selectType == 0 && ChatObject.canAddUsers(currentChat)) { - if (!ChatObject.isChannel(currentChat) || currentChat.megagroup || TextUtils.isEmpty(currentChat.username)) { + if (ChatObject.canUserDoAdminAction(currentChat, ChatObject.ACTION_INVITE) && (!ChatObject.isChannel(currentChat) || currentChat.megagroup || TextUtils.isEmpty(currentChat.username))) { addNew2Row = rowCount++; addNewSectionRow = rowCount++; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AlertsCreator.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AlertsCreator.java index f2029cbab..5131d0542 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AlertsCreator.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AlertsCreator.java @@ -17,9 +17,13 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.text.Html; import android.text.Spannable; +import android.text.SpannableString; import android.text.SpannableStringBuilder; import android.text.TextUtils; +import android.text.style.URLSpan; +import android.util.Base64; import android.util.TypedValue; import android.view.Gravity; import android.view.View; @@ -32,6 +36,7 @@ import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.BuildVars; import org.telegram.messenger.ChatObject; +import org.telegram.messenger.FileLog; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MessagesController; import org.telegram.messenger.MessagesStorage; @@ -45,6 +50,7 @@ import org.telegram.messenger.UserObject; import org.telegram.messenger.Utilities; import org.telegram.messenger.browser.Browser; import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.SerializedData; import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.AlertDialog; @@ -55,12 +61,14 @@ import org.telegram.ui.CacheControlActivity; import org.telegram.ui.Cells.AccountSelectCell; import org.telegram.ui.Cells.RadioColorCell; import org.telegram.ui.Cells.TextColorCell; +import org.telegram.ui.ChatActivity; import org.telegram.ui.LanguageSelectActivity; import org.telegram.ui.LaunchActivity; import org.telegram.ui.NotificationsCustomSettingsActivity; import org.telegram.ui.NotificationsSettingsActivity; import org.telegram.ui.ProfileNotificationsActivity; import org.telegram.ui.ReportOtherActivity; +import org.telegram.ui.SettingsActivity; import java.util.ArrayList; import java.util.Calendar; @@ -617,6 +625,117 @@ public class AlertsCreator { } } + public static AlertDialog createSupportAlert(BaseFragment fragment) { + if (fragment == null || fragment.getParentActivity() == null) { + return null; + } + final TextView message = new TextView(fragment.getParentActivity()); + Spannable spanned = new SpannableString(Html.fromHtml(LocaleController.getString("AskAQuestionInfo", R.string.AskAQuestionInfo).replace("\n", "
    "))); + URLSpan[] spans = spanned.getSpans(0, spanned.length(), URLSpan.class); + for (int i = 0; i < spans.length; i++) { + URLSpan span = spans[i]; + int start = spanned.getSpanStart(span); + int end = spanned.getSpanEnd(span); + spanned.removeSpan(span); + span = new URLSpanNoUnderline(span.getURL()) { + @Override + public void onClick(View widget) { + fragment.dismissCurrentDialig(); + super.onClick(widget); + } + }; + spanned.setSpan(span, start, end, 0); + } + message.setText(spanned); + message.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + message.setLinkTextColor(Theme.getColor(Theme.key_dialogTextLink)); + message.setHighlightColor(Theme.getColor(Theme.key_dialogLinkSelection)); + message.setPadding(AndroidUtilities.dp(23), 0, AndroidUtilities.dp(23), 0); + message.setMovementMethod(new AndroidUtilities.LinkMovementMethodMy()); + message.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + + AlertDialog.Builder builder1 = new AlertDialog.Builder(fragment.getParentActivity()); + builder1.setView(message); + builder1.setTitle(LocaleController.getString("AskAQuestion", R.string.AskAQuestion)); + builder1.setPositiveButton(LocaleController.getString("AskButton", R.string.AskButton), (dialogInterface, i) -> performAskAQuestion(fragment)); + builder1.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + return builder1.create(); + } + + private static void performAskAQuestion(BaseFragment fragment) { + int currentAccount = fragment.getCurrentAccount(); + final SharedPreferences preferences = MessagesController.getMainSettings(currentAccount); + int uid = preferences.getInt("support_id", 0); + TLRPC.User supportUser = null; + if (uid != 0) { + supportUser = MessagesController.getInstance(currentAccount).getUser(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.TLdeserialize(data, data.readInt32(false), false); + if (supportUser != null && supportUser.id == 333000) { + supportUser = null; + } + data.cleanup(); + } + } catch (Exception e) { + FileLog.e(e); + supportUser = null; + } + } + } + } + if (supportUser == null) { + final AlertDialog progressDialog = new AlertDialog(fragment.getParentActivity(), 3); + progressDialog.setCanCacnel(false); + progressDialog.show(); + TLRPC.TL_help_getSupport req = new TLRPC.TL_help_getSupport(); + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> { + if (error == null) { + final TLRPC.TL_help_support res = (TLRPC.TL_help_support) response; + AndroidUtilities.runOnUIThread(() -> { + 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(); + data.cleanup(); + try { + progressDialog.dismiss(); + } catch (Exception e) { + FileLog.e(e); + } + ArrayList users = new ArrayList<>(); + users.add(res.user); + MessagesStorage.getInstance(currentAccount).putUsersAndChats(users, null, true, true); + MessagesController.getInstance(currentAccount).putUser(res.user, false); + Bundle args = new Bundle(); + args.putInt("user_id", res.user.id); + fragment.presentFragment(new ChatActivity(args)); + }); + } else { + AndroidUtilities.runOnUIThread(() -> { + try { + progressDialog.dismiss(); + } catch (Exception e) { + FileLog.e(e); + } + }); + } + }); + } else { + MessagesController.getInstance(currentAccount).putUser(supportUser, true); + Bundle args = new Bundle(); + args.putInt("user_id", supportUser.id); + fragment.presentFragment(new ChatActivity(args)); + } + } + public static void createClearOrDeleteDialogAlert(BaseFragment fragment, boolean clear, TLRPC.Chat chat, TLRPC.User user, boolean secret, Runnable onProcessRunnable) { if (fragment == null || fragment.getParentActivity() == null || chat == null && user == null) { return; @@ -823,38 +942,38 @@ public class AlertsCreator { LocaleController.getString("MuteDisable", R.string.MuteDisable) }; builder.setItems(items, (dialogInterface, i) -> { - int untilTime = ConnectionsManager.getInstance(UserConfig.selectedAccount).getCurrentTime(); - if (i == 0) { - untilTime += 60 * 60; - } else if (i == 1) { - untilTime += 60 * 60 * 8; - } else if (i == 2) { - untilTime += 60 * 60 * 48; - } else if (i == 3) { - untilTime = Integer.MAX_VALUE; - } + int untilTime = ConnectionsManager.getInstance(UserConfig.selectedAccount).getCurrentTime(); + if (i == 0) { + untilTime += 60 * 60; + } else if (i == 1) { + untilTime += 60 * 60 * 8; + } else if (i == 2) { + untilTime += 60 * 60 * 48; + } else if (i == 3) { + untilTime = Integer.MAX_VALUE; + } - SharedPreferences preferences = MessagesController.getNotificationsSettings(UserConfig.selectedAccount); - SharedPreferences.Editor editor = preferences.edit(); - long flags; - if (i == 3) { - editor.putInt("notify2_" + dialog_id, 2); - flags = 1; - } else { - editor.putInt("notify2_" + dialog_id, 3); - editor.putInt("notifyuntil_" + dialog_id, untilTime); - flags = ((long) untilTime << 32) | 1; - } - NotificationsController.getInstance(UserConfig.selectedAccount).removeNotificationsForDialog(dialog_id); - MessagesStorage.getInstance(UserConfig.selectedAccount).setDialogFlags(dialog_id, flags); - editor.commit(); - TLRPC.TL_dialog dialog = MessagesController.getInstance(UserConfig.selectedAccount).dialogs_dict.get(dialog_id); - if (dialog != null) { - dialog.notify_settings = new TLRPC.TL_peerNotifySettings(); - dialog.notify_settings.mute_until = untilTime; - } - NotificationsController.getInstance(UserConfig.selectedAccount).updateServerNotificationsSettings(dialog_id); - } + SharedPreferences preferences = MessagesController.getNotificationsSettings(UserConfig.selectedAccount); + SharedPreferences.Editor editor = preferences.edit(); + long flags; + if (i == 3) { + editor.putInt("notify2_" + dialog_id, 2); + flags = 1; + } else { + editor.putInt("notify2_" + dialog_id, 3); + editor.putInt("notifyuntil_" + dialog_id, untilTime); + flags = ((long) untilTime << 32) | 1; + } + NotificationsController.getInstance(UserConfig.selectedAccount).removeNotificationsForDialog(dialog_id); + MessagesStorage.getInstance(UserConfig.selectedAccount).setDialogFlags(dialog_id, flags); + editor.commit(); + TLRPC.TL_dialog dialog = MessagesController.getInstance(UserConfig.selectedAccount).dialogs_dict.get(dialog_id); + if (dialog != null) { + dialog.notify_settings = new TLRPC.TL_peerNotifySettings(); + dialog.notify_settings.mute_until = untilTime; + } + NotificationsController.getInstance(UserConfig.selectedAccount).updateServerNotificationsSettings(dialog_id); + } ); return builder.create(); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedFileDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedFileDrawable.java index 8be16f5f0..4283955ae 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedFileDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedFileDrawable.java @@ -24,7 +24,11 @@ import android.os.Looper; import android.view.View; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimatedFileDrawableStream; +import org.telegram.messenger.DispatchQueue; +import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; +import org.telegram.tgnet.TLRPC; import java.io.File; import java.util.concurrent.ScheduledThreadPoolExecutor; @@ -33,24 +37,37 @@ import java.util.concurrent.TimeUnit; public class AnimatedFileDrawable extends BitmapDrawable implements Animatable { - private static native long createDecoder(String src, int[] params); + private static native long createDecoder(String src, int[] params, int account, long streamFileSize, Object readCallback); private static native void destroyDecoder(long ptr); - private static native int getVideoFrame(long ptr, Bitmap bitmap, int[] params); + private static native void stopDecoder(long ptr); + private static native int getVideoFrame(long ptr, Bitmap bitmap, int[] params, int stride); + private static native void seekToMs(long ptr, long ms); + private static native void prepareToSeek(long ptr); private long lastFrameTime; private int lastTimeStamp; private int invalidateAfter = 50; - private final int[] metaData = new int[4]; + private final int[] metaData = new int[5]; private Runnable loadFrameTask; private Bitmap renderingBitmap; + private int renderingBitmapTime; private Bitmap nextRenderingBitmap; + private int nextRenderingBitmapTime; private Bitmap backgroundBitmap; + private int backgroundBitmapTime; private boolean destroyWhenDone; private boolean decoderCreated; private boolean decodeSingleFrame; private boolean singleFrameDecoded; private File path; + private long streamFileSize; + private int currentAccount; private boolean recycleWithSecond; + private volatile long pendingSeekTo = -1; + private volatile long pendingSeekToUI = -1; + private boolean pendingRemoveLoading; + private int pendingRemoveLoadingFramesReset; + private final Object sync = new Object(); private long lastFrameDecodeTime; @@ -72,12 +89,15 @@ public class AnimatedFileDrawable extends BitmapDrawable implements Animatable { private static final Handler uiHandler = new Handler(Looper.getMainLooper()); private volatile boolean isRunning; private volatile boolean isRecycled; - private volatile long nativePtr; + public volatile long nativePtr; private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(2, new ThreadPoolExecutor.DiscardPolicy()); + private DispatchQueue decodeQueue; private View parentView = null; private View secondParentView = null; + private AnimatedFileDrawableStream stream; + protected final Runnable mInvalidateTask = () -> { if (secondParentView != null) { secondParentView.invalidate(); @@ -102,6 +122,10 @@ public class AnimatedFileDrawable extends BitmapDrawable implements Animatable { backgroundBitmap.recycle(); backgroundBitmap = null; } + if (decodeQueue != null) { + decodeQueue.recycle(); + decodeQueue = null; + } return; } loadFrameTask = null; @@ -125,11 +149,24 @@ public class AnimatedFileDrawable extends BitmapDrawable implements Animatable { backgroundBitmap.recycle(); backgroundBitmap = null; } + if (decodeQueue != null) { + decodeQueue.recycle(); + decodeQueue = null; + } return; } + if (stream != null && pendingRemoveLoading) { + FileLoader.getInstance(currentAccount).removeLoadingVideo(stream.getDocument(), false, false); + } + if (pendingRemoveLoadingFramesReset <= 0) { + pendingRemoveLoading = true; + } else { + pendingRemoveLoadingFramesReset--; + } singleFrameDecoded = true; loadFrameTask = null; nextRenderingBitmap = backgroundBitmap; + nextRenderingBitmapTime = backgroundBitmapTime; nextRenderingShader = backgroundShader; if (metaData[3] < lastTimeStamp) { lastTimeStamp = 0; @@ -137,6 +174,10 @@ public class AnimatedFileDrawable extends BitmapDrawable implements Animatable { if (metaData[3] - lastTimeStamp != 0) { invalidateAfter = metaData[3] - lastTimeStamp; } + if (pendingSeekToUI >= 0 && pendingSeekTo == -1) { + pendingSeekToUI = -1; + invalidateAfter = 0; + } lastTimeStamp = metaData[3]; if (secondParentView != null) { secondParentView.invalidate(); @@ -152,12 +193,12 @@ public class AnimatedFileDrawable extends BitmapDrawable implements Animatable { public void run() { if (!isRecycled) { if (!decoderCreated && nativePtr == 0) { - nativePtr = createDecoder(path.getAbsolutePath(), metaData); + nativePtr = createDecoder(path.getAbsolutePath(), metaData, currentAccount, streamFileSize, stream); decoderCreated = true; } try { if (nativePtr != 0 || metaData[0] == 0 || metaData[1] == 0) { - if (backgroundBitmap == null) { + if (backgroundBitmap == null && metaData[0] > 0 && metaData[1] > 0) { try { backgroundBitmap = Bitmap.createBitmap(metaData[0], metaData[1], Bitmap.Config.ARGB_8888); } catch (Throwable e) { @@ -167,12 +208,27 @@ public class AnimatedFileDrawable extends BitmapDrawable implements Animatable { backgroundShader = new BitmapShader(backgroundBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); } } + boolean seekWas = false; + if (pendingSeekTo >= 0) { + metaData[3] = (int) pendingSeekTo; + long seekTo = pendingSeekTo; + synchronized(sync) { + pendingSeekTo = -1; + } + seekWas = true; + stream.reset(); + seekToMs(nativePtr, seekTo); + } if (backgroundBitmap != null) { lastFrameDecodeTime = System.currentTimeMillis(); - if (getVideoFrame(nativePtr, backgroundBitmap, metaData) == 0) { + if (getVideoFrame(nativePtr, backgroundBitmap, metaData, backgroundBitmap.getRowBytes()) == 0) { AndroidUtilities.runOnUIThread(uiRunnableNoFrame); return; } + if (seekWas) { + lastTimeStamp = metaData[3]; + } + backgroundBitmapTime = metaData[3]; } } else { AndroidUtilities.runOnUIThread(uiRunnableNoFrame); @@ -194,15 +250,23 @@ public class AnimatedFileDrawable extends BitmapDrawable implements Animatable { } }; - public AnimatedFileDrawable(File file, boolean createDecoder) { + public AnimatedFileDrawable(File file, boolean createDecoder, long streamSize, TLRPC.Document document, Object parentObject, int account) { path = file; + streamFileSize = streamSize; + currentAccount = account; + if (streamSize != 0 && document != null) { + stream = new AnimatedFileDrawableStream(document, parentObject, account); + } if (createDecoder) { - nativePtr = createDecoder(file.getAbsolutePath(), metaData); + nativePtr = createDecoder(file.getAbsolutePath(), metaData, currentAccount, streamFileSize, stream); decoderCreated = true; } } public void setParentView(View view) { + if (parentView != null) { + return; + } parentView = view; } @@ -220,6 +284,19 @@ public class AnimatedFileDrawable extends BitmapDrawable implements Animatable { } } + public void seekTo(long ms, boolean removeLoading) { + synchronized (sync) { + pendingSeekTo = ms; + pendingSeekToUI = ms; + prepareToSeek(nativePtr); + if (decoderCreated && stream != null) { + stream.cancel(removeLoading); + pendingRemoveLoading = removeLoading; + pendingRemoveLoadingFramesReset = pendingRemoveLoading ? 0 : 10; + } + } + } + public void recycle() { if (secondParentView != null) { recycleWithSecond = true; @@ -240,9 +317,16 @@ public class AnimatedFileDrawable extends BitmapDrawable implements Animatable { nextRenderingBitmap.recycle(); nextRenderingBitmap = null; } + if (decodeQueue != null) { + decodeQueue.recycle(); + decodeQueue = null; + } } else { destroyWhenDone = true; } + if (stream != null) { + stream.cancel(true); + } } protected static void runOnUiThread(Runnable task) { @@ -277,6 +361,27 @@ public class AnimatedFileDrawable extends BitmapDrawable implements Animatable { runOnUiThread(mStartTask); } + public float getCurrentProgress() { + if (metaData[4] == 0) { + return 0; + } + if (pendingSeekToUI >= 0) { + return pendingSeekToUI / (float) metaData[4]; + } + return metaData[3] / (float) metaData[4]; + } + + public int getCurrentProgressMs() { + if (pendingSeekToUI >= 0) { + return (int) pendingSeekToUI; + } + return nextRenderingBitmapTime != 0 ? nextRenderingBitmapTime : renderingBitmapTime; + } + + public int getDurationMs() { + return metaData[4]; + } + private void scheduleNextGetFrame() { if (loadFrameTask != null || nativePtr == 0 && decoderCreated || destroyWhenDone || !isRunning && (!decodeSingleFrame || decodeSingleFrame && singleFrameDecoded)) { return; @@ -285,7 +390,14 @@ public class AnimatedFileDrawable extends BitmapDrawable implements Animatable { if (lastFrameDecodeTime != 0) { ms = Math.min(invalidateAfter, Math.max(0, invalidateAfter - (System.currentTimeMillis() - lastFrameDecodeTime))); } - executor.schedule(loadFrameTask = loadFrameRunnable, ms, TimeUnit.MILLISECONDS); + if (streamFileSize != 0) { + if (decodeQueue == null) { + decodeQueue = new DispatchQueue("decodeQueue" + this); + } + decodeQueue.postRunnable(loadFrameTask = loadFrameRunnable, ms); + } else { + executor.schedule(loadFrameTask = loadFrameRunnable, ms, TimeUnit.MILLISECONDS); + } } @Override @@ -331,19 +443,21 @@ public class AnimatedFileDrawable extends BitmapDrawable implements Animatable { if (isRunning) { if (renderingBitmap == null && nextRenderingBitmap == null) { scheduleNextGetFrame(); - } else if (Math.abs(now - lastFrameTime) >= invalidateAfter) { - if (nextRenderingBitmap != null) { - renderingBitmap = nextRenderingBitmap; - renderingShader = nextRenderingShader; - nextRenderingBitmap = null; - nextRenderingShader = null; - lastFrameTime = now; - } + } else if (nextRenderingBitmap != null && (renderingBitmap == null || Math.abs(now - lastFrameTime) >= invalidateAfter)) { + renderingBitmap = nextRenderingBitmap; + renderingBitmapTime = nextRenderingBitmapTime; + renderingShader = nextRenderingShader; + nextRenderingBitmap = null; + nextRenderingBitmapTime = 0; + nextRenderingShader = null; + lastFrameTime = now; } } else if (!isRunning && decodeSingleFrame && Math.abs(now - lastFrameTime) >= invalidateAfter && nextRenderingBitmap != null) { renderingBitmap = nextRenderingBitmap; + renderingBitmapTime = nextRenderingBitmapTime; renderingShader = nextRenderingShader; nextRenderingBitmap = null; + nextRenderingBitmapTime = 0; nextRenderingShader = null; lastFrameTime = now; } @@ -371,24 +485,9 @@ public class AnimatedFileDrawable extends BitmapDrawable implements Animatable { getPaint().setShader(renderingShader); roundRect.set(dstRect); shaderMatrix.reset(); - if (Math.abs(scaleX - scaleY) > 0.00001f) { - int w; - int h; - if (metaData[2] == 90 || metaData[2] == 270) { - w = (int) Math.floor(dstRect.height() / scale); - h = (int) Math.floor(dstRect.width() / scale); - } else { - w = (int) Math.floor(dstRect.width() / scale); - h = (int) Math.floor(dstRect.height() / scale); - } - bitmapRect.set((renderingBitmap.getWidth() - w) / 2, (renderingBitmap.getHeight() - h) / 2, w, h); - AndroidUtilities.setRectToRect(shaderMatrix, bitmapRect, roundRect, metaData[2], Matrix.ScaleToFit.START); - } else { - bitmapRect.set(0, 0, renderingBitmap.getWidth(), renderingBitmap.getHeight()); - AndroidUtilities.setRectToRect(shaderMatrix, bitmapRect, roundRect, metaData[2], Matrix.ScaleToFit.FILL); - } + bitmapRect.set(0, 0, renderingBitmap.getWidth(), renderingBitmap.getHeight()); + AndroidUtilities.setRectToRect(shaderMatrix, bitmapRect, roundRect, metaData[2], true); renderingShader.setLocalMatrix(shaderMatrix); - canvas.drawRoundRect(actualDrawRect, roundRadius, roundRadius, getPaint()); } else { canvas.translate(dstRect.left, dstRect.top); @@ -431,6 +530,18 @@ public class AnimatedFileDrawable extends BitmapDrawable implements Animatable { return width; } + public Bitmap getRenderingBitmap() { + return renderingBitmap; + } + + public Bitmap getNextRenderingBitmap() { + return nextRenderingBitmap; + } + + public Bitmap getBackgroundBitmap() { + return backgroundBitmap; + } + public Bitmap getAnimatedBitmap() { if (renderingBitmap != null) { return renderingBitmap; @@ -458,7 +569,12 @@ public class AnimatedFileDrawable extends BitmapDrawable implements Animatable { } public AnimatedFileDrawable makeCopy() { - AnimatedFileDrawable drawable = new AnimatedFileDrawable(path, false); + AnimatedFileDrawable drawable; + if (stream != null) { + drawable = new AnimatedFileDrawable(path, false, streamFileSize, stream.getDocument(), stream.getParentObject(), currentAccount); + } else { + drawable = new AnimatedFileDrawable(path, false, streamFileSize, null, null, currentAccount); + } drawable.metaData[0] = metaData[0]; drawable.metaData[1] = metaData[1]; return drawable; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlert.java index ed92587b7..401aed0f3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlert.java @@ -289,7 +289,7 @@ public class ChatAttachAlert extends BottomSheet implements NotificationCenter.N private PhotoViewer.PhotoViewerProvider photoViewerProvider = new BasePhotoProvider() { @Override - public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index) { + public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index, boolean needPreview) { PhotoAttachPhotoCell cell = getCellForIndex(index); if (cell != null) { int coords[] = new int[2]; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAvatarContainer.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAvatarContainer.java index d7ded54e7..86463dff1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAvatarContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAvatarContainer.java @@ -316,8 +316,10 @@ public class ChatAvatarContainer extends FrameLayout implements NotificationCent String newStatus; if (user.id == UserConfig.getInstance(currentAccount).getClientUserId()) { newStatus = LocaleController.getString("ChatYourSelf", R.string.ChatYourSelf); - } else if (user.id == 333000 || user.id == 777000) { + } else if (user.id == 333000 || user.id == 777000 || user.id == 42777) { newStatus = LocaleController.getString("ServiceNotifications", R.string.ServiceNotifications); + } else if (MessagesController.isSupportUser(user)) { + newStatus = LocaleController.getString("SupportStatus", R.string.SupportStatus); } else if (user.bot) { newStatus = LocaleController.getString("Bot", R.string.Bot); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ClippingImageView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ClippingImageView.java index 7a65d5e16..e9f6dac52 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ClippingImageView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ClippingImageView.java @@ -30,6 +30,8 @@ public class ClippingImageView extends View { private int clipRight; private int clipTop; private int orientation; + private int imageY; + private int imageX; private RectF drawRect; private Paint paint; private ImageReceiver.BitmapHolder bmp; @@ -48,12 +50,13 @@ public class ClippingImageView extends View { public ClippingImageView(Context context) { super(context); - paint = new Paint(); + paint = new Paint(Paint.FILTER_BITMAP_FLAG); paint.setFilterBitmap(true); matrix = new Matrix(); drawRect = new RectF(); bitmapRect = new RectF(); - roundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + roundPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + roundRect = new RectF(); shaderMatrix = new Matrix(); } @@ -79,6 +82,10 @@ public class ClippingImageView extends View { setClipTop((int) (animationValues[0][5] + (animationValues[1][5] - animationValues[0][5]) * animationProgress)); setClipBottom((int) (animationValues[0][6] + (animationValues[1][6] - animationValues[0][6]) * animationProgress)); setRadius((int) (animationValues[0][7] + (animationValues[1][7] - animationValues[0][7]) * animationProgress)); + if (animationValues[0].length > 8) { + setImageY((int) (animationValues[0][8] + (animationValues[1][8] - animationValues[0][8]) * animationProgress)); + setImageX((int) (animationValues[0][9] + (animationValues[1][9] - animationValues[0][9]) * animationProgress)); + } invalidate(); } @@ -117,29 +124,9 @@ public class ClippingImageView extends View { if (needRadius) { shaderMatrix.reset(); - roundRect.set(0, 0, getWidth(), getHeight()); - - int bitmapW; - int bitmapH; - if (orientation % 360 == 90 || orientation % 360 == 270) { - bitmapW = bmp.getHeight(); - bitmapH = bmp.getWidth(); - } else { - bitmapW = bmp.getWidth(); - bitmapH = bmp.getHeight(); - } - float scaleW = getWidth() != 0 ? (float) bitmapW / getWidth() : 1.0f; - float scaleH = getHeight() != 0 ? (float) bitmapH / getHeight() : 1.0f; - float scale = Math.min(scaleW, scaleH); - if (Math.abs(scaleW - scaleH) > 0.00001f) { - int w = (int) Math.floor(getWidth() * scale); - int h = (int) Math.floor(getHeight() * scale); - bitmapRect.set((bitmapW - w) / 2, (bitmapH - h) / 2, w, h); - AndroidUtilities.setRectToRect(shaderMatrix, bitmapRect, roundRect, orientation, Matrix.ScaleToFit.START); - } else { - bitmapRect.set(0, 0, bmp.getWidth(), bmp.getHeight()); - AndroidUtilities.setRectToRect(shaderMatrix, bitmapRect, roundRect, orientation, Matrix.ScaleToFit.FILL); - } + roundRect.set(imageX / scaleY, imageY / scaleY, getWidth() - imageX / scaleY, getHeight() - imageY / scaleY); + bitmapRect.set(0, 0, bmp.getWidth(), bmp.getHeight()); + AndroidUtilities.setRectToRect(shaderMatrix, bitmapRect, roundRect, orientation, false); bitmapShader.setLocalMatrix(shaderMatrix); canvas.clipRect(clipLeft / scaleY, clipTop / scaleY, getWidth() - clipRight / scaleY, getHeight() - clipBottom / scaleY); canvas.drawRoundRect(roundRect, radius, radius, roundPaint); @@ -202,6 +189,14 @@ public class ClippingImageView extends View { invalidate(); } + public void setImageY(int value) { + imageY = value; + } + + public void setImageX(int value) { + imageX = value; + } + public void setOrientation(int angle) { orientation = angle; } @@ -215,7 +210,7 @@ public class ClippingImageView extends View { if (bitmap != null && bitmap.bitmap != null) { bitmapRect.set(0, 0, bitmap.getWidth(), bitmap.getHeight()); if (needRadius) { - bitmapShader = new BitmapShader(bitmap.bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); + bitmapShader = new BitmapShader(bmp.bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); roundPaint.setShader(bitmapShader); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ContextProgressView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ContextProgressView.java index ba5a9e946..361dad03d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ContextProgressView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ContextProgressView.java @@ -25,6 +25,8 @@ public class ContextProgressView extends View { private int radOffset = 0; private long lastUpdateTime; private int currentColorType; + private String innerKey; + private String outerKey; public ContextProgressView(Context context, int colorType) { super(context); @@ -33,21 +35,25 @@ public class ContextProgressView extends View { outerPaint.setStyle(Paint.Style.STROKE); outerPaint.setStrokeWidth(AndroidUtilities.dp(2)); outerPaint.setStrokeCap(Paint.Cap.ROUND); - currentColorType = colorType; + if (colorType == 0) { + innerKey = Theme.key_contextProgressInner1; + outerKey = Theme.key_contextProgressOuter1; + } else if (colorType == 1) { + innerKey = Theme.key_contextProgressInner2; + outerKey = Theme.key_contextProgressOuter2; + } else if (colorType == 2) { + innerKey = Theme.key_contextProgressInner3; + outerKey = Theme.key_contextProgressOuter3; + } else if (colorType == 3) { + innerKey = Theme.key_contextProgressInner4; + outerKey = Theme.key_contextProgressOuter4; + } updateColors(); } public void updateColors() { - if (currentColorType == 0) { - innerPaint.setColor(Theme.getColor(Theme.key_contextProgressInner1)); - outerPaint.setColor(Theme.getColor(Theme.key_contextProgressOuter1)); - } else if (currentColorType == 1) { - innerPaint.setColor(Theme.getColor(Theme.key_contextProgressInner2)); - outerPaint.setColor(Theme.getColor(Theme.key_contextProgressOuter2)); - } else if (currentColorType == 2) { - innerPaint.setColor(Theme.getColor(Theme.key_contextProgressInner3)); - outerPaint.setColor(Theme.getColor(Theme.key_contextProgressOuter3)); - } + innerPaint.setColor(Theme.getColor(innerKey)); + outerPaint.setColor(Theme.getColor(outerKey)); invalidate(); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java index b0991500d..f9918da95 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java @@ -1875,7 +1875,10 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific public void updateUIColors() { if (AndroidUtilities.isInMultiwindow || forseMultiwindowLayout) { - getBackground().setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chat_emojiPanelBackground), PorterDuff.Mode.MULTIPLY)); + Drawable background = getBackground(); + if (background != null) { + background.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chat_emojiPanelBackground), PorterDuff.Mode.MULTIPLY)); + } } else { setBackgroundColor(Theme.getColor(Theme.key_chat_emojiPanelBackground)); emojiTab.setBackgroundColor(Theme.getColor(Theme.key_chat_emojiPanelBackground)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/FragmentContextView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/FragmentContextView.java index 354599e95..d4e8fbb77 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/FragmentContextView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/FragmentContextView.java @@ -633,7 +633,7 @@ public class FragmentContextView extends FrameLayout implements NotificationCent create = true; } } - if (messageObject == null || messageObject.getId() == 0/* || !messageObject.isMusic()*/) { + if (messageObject == null || messageObject.getId() == 0 || messageObject.isVideo()) { lastMessageObject = null; boolean callAvailable = VoIPService.getSharedInstance() != null && VoIPService.getSharedInstance().getCallState() != VoIPService.STATE_WAITING_INCOMING; if (callAvailable) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/InstantCameraView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/InstantCameraView.java index fdd232a41..05f391335 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/InstantCameraView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/InstantCameraView.java @@ -614,7 +614,7 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter } stopProgressTimer(); if (videoPlayer != null) { - videoPlayer.releasePlayer(); + videoPlayer.releasePlayer(true); videoPlayer = null; } if (state == 4) { @@ -689,7 +689,7 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter public void cancel() { stopProgressTimer(); if (videoPlayer != null) { - videoPlayer.releasePlayer(); + videoPlayer.releasePlayer(true); videoPlayer = null; } if (textureView == null) { @@ -1904,7 +1904,7 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter videoEditedInfo.estimatedDuration = duration; NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.audioDidSent, videoEditedInfo, videoFile.getAbsolutePath()); } - didWriteData(videoFile, true); + didWriteData(videoFile, 0, true); }); } else { FileLoader.getInstance(currentAccount).cancelUploadFile(videoFile.getAbsolutePath(), false); @@ -2100,15 +2100,15 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter return surface; } - private void didWriteData(File file, boolean last) { + private void didWriteData(File file, long availableSize, boolean last) { if (videoConvertFirstWrite) { FileLoader.getInstance(currentAccount).uploadFile(file.toString(), isSecretChat, false, 1, ConnectionsManager.FileTypeVideo); videoConvertFirstWrite = false; if (last) { - FileLoader.getInstance(currentAccount).checkUploadNewDataAvailable(file.toString(), isSecretChat, file.length(), last ? file.length() : 0); + FileLoader.getInstance(currentAccount).checkUploadNewDataAvailable(file.toString(), isSecretChat, availableSize, last ? file.length() : 0); } } else { - FileLoader.getInstance(currentAccount).checkUploadNewDataAvailable(file.toString(), isSecretChat, file.length(), last ? file.length() : 0); + FileLoader.getInstance(currentAccount).checkUploadNewDataAvailable(file.toString(), isSecretChat, availableSize, last ? file.length() : 0); } } @@ -2148,8 +2148,9 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter } if (videoBufferInfo.size > 1) { if ((videoBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) { - if (mediaMuxer.writeSampleData(videoTrackIndex, encodedData, videoBufferInfo, true)) { - didWriteData(videoFile, false); + long availableSize = mediaMuxer.writeSampleData(videoTrackIndex, encodedData, videoBufferInfo, true); + if (availableSize != 0) { + didWriteData(videoFile, availableSize, false); } } else if (videoTrackIndex == -5) { byte[] csd = new byte[videoBufferInfo.size]; @@ -2220,8 +2221,9 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter audioBufferInfo.size = 0; } if (audioBufferInfo.size != 0) { - if (mediaMuxer.writeSampleData(audioTrackIndex, encodedData, audioBufferInfo, false)) { - didWriteData(videoFile, false); + long availableSize = mediaMuxer.writeSampleData(audioTrackIndex, encodedData, audioBufferInfo, false); + if (availableSize != 0) { + didWriteData(videoFile, availableSize, false); } } audioEncoder.releaseOutputBuffer(encoderStatus, false); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActionDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActionDrawable.java index eccae9398..fe7c76d77 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActionDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActionDrawable.java @@ -32,6 +32,7 @@ public class MediaActionDrawable extends Drawable { public static final int ICON_EMPTY_NOPROGRESS = 11; public static final int ICON_CANCEL_NOPROFRESS = 12; public static final int ICON_CANCEL_PERCENT = 13; + public static final int ICON_CANCEL_FILL = 14; private TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); @@ -146,14 +147,20 @@ public class MediaActionDrawable extends Drawable { } public boolean setIcon(int icon, boolean animated) { + if (animated && currentIcon == icon && nextIcon != icon) { + currentIcon = nextIcon; + transitionProgress = 1.0f; + } if (animated) { if (currentIcon == icon || nextIcon == icon) { return false; } - if (icon == ICON_CANCEL) { + if (currentIcon == ICON_DOWNLOAD && (icon == ICON_CANCEL || icon == ICON_CANCEL_FILL)) { transitionAnimationTime = 400.0f; } else if (currentIcon != ICON_NONE && icon == ICON_CHECK) { transitionAnimationTime = 360.0f; + } else if (currentIcon == ICON_NONE && icon == ICON_CANCEL_FILL || currentIcon == ICON_CANCEL_FILL && icon == ICON_NONE) { + transitionAnimationTime = 160.0f; } else { transitionAnimationTime = 220.0f; } @@ -170,7 +177,7 @@ public class MediaActionDrawable extends Drawable { savedTransitionProgress = transitionProgress; transitionProgress = 1.0f; } - if (icon == ICON_CANCEL) { + if (icon == ICON_CANCEL || icon == ICON_CANCEL_FILL) { downloadRadOffset = 112; animatedDownloadProgress = 0.0f; downloadProgressAnimationStart = 0.0f; @@ -222,6 +229,9 @@ public class MediaActionDrawable extends Drawable { public void setBounds(int left, int top, int right, int bottom) { super.setBounds(left, top, right, bottom); scale = (right - left) / (float) getIntrinsicWidth(); + if (scale < 0.7f) { + paint.setStrokeWidth(AndroidUtilities.dp(2)); + } } @Override @@ -256,12 +266,12 @@ public class MediaActionDrawable extends Drawable { float yEnd2 = cy + AndroidUtilities.dp(12) * scale; float transition; - if (currentIcon == ICON_CANCEL && nextIcon == ICON_DOWNLOAD) { + if ((currentIcon == ICON_CANCEL || currentIcon == ICON_CANCEL_FILL) && nextIcon == ICON_DOWNLOAD) { paint.setAlpha((int) (255 * Math.min(1.0f, transitionProgress / 0.5f))); transition = transitionProgress; yStart2 = cy + AndroidUtilities.dp(12) * scale; } else { - if (nextIcon != ICON_CANCEL && nextIcon != ICON_DOWNLOAD) { + if (nextIcon != ICON_CANCEL && nextIcon != ICON_CANCEL_FILL && nextIcon != ICON_DOWNLOAD) { paint.setAlpha((int) (255 * Math.min(1.0f, savedTransitionProgress / 0.5f) * (1.0f - transitionProgress))); transition = savedTransitionProgress; } else { @@ -317,7 +327,7 @@ public class MediaActionDrawable extends Drawable { float rotation = -45 * (1.0f - currentProgress2); d = AndroidUtilities.dp(7) * currentProgress2 * scale; int alpha = (int) (255 * currentProgress2); - if (nextIcon != ICON_CANCEL && nextIcon != ICON_DOWNLOAD) { + if (nextIcon != ICON_CANCEL && nextIcon != ICON_CANCEL_FILL && nextIcon != ICON_DOWNLOAD) { float backProgress = (1.0f - Math.min(1.0f, transitionProgress / 0.5f)); //d *= backProgress; alpha *= backProgress; @@ -331,6 +341,13 @@ public class MediaActionDrawable extends Drawable { paint.setAlpha(alpha); canvas.drawLine(cx - d, cy - d, cx + d, cy + d, paint); canvas.drawLine(cx + d, cy - d, cx - d, cy + d, paint); + if (nextIcon == ICON_CANCEL_FILL) { + paint.setAlpha((int) (alpha * 0.15f)); + int diff = AndroidUtilities.dp(isMini ? 2 : 4); + rect.set(bounds.left + diff, bounds.top + diff, bounds.right - diff, bounds.bottom - diff); + canvas.drawArc(rect, 0, 360, false, paint); + paint.setAlpha(alpha); + } } if (rotation != 0) { canvas.restore(); @@ -353,9 +370,10 @@ public class MediaActionDrawable extends Drawable { } } - if (currentIcon == ICON_CANCEL) { + if (currentIcon == ICON_CANCEL || currentIcon == ICON_CANCEL_FILL || currentIcon == ICON_NONE && nextIcon == ICON_CANCEL_FILL) { float d; float rotation; + float iconScale = 1.0f; int alpha; if (nextIcon == ICON_DOWNLOAD) { if (transitionProgress <= DOWNLOAD_TO_CANCEL_STAGE3 + DOWNLOAD_TO_CANCEL_STAGE2) { @@ -384,32 +402,63 @@ public class MediaActionDrawable extends Drawable { } else if (nextIcon == ICON_NONE) { float progress = transitionProgress; float backProgress = 1.0f - progress; - rotation = 45 * progress; d = AndroidUtilities.dp(7) * scale; alpha = (int) (255 * backProgress); + if (currentIcon == ICON_CANCEL_FILL) { + rotation = 0; + iconScale = backProgress; + } else { + rotation = 45 * progress; + iconScale = 1.0f; + } + } else if (nextIcon == ICON_CANCEL_FILL) { + float progress = transitionProgress; + float backProgress = 1.0f - progress; + if (currentIcon == ICON_NONE) { + rotation = 0; + iconScale = progress; + } else { + rotation = 45 * backProgress; + iconScale = 1.0f; + } + d = AndroidUtilities.dp(7) * scale; + alpha = (int) (255 * progress); } else { rotation = 0; d = AndroidUtilities.dp(7) * scale; alpha = 255; } + if (iconScale != 1.0f) { + canvas.save(); + canvas.scale(iconScale, iconScale, bounds.left, bounds.top); + } if (rotation != 0) { canvas.save(); canvas.rotate(rotation, cx, cy); } if (alpha != 0) { - paint.setAlpha(alpha); + paint.setAlpha((int) (alpha * overrideAlpha)); canvas.drawLine(cx - d, cy - d, cx + d, cy + d, paint); canvas.drawLine(cx + d, cy - d, cx - d, cy + d, paint); } if (rotation != 0) { canvas.restore(); } - if (currentIcon == ICON_CANCEL && alpha != 0) { + if ((currentIcon == ICON_CANCEL || currentIcon == ICON_CANCEL_FILL || currentIcon == ICON_NONE && nextIcon == ICON_CANCEL_FILL) && alpha != 0) { float rad = Math.max(4, 360 * animatedDownloadProgress); int diff = AndroidUtilities.dp(isMini ? 2 : 4); rect.set(bounds.left + diff, bounds.top + diff, bounds.right - diff, bounds.bottom - diff); + + if (currentIcon == ICON_CANCEL_FILL || currentIcon == ICON_NONE && nextIcon == ICON_CANCEL_FILL) { + paint.setAlpha((int) (alpha * 0.15f * overrideAlpha)); + canvas.drawArc(rect, 0, 360, false, paint); + paint.setAlpha(alpha); + } canvas.drawArc(rect, downloadRadOffset, rad, false, paint); } + if (iconScale != 1.0f) { + canvas.restore(); + } } else if (currentIcon == ICON_EMPTY || nextIcon == ICON_EMPTY || currentIcon == ICON_CANCEL_PERCENT) { int alpha; if (nextIcon == ICON_NONE || nextIcon == ICON_CHECK) { @@ -687,7 +736,7 @@ public class MediaActionDrawable extends Drawable { } lastAnimationTime = newTime; - if (currentIcon == ICON_CANCEL || currentIcon == ICON_EMPTY || currentIcon == ICON_CANCEL_PERCENT) { + if (currentIcon == ICON_CANCEL || currentIcon == ICON_CANCEL_FILL || currentIcon == ICON_NONE && nextIcon == ICON_CANCEL_FILL || currentIcon == ICON_EMPTY || currentIcon == ICON_CANCEL_PERCENT) { downloadRadOffset += 360 * dt / 2500.0f; downloadRadOffset = getCircleValue(downloadRadOffset); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/NoSoundHintView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/NoSoundHintView.java new file mode 100644 index 000000000..7ff60f07d --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/NoSoundHintView.java @@ -0,0 +1,142 @@ +package org.telegram.ui.Components; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.content.Context; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ImageReceiver; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.R; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Cells.ChatMessageCell; + + +@SuppressWarnings("FieldCanBeLocal") +public class NoSoundHintView extends FrameLayout { + + private TextView textView; + private ImageView imageView; + private ImageView arrowImageView; + private ChatMessageCell messageCell; + private AnimatorSet animatorSet; + private Runnable hideRunnable; + + public NoSoundHintView(Context context) { + super(context); + + textView = new CorrectlyMeasuringTextView(context); + textView.setTextColor(Theme.getColor(Theme.key_chat_gifSaveHintText)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + textView.setMaxLines(2); + textView.setMaxWidth(AndroidUtilities.dp(250)); + textView.setGravity(Gravity.LEFT | Gravity.TOP); + textView.setText(LocaleController.getString("AutoplayVideoInfo", R.string.AutoplayVideoInfo)); + textView.setBackgroundDrawable(Theme.createRoundRectDrawable(AndroidUtilities.dp(3), Theme.getColor(Theme.key_chat_gifSaveHintBackground))); + textView.setPadding(AndroidUtilities.dp(54), AndroidUtilities.dp(6), AndroidUtilities.dp(5), AndroidUtilities.dp(7)); + addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 6)); + + imageView = new ImageView(context); + imageView.setImageResource(R.drawable.tooltip_sound); + imageView.setScaleType(ImageView.ScaleType.CENTER); + imageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chat_gifSaveHintText), PorterDuff.Mode.MULTIPLY)); + addView(imageView, LayoutHelper.createFrame(38, 34, Gravity.LEFT | Gravity.TOP, 7, 7, 0, 0)); + + arrowImageView = new ImageView(context); + arrowImageView.setImageResource(R.drawable.tooltip_arrow); + arrowImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chat_gifSaveHintBackground), PorterDuff.Mode.MULTIPLY)); + addView(arrowImageView, LayoutHelper.createFrame(14, 6, Gravity.LEFT | Gravity.BOTTOM, 0, 0, 0, 0)); + } + + public boolean showForMessageCell(ChatMessageCell cell) { + if (getTag() != null) { + return false; + } + ImageReceiver imageReceiver = cell.getPhotoImage(); + int top = cell.getTop() + imageReceiver.getImageY(); + int height = imageReceiver.getImageHeight(); + int bottom = top + height; + View parentView = (View) cell.getParent(); + int parentHeight = parentView.getMeasuredHeight(); + if (top <= getMeasuredHeight() + AndroidUtilities.dp(10) || bottom > parentHeight + height / 4) { + return false; + } + int parentWidth = parentView.getMeasuredWidth(); + setTranslationY(top - getMeasuredHeight()); + int iconX = cell.getLeft() + cell.getNoSoundIconCenterX(); + int left = getLeft(); + if (iconX > parentView.getMeasuredWidth() / 2) { + int offset = parentWidth - getMeasuredWidth() - AndroidUtilities.dp(38); + setTranslationX(offset); + left += offset; + } else { + setTranslationX(0); + } + arrowImageView.setTranslationX(cell.getLeft() + cell.getNoSoundIconCenterX() - left - arrowImageView.getMeasuredWidth() / 2); + messageCell = cell; + if (animatorSet != null) { + animatorSet.cancel(); + animatorSet = null; + } + + setTag(1); + setVisibility(VISIBLE); + animatorSet = new AnimatorSet(); + animatorSet.playTogether( + ObjectAnimator.ofFloat(this, "alpha", 0.0f, 1.0f) + ); + animatorSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + animatorSet = null; + AndroidUtilities.runOnUIThread(hideRunnable = () -> hide(), 10000); + } + }); + animatorSet.setDuration(300); + animatorSet.start(); + + return true; + } + + public void hide() { + if (getTag() == null) { + return; + } + setTag(null); + if (hideRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(hideRunnable); + hideRunnable = null; + } + if (animatorSet != null) { + animatorSet.cancel(); + animatorSet = null; + } + animatorSet = new AnimatorSet(); + animatorSet.playTogether( + ObjectAnimator.ofFloat(this, "alpha", 0.0f) + ); + animatorSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + setVisibility(View.INVISIBLE); + animatorSet = null; + } + }); + animatorSet.setDuration(300); + animatorSet.start(); + } + + public ChatMessageCell getMessageCell() { + return messageCell; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerCaptionEnterView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerCaptionEnterView.java index e49eddec4..9583d7b09 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerCaptionEnterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerCaptionEnterView.java @@ -586,7 +586,7 @@ public class PhotoViewerCaptionEnterView extends FrameLayout implements Notifica } public CharSequence getFieldCharSequence() { - return messageEditText.getText(); + return AndroidUtilities.getTrimmedString(messageEditText.getText()); } public int getEmojiPadding() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/RadialProgress2.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/RadialProgress2.java index b181abfda..848d012b5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/RadialProgress2.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RadialProgress2.java @@ -51,6 +51,8 @@ public class RadialProgress2 { private boolean isPressed; private boolean isPressedMini; + private boolean drawBackground = true; + private Bitmap miniDrawBitmap; private Canvas miniDrawCanvas; @@ -117,6 +119,10 @@ public class RadialProgress2 { iconPressedColorKey = iconPressed; } + public void setDrawBackground(boolean value) { + drawBackground = value; + } + public void setProgressRect(int left, int top, int right, int bottom) { progressRect.set(left, top, right, bottom); } @@ -150,6 +156,10 @@ public class RadialProgress2 { return mediaActionDrawable.getCurrentIcon(); } + public int getMiniIcon() { + return miniMediaActionDrawable.getCurrentIcon(); + } + public void setIcon(int icon, boolean ifSame, boolean animated) { if (ifSame && icon == mediaActionDrawable.getCurrentIcon()) { return; @@ -319,7 +329,7 @@ public class RadialProgress2 { overlayImageView.setImageCoords(centerX - circleRadius, centerY - circleRadius, circleRadius * 2, circleRadius * 2); } - if (drawCircle) { + if (drawCircle && drawBackground) { if (drawMiniIcon && miniDrawCanvas != null) { miniDrawCanvas.drawCircle(centerX, centerY, circleRadius, circlePaint); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerListView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerListView.java index bd11808a0..aba36452e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerListView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerListView.java @@ -565,6 +565,7 @@ public class RecyclerListView extends RecyclerView { } } }); + gestureDetector.setIsLongpressEnabled(false); } @Override @@ -1052,10 +1053,12 @@ public class RecyclerListView extends RecyclerView { public void setOnItemLongClickListener(OnItemLongClickListener listener) { onItemLongClickListener = listener; + gestureDetector.setIsLongpressEnabled(listener != null); } public void setOnItemLongClickListener(OnItemLongClickListenerExtended listener) { onItemLongClickListenerExtended = listener; + gestureDetector.setIsLongpressEnabled(listener != null); } public void setEmptyView(View view) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ScrollSlidingTextTabStrip.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ScrollSlidingTextTabStrip.java index 64a8847b8..cd067c39a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ScrollSlidingTextTabStrip.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ScrollSlidingTextTabStrip.java @@ -36,6 +36,8 @@ public class ScrollSlidingTextTabStrip extends HorizontalScrollView { private LinearLayout tabsContainer; private ScrollSlidingTabStripDelegate delegate; + private boolean useSameWidth; + private int tabCount; private int currentPosition; private int selectedTabId = -1; @@ -152,6 +154,10 @@ public class ScrollSlidingTextTabStrip extends HorizontalScrollView { } } + public void setUseSameWidth(boolean value) { + useSameWidth = value; + } + public Paint getRectPaint() { return rectPaint; } @@ -282,6 +288,9 @@ public class ScrollSlidingTextTabStrip extends HorizontalScrollView { if (allTextWidth > width) { layoutParams.weight = 0; layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT; + } else if (useSameWidth) { + layoutParams.weight = 1.0f / count; + layoutParams.width = 0; } else { layoutParams.weight = 1.0f / allTextWidth * positionToWidth.get(a); layoutParams.width = 0; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Switch.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Switch.java index 4fc783f1d..991e910ed 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Switch.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Switch.java @@ -12,14 +12,22 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.ColorFilter; import android.graphics.Paint; +import android.graphics.PixelFormat; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; +import android.graphics.PorterDuffXfermode; import android.graphics.RectF; import android.graphics.drawable.Drawable; +import android.graphics.drawable.RippleDrawable; +import android.os.Build; import android.support.annotation.Keep; +import android.util.StateSet; import android.view.View; import org.telegram.messenger.AndroidUtilities; @@ -51,6 +59,25 @@ public class Switch extends View { private Drawable iconDrawable; private int lastIconColor; + private boolean drawRipple; + private RippleDrawable rippleDrawable; + private Paint ripplePaint; + private int pressedState[] = new int[] {android.R.attr.state_enabled, android.R.attr.state_pressed}; + private int colorSet; + + private boolean bitmapsCreated; + private Bitmap[] overlayBitmap; + private Canvas[] overlayCanvas; + private Bitmap overlayMaskBitmap; + private Canvas overlayMaskCanvas; + private float overlayCx; + private float overlayCy; + private float overlayRad; + private Paint overlayEraserPaint; + private Paint overlayMaskPaint; + + private int overrideColorProgress; + public interface OnCheckedChangeListener { void onCheckedChanged(Switch view, boolean isChecked); } @@ -112,6 +139,68 @@ public class Switch extends View { drawIconType = type; } + public void setDrawRipple(boolean value) { + if (Build.VERSION.SDK_INT < 21 || value == drawRipple) { + return; + } + drawRipple = value; + + if (rippleDrawable == null) { + ripplePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + ripplePaint.setColor(0xffffffff); + Drawable maskDrawable = new Drawable() { + @Override + public void draw(Canvas canvas) { + android.graphics.Rect bounds = getBounds(); + canvas.drawCircle(bounds.centerX(), bounds.centerY(), AndroidUtilities.dp(18), ripplePaint); + } + + @Override + public void setAlpha(int alpha) { + + } + + @Override + public void setColorFilter(ColorFilter colorFilter) { + + } + + @Override + public int getOpacity() { + return PixelFormat.UNKNOWN; + } + }; + ColorStateList colorStateList = new ColorStateList( + new int[][]{StateSet.WILD_CARD}, + new int[]{0} + ); + rippleDrawable = new RippleDrawable(colorStateList, null, maskDrawable); + rippleDrawable.setCallback(this); + } + if (isChecked && colorSet != 2 || !isChecked && colorSet != 1) { + int color = isChecked ? Theme.getColor(Theme.key_switchTrackBlueSelectorChecked) : Theme.getColor(Theme.key_switchTrackBlueSelector); + /*if (Build.VERSION.SDK_INT < 28) { + color = Color.argb(Color.alpha(color) * 2, Color.red(color), Color.green(color), Color.blue(color)); + }*/ + ColorStateList colorStateList = new ColorStateList( + new int[][]{StateSet.WILD_CARD}, + new int[]{color} + ); + rippleDrawable.setColor(colorStateList); + colorSet = isChecked ? 2 : 1; + } + if (Build.VERSION.SDK_INT >= 28 && value) { + rippleDrawable.setHotspot(isChecked ? 0 : AndroidUtilities.dp(100), AndroidUtilities.dp(18)); + } + rippleDrawable.setState(value ? pressedState : StateSet.NOTHING); + invalidate(); + } + + @Override + protected boolean verifyDrawable(Drawable who) { + return super.verifyDrawable(who) || rippleDrawable != null && who == rippleDrawable; + } + public void setColors(String track, String trackChecked, String thumb, String thumbChecked) { trackColorKey = track; trackCheckedColorKey = trackChecked; @@ -206,6 +295,48 @@ public class Switch extends View { return isChecked; } + public void setOverrideColor(int override) { + if (overrideColorProgress == override) { + return; + } + if (overlayBitmap == null) { + try { + overlayBitmap = new Bitmap[2]; + overlayCanvas = new Canvas[2]; + for (int a = 0; a < 2; a++) { + overlayBitmap[a] = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888); + overlayCanvas[a] = new Canvas(overlayBitmap[a]); + } + overlayMaskBitmap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888); + overlayMaskCanvas = new Canvas(overlayMaskBitmap); + + overlayEraserPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + overlayEraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + + overlayMaskPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + overlayMaskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + bitmapsCreated = true; + } catch (Throwable e) { + return; + } + } + if (!bitmapsCreated) { + return; + } + overrideColorProgress = override; + overlayCx = 0; + overlayCy = 0; + overlayRad = 0; + invalidate(); + } + + public void setOverrideColorProgress(float cx, float cy, float rad) { + overlayCx = cx; + overlayCy = cy; + overlayRad = rad; + invalidate(); + } + @Override protected void onDraw(Canvas canvas) { if (getVisibility() != VISIBLE) { @@ -219,86 +350,161 @@ public class Switch extends View { int tx = x + AndroidUtilities.dp(7) + (int) (AndroidUtilities.dp(17) * progress); int ty = getMeasuredHeight() / 2; - int color1 = Theme.getColor(trackColorKey); - int color2 = Theme.getColor(trackCheckedColorKey); - if (iconDrawable != null && lastIconColor != (isChecked ? color2 : color1)) { - iconDrawable.setColorFilter(new PorterDuffColorFilter(lastIconColor = (isChecked ? color2 : color1), PorterDuff.Mode.MULTIPLY)); + + int color1; + int color2; + float colorProgress; + int r1; + int r2; + int g1; + int g2; + int b1; + int b2; + int a1; + int a2; + int red; + int green; + int blue; + int alpha; + int color; + + for (int a = 0; a < 2; a++) { + if (a == 1 && overrideColorProgress == 0) { + continue; + } + Canvas canvasToDraw = a == 0 ? canvas : overlayCanvas[0]; + + if (a == 1) { + overlayBitmap[0].eraseColor(0); + paint.setColor(0xff000000); + overlayMaskCanvas.drawRect(0, 0, overlayMaskBitmap.getWidth(), overlayMaskBitmap.getHeight(), paint); + overlayMaskCanvas.drawCircle(overlayCx - getX(), overlayCy - getY(), overlayRad, overlayEraserPaint); + } + if (overrideColorProgress == 1) { + colorProgress = a == 0 ? 0 : 1; + } else if (overrideColorProgress == 2) { + colorProgress = a == 0 ? 1 : 0; + } else { + colorProgress = progress; + } + + color1 = Theme.getColor(trackColorKey); + color2 = Theme.getColor(trackCheckedColorKey); + if (a == 0 && iconDrawable != null && lastIconColor != (isChecked ? color2 : color1)) { + iconDrawable.setColorFilter(new PorterDuffColorFilter(lastIconColor = (isChecked ? color2 : color1), PorterDuff.Mode.MULTIPLY)); + } + + r1 = Color.red(color1); + r2 = Color.red(color2); + g1 = Color.green(color1); + g2 = Color.green(color2); + b1 = Color.blue(color1); + b2 = Color.blue(color2); + a1 = Color.alpha(color1); + a2 = Color.alpha(color2); + + red = (int) (r1 + (r2 - r1) * colorProgress); + green = (int) (g1 + (g2 - g1) * colorProgress); + blue = (int) (b1 + (b2 - b1) * colorProgress); + alpha = (int) (a1 + (a2 - a1) * colorProgress); + color = ((alpha & 0xff) << 24) | ((red & 0xff) << 16) | ((green & 0xff) << 8) | (blue & 0xff); + paint.setColor(color); + paint2.setColor(color); + + rectF.set(x, y, x + width, y + AndroidUtilities.dp(14)); + canvasToDraw.drawRoundRect(rectF, AndroidUtilities.dp(7), AndroidUtilities.dp(7), paint); + canvasToDraw.drawCircle(tx, ty, AndroidUtilities.dp(10), paint); + + if (a == 0 && rippleDrawable != null) { + rippleDrawable.setBounds(tx - AndroidUtilities.dp(18), ty - AndroidUtilities.dp(18), tx + AndroidUtilities.dp(18), ty + AndroidUtilities.dp(18)); + rippleDrawable.draw(canvasToDraw); + } else if (a == 1) { + canvasToDraw.drawBitmap(overlayMaskBitmap, 0, 0, overlayMaskPaint); + } + } + if (overrideColorProgress != 0) { + canvas.drawBitmap(overlayBitmap[0], 0, 0, null); } - int r1 = Color.red(color1); - int r2 = Color.red(color2); - int g1 = Color.green(color1); - int g2 = Color.green(color2); - int b1 = Color.blue(color1); - int b2 = Color.blue(color2); - int a1 = Color.alpha(color1); - int a2 = Color.alpha(color2); + for (int a = 0; a < 2; a++) { + if (a == 1 && overrideColorProgress == 0) { + continue; + } + Canvas canvasToDraw = a == 0 ? canvas : overlayCanvas[1]; - int red = (int) (r1 + (r2 - r1) * progress); - int green = (int) (g1 + (g2 - g1) * progress); - int blue = (int) (b1 + (b2 - b1) * progress); - int alpha = (int) (a1 + (a2 - a1) * progress); - int color = ((alpha & 0xff) << 24) | ((red & 0xff) << 16) | ((green & 0xff) << 8) | (blue & 0xff); - paint.setColor(color); - paint2.setColor(color); + if (a == 1) { + overlayBitmap[1].eraseColor(0); + } + if (overrideColorProgress == 1) { + colorProgress = a == 0 ? 0 : 1; + } else if (overrideColorProgress == 2) { + colorProgress = a == 0 ? 1 : 0; + } else { + colorProgress = progress; + } - rectF.set(x, y, x + width, y + AndroidUtilities.dp(14)); - canvas.drawRoundRect(rectF, AndroidUtilities.dp(7), AndroidUtilities.dp(7), paint); - canvas.drawCircle(tx, ty, AndroidUtilities.dp(10), paint); + color1 = Theme.getColor(thumbColorKey); + color2 = Theme.getColor(thumbCheckedColorKey); + r1 = Color.red(color1); + r2 = Color.red(color2); + g1 = Color.green(color1); + g2 = Color.green(color2); + b1 = Color.blue(color1); + b2 = Color.blue(color2); + a1 = Color.alpha(color1); + a2 = Color.alpha(color2); - color1 = Theme.getColor(thumbColorKey); - color2 = Theme.getColor(thumbCheckedColorKey); - r1 = Color.red(color1); - r2 = Color.red(color2); - g1 = Color.green(color1); - g2 = Color.green(color2); - b1 = Color.blue(color1); - b2 = Color.blue(color2); - a1 = Color.alpha(color1); - a2 = Color.alpha(color2); + red = (int) (r1 + (r2 - r1) * colorProgress); + green = (int) (g1 + (g2 - g1) * colorProgress); + blue = (int) (b1 + (b2 - b1) * colorProgress); + alpha = (int) (a1 + (a2 - a1) * colorProgress); + paint.setColor(((alpha & 0xff) << 24) | ((red & 0xff) << 16) | ((green & 0xff) << 8) | (blue & 0xff)); - red = (int) (r1 + (r2 - r1) * progress); - green = (int) (g1 + (g2 - g1) * progress); - blue = (int) (b1 + (b2 - b1) * progress); - alpha = (int) (a1 + (a2 - a1) * progress); - paint.setColor(((alpha & 0xff) << 24) | ((red & 0xff) << 16) | ((green & 0xff) << 8) | (blue & 0xff)); + canvasToDraw.drawCircle(tx, ty, AndroidUtilities.dp(8), paint); - canvas.drawCircle(tx, ty, AndroidUtilities.dp(8), paint); + if (a == 0) { + if (iconDrawable != null) { + iconDrawable.setBounds(tx - iconDrawable.getIntrinsicWidth() / 2, ty - iconDrawable.getIntrinsicHeight() / 2, tx + iconDrawable.getIntrinsicWidth() / 2, ty + iconDrawable.getIntrinsicHeight() / 2); + iconDrawable.draw(canvasToDraw); + } else if (drawIconType == 1) { + tx -= AndroidUtilities.dp(10.8f) - AndroidUtilities.dp(1.3f) * progress; + ty -= AndroidUtilities.dp(8.5f) - AndroidUtilities.dp(0.5f) * progress; + int startX2 = (int) AndroidUtilities.dpf2(4.6f) + tx; + int startY2 = (int) (AndroidUtilities.dpf2(9.5f) + ty); + int endX2 = startX2 + AndroidUtilities.dp(2); + int endY2 = startY2 + AndroidUtilities.dp(2); - if (iconDrawable != null) { - iconDrawable.setBounds(tx - iconDrawable.getIntrinsicWidth() / 2, ty - iconDrawable.getIntrinsicHeight() / 2, tx + iconDrawable.getIntrinsicWidth() / 2, ty + iconDrawable.getIntrinsicHeight() / 2); - iconDrawable.draw(canvas); - } else if (drawIconType == 1) { - tx -= AndroidUtilities.dp(10.8f) - AndroidUtilities.dp(1.3f) * progress; - ty -= AndroidUtilities.dp(8.5f) - AndroidUtilities.dp(0.5f) * progress; - int startX2 = (int) AndroidUtilities.dpf2(4.6f) + tx; - int startY2 = (int) (AndroidUtilities.dpf2(9.5f) + ty); - int endX2 = startX2 + AndroidUtilities.dp(2); - int endY2 = startY2 + AndroidUtilities.dp(2); + int startX = (int) AndroidUtilities.dpf2(7.5f) + tx; + int startY = (int) AndroidUtilities.dpf2(5.4f) + ty; + int endX = startX + AndroidUtilities.dp(7); + int endY = startY + AndroidUtilities.dp(7); - int startX = (int) AndroidUtilities.dpf2(7.5f) + tx; - int startY = (int) AndroidUtilities.dpf2(5.4f) + ty; - int endX = startX + AndroidUtilities.dp(7); - int endY = startY + AndroidUtilities.dp(7); + startX = (int) (startX + (startX2 - startX) * progress); + startY = (int) (startY + (startY2 - startY) * progress); + endX = (int) (endX + (endX2 - endX) * progress); + endY = (int) (endY + (endY2 - endY) * progress); + canvasToDraw.drawLine(startX, startY, endX, endY, paint2); - startX = (int) (startX + (startX2 - startX) * progress); - startY = (int) (startY + (startY2 - startY) * progress); - endX = (int) (endX + (endX2 - endX) * progress); - endY = (int) (endY + (endY2 - endY) * progress); - canvas.drawLine(startX, startY, endX, endY, paint2); - - startX = (int) AndroidUtilities.dpf2(7.5f) + tx; - startY = (int) AndroidUtilities.dpf2(12.5f) + ty; - endX = startX + AndroidUtilities.dp(7); - endY = startY - AndroidUtilities.dp(7); - canvas.drawLine(startX, startY, endX, endY, paint2); - } else if (drawIconType == 2 || iconAnimator != null) { - paint2.setAlpha((int) (255 * (1.0f - iconProgress))); - canvas.drawLine(tx, ty, tx, ty - AndroidUtilities.dp(5), paint2); - canvas.save(); - canvas.rotate(-90 * iconProgress, tx, ty); - canvas.drawLine(tx, ty, tx + AndroidUtilities.dp(4), ty, paint2); - canvas.restore(); + startX = (int) AndroidUtilities.dpf2(7.5f) + tx; + startY = (int) AndroidUtilities.dpf2(12.5f) + ty; + endX = startX + AndroidUtilities.dp(7); + endY = startY - AndroidUtilities.dp(7); + canvasToDraw.drawLine(startX, startY, endX, endY, paint2); + } else if (drawIconType == 2 || iconAnimator != null) { + paint2.setAlpha((int) (255 * (1.0f - iconProgress))); + canvasToDraw.drawLine(tx, ty, tx, ty - AndroidUtilities.dp(5), paint2); + canvasToDraw.save(); + canvasToDraw.rotate(-90 * iconProgress, tx, ty); + canvasToDraw.drawLine(tx, ty, tx + AndroidUtilities.dp(4), ty, paint2); + canvasToDraw.restore(); + } + } + if (a == 1) { + canvasToDraw.drawBitmap(overlayMaskBitmap, 0, 0, overlayMaskPaint); + } + } + if (overrideColorProgress != 0) { + canvas.drawBitmap(overlayBitmap[1], 0, 0, null); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoPlayer.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoPlayer.java index c7be083ba..db86d0f09 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoPlayer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoPlayer.java @@ -14,6 +14,7 @@ import android.net.Uri; import android.os.Handler; import android.view.TextureView; +import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.source.LoopingMediaSource; import com.google.android.exoplayer2.DefaultLoadControl; @@ -39,6 +40,7 @@ import com.google.android.exoplayer2.trackselection.MappingTrackSelector; import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.upstream.DataSource; +import com.google.android.exoplayer2.upstream.DefaultAllocator; import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; @@ -110,8 +112,16 @@ public class VideoPlayer implements ExoPlayer.EventListener, SimpleExoPlayer.Vid } private void ensurePleyaerCreated() { + DefaultLoadControl loadControl = new DefaultLoadControl( + new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE), + DefaultLoadControl.DEFAULT_MIN_BUFFER_MS, + DefaultLoadControl.DEFAULT_MAX_BUFFER_MS, + 100, + DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS, + DefaultLoadControl.DEFAULT_TARGET_BUFFER_BYTES, + DefaultLoadControl.DEFAULT_PRIORITIZE_TIME_OVER_SIZE_THRESHOLDS); if (player == null) { - player = ExoPlayerFactory.newSimpleInstance(ApplicationLoader.applicationContext, trackSelector, new DefaultLoadControl(), null, DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER); + player = ExoPlayerFactory.newSimpleInstance(ApplicationLoader.applicationContext, trackSelector, loadControl, null, DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER); player.addListener(this); player.setVideoListener(this); player.setVideoTextureView(textureView); @@ -119,7 +129,7 @@ public class VideoPlayer implements ExoPlayer.EventListener, SimpleExoPlayer.Vid } if (mixedAudio) { if (audioPlayer == null) { - audioPlayer = ExoPlayerFactory.newSimpleInstance(ApplicationLoader.applicationContext, trackSelector, new DefaultLoadControl(), null, DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER); + audioPlayer = ExoPlayerFactory.newSimpleInstance(ApplicationLoader.applicationContext, trackSelector, loadControl, null, DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER); audioPlayer.addListener(new Player.EventListener() { @Override @@ -250,13 +260,13 @@ public class VideoPlayer implements ExoPlayer.EventListener, SimpleExoPlayer.Vid return player != null; } - public void releasePlayer() { + public void releasePlayer(boolean async) { if (player != null) { - player.release(); + player.release(async); player = null; } if (audioPlayer != null) { - audioPlayer.release(); + audioPlayer.release(async); audioPlayer = null; } NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.playerDidStartPlaying); @@ -273,6 +283,14 @@ public class VideoPlayer implements ExoPlayer.EventListener, SimpleExoPlayer.Vid player.setVideoTextureView(textureView); } + public boolean getPlayWhenReady() { + return player.getPlayWhenReady(); + } + + public int getPlaybackState() { + return player.getPlaybackState(); + } + public void play() { mixedPlayWhenReady = true; if (mixedAudio) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/WebPlayerView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/WebPlayerView.java index 0a937cf1c..c578d14be 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/WebPlayerView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/WebPlayerView.java @@ -2286,7 +2286,7 @@ public class WebPlayerView extends ViewGroup implements VideoPlayer.VideoPlayerD } public void destroy() { - videoPlayer.releasePlayer(); + videoPlayer.releasePlayer(false); if (currentTask != null) { currentTask.cancel(true); currentTask = null; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPHelper.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPHelper.java index 10fb5ae70..541fece9b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPHelper.java @@ -17,7 +17,6 @@ import android.util.TypedValue; import android.view.Gravity; import android.view.View; import android.view.WindowManager; -import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView; @@ -27,12 +26,14 @@ import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.BuildVars; import org.telegram.messenger.ContactsController; +import org.telegram.messenger.DownloadController; import org.telegram.messenger.FileLog; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MessagesController; import org.telegram.messenger.R; import org.telegram.messenger.SendMessagesHelper; import org.telegram.messenger.UserConfig; +import org.telegram.messenger.voip.VoIPController; import org.telegram.messenger.voip.VoIPService; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.RequestDelegate; @@ -506,4 +507,22 @@ public class VoIPHelper{ .setView(ll) .show(); } + + public static int getDataSavingDefault(){ + boolean low=DownloadController.getInstance(0).lowPreset.lessCallData, + medium=DownloadController.getInstance(0).mediumPreset.lessCallData, + high=DownloadController.getInstance(0).highPreset.lessCallData; + if(!low && !medium && !high){ + return VoIPController.DATA_SAVING_NEVER; + }else if(low && !medium && !high){ + return VoIPController.DATA_SAVING_ROAMING; + }else if(low && medium && !high){ + return VoIPController.DATA_SAVING_MOBILE; + }else if(low && medium && high){ + return VoIPController.DATA_SAVING_ALWAYS; + } + if(BuildVars.LOGS_ENABLED) + FileLog.w("Invalid call data saving preset configuration: "+low+"/"+medium+"/"+high); + return VoIPController.DATA_SAVING_NEVER; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DataAutoDownloadActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/DataAutoDownloadActivity.java index 6b9009cb0..726ff703d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DataAutoDownloadActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DataAutoDownloadActivity.java @@ -8,171 +8,325 @@ package org.telegram.ui; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; import android.content.Context; import android.content.SharedPreferences; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.text.TextPaint; +import android.util.TypedValue; import android.view.Gravity; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.TextView; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.DownloadController; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MessagesController; import org.telegram.messenger.R; +import org.telegram.messenger.support.widget.DefaultItemAnimator; import org.telegram.messenger.support.widget.LinearLayoutManager; import org.telegram.messenger.support.widget.RecyclerView; import org.telegram.ui.ActionBar.ActionBar; -import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.BottomSheet; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.ActionBar.ThemeDescription; import org.telegram.ui.Cells.HeaderCell; import org.telegram.ui.Cells.MaxFileSizeCell; +import org.telegram.ui.Cells.NotificationsCheckCell; import org.telegram.ui.Cells.ShadowSectionCell; import org.telegram.ui.Cells.TextCheckBoxCell; +import org.telegram.ui.Cells.TextCheckCell; +import org.telegram.ui.Cells.TextInfoPrivacyCell; +import org.telegram.ui.Components.CombinedDrawable; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.RecyclerListView; +import java.util.ArrayList; +import java.util.Collections; + public class DataAutoDownloadActivity extends BaseFragment { private ListAdapter listAdapter; private RecyclerListView listView; + private ArrayList presets = new ArrayList<>(); + private int selectedPreset = 1; + private int currentPresetNum; + private int currentType; - private int mobileDataDownloadMask; - private int wifiDownloadMask; - private int roamingDownloadMask; - private int mobileDataPrivateDownloadMask; - private int wifiPrivateDownloadMask; - private int roamingPrivateDownloadMask; - private int mobileDataGroupDownloadMask; - private int wifiGroupDownloadMask; - private int roamingGroupDownloadMask; - private int mobileDataChannelDownloadMask; - private int wifiChannelDownloadMask; - private int roamingChannelDownloadMask; - private int mobileMaxSize; - private int wifiMaxSize; - private int roamingMaxSize; + private boolean animateChecked; + + private int autoDownloadRow; + private int autoDownloadSectionRow; + private int usageHeaderRow; + private int usageProgressRow; + private int usageSectionRow; + private int typeHeaderRow; + private int photosRow; + private int videosRow; + private int filesRow; + private int typeSectionRow; - private int mobileSectionRow; - private int mContactsRow; - private int mPrivateRow; - private int mGroupRow; - private int mChannelsRow; - private int mSizeRow; - private int mobileSection2Row; - private int wifiSectionRow; - private int wContactsRow; - private int wPrivateRow; - private int wGroupRow; - private int wChannelsRow; - private int wSizeRow; - private int wifiSection2Row; - private int roamingSectionRow; - private int rContactsRow; - private int rPrivateRow; - private int rGroupRow; - private int rChannelsRow; - private int rSizeRow; - private int roamingSection2Row; private int rowCount; - private long maxSize; + private DownloadController.Preset lowPreset; + private DownloadController.Preset mediumPreset; + private DownloadController.Preset highPreset; + private DownloadController.Preset typePreset; + private DownloadController.Preset defaultPreset; - private final static int done_button = 1; + private boolean wereAnyChanges; + + private String key; + private String key2; + + private class PresetChooseView extends View { + + private Paint paint; + private TextPaint textPaint; + + private int circleSize; + private int gapSize; + private int sideSide; + private int lineSize; + + private boolean moving; + private boolean startMoving; + private float startX; + + private int startMovingPreset; + + private String low; + private int lowSize; + private String medium; + private int mediumSize; + private String high; + private int highSize; + private String custom; + private int customSize; + + public PresetChooseView(Context context) { + super(context); + + paint = new Paint(Paint.ANTI_ALIAS_FLAG); + textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + textPaint.setTextSize(AndroidUtilities.dp(13)); + + low = LocaleController.getString("AutoDownloadLow", R.string.AutoDownloadLow); + lowSize = (int) Math.ceil(textPaint.measureText(low)); + medium = LocaleController.getString("AutoDownloadMedium", R.string.AutoDownloadMedium); + mediumSize = (int) Math.ceil(textPaint.measureText(medium)); + high = LocaleController.getString("AutoDownloadHigh", R.string.AutoDownloadHigh); + highSize = (int) Math.ceil(textPaint.measureText(high)); + custom = LocaleController.getString("AutoDownloadCustom", R.string.AutoDownloadCustom); + customSize = (int) Math.ceil(textPaint.measureText(custom)); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + float x = event.getX(); + if (event.getAction() == MotionEvent.ACTION_DOWN) { + getParent().requestDisallowInterceptTouchEvent(true); + for (int a = 0; a < presets.size(); a++) { + int cx = sideSide + (lineSize + gapSize * 2 + circleSize) * a + circleSize / 2; + if (x > cx - AndroidUtilities.dp(15) && x < cx + AndroidUtilities.dp(15)) { + startMoving = a == selectedPreset; + startX = x; + startMovingPreset = selectedPreset; + break; + } + } + } else if (event.getAction() == MotionEvent.ACTION_MOVE) { + if (startMoving) { + if (Math.abs(startX - x) >= AndroidUtilities.getPixelsInCM(0.5f, true)) { + moving = true; + startMoving = false; + } + } else if (moving) { + for (int a = 0; a < presets.size(); a++) { + int cx = sideSide + (lineSize + gapSize * 2 + circleSize) * a + circleSize / 2; + int diff = lineSize / 2 + circleSize / 2 + gapSize; + if (x > cx - diff && x < cx + diff) { + if (selectedPreset != a) { + setPreset(a); + } + break; + } + } + } + } else if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) { + if (!moving) { + for (int a = 0; a < 5; a++) { + int cx = sideSide + (lineSize + gapSize * 2 + circleSize) * a + circleSize / 2; + if (x > cx - AndroidUtilities.dp(15) && x < cx + AndroidUtilities.dp(15)) { + if (selectedPreset != a) { + setPreset(a); + } + break; + } + } + } else { + if (selectedPreset != startMovingPreset) { + setPreset(selectedPreset); + } + } + startMoving = false; + moving = false; + } + return true; + } + + private void setPreset(int index) { + selectedPreset = index; + DownloadController.Preset preset = presets.get(selectedPreset); + if (preset == lowPreset) { + currentPresetNum = 0; + } else if (preset == mediumPreset) { + currentPresetNum = 1; + } else if (preset == highPreset) { + currentPresetNum = 2; + } else { + currentPresetNum = 3; + } + if (currentType == 0) { + DownloadController.getInstance(currentAccount).currentMobilePreset = currentPresetNum; + } else if (currentType == 1) { + DownloadController.getInstance(currentAccount).currentWifiPreset = currentPresetNum; + } else { + DownloadController.getInstance(currentAccount).currentRoamingPreset = currentPresetNum; + } + SharedPreferences.Editor editor = MessagesController.getMainSettings(currentAccount).edit(); + editor.putInt(key2, currentPresetNum); + editor.commit(); + DownloadController.getInstance(currentAccount).checkAutodownloadSettings(); + for (int a = 0; a < 3; a++) { + RecyclerView.ViewHolder holder = listView.findViewHolderForAdapterPosition(photosRow + a); + if (holder != null) { + listAdapter.onBindViewHolder(holder, photosRow + a); + } + } + wereAnyChanges = true; + invalidate(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(74), MeasureSpec.EXACTLY)); + int width = MeasureSpec.getSize(widthMeasureSpec); + circleSize = AndroidUtilities.dp(6); + gapSize = AndroidUtilities.dp(2); + sideSide = AndroidUtilities.dp(22); + lineSize = (getMeasuredWidth() - circleSize * presets.size() - gapSize * 2 * (presets.size() - 1) - sideSide * 2) / (presets.size() - 1); + } + + @Override + protected void onDraw(Canvas canvas) { + textPaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText)); + int cy = getMeasuredHeight() / 2 + AndroidUtilities.dp(11); + + for (int a = 0; a < presets.size(); a++) { + int cx = sideSide + (lineSize + gapSize * 2 + circleSize) * a + circleSize / 2; + if (a <= selectedPreset) { + paint.setColor(Theme.getColor(Theme.key_switchTrackChecked)); + } else { + paint.setColor(Theme.getColor(Theme.key_switchTrack)); + } + canvas.drawCircle(cx, cy, a == selectedPreset ? AndroidUtilities.dp(6) : circleSize / 2, paint); + if (a != 0) { + int x = cx - circleSize / 2 - gapSize - lineSize; + int width = lineSize; + if (a == selectedPreset || a == selectedPreset + 1) { + width -= AndroidUtilities.dp(3); + } + if (a == selectedPreset + 1) { + x += AndroidUtilities.dp(3); + } + canvas.drawRect(x, cy - AndroidUtilities.dp(1), x + width, cy + AndroidUtilities.dp(1), paint); + } + DownloadController.Preset preset = presets.get(a); + int size; + String text; + if (preset == lowPreset) { + text = low; + size = lowSize; + } else if (preset == mediumPreset) { + text = medium; + size = mediumSize; + } else if (preset == highPreset) { + text = high; + size = highSize; + } else { + text = custom; + size = customSize; + } + if (a == 0) { + canvas.drawText(text, AndroidUtilities.dp(22), AndroidUtilities.dp(28), textPaint); + } else if (a == presets.size() - 1) { + canvas.drawText(text, getMeasuredWidth() - size - AndroidUtilities.dp(22), AndroidUtilities.dp(28), textPaint); + } else { + canvas.drawText(text, cx - customSize / 2, AndroidUtilities.dp(28), textPaint); + } + } + } + } public DataAutoDownloadActivity(int type) { super(); currentType = type; - if (currentType == DownloadController.AUTODOWNLOAD_MASK_VIDEOMESSAGE) { - maxSize = 8 * 1024 * 1024; - } else if (currentType == DownloadController.AUTODOWNLOAD_MASK_GIF) { - maxSize = 10 * 1024 * 1024; + lowPreset = DownloadController.getInstance(currentAccount).lowPreset; + mediumPreset = DownloadController.getInstance(currentAccount).mediumPreset; + highPreset = DownloadController.getInstance(currentAccount).highPreset; + + if (currentType == 0) { + currentPresetNum = DownloadController.getInstance(currentAccount).currentMobilePreset; + typePreset = DownloadController.getInstance(currentAccount).mobilePreset; + defaultPreset = mediumPreset; + key = "mobilePreset"; + key2 = "currentMobilePreset"; + } else if (currentType == 1) { + currentPresetNum = DownloadController.getInstance(currentAccount).currentWifiPreset; + typePreset = DownloadController.getInstance(currentAccount).wifiPreset; + defaultPreset = highPreset; + key = "wifiPreset"; + key2 = "currentWifiPreset"; } else { - maxSize = 1536 * 1024 * 1024; + currentPresetNum = DownloadController.getInstance(currentAccount).currentRoamingPreset; + typePreset = DownloadController.getInstance(currentAccount).roamingPreset; + defaultPreset = lowPreset; + key = "roamingPreset"; + key2 = "currentRoamingPreset"; } - - mobileDataDownloadMask = DownloadController.getInstance(currentAccount).mobileDataDownloadMask[0]; - mobileDataPrivateDownloadMask = DownloadController.getInstance(currentAccount).mobileDataDownloadMask[1]; - mobileDataGroupDownloadMask = DownloadController.getInstance(currentAccount).mobileDataDownloadMask[2]; - mobileDataChannelDownloadMask = DownloadController.getInstance(currentAccount).mobileDataDownloadMask[3]; - wifiDownloadMask = DownloadController.getInstance(currentAccount).wifiDownloadMask[0]; - wifiPrivateDownloadMask = DownloadController.getInstance(currentAccount).wifiDownloadMask[1]; - wifiGroupDownloadMask = DownloadController.getInstance(currentAccount).wifiDownloadMask[2]; - wifiChannelDownloadMask = DownloadController.getInstance(currentAccount).wifiDownloadMask[3]; - roamingDownloadMask = DownloadController.getInstance(currentAccount).roamingDownloadMask[0]; - roamingPrivateDownloadMask = DownloadController.getInstance(currentAccount).roamingDownloadMask[1]; - roamingGroupDownloadMask = DownloadController.getInstance(currentAccount).roamingDownloadMask[2]; - roamingChannelDownloadMask = DownloadController.getInstance(currentAccount).roamingDownloadMask[2]; - - mobileMaxSize = DownloadController.getInstance(currentAccount).mobileMaxFileSize[DownloadController.maskToIndex(currentType)]; - wifiMaxSize = DownloadController.getInstance(currentAccount).wifiMaxFileSize[DownloadController.maskToIndex(currentType)]; - roamingMaxSize = DownloadController.getInstance(currentAccount).roamingMaxFileSize[DownloadController.maskToIndex(currentType)]; } @Override public boolean onFragmentCreate() { super.onFragmentCreate(); - - rowCount = 0; - mobileSectionRow = rowCount++; - mContactsRow = rowCount++; - mPrivateRow = rowCount++; - mGroupRow = rowCount++; - mChannelsRow = rowCount++; - if (currentType != DownloadController.AUTODOWNLOAD_MASK_PHOTO) { - mSizeRow = rowCount++; - } else { - mSizeRow = -1; - } - mobileSection2Row = rowCount++; - wifiSectionRow = rowCount++; - wContactsRow = rowCount++; - wPrivateRow = rowCount++; - wGroupRow = rowCount++; - wChannelsRow = rowCount++; - if (currentType != DownloadController.AUTODOWNLOAD_MASK_PHOTO) { - wSizeRow = rowCount++; - } else { - wSizeRow = -1; - } - wifiSection2Row = rowCount++; - roamingSectionRow = rowCount++; - rContactsRow = rowCount++; - rPrivateRow = rowCount++; - rGroupRow = rowCount++; - rChannelsRow = rowCount++; - if (currentType != DownloadController.AUTODOWNLOAD_MASK_PHOTO) { - rSizeRow = rowCount++; - } else { - rSizeRow = -1; - } - roamingSection2Row = rowCount++; - + fillPresets(); + updateRows(); return true; } @Override public View createView(Context context) { actionBar.setBackButtonImage(R.drawable.ic_ab_back); - if (currentType == DownloadController.AUTODOWNLOAD_MASK_PHOTO) { - actionBar.setTitle(LocaleController.getString("LocalPhotoCache", R.string.LocalPhotoCache)); - } else if (currentType == DownloadController.AUTODOWNLOAD_MASK_AUDIO) { - actionBar.setTitle(LocaleController.getString("AudioAutodownload", R.string.AudioAutodownload)); - } else if (currentType == DownloadController.AUTODOWNLOAD_MASK_VIDEOMESSAGE) { - actionBar.setTitle(LocaleController.getString("VideoMessagesAutodownload", R.string.VideoMessagesAutodownload)); - } else if (currentType == DownloadController.AUTODOWNLOAD_MASK_VIDEO) { - actionBar.setTitle(LocaleController.getString("LocalVideoCache", R.string.LocalVideoCache)); - } else if (currentType == DownloadController.AUTODOWNLOAD_MASK_DOCUMENT) { - actionBar.setTitle(LocaleController.getString("FilesDataUsage", R.string.FilesDataUsage)); - } else if (currentType == DownloadController.AUTODOWNLOAD_MASK_MUSIC) { - actionBar.setTitle(LocaleController.getString("AttachMusic", R.string.AttachMusic)); - } else if (currentType == DownloadController.AUTODOWNLOAD_MASK_GIF) { - actionBar.setTitle(LocaleController.getString("LocalGifCache", R.string.LocalGifCache)); + if (currentType == 0) { + actionBar.setTitle(LocaleController.getString("AutoDownloadOnMobileData", R.string.AutoDownloadOnMobileData)); + } else if (currentType == 1) { + actionBar.setTitle(LocaleController.getString("AutoDownloadOnWiFiData", R.string.AutoDownloadOnWiFiData)); + } else if (currentType == 2) { + actionBar.setTitle(LocaleController.getString("AutoDownloadOnRoamingData", R.string.AutoDownloadOnRoamingData)); } if (AndroidUtilities.isTablet()) { actionBar.setOccupyStatusBar(false); @@ -183,42 +337,10 @@ public class DataAutoDownloadActivity extends BaseFragment { public void onItemClick(int id) { if (id == -1) { finishFragment(); - } else if (id == done_button) { - DownloadController.getInstance(currentAccount).mobileDataDownloadMask[0] = mobileDataDownloadMask; - DownloadController.getInstance(currentAccount).mobileDataDownloadMask[1] = mobileDataPrivateDownloadMask; - DownloadController.getInstance(currentAccount).mobileDataDownloadMask[2] = mobileDataGroupDownloadMask; - DownloadController.getInstance(currentAccount).mobileDataDownloadMask[3] = mobileDataChannelDownloadMask; - DownloadController.getInstance(currentAccount).wifiDownloadMask[0] = wifiDownloadMask; - DownloadController.getInstance(currentAccount).wifiDownloadMask[1] = wifiPrivateDownloadMask; - DownloadController.getInstance(currentAccount).wifiDownloadMask[2] = wifiGroupDownloadMask; - DownloadController.getInstance(currentAccount).wifiDownloadMask[3] = wifiChannelDownloadMask; - DownloadController.getInstance(currentAccount).roamingDownloadMask[0] = roamingDownloadMask; - DownloadController.getInstance(currentAccount).roamingDownloadMask[1] = roamingPrivateDownloadMask; - DownloadController.getInstance(currentAccount).roamingDownloadMask[2] = roamingGroupDownloadMask; - DownloadController.getInstance(currentAccount).roamingDownloadMask[3] = roamingChannelDownloadMask; - DownloadController.getInstance(currentAccount).mobileMaxFileSize[DownloadController.maskToIndex(currentType)] = mobileMaxSize; - DownloadController.getInstance(currentAccount).wifiMaxFileSize[DownloadController.maskToIndex(currentType)] = wifiMaxSize; - DownloadController.getInstance(currentAccount).roamingMaxFileSize[DownloadController.maskToIndex(currentType)] = roamingMaxSize; - SharedPreferences.Editor editor = MessagesController.getMainSettings(currentAccount).edit(); - for (int a = 0; a < 4; a++) { - editor.putInt("mobileDataDownloadMask" + (a != 0 ? a : ""), DownloadController.getInstance(currentAccount).mobileDataDownloadMask[a]); - editor.putInt("wifiDownloadMask" + (a != 0 ? a : ""), DownloadController.getInstance(currentAccount).wifiDownloadMask[a]); - editor.putInt("roamingDownloadMask" + (a != 0 ? a : ""), DownloadController.getInstance(currentAccount).roamingDownloadMask[a]); - } - editor.putInt("mobileMaxDownloadSize" + DownloadController.maskToIndex(currentType), mobileMaxSize); - editor.putInt("wifiMaxDownloadSize" + DownloadController.maskToIndex(currentType), wifiMaxSize); - editor.putInt("roamingMaxDownloadSize" + DownloadController.maskToIndex(currentType), roamingMaxSize); - editor.commit(); - - DownloadController.getInstance(currentAccount).checkAutodownloadSettings(); - finishFragment(); } } }); - ActionBarMenu menu = actionBar.createMenu(); - menu.addItemWithWidth(done_button, R.drawable.ic_done, AndroidUtilities.dp(56)); - listAdapter = new ListAdapter(context); fragmentView = new FrameLayout(context); @@ -227,25 +349,371 @@ public class DataAutoDownloadActivity extends BaseFragment { listView = new RecyclerListView(context); listView.setVerticalScrollBarEnabled(false); + ((DefaultItemAnimator) listView.getItemAnimator()).setDelayAnimations(false); listView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)); frameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT)); listView.setAdapter(listAdapter); - listView.setOnItemClickListener((view, position) -> { - if (!(view instanceof TextCheckBoxCell)) { - return; - } - int mask = getMaskForRow(position); - TextCheckBoxCell textCell = (TextCheckBoxCell) view; - boolean isChecked = !textCell.isChecked(); - if (isChecked) { - mask |= currentType; - } else { - mask &=~ currentType; - } - setMaskForRow(position, mask); - textCell.setChecked(isChecked); - }); + listView.setOnItemClickListener((view, position, x, y) -> { + if (position == autoDownloadRow) { + if (currentPresetNum != 3) { + if (currentPresetNum == 0) { + typePreset.set(lowPreset); + } else if (currentPresetNum == 1) { + typePreset.set(mediumPreset); + } else if (currentPresetNum == 2) { + typePreset.set(highPreset); + } + } + TextCheckCell cell = (TextCheckCell) view; + boolean checked = cell.isChecked(); + + if (!checked && typePreset.enabled) { + System.arraycopy(defaultPreset.mask, 0, typePreset.mask, 0, 4); + } else { + typePreset.enabled = !typePreset.enabled; + } + view.setTag(typePreset.enabled ? Theme.key_windowBackgroundChecked : Theme.key_windowBackgroundUnchecked); + cell.setBackgroundColorAnimated(!checked, Theme.getColor(typePreset.enabled ? Theme.key_windowBackgroundChecked : Theme.key_windowBackgroundUnchecked)); + updateRows(); + if (typePreset.enabled) { + listAdapter.notifyItemRangeInserted(autoDownloadSectionRow + 1, 8); + } else { + listAdapter.notifyItemRangeRemoved(autoDownloadSectionRow + 1, 8); + } + listAdapter.notifyItemChanged(autoDownloadSectionRow); + SharedPreferences.Editor editor = MessagesController.getMainSettings(currentAccount).edit(); + editor.putString(key, typePreset.toString()); + editor.putInt(key2, currentPresetNum = 3); + if (currentType == 0) { + DownloadController.getInstance(currentAccount).currentMobilePreset = currentPresetNum; + } else if (currentType == 1) { + DownloadController.getInstance(currentAccount).currentWifiPreset = currentPresetNum; + } else { + DownloadController.getInstance(currentAccount).currentRoamingPreset = currentPresetNum; + } + editor.commit(); + + cell.setChecked(!checked); + DownloadController.getInstance(currentAccount).checkAutodownloadSettings(); + wereAnyChanges = true; + } else if (position == photosRow || position == videosRow || position == filesRow) { + if (!view.isEnabled()) { + return; + } + int type; + if (position == photosRow) { + type = DownloadController.AUTODOWNLOAD_TYPE_PHOTO; + } else if (position == videosRow) { + type = DownloadController.AUTODOWNLOAD_TYPE_VIDEO; + } else { + type = DownloadController.AUTODOWNLOAD_TYPE_DOCUMENT; + } + int index = DownloadController.typeToIndex(type); + + DownloadController.Preset currentPreset; + String key; + String key2; + if (currentType == 0) { + currentPreset = DownloadController.getInstance(currentAccount).getCurrentMobilePreset(); + key = "mobilePreset"; + key2 = "currentMobilePreset"; + } else if (currentType == 1) { + currentPreset = DownloadController.getInstance(currentAccount).getCurrentWiFiPreset(); + key = "wifiPreset"; + key2 = "currentWifiPreset"; + } else { + currentPreset = DownloadController.getInstance(currentAccount).getCurrentRoamingPreset(); + key = "roamingPreset"; + key2 = "currentRoamingPreset"; + } + + NotificationsCheckCell cell = (NotificationsCheckCell) view; + boolean checked = cell.isChecked(); + + if (LocaleController.isRTL && x <= AndroidUtilities.dp(76) || !LocaleController.isRTL && x >= view.getMeasuredWidth() - AndroidUtilities.dp(76)) { + + if (currentPresetNum != 3) { + if (currentPresetNum == 0) { + typePreset.set(lowPreset); + } else if (currentPresetNum == 1) { + typePreset.set(mediumPreset); + } else if (currentPresetNum == 2) { + typePreset.set(highPreset); + } + } + + boolean hasAny = false; + for (int a = 0; a < typePreset.mask.length; a++) { + if ((currentPreset.mask[a] & type) != 0) { + hasAny = true; + break; + } + } + for (int a = 0; a < typePreset.mask.length; a++) { + if (checked) { + typePreset.mask[a] &=~ type; + } else if (!hasAny) { + typePreset.mask[a] |= type; + } + } + + SharedPreferences.Editor editor = MessagesController.getMainSettings(currentAccount).edit(); + editor.putString(key, typePreset.toString()); + editor.putInt(key2, currentPresetNum = 3); + if (currentType == 0) { + DownloadController.getInstance(currentAccount).currentMobilePreset = currentPresetNum; + } else if (currentType == 1) { + DownloadController.getInstance(currentAccount).currentWifiPreset = currentPresetNum; + } else { + DownloadController.getInstance(currentAccount).currentRoamingPreset = currentPresetNum; + } + editor.commit(); + + cell.setChecked(!checked); + RecyclerView.ViewHolder holder = listView.findContainingViewHolder(view); + if (holder != null) { + listAdapter.onBindViewHolder(holder, position); + } + DownloadController.getInstance(currentAccount).checkAutodownloadSettings(); + wereAnyChanges = true; + fillPresets(); + } else { + if (getParentActivity() == null) { + return; + } + BottomSheet.Builder builder = new BottomSheet.Builder(getParentActivity()); + builder.setApplyTopPadding(false); + builder.setApplyBottomPadding(false); + LinearLayout linearLayout = new LinearLayout(getParentActivity()); + linearLayout.setOrientation(LinearLayout.VERTICAL); + builder.setCustomView(linearLayout); + + HeaderCell headerCell = new HeaderCell(getParentActivity(), true, 21, 15, false); + if (position == photosRow) { + headerCell.setText(LocaleController.getString("AutoDownloadPhotosTitle", R.string.AutoDownloadPhotosTitle)); + } else if (position == videosRow) { + headerCell.setText(LocaleController.getString("AutoDownloadVideosTitle", R.string.AutoDownloadVideosTitle)); + } else { + headerCell.setText(LocaleController.getString("AutoDownloadFilesTitle", R.string.AutoDownloadFilesTitle)); + } + linearLayout.addView(headerCell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + final MaxFileSizeCell[] sizeCell = new MaxFileSizeCell[1]; + final TextCheckCell[] checkCell = new TextCheckCell[1]; + final AnimatorSet[] animatorSet = new AnimatorSet[1]; + + TextCheckBoxCell[] cells = new TextCheckBoxCell[4]; + for (int a = 0; a < 4; a++) { + TextCheckBoxCell checkBoxCell = cells[a] = new TextCheckBoxCell(getParentActivity(), true); + if (a == 0) { + cells[a].setTextAndCheck(LocaleController.getString("AutodownloadContacts", R.string.AutodownloadContacts), (currentPreset.mask[DownloadController.PRESET_NUM_CONTACT] & type) != 0, true); + } else if (a == 1) { + cells[a].setTextAndCheck(LocaleController.getString("AutodownloadPrivateChats", R.string.AutodownloadPrivateChats), (currentPreset.mask[DownloadController.PRESET_NUM_PM] & type) != 0, true); + } else if (a == 2) { + cells[a].setTextAndCheck(LocaleController.getString("AutodownloadGroupChats", R.string.AutodownloadGroupChats), (currentPreset.mask[DownloadController.PRESET_NUM_GROUP] & type) != 0, true); + } else if (a == 3) { + cells[a].setTextAndCheck(LocaleController.getString("AutodownloadChannels", R.string.AutodownloadChannels), (currentPreset.mask[DownloadController.PRESET_NUM_CHANNEL] & type) != 0, position != photosRow); + } + cells[a].setBackgroundDrawable(Theme.getSelectorDrawable(false)); + cells[a].setOnClickListener(v -> { + if (!v.isEnabled()) { + return; + } + checkBoxCell.setChecked(!checkBoxCell.isChecked()); + boolean hasAny = false; + for (int b = 0; b < cells.length; b++) { + if (cells[b].isChecked()) { + hasAny = true; + break; + } + } + if (position == videosRow && sizeCell[0].isEnabled() != hasAny) { + ArrayList animators = new ArrayList<>(); + sizeCell[0].setEnabled(hasAny, animators); + if (sizeCell[0].getSize() > 2 * 1024 * 1024) { + checkCell[0].setEnabled(hasAny, animators); + } + + if (animatorSet[0] != null) { + animatorSet[0].cancel(); + animatorSet[0] = null; + } + animatorSet[0] = new AnimatorSet(); + animatorSet[0].playTogether(animators); + animatorSet[0].addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animator) { + if (animator.equals(animatorSet[0])) { + animatorSet[0] = null; + } + } + }); + animatorSet[0].setDuration(150); + animatorSet[0].start(); + } + }); + linearLayout.addView(cells[a], LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 50)); + } + + if (position != photosRow) { + TextInfoPrivacyCell infoCell = new TextInfoPrivacyCell(getParentActivity()); + + sizeCell[0] = new MaxFileSizeCell(getParentActivity()) { + @Override + protected void didChangedSizeValue(int value) { + if (position == videosRow) { + infoCell.setText(LocaleController.formatString("AutoDownloadPreloadVideoInfo", R.string.AutoDownloadPreloadVideoInfo, AndroidUtilities.formatFileSize(value))); + boolean enabled = value > 2 * 1024 * 1024; + if (enabled != checkCell[0].isEnabled()) { + ArrayList animators = new ArrayList<>(); + checkCell[0].setEnabled(enabled, animators); + + if (animatorSet[0] != null) { + animatorSet[0].cancel(); + animatorSet[0] = null; + } + animatorSet[0] = new AnimatorSet(); + animatorSet[0].playTogether(animators); + animatorSet[0].addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animator) { + if (animator.equals(animatorSet[0])) { + animatorSet[0] = null; + } + } + }); + animatorSet[0].setDuration(150); + animatorSet[0].start(); + } + } + } + }; + sizeCell[0].setSize(currentPreset.sizes[index]); + linearLayout.addView(sizeCell[0], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 50)); + + checkCell[0] = new TextCheckCell(getParentActivity(), 21, true); + linearLayout.addView(checkCell[0], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); + checkCell[0].setOnClickListener(v -> checkCell[0].setChecked(!checkCell[0].isChecked())); + + Drawable drawable = Theme.getThemedDrawable(getParentActivity(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow); + CombinedDrawable combinedDrawable = new CombinedDrawable(new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundGray)), drawable); + combinedDrawable.setFullsize(true); + infoCell.setBackgroundDrawable(combinedDrawable); + linearLayout.addView(infoCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + if (position == videosRow) { + sizeCell[0].setText(LocaleController.getString("AutoDownloadMaxVideoSize", R.string.AutoDownloadMaxVideoSize)); + checkCell[0].setTextAndCheck(LocaleController.getString("AutoDownloadPreloadVideo", R.string.AutoDownloadPreloadVideo), currentPreset.preloadVideo, false); + infoCell.setText(LocaleController.formatString("AutoDownloadPreloadVideoInfo", R.string.AutoDownloadPreloadVideoInfo, AndroidUtilities.formatFileSize(currentPreset.sizes[index]))); + } else { + sizeCell[0].setText(LocaleController.getString("AutoDownloadMaxFileSize", R.string.AutoDownloadMaxFileSize)); + checkCell[0].setTextAndCheck(LocaleController.getString("AutoDownloadPreloadMusic", R.string.AutoDownloadPreloadMusic), currentPreset.preloadMusic, false); + infoCell.setText(LocaleController.getString("AutoDownloadPreloadMusicInfo", R.string.AutoDownloadPreloadMusicInfo)); + } + } else { + sizeCell[0] = null; + checkCell[0] = null; + + View divider = new View(getParentActivity()); + divider.setBackgroundColor(Theme.getColor(Theme.key_divider)); + linearLayout.addView(divider, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1)); + } + if (position == videosRow) { + boolean hasAny = false; + for (int b = 0; b < cells.length; b++) { + if (cells[b].isChecked()) { + hasAny = true; + break; + } + } + if (!hasAny) { + sizeCell[0].setEnabled(hasAny, null); + checkCell[0].setEnabled(hasAny, null); + } + if (currentPreset.sizes[index] <= 2 * 1024 * 1024) { + checkCell[0].setEnabled(false, null); + } + } + + FrameLayout buttonsLayout = new FrameLayout(getParentActivity()); + buttonsLayout.setPadding(AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8)); + linearLayout.addView(buttonsLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 52)); + + TextView textView = new TextView(getParentActivity()); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + textView.setTextColor(Theme.getColor(Theme.key_dialogTextBlue2)); + textView.setGravity(Gravity.CENTER); + textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + textView.setText(LocaleController.getString("Cancel", R.string.Cancel).toUpperCase()); + textView.setPadding(AndroidUtilities.dp(10), 0, AndroidUtilities.dp(10), 0); + buttonsLayout.addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 36, Gravity.TOP | Gravity.LEFT)); + textView.setOnClickListener(v14 -> builder.getDismissRunnable().run()); + + textView = new TextView(getParentActivity()); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + textView.setTextColor(Theme.getColor(Theme.key_dialogTextBlue2)); + textView.setGravity(Gravity.CENTER); + textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + textView.setText(LocaleController.getString("Save", R.string.Save).toUpperCase()); + textView.setPadding(AndroidUtilities.dp(10), 0, AndroidUtilities.dp(10), 0); + buttonsLayout.addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 36, Gravity.TOP | Gravity.RIGHT)); + textView.setOnClickListener(v1 -> { + if (currentPresetNum != 3) { + if (currentPresetNum == 0) { + typePreset.set(lowPreset); + } else if (currentPresetNum == 1) { + typePreset.set(mediumPreset); + } else if (currentPresetNum == 2) { + typePreset.set(highPreset); + } + } + + for (int a = 0; a < 4; a++) { + if (cells[a].isChecked()) { + typePreset.mask[a] |= type; + } else { + typePreset.mask[a] &= ~type; + } + } + if (sizeCell[0] != null) { + int size = (int) sizeCell[0].getSize(); + typePreset.sizes[index] = (int) sizeCell[0].getSize(); + } + if (checkCell[0] != null) { + if (position == videosRow) { + typePreset.preloadVideo = checkCell[0].isChecked(); + } else { + typePreset.preloadMusic = checkCell[0].isChecked(); + } + } + SharedPreferences.Editor editor = MessagesController.getMainSettings(currentAccount).edit(); + editor.putString(key, typePreset.toString()); + editor.putInt(key2, currentPresetNum = 3); + if (currentType == 0) { + DownloadController.getInstance(currentAccount).currentMobilePreset = currentPresetNum; + } else if (currentType == 1) { + DownloadController.getInstance(currentAccount).currentWifiPreset = currentPresetNum; + } else { + DownloadController.getInstance(currentAccount).currentRoamingPreset = currentPresetNum; + } + editor.commit(); + builder.getDismissRunnable().run(); + + RecyclerView.ViewHolder holder = listView.findContainingViewHolder(view); + if (holder != null) { + animateChecked = true; + listAdapter.onBindViewHolder(holder, position); + animateChecked = false; + } + DownloadController.getInstance(currentAccount).checkAutodownloadSettings(); + wereAnyChanges = true; + fillPresets(); + }); + showDialog(builder.create()); + } + } + }); return fragmentView; } @@ -257,60 +725,102 @@ public class DataAutoDownloadActivity extends BaseFragment { } } - private int getMaskForRow(int position) { - if (position == mContactsRow) { - return mobileDataDownloadMask; - } else if (position == mPrivateRow) { - return mobileDataPrivateDownloadMask; - } else if (position == mGroupRow) { - return mobileDataGroupDownloadMask; - } else if (position == mChannelsRow) { - return mobileDataChannelDownloadMask; - } else if (position == wContactsRow) { - return wifiDownloadMask; - } else if (position == wPrivateRow) { - return wifiPrivateDownloadMask; - } else if (position == wGroupRow) { - return wifiGroupDownloadMask; - } else if (position == wChannelsRow) { - return wifiChannelDownloadMask; - } else if (position == rContactsRow) { - return roamingDownloadMask; - } else if (position == rPrivateRow) { - return roamingPrivateDownloadMask; - } else if (position == rGroupRow) { - return roamingGroupDownloadMask; - } else if (position == rChannelsRow) { - return roamingChannelDownloadMask; + @Override + public void onPause() { + super.onPause(); + if (wereAnyChanges) { + DownloadController.getInstance(currentAccount).savePresetToServer(currentType); + wereAnyChanges = false; } - return 0; } - private void setMaskForRow(int position, int mask) { - if (position == mContactsRow) { - mobileDataDownloadMask = mask; - } else if (position == mPrivateRow) { - mobileDataPrivateDownloadMask = mask; - } else if (position == mGroupRow) { - mobileDataGroupDownloadMask = mask; - } else if (position == mChannelsRow) { - mobileDataChannelDownloadMask = mask; - } else if (position == wContactsRow) { - wifiDownloadMask = mask; - } else if (position == wPrivateRow) { - wifiPrivateDownloadMask = mask; - } else if (position == wGroupRow) { - wifiGroupDownloadMask = mask; - } else if (position == wChannelsRow) { - wifiChannelDownloadMask = mask; - } else if (position == rContactsRow) { - roamingDownloadMask = mask; - } else if (position == rPrivateRow) { - roamingPrivateDownloadMask = mask; - } else if (position == rGroupRow) { - roamingGroupDownloadMask = mask; - } else if (position == rChannelsRow) { - roamingChannelDownloadMask = mask; + private void fillPresets() { + presets.clear(); + presets.add(lowPreset); + presets.add(mediumPreset); + presets.add(highPreset); + if (!typePreset.equals(lowPreset) && !typePreset.equals(mediumPreset) && !typePreset.equals(highPreset)) { + presets.add(typePreset); + } + Collections.sort(presets, (o1, o2) -> { + int index1 = DownloadController.typeToIndex(DownloadController.AUTODOWNLOAD_TYPE_VIDEO); + int index2 = DownloadController.typeToIndex(DownloadController.AUTODOWNLOAD_TYPE_DOCUMENT); + boolean video1 = false; + boolean doc1 = false; + for (int a = 0; a < o1.mask.length; a++) { + if ((o1.mask[a] & DownloadController.AUTODOWNLOAD_TYPE_VIDEO) != 0) { + video1 = true; + } + if ((o1.mask[a] & DownloadController.AUTODOWNLOAD_TYPE_DOCUMENT) != 0) { + doc1 = true; + } + if (video1 && doc1) { + break; + } + } + boolean video2 = false; + boolean doc2 = false; + for (int a = 0; a < o2.mask.length; a++) { + if ((o2.mask[a] & DownloadController.AUTODOWNLOAD_TYPE_VIDEO) != 0) { + video2 = true; + } + if ((o2.mask[a] & DownloadController.AUTODOWNLOAD_TYPE_DOCUMENT) != 0) { + doc2 = true; + } + if (video2 && doc2) { + break; + } + } + int size1 = (video1 ? o1.sizes[index1] : 0) + (doc1 ? o1.sizes[index2] : 0); + int size2 = (video2 ? o2.sizes[index1] : 0) + (doc2 ? o2.sizes[index2] : 0); + if (size1 > size2) { + return 1; + } else if (size1 < size2) { + return -1; + } + return 0; + }); + if (listView != null) { + RecyclerView.ViewHolder holder = listView.findViewHolderForAdapterPosition(usageProgressRow); + if (holder != null) { + holder.itemView.requestLayout(); + } else { + listAdapter.notifyItemChanged(usageProgressRow); + } + } + if (currentPresetNum == 0 || currentPresetNum == 3 && typePreset.equals(lowPreset)) { + selectedPreset = presets.indexOf(lowPreset); + } else if (currentPresetNum == 1 || currentPresetNum == 3 && typePreset.equals(mediumPreset)) { + selectedPreset = presets.indexOf(mediumPreset); + } else if (currentPresetNum == 2 || currentPresetNum == 3 && typePreset.equals(highPreset)) { + selectedPreset = presets.indexOf(highPreset); + } else { + selectedPreset = presets.indexOf(typePreset); + } + } + + private void updateRows() { + rowCount = 0; + autoDownloadRow = rowCount++; + autoDownloadSectionRow = rowCount++; + if (typePreset.enabled) { + usageHeaderRow = rowCount++; + usageProgressRow = rowCount++; + usageSectionRow = rowCount++; + typeHeaderRow = rowCount++; + photosRow = rowCount++; + videosRow = rowCount++; + filesRow = rowCount++; + typeSectionRow = rowCount++; + } else { + usageHeaderRow = -1; + usageProgressRow = -1; + usageSectionRow = -1; + typeHeaderRow = -1; + photosRow = -1; + videosRow = -1; + filesRow = -1; + typeSectionRow = -1; } } @@ -331,91 +841,162 @@ public class DataAutoDownloadActivity extends BaseFragment { public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { switch (holder.getItemViewType()) { case 0: { - if (position == mobileSection2Row || position == wifiSection2Row) { - holder.itemView.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); - } else { - holder.itemView.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); - } - break; - } - case 1: { - TextCheckBoxCell textCell = (TextCheckBoxCell) holder.itemView; - if (position == mContactsRow || position == wContactsRow || position == rContactsRow) { - textCell.setTextAndCheck(LocaleController.getString("AutodownloadContacts", R.string.AutodownloadContacts), (getMaskForRow(position) & currentType) != 0, true); - } else if (position == mPrivateRow || position == wPrivateRow || position == rPrivateRow) { - textCell.setTextAndCheck(LocaleController.getString("AutodownloadPrivateChats", R.string.AutodownloadPrivateChats), (getMaskForRow(position) & currentType) != 0, true); - } else if (position == mChannelsRow || position == wChannelsRow || position == rChannelsRow) { - textCell.setTextAndCheck(LocaleController.getString("AutodownloadChannels", R.string.AutodownloadChannels), (getMaskForRow(position) & currentType) != 0, mSizeRow != -1); - } else if (position == mGroupRow || position == wGroupRow || position == rGroupRow) { - textCell.setTextAndCheck(LocaleController.getString("AutodownloadGroupChats", R.string.AutodownloadGroupChats), (getMaskForRow(position) & currentType) != 0, true); + TextCheckCell view = (TextCheckCell) holder.itemView; + if (position == autoDownloadRow) { + view.setDrawCheckRipple(true); + view.setTextAndCheck(LocaleController.getString("AutoDownloadMedia", R.string.AutoDownloadMedia), typePreset.enabled, false); + view.setTag(typePreset.enabled ? Theme.key_windowBackgroundChecked : Theme.key_windowBackgroundUnchecked); + view.setBackgroundColor(Theme.getColor(typePreset.enabled ? Theme.key_windowBackgroundChecked : Theme.key_windowBackgroundUnchecked)); } break; } case 2: { - HeaderCell headerCell = (HeaderCell) holder.itemView; - if (position == mobileSectionRow) { - headerCell.setText(LocaleController.getString("WhenUsingMobileData", R.string.WhenUsingMobileData)); - } else if (position == wifiSectionRow) { - headerCell.setText(LocaleController.getString("WhenConnectedOnWiFi", R.string.WhenConnectedOnWiFi)); - } else if (position == roamingSectionRow) { - headerCell.setText(LocaleController.getString("WhenRoaming", R.string.WhenRoaming)); + HeaderCell view = (HeaderCell) holder.itemView; + if (position == usageHeaderRow) { + view.setText(LocaleController.getString("AutoDownloadDataUsage", R.string.AutoDownloadDataUsage)); + } else if (position == typeHeaderRow) { + view.setText(LocaleController.getString("AutoDownloadTypes", R.string.AutoDownloadTypes)); } break; } - case 3: { - MaxFileSizeCell cell = (MaxFileSizeCell) holder.itemView; - if (position == mSizeRow) { - cell.setSize(mobileMaxSize, maxSize); - cell.setTag(0); - } else if (position == wSizeRow) { - cell.setSize(wifiMaxSize, maxSize); - cell.setTag(1); - } else if (position == rSizeRow) { - cell.setSize(roamingMaxSize, maxSize); - cell.setTag(2); + case 4: { + NotificationsCheckCell view = (NotificationsCheckCell) holder.itemView; + DownloadController.Preset preset; + String text; + int type; + if (position == photosRow) { + text = LocaleController.getString("AutoDownloadPhotos", R.string.AutoDownloadPhotos); + type = DownloadController.AUTODOWNLOAD_TYPE_PHOTO; + } else if (position == videosRow) { + text = LocaleController.getString("AutoDownloadVideos", R.string.AutoDownloadVideos); + type = DownloadController.AUTODOWNLOAD_TYPE_VIDEO; + } else { + text = LocaleController.getString("AutoDownloadFiles", R.string.AutoDownloadFiles); + type = DownloadController.AUTODOWNLOAD_TYPE_DOCUMENT; } + if (currentType == 0) { + preset = DownloadController.getInstance(currentAccount).getCurrentMobilePreset(); + } else if (currentType == 1) { + preset = DownloadController.getInstance(currentAccount).getCurrentWiFiPreset(); + } else { + preset = DownloadController.getInstance(currentAccount).getCurrentRoamingPreset(); + } + int maxSize = preset.sizes[DownloadController.typeToIndex(type)]; + + int count = 0; + StringBuilder builder = new StringBuilder(); + for (int a = 0; a < preset.mask.length; a++) { + if ((preset.mask[a] & type) != 0) { + if (builder.length() != 0) { + builder.append(", "); + } + switch (a) { + case 0: + builder.append(LocaleController.getString("AutoDownloadContacts", R.string.AutoDownloadContacts)); + break; + case 1: + builder.append(LocaleController.getString("AutoDownloadPm", R.string.AutoDownloadPm)); + break; + case 2: + builder.append(LocaleController.getString("AutoDownloadGroups", R.string.AutoDownloadGroups)); + break; + case 3: + builder.append(LocaleController.getString("AutoDownloadChannels", R.string.AutoDownloadChannels)); + break; + } + count++; + } + } + if (count == 4) { + builder.setLength(0); + if (position == photosRow) { + builder.append(LocaleController.getString("AutoDownloadOnAllChats", R.string.AutoDownloadOnAllChats)); + } else { + builder.append(LocaleController.formatString("AutoDownloadUpToOnAllChats", R.string.AutoDownloadUpToOnAllChats, AndroidUtilities.formatFileSize(maxSize))); + } + } else if (count == 0) { + builder.append(LocaleController.getString("AutoDownloadOff", R.string.AutoDownloadOff)); + } else { + if (position == photosRow) { + builder = new StringBuilder(LocaleController.formatString("AutoDownloadOnFor", R.string.AutoDownloadOnFor, builder.toString())); + } else { + builder = new StringBuilder(LocaleController.formatString("AutoDownloadOnUpToFor", R.string.AutoDownloadOnUpToFor, AndroidUtilities.formatFileSize(maxSize), builder.toString())); + } + } + if (animateChecked) { + view.setChecked(count != 0); + } + view.setTextAndValueAndCheck(text, builder, count != 0, 0, true, position != filesRow); break; } + case 5: { + TextInfoPrivacyCell view = (TextInfoPrivacyCell) holder.itemView; + if (position == typeSectionRow) { + view.setText(LocaleController.getString("AutoDownloadAudioInfo", R.string.AutoDownloadAudioInfo)); + view.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + view.setFixedSize(0); + } else if (position == autoDownloadSectionRow) { + if (usageHeaderRow == -1) { + view.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + if (currentType == 0) { + view.setText(LocaleController.getString("AutoDownloadOnMobileDataInfo", R.string.AutoDownloadOnMobileDataInfo)); + } else if (currentType == 1) { + view.setText(LocaleController.getString("AutoDownloadOnWiFiDataInfo", R.string.AutoDownloadOnWiFiDataInfo)); + } else if (currentType == 2) { + view.setText(LocaleController.getString("AutoDownloadOnRoamingDataInfo", R.string.AutoDownloadOnRoamingDataInfo)); + } + } else { + view.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + view.setText(null); + view.setFixedSize(12); + } + break; + } + } } } @Override public boolean isEnabled(RecyclerView.ViewHolder holder) { int position = holder.getAdapterPosition(); - return position != mSizeRow && position != rSizeRow && position != wSizeRow && position != mobileSectionRow && position != wifiSectionRow && position != roamingSectionRow && position != mobileSection2Row && position != wifiSection2Row && position != roamingSection2Row; + return position == photosRow || position == videosRow || position == filesRow; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = null; switch (viewType) { - case 0: + case 0: { + TextCheckCell cell = new TextCheckCell(mContext); + cell.setColors(Theme.key_windowBackgroundCheckText, Theme.key_switchTrackBlue, Theme.key_switchTrackBlueChecked, Theme.key_switchTrackBlueThumb, Theme.key_switchTrackBlueThumbChecked); + cell.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + cell.setHeight(56); + view = cell; + break; + } + case 1: { view = new ShadowSectionCell(mContext); break; - case 1: - view = new TextCheckBoxCell(mContext); - view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - break; - case 2: + } + case 2: { view = new HeaderCell(mContext); view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; - case 3: - view = new MaxFileSizeCell(mContext) { - @Override - protected void didChangedSizeValue(int value) { - Integer tag = (Integer) getTag(); - if (tag == 0) { - mobileMaxSize = value; - } else if (tag == 1) { - wifiMaxSize = value; - } else if (tag == 2) { - roamingMaxSize = value; - } - } - }; + } + case 3: { + view = new PresetChooseView(mContext); view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; + } + case 4: { + view = new NotificationsCheckCell(mContext); + view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + break; + } + case 5: { + view = new TextInfoPrivacyCell(mContext); + view.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + } } view.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT)); return new RecyclerListView.Holder(view); @@ -423,14 +1004,18 @@ public class DataAutoDownloadActivity extends BaseFragment { @Override public int getItemViewType(int position) { - if (position == mobileSection2Row || position == wifiSection2Row || position == roamingSection2Row) { + if (position == autoDownloadRow) { return 0; - } else if (position == mobileSectionRow || position == wifiSectionRow || position == roamingSectionRow) { - return 2; - } else if (position == wSizeRow || position == mSizeRow || position == rSizeRow) { - return 3; - } else { + } else if (position == usageSectionRow) { return 1; + } else if (position == usageHeaderRow || position == typeHeaderRow) { + return 2; + } else if (position == usageProgressRow) { + return 3; + } else if (position == photosRow || position == videosRow || position == filesRow) { + return 4; + } else { + return 5; } } } @@ -438,7 +1023,7 @@ public class DataAutoDownloadActivity extends BaseFragment { @Override public ThemeDescription[] getThemeDescriptions() { return new ThemeDescription[]{ - new ThemeDescription(listView, ThemeDescription.FLAG_CELLBACKGROUNDCOLOR, new Class[]{TextCheckBoxCell.class, MaxFileSizeCell.class, HeaderCell.class}, null, null, null, Theme.key_windowBackgroundWhite), + new ThemeDescription(listView, ThemeDescription.FLAG_CELLBACKGROUNDCOLOR, new Class[]{HeaderCell.class, NotificationsCheckCell.class, PresetChooseView.class}, null, null, null, Theme.key_windowBackgroundWhite), new ThemeDescription(fragmentView, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundGray), new ThemeDescription(actionBar, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_actionBarDefault), @@ -453,16 +1038,29 @@ public class DataAutoDownloadActivity extends BaseFragment { new ThemeDescription(listView, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{ShadowSectionCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow), - new ThemeDescription(listView, 0, new Class[]{MaxFileSizeCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText), - new ThemeDescription(listView, 0, new Class[]{MaxFileSizeCell.class}, new String[]{"sizeTextView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText), - - new ThemeDescription(listView, 0, new Class[]{TextCheckBoxCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText), - new ThemeDescription(listView, 0, new Class[]{TextCheckBoxCell.class}, null, null, null, Theme.key_checkboxSquareUnchecked), - new ThemeDescription(listView, 0, new Class[]{TextCheckBoxCell.class}, null, null, null, Theme.key_checkboxSquareDisabled), - new ThemeDescription(listView, 0, new Class[]{TextCheckBoxCell.class}, null, null, null, Theme.key_checkboxSquareBackground), - new ThemeDescription(listView, 0, new Class[]{TextCheckBoxCell.class}, null, null, null, Theme.key_checkboxSquareCheck), - new ThemeDescription(listView, 0, new Class[]{HeaderCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueHeader), + + new ThemeDescription(listView, ThemeDescription.FLAG_CELLBACKGROUNDCOLOR | ThemeDescription.FLAG_CHECKTAG, new Class[]{TextCheckCell.class}, null, null, null, Theme.key_windowBackgroundChecked), + new ThemeDescription(listView, ThemeDescription.FLAG_CELLBACKGROUNDCOLOR | ThemeDescription.FLAG_CHECKTAG, new Class[]{TextCheckCell.class}, null, null, null, Theme.key_windowBackgroundUnchecked), + new ThemeDescription(listView, 0, new Class[]{TextCheckCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundCheckText), + new ThemeDescription(listView, 0, new Class[]{TextCheckCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_switchTrackBlue), + new ThemeDescription(listView, 0, new Class[]{TextCheckCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_switchTrackBlueChecked), + new ThemeDescription(listView, 0, new Class[]{TextCheckCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_switchTrackBlueThumb), + new ThemeDescription(listView, 0, new Class[]{TextCheckCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_switchTrackBlueThumbChecked), + new ThemeDescription(listView, 0, new Class[]{TextCheckCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_switchTrackBlueSelector), + new ThemeDescription(listView, 0, new Class[]{TextCheckCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_switchTrackBlueSelectorChecked), + + new ThemeDescription(listView, 0, new Class[]{NotificationsCheckCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText), + new ThemeDescription(listView, 0, new Class[]{NotificationsCheckCell.class}, new String[]{"valueTextView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText2), + new ThemeDescription(listView, 0, new Class[]{NotificationsCheckCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_switchTrack), + new ThemeDescription(listView, 0, new Class[]{NotificationsCheckCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_switchTrackChecked), + + new ThemeDescription(listView, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{TextInfoPrivacyCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow), + new ThemeDescription(listView, 0, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText4), + + new ThemeDescription(listView, 0, new Class[]{PresetChooseView.class}, null, null, null, Theme.key_switchTrack), + new ThemeDescription(listView, 0, new Class[]{PresetChooseView.class}, null, null, null, Theme.key_switchTrackChecked), + new ThemeDescription(listView, 0, new Class[]{PresetChooseView.class}, null, null, null, Theme.key_windowBackgroundWhiteGrayText), }; } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DataSettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/DataSettingsActivity.java index 28667e474..ad37f82c8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DataSettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DataSettingsActivity.java @@ -8,8 +8,6 @@ package org.telegram.ui; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.app.Dialog; import android.content.Context; @@ -35,6 +33,7 @@ import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.ActionBar.ThemeDescription; import org.telegram.ui.Cells.HeaderCell; +import org.telegram.ui.Cells.NotificationsCheckCell; import org.telegram.ui.Cells.ShadowSectionCell; import org.telegram.ui.Cells.TextCheckCell; import org.telegram.ui.Cells.TextInfoPrivacyCell; @@ -42,8 +41,7 @@ import org.telegram.ui.Cells.TextSettingsCell; import org.telegram.ui.Components.AlertsCreator; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.RecyclerListView; - -import java.util.ArrayList; +import org.telegram.ui.Components.voip.VoIPHelper; public class DataSettingsActivity extends BaseFragment { @@ -52,27 +50,25 @@ public class DataSettingsActivity extends BaseFragment { private AnimatorSet animatorSet; private int mediaDownloadSectionRow; - private int autoDownloadMediaRow; - private int photosRow; - private int voiceMessagesRow; - private int videoMessagesRow; - private int videosRow; - private int filesRow; - private int musicRow; - private int gifsRow; + private int mobileRow; + private int roamingRow; + private int wifiRow; private int resetDownloadRow; private int mediaDownloadSection2Row; private int usageSectionRow; private int storageUsageRow; - private int mobileUsageRow; - private int wifiUsageRow; - private int roamingUsageRow; + private int dataUsageRow; private int usageSection2Row; private int streamSectionRow; private int enableStreamRow; private int enableCacheStreamRow; private int enableAllStreamRow; + private int enableMkvRow; private int enableAllStreamInfoRow; + private int autoplayHeaderRow; + private int autoplayGifsRow; + private int autoplayVideoRow; + private int autoplaySectionRow; private int callsSectionRow; private int useLessDataForCallsRow; private int quickRepliesRow; @@ -86,30 +82,31 @@ public class DataSettingsActivity extends BaseFragment { public boolean onFragmentCreate() { super.onFragmentCreate(); + DownloadController.getInstance(currentAccount).loadAutoDownloadConfig(true); + rowCount = 0; usageSectionRow = rowCount++; storageUsageRow = rowCount++; - mobileUsageRow = rowCount++; - wifiUsageRow = rowCount++; - roamingUsageRow = rowCount++; + dataUsageRow = rowCount++; usageSection2Row = rowCount++; mediaDownloadSectionRow = rowCount++; - autoDownloadMediaRow = rowCount++; - photosRow = rowCount++; - voiceMessagesRow = rowCount++; - videoMessagesRow = rowCount++; - videosRow = rowCount++; - filesRow = rowCount++; - musicRow = rowCount++; - gifsRow = rowCount++; + mobileRow = rowCount++; + wifiRow = rowCount++; + roamingRow = rowCount++; resetDownloadRow = rowCount++; mediaDownloadSection2Row = rowCount++; + autoplayHeaderRow = rowCount++; + autoplayGifsRow = rowCount++; + autoplayVideoRow = rowCount++; + autoplaySectionRow = rowCount++; streamSectionRow = rowCount++; enableStreamRow = rowCount++; if (BuildVars.DEBUG_VERSION) { + enableMkvRow = rowCount++; enableAllStreamRow = rowCount++; } else { enableAllStreamRow = -1; + enableMkvRow = -1; } enableAllStreamInfoRow = rowCount++; enableCacheStreamRow = -1;//rowCount++; @@ -152,95 +149,143 @@ public class DataSettingsActivity extends BaseFragment { listView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)); frameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT)); listView.setAdapter(listAdapter); - listView.setOnItemClickListener((view, position) -> { - if (position == photosRow || position == voiceMessagesRow || position == videoMessagesRow || position == videosRow || position == filesRow || position == musicRow || position == gifsRow) { - if (!DownloadController.getInstance(currentAccount).globalAutodownloadEnabled) { - return; - } - if (position == photosRow) { - presentFragment(new DataAutoDownloadActivity(DownloadController.AUTODOWNLOAD_MASK_PHOTO)); - } else if (position == voiceMessagesRow) { - presentFragment(new DataAutoDownloadActivity(DownloadController.AUTODOWNLOAD_MASK_AUDIO)); - } else if (position == videoMessagesRow) { - presentFragment(new DataAutoDownloadActivity(DownloadController.AUTODOWNLOAD_MASK_VIDEOMESSAGE)); - } else if (position == videosRow) { - presentFragment(new DataAutoDownloadActivity(DownloadController.AUTODOWNLOAD_MASK_VIDEO)); - } else if (position == filesRow) { - presentFragment(new DataAutoDownloadActivity(DownloadController.AUTODOWNLOAD_MASK_DOCUMENT)); - } else if (position == musicRow) { - presentFragment(new DataAutoDownloadActivity(DownloadController.AUTODOWNLOAD_MASK_MUSIC)); - } else if (position == gifsRow) { - presentFragment(new DataAutoDownloadActivity(DownloadController.AUTODOWNLOAD_MASK_GIF)); + listView.setOnItemClickListener((view, position, x, y) -> { + if (position == mobileRow || position == roamingRow || position == wifiRow) { + if (LocaleController.isRTL && x <= AndroidUtilities.dp(76) || !LocaleController.isRTL && x >= view.getMeasuredWidth() - AndroidUtilities.dp(76)) { + NotificationsCheckCell cell = (NotificationsCheckCell) view; + boolean checked = cell.isChecked(); + + DownloadController.Preset preset; + DownloadController.Preset defaultPreset; + String key; + String key2; + int num; + if (position == mobileRow) { + preset = DownloadController.getInstance(currentAccount).mobilePreset; + defaultPreset = DownloadController.getInstance(currentAccount).mediumPreset; + key = "mobilePreset"; + key2 = "currentMobilePreset"; + num = 0; + } else if (position == wifiRow) { + preset = DownloadController.getInstance(currentAccount).wifiPreset; + defaultPreset = DownloadController.getInstance(currentAccount).highPreset; + key = "wifiPreset"; + key2 = "currentWifiPreset"; + num = 1; + } else { + preset = DownloadController.getInstance(currentAccount).roamingPreset; + defaultPreset = DownloadController.getInstance(currentAccount).lowPreset; + key = "roamingPreset"; + key2 = "currentRoamingPreset"; + num = 2; + } + if (!checked && preset.enabled) { + preset.set(defaultPreset); + } else { + preset.enabled = !preset.enabled; + } + SharedPreferences.Editor editor = MessagesController.getMainSettings(currentAccount).edit(); + editor.putString(key, preset.toString()); + editor.putInt(key2, 3); + editor.commit(); + + cell.setChecked(!checked); + RecyclerView.ViewHolder holder = listView.findContainingViewHolder(view); + if (holder != null) { + listAdapter.onBindViewHolder(holder, position); + } + DownloadController.getInstance(currentAccount).checkAutodownloadSettings(); + DownloadController.getInstance(currentAccount).savePresetToServer(num); + } else { + int type; + if (position == mobileRow) { + type = 0; + } else if (position == wifiRow) { + type = 1; + } else { + type = 2; + } + presentFragment(new DataAutoDownloadActivity(type)); } } else if (position == resetDownloadRow) { - if (getParentActivity() == null) { + if (getParentActivity() == null || !view.isEnabled()) { return; } AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); builder.setMessage(LocaleController.getString("ResetAutomaticMediaDownloadAlert", R.string.ResetAutomaticMediaDownloadAlert)); builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), (dialogInterface, i) -> { + DownloadController.Preset preset; + DownloadController.Preset defaultPreset; + String key; + SharedPreferences.Editor editor = MessagesController.getMainSettings(currentAccount).edit(); - DownloadController downloadController = DownloadController.getInstance(currentAccount); - for (int a = 0; a < 4; a++) { - downloadController.mobileDataDownloadMask[a] = DownloadController.AUTODOWNLOAD_MASK_PHOTO | DownloadController.AUTODOWNLOAD_MASK_AUDIO | DownloadController.AUTODOWNLOAD_MASK_MUSIC | DownloadController.AUTODOWNLOAD_MASK_GIF | DownloadController.AUTODOWNLOAD_MASK_VIDEOMESSAGE; - downloadController.wifiDownloadMask[a] = DownloadController.AUTODOWNLOAD_MASK_PHOTO | DownloadController.AUTODOWNLOAD_MASK_AUDIO | DownloadController.AUTODOWNLOAD_MASK_MUSIC | DownloadController.AUTODOWNLOAD_MASK_GIF | DownloadController.AUTODOWNLOAD_MASK_VIDEOMESSAGE; - downloadController.roamingDownloadMask[a] = 0; - editor.putInt("mobileDataDownloadMask" + (a != 0 ? a : ""), downloadController.mobileDataDownloadMask[a]); - editor.putInt("wifiDownloadMask" + (a != 0 ? a : ""), downloadController.wifiDownloadMask[a]); - editor.putInt("roamingDownloadMask" + (a != 0 ? a : ""), downloadController.roamingDownloadMask[a]); - } - for (int a = 0; a < 7; a++) { - int sdefault; - if (a == 1) { - sdefault = 2 * 1024 * 1024; - } else if (a == 6) { - sdefault = 5 * 1024 * 1024; + for (int a = 0; a < 3; a++) { + if (a == 0) { + preset = DownloadController.getInstance(currentAccount).mobilePreset; + defaultPreset = DownloadController.getInstance(currentAccount).mediumPreset; + key = "mobilePreset"; + } else if (a == 1) { + preset = DownloadController.getInstance(currentAccount).wifiPreset; + defaultPreset = DownloadController.getInstance(currentAccount).highPreset; + key = "wifiPreset"; } else { - sdefault = 10 * 1024 * 1024; + preset = DownloadController.getInstance(currentAccount).roamingPreset; + defaultPreset = DownloadController.getInstance(currentAccount).lowPreset; + key = "roamingPreset"; } - downloadController.mobileMaxFileSize[a] = sdefault; - downloadController.wifiMaxFileSize[a] = sdefault; - downloadController.roamingMaxFileSize[a] = sdefault; - editor.putInt("mobileMaxDownloadSize" + a, sdefault); - editor.putInt("wifiMaxDownloadSize" + a, sdefault); - editor.putInt("roamingMaxDownloadSize" + a, sdefault); - } - if (!DownloadController.getInstance(currentAccount).globalAutodownloadEnabled) { - DownloadController.getInstance(currentAccount).globalAutodownloadEnabled = true; - editor.putBoolean("globalAutodownloadEnabled", DownloadController.getInstance(currentAccount).globalAutodownloadEnabled); - updateAutodownloadRows(true); + preset.set(defaultPreset); + editor.putInt("currentMobilePreset", DownloadController.getInstance(currentAccount).currentMobilePreset = 3); + editor.putInt("currentWifiPreset", DownloadController.getInstance(currentAccount).currentWifiPreset = 3); + editor.putInt("currentRoamingPreset", DownloadController.getInstance(currentAccount).currentRoamingPreset = 3); + editor.putString(key, preset.toString()); } editor.commit(); DownloadController.getInstance(currentAccount).checkAutodownloadSettings(); + for (int a = 0; a < 3; a++) { + DownloadController.getInstance(currentAccount).savePresetToServer(a); + } + listAdapter.notifyItemRangeChanged(mobileRow, 4); }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); builder.show(); - } else if (position == autoDownloadMediaRow) { - DownloadController.getInstance(currentAccount).globalAutodownloadEnabled = !DownloadController.getInstance(currentAccount).globalAutodownloadEnabled; - final SharedPreferences preferences = MessagesController.getMainSettings(currentAccount); - preferences.edit().putBoolean("globalAutodownloadEnabled", DownloadController.getInstance(currentAccount).globalAutodownloadEnabled).commit(); - TextCheckCell textCheckCell = (TextCheckCell) view; - textCheckCell.setChecked(DownloadController.getInstance(currentAccount).globalAutodownloadEnabled); - updateAutodownloadRows(false); } else if (position == storageUsageRow) { presentFragment(new CacheControlActivity()); } else if (position == useLessDataForCallsRow) { final SharedPreferences preferences = MessagesController.getGlobalMainSettings(); + int selected=0; + switch(preferences.getInt("VoipDataSaving", VoIPHelper.getDataSavingDefault())){ + case VoIPController.DATA_SAVING_NEVER: + selected=0; + break; + case VoIPController.DATA_SAVING_ROAMING: + selected=1; + break; + case VoIPController.DATA_SAVING_MOBILE: + selected=2; + break; + case VoIPController.DATA_SAVING_ALWAYS: + selected=3; + break; + } Dialog dlg = AlertsCreator.createSingleChoiceDialog(getParentActivity(), new String[]{ LocaleController.getString("UseLessDataNever", R.string.UseLessDataNever), + LocaleController.getString("UseLessDataOnRoaming", R.string.UseLessDataOnRoaming), LocaleController.getString("UseLessDataOnMobile", R.string.UseLessDataOnMobile), LocaleController.getString("UseLessDataAlways", R.string.UseLessDataAlways)}, - LocaleController.getString("VoipUseLessData", R.string.VoipUseLessData), preferences.getInt("VoipDataSaving", VoIPController.DATA_SAVING_NEVER), (dialog, which) -> { + LocaleController.getString("VoipUseLessData", R.string.VoipUseLessData), selected, (dialog, which) -> { int val = -1; switch (which) { case 0: val = VoIPController.DATA_SAVING_NEVER; break; case 1: - val = VoIPController.DATA_SAVING_MOBILE; + val=VoIPController.DATA_SAVING_ROAMING; break; case 2: + val = VoIPController.DATA_SAVING_MOBILE; + break; + case 3: val = VoIPController.DATA_SAVING_ALWAYS; break; } @@ -253,12 +298,8 @@ public class DataSettingsActivity extends BaseFragment { }); setVisibleDialog(dlg); dlg.show(); - } else if (position == mobileUsageRow) { - presentFragment(new DataUsageActivity(0)); - } else if (position == roamingUsageRow) { - presentFragment(new DataUsageActivity(2)); - } else if (position == wifiUsageRow) { - presentFragment(new DataUsageActivity(1)); + } else if (position == dataUsageRow) { + presentFragment(new DataUsageActivity()); } else if (position == proxyRow) { presentFragment(new ProxyListActivity()); } else if (position == enableStreamRow) { @@ -269,12 +310,26 @@ public class DataSettingsActivity extends BaseFragment { SharedConfig.toggleStreamAllVideo(); TextCheckCell textCheckCell = (TextCheckCell) view; textCheckCell.setChecked(SharedConfig.streamAllVideo); + } else if (position == enableMkvRow) { + SharedConfig.toggleStreamMkv(); + TextCheckCell textCheckCell = (TextCheckCell) view; + textCheckCell.setChecked(SharedConfig.streamMkv); } else if (position == enableCacheStreamRow) { SharedConfig.toggleSaveStreamMedia(); TextCheckCell textCheckCell = (TextCheckCell) view; textCheckCell.setChecked(SharedConfig.saveStreamMedia); } else if (position == quickRepliesRow) { presentFragment(new QuickRepliesSettingsActivity()); + } else if (position == autoplayGifsRow) { + SharedConfig.toggleAutoplayGifs(); + if (view instanceof TextCheckCell) { + ((TextCheckCell) view).setChecked(SharedConfig.autoplayGifs); + } + } else if (position == autoplayVideoRow) { + SharedConfig.toggleAutoplayVideo(); + if (view instanceof TextCheckCell) { + ((TextCheckCell) view).setChecked(SharedConfig.autoplayVideo); + } } }); @@ -294,41 +349,6 @@ public class DataSettingsActivity extends BaseFragment { } } - private void updateAutodownloadRows(boolean check) { - int count = listView.getChildCount(); - ArrayList animators = new ArrayList<>(); - for (int a = 0; a < count; a++) { - View child = listView.getChildAt(a); - RecyclerListView.Holder holder = (RecyclerListView.Holder) listView.getChildViewHolder(child); - int type = holder.getItemViewType(); - int p = holder.getAdapterPosition(); - if (p >= photosRow && p <= gifsRow) { - TextSettingsCell textCell = (TextSettingsCell) holder.itemView; - textCell.setEnabled(DownloadController.getInstance(currentAccount).globalAutodownloadEnabled, animators); - } else if (check && p == autoDownloadMediaRow) { - TextCheckCell textCell = (TextCheckCell) holder.itemView; - textCell.setChecked(true); - } - } - if (!animators.isEmpty()) { - if (animatorSet != null) { - animatorSet.cancel(); - } - animatorSet = new AnimatorSet(); - animatorSet.playTogether(animators); - animatorSet.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animator) { - if (animator.equals(animatorSet)) { - animatorSet = null; - } - } - }); - animatorSet.setDuration(150); - animatorSet.start(); - } - } - private class ListAdapter extends RecyclerListView.SelectionAdapter { private Context mContext; @@ -355,49 +375,36 @@ public class DataSettingsActivity extends BaseFragment { } case 1: { TextSettingsCell textCell = (TextSettingsCell) holder.itemView; + textCell.setCanDisable(false); textCell.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); if (position == storageUsageRow) { textCell.setText(LocaleController.getString("StorageUsage", R.string.StorageUsage), true); } else if (position == useLessDataForCallsRow) { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); String value = null; - switch (preferences.getInt("VoipDataSaving", VoIPController.DATA_SAVING_NEVER)) { + switch (preferences.getInt("VoipDataSaving", VoIPHelper.getDataSavingDefault())) { case VoIPController.DATA_SAVING_NEVER: value = LocaleController.getString("UseLessDataNever", R.string.UseLessDataNever); break; case VoIPController.DATA_SAVING_MOBILE: value = LocaleController.getString("UseLessDataOnMobile", R.string.UseLessDataOnMobile); break; + case VoIPController.DATA_SAVING_ROAMING: + value = LocaleController.getString("UseLessDataOnRoaming", R.string.UseLessDataOnRoaming); + break; case VoIPController.DATA_SAVING_ALWAYS: value = LocaleController.getString("UseLessDataAlways", R.string.UseLessDataAlways); break; } textCell.setTextAndValue(LocaleController.getString("VoipUseLessData", R.string.VoipUseLessData), value, true); - } else if (position == mobileUsageRow) { - textCell.setText(LocaleController.getString("MobileUsage", R.string.MobileUsage), true); - } else if (position == roamingUsageRow) { - textCell.setText(LocaleController.getString("RoamingUsage", R.string.RoamingUsage), false); - } else if (position == wifiUsageRow) { - textCell.setText(LocaleController.getString("WiFiUsage", R.string.WiFiUsage), true); + } else if (position == dataUsageRow) { + textCell.setText(LocaleController.getString("NetworkUsage", R.string.NetworkUsage), false); } else if (position == proxyRow) { textCell.setText(LocaleController.getString("ProxySettings", R.string.ProxySettings), false); } else if (position == resetDownloadRow) { + textCell.setCanDisable(true); textCell.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText)); textCell.setText(LocaleController.getString("ResetAutomaticMediaDownload", R.string.ResetAutomaticMediaDownload), false); - } else if (position == photosRow) { - textCell.setText(LocaleController.getString("LocalPhotoCache", R.string.LocalPhotoCache), true); - } else if (position == voiceMessagesRow) { - textCell.setText(LocaleController.getString("AudioAutodownload", R.string.AudioAutodownload), true); - } else if (position == videoMessagesRow) { - textCell.setText(LocaleController.getString("VideoMessagesAutodownload", R.string.VideoMessagesAutodownload), true); - } else if (position == videosRow) { - textCell.setText(LocaleController.getString("LocalVideoCache", R.string.LocalVideoCache), true); - } else if (position == filesRow) { - textCell.setText(LocaleController.getString("FilesDataUsage", R.string.FilesDataUsage), true); - } else if (position == musicRow) { - textCell.setText(LocaleController.getString("AttachMusic", R.string.AttachMusic), true); - } else if (position == gifsRow) { - textCell.setText(LocaleController.getString("LocalGifCache", R.string.LocalGifCache), true); } else if (position == quickRepliesRow){ textCell.setText(LocaleController.getString("VoipQuickReplies", R.string.VoipQuickReplies), false); } @@ -415,19 +422,25 @@ public class DataSettingsActivity extends BaseFragment { headerCell.setText(LocaleController.getString("Proxy", R.string.Proxy)); } else if (position == streamSectionRow) { headerCell.setText(LocaleController.getString("Streaming", R.string.Streaming)); + } else if (position == autoplayHeaderRow) { + headerCell.setText(LocaleController.getString("AutoplayMedia", R.string.AutoplayMedia)); } break; } case 3: { TextCheckCell checkCell = (TextCheckCell) holder.itemView; - if (position == autoDownloadMediaRow) { - checkCell.setTextAndCheck(LocaleController.getString("AutoDownloadMedia", R.string.AutoDownloadMedia), DownloadController.getInstance(currentAccount).globalAutodownloadEnabled, true); - } else if (position == enableStreamRow) { + if (position == enableStreamRow) { checkCell.setTextAndCheck(LocaleController.getString("EnableStreaming", R.string.EnableStreaming), SharedConfig.streamMedia, enableAllStreamRow != -1); } else if (position == enableCacheStreamRow) { //checkCell.setTextAndCheck(LocaleController.getString("CacheStreamFile", R.string.CacheStreamFile), SharedConfig.saveStreamMedia, true); + } else if (position == enableMkvRow) { + checkCell.setTextAndCheck("(beta only) Show MKV as Video", SharedConfig.streamMkv, true); } else if (position == enableAllStreamRow) { - checkCell.setTextAndCheck("Try to Stream All Videos", SharedConfig.streamAllVideo, false); + checkCell.setTextAndCheck("(beta only) Stream All Videos", SharedConfig.streamAllVideo, false); + } else if (position == autoplayGifsRow) { + checkCell.setTextAndCheck(LocaleController.getString("AutoplayGIF", R.string.AutoplayGIF), SharedConfig.autoplayGifs, true); + } else if (position == autoplayVideoRow) { + checkCell.setTextAndCheck(LocaleController.getString("AutoplayVideo", R.string.AutoplayVideo), SharedConfig.autoplayVideo, false); } break; } @@ -436,6 +449,70 @@ public class DataSettingsActivity extends BaseFragment { if (position == enableAllStreamInfoRow) { cell.setText(LocaleController.getString("EnableAllStreamingInfo", R.string.EnableAllStreamingInfo)); } + break; + } + case 5: { + NotificationsCheckCell checkCell = (NotificationsCheckCell) holder.itemView; + + String text; + StringBuilder builder = new StringBuilder(); + DownloadController.Preset preset; + boolean enabled; + if (position == mobileRow) { + text = LocaleController.getString("WhenUsingMobileData", R.string.WhenUsingMobileData); + enabled = DownloadController.getInstance(currentAccount).mobilePreset.enabled; + preset = DownloadController.getInstance(currentAccount).getCurrentMobilePreset(); + } else if (position == wifiRow) { + text = LocaleController.getString("WhenConnectedOnWiFi", R.string.WhenConnectedOnWiFi); + enabled = DownloadController.getInstance(currentAccount).wifiPreset.enabled; + preset = DownloadController.getInstance(currentAccount).getCurrentWiFiPreset(); + } else { + text = LocaleController.getString("WhenRoaming", R.string.WhenRoaming); + enabled = DownloadController.getInstance(currentAccount).roamingPreset.enabled; + preset = DownloadController.getInstance(currentAccount).getCurrentRoamingPreset(); + } + + boolean photos = false; + boolean videos = false; + boolean files = false; + int count = 0; + for (int a = 0; a < preset.mask.length; a++) { + if (!photos && (preset.mask[a] & DownloadController.AUTODOWNLOAD_TYPE_PHOTO) != 0) { + photos = true; + count++; + } + if (!videos && (preset.mask[a] & DownloadController.AUTODOWNLOAD_TYPE_VIDEO) != 0) { + videos = true; + count++; + } + if (!files && (preset.mask[a] & DownloadController.AUTODOWNLOAD_TYPE_DOCUMENT) != 0) { + files = true; + count++; + } + } + if (preset.enabled && count != 0) { + if (photos) { + builder.append(LocaleController.getString("AutoDownloadPhotos", R.string.AutoDownloadPhotos)); + } + if (videos) { + if (builder.length() > 0) { + builder.append(", "); + } + builder.append(LocaleController.getString("AutoDownloadVideos", R.string.AutoDownloadVideos)); + builder.append(String.format(" (%1$s)", AndroidUtilities.formatFileSize(preset.sizes[DownloadController.typeToIndex(DownloadController.AUTODOWNLOAD_TYPE_VIDEO)], true))); + } + if (files) { + if (builder.length() > 0) { + builder.append(", "); + } + builder.append(LocaleController.getString("AutoDownloadFiles", R.string.AutoDownloadFiles)); + builder.append(String.format(" (%1$s)", AndroidUtilities.formatFileSize(preset.sizes[DownloadController.typeToIndex(DownloadController.AUTODOWNLOAD_TYPE_DOCUMENT)], true))); + } + } else { + builder.append(LocaleController.getString("NoMediaAutoDownload", R.string.NoMediaAutoDownload)); + } + checkCell.setTextAndValueAndCheck(text, builder, (photos || videos || files) && enabled, 0, true, true); + break; } } } @@ -443,25 +520,21 @@ public class DataSettingsActivity extends BaseFragment { @Override public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { int viewType = holder.getItemViewType(); - if (viewType == 1) { - int position = holder.getAdapterPosition(); - TextSettingsCell textCell = (TextSettingsCell) holder.itemView; - if (position >= photosRow && position <= gifsRow) { - textCell.setEnabled(DownloadController.getInstance(currentAccount).globalAutodownloadEnabled, null); - } else { - textCell.setEnabled(true, null); - } - } else if (viewType == 3) { + if (viewType == 3) { TextCheckCell checkCell = (TextCheckCell) holder.itemView; int position = holder.getAdapterPosition(); if (position == enableCacheStreamRow) { checkCell.setChecked(SharedConfig.saveStreamMedia); } else if (position == enableStreamRow) { checkCell.setChecked(SharedConfig.streamMedia); - } else if (position == autoDownloadMediaRow) { - checkCell.setChecked(DownloadController.getInstance(currentAccount).globalAutodownloadEnabled); } else if (position == enableAllStreamRow) { checkCell.setChecked(SharedConfig.streamAllVideo); + } else if (position == enableMkvRow) { + checkCell.setChecked(SharedConfig.streamMkv); + } else if (position == autoplayGifsRow) { + checkCell.setChecked(SharedConfig.autoplayGifs); + } else if (position == autoplayVideoRow) { + checkCell.setChecked(SharedConfig.autoplayVideo); } } } @@ -469,11 +542,14 @@ public class DataSettingsActivity extends BaseFragment { @Override public boolean isEnabled(RecyclerView.ViewHolder holder) { int position = holder.getAdapterPosition(); - if (position == photosRow || position == voiceMessagesRow || position == videoMessagesRow || position == videosRow || position == filesRow || position == musicRow || position == gifsRow) { - return DownloadController.getInstance(currentAccount).globalAutodownloadEnabled; + if (position == resetDownloadRow) { + DownloadController controller = DownloadController.getInstance(currentAccount); + return !controller.lowPreset.equals(controller.getCurrentRoamingPreset()) || + !controller.mediumPreset.equals(controller.getCurrentMobilePreset()) || + !controller.highPreset.equals(controller.getCurrentWiFiPreset()); } - return position == storageUsageRow || position == useLessDataForCallsRow || position == mobileUsageRow || position == roamingUsageRow || position == wifiUsageRow || position == proxyRow || - position == resetDownloadRow || position == autoDownloadMediaRow || position == enableCacheStreamRow || position == enableStreamRow || position == enableAllStreamRow || position == quickRepliesRow; + return position == mobileRow || position == roamingRow || position == wifiRow || position == storageUsageRow || position == useLessDataForCallsRow || position == dataUsageRow || position == proxyRow || + position == enableCacheStreamRow || position == enableStreamRow || position == enableAllStreamRow || position == enableMkvRow || position == quickRepliesRow || position == autoplayVideoRow || position == autoplayGifsRow; } @Override @@ -499,6 +575,10 @@ public class DataSettingsActivity extends BaseFragment { view = new TextInfoPrivacyCell(mContext); view.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); break; + case 5: + view = new NotificationsCheckCell(mContext); + view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + break; } view.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT)); return new RecyclerListView.Holder(view); @@ -506,14 +586,16 @@ public class DataSettingsActivity extends BaseFragment { @Override public int getItemViewType(int position) { - if (position == mediaDownloadSection2Row || position == usageSection2Row || position == callsSection2Row || position == proxySection2Row) { + if (position == mediaDownloadSection2Row || position == usageSection2Row || position == callsSection2Row || position == proxySection2Row || position == autoplaySectionRow) { return 0; - } else if (position == mediaDownloadSectionRow || position == streamSectionRow || position == callsSectionRow || position == usageSectionRow || position == proxySectionRow) { + } else if (position == mediaDownloadSectionRow || position == streamSectionRow || position == callsSectionRow || position == usageSectionRow || position == proxySectionRow || position == autoplayHeaderRow) { return 2; - } else if (position == autoDownloadMediaRow || position == enableCacheStreamRow || position == enableStreamRow || position == enableAllStreamRow) { + } else if (position == enableCacheStreamRow || position == enableStreamRow || position == enableAllStreamRow || position == enableMkvRow || position == autoplayGifsRow || position == autoplayVideoRow) { return 3; } else if (position == enableAllStreamInfoRow) { return 4; + } else if (position == mobileRow || position == wifiRow || position == roamingRow) { + return 5; } else { return 1; } @@ -523,7 +605,7 @@ public class DataSettingsActivity extends BaseFragment { @Override public ThemeDescription[] getThemeDescriptions() { return new ThemeDescription[]{ - new ThemeDescription(listView, ThemeDescription.FLAG_CELLBACKGROUNDCOLOR, new Class[]{TextSettingsCell.class, TextCheckCell.class, HeaderCell.class}, null, null, null, Theme.key_windowBackgroundWhite), + new ThemeDescription(listView, ThemeDescription.FLAG_CELLBACKGROUNDCOLOR, new Class[]{TextSettingsCell.class, TextCheckCell.class, HeaderCell.class, NotificationsCheckCell.class}, null, null, null, Theme.key_windowBackgroundWhite), new ThemeDescription(fragmentView, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundGray), new ThemeDescription(actionBar, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_actionBarDefault), @@ -532,6 +614,11 @@ public class DataSettingsActivity extends BaseFragment { new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_TITLECOLOR, null, null, null, null, Theme.key_actionBarDefaultTitle), new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SELECTORCOLOR, null, null, null, null, Theme.key_actionBarDefaultSelector), + new ThemeDescription(listView, 0, new Class[]{NotificationsCheckCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText), + new ThemeDescription(listView, 0, new Class[]{NotificationsCheckCell.class}, new String[]{"valueTextView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText2), + new ThemeDescription(listView, 0, new Class[]{NotificationsCheckCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_switchTrack), + new ThemeDescription(listView, 0, new Class[]{NotificationsCheckCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_switchTrackChecked), + new ThemeDescription(listView, ThemeDescription.FLAG_SELECTOR, null, null, null, null, Theme.key_listSelector), new ThemeDescription(listView, 0, new Class[]{View.class}, Theme.dividerPaint, null, null, Theme.key_divider), diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DataUsageActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/DataUsageActivity.java index 0e27502d5..57d908425 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DataUsageActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DataUsageActivity.java @@ -8,11 +8,22 @@ package org.telegram.ui; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; import android.view.Gravity; +import android.view.MotionEvent; +import android.view.VelocityTracker; import android.view.View; +import android.view.ViewConfiguration; import android.view.ViewGroup; +import android.view.animation.Interpolator; import android.widget.FrameLayout; +import android.widget.TextView; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.LocaleController; @@ -31,147 +42,55 @@ import org.telegram.ui.Cells.TextInfoPrivacyCell; import org.telegram.ui.Cells.TextSettingsCell; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.ScrollSlidingTextTabStrip; + +import java.util.ArrayList; public class DataUsageActivity extends BaseFragment { - private ListAdapter listAdapter; - private RecyclerListView listView; + private class ViewPage extends FrameLayout { + private RecyclerListView listView; + private ListAdapter listAdapter; + private LinearLayoutManager layoutManager; + private int selectedType; - private int currentType; - - private int messagesSectionRow; - private int messagesSentRow = -1; - private int messagesReceivedRow = -1; - private int messagesBytesSentRow; - private int messagesBytesReceivedRow; - private int messagesSection2Row; - - private int photosSectionRow; - private int photosSentRow; - private int photosReceivedRow; - private int photosBytesSentRow; - private int photosBytesReceivedRow; - private int photosSection2Row; - - private int videosSectionRow; - private int videosSentRow; - private int videosReceivedRow; - private int videosBytesSentRow; - private int videosBytesReceivedRow; - private int videosSection2Row; - - private int audiosSectionRow; - private int audiosSentRow; - private int audiosReceivedRow; - private int audiosBytesSentRow; - private int audiosBytesReceivedRow; - private int audiosSection2Row; - - private int filesSectionRow; - private int filesSentRow; - private int filesReceivedRow; - private int filesBytesSentRow; - private int filesBytesReceivedRow; - private int filesSection2Row; - - private int callsSectionRow; - private int callsSentRow; - private int callsReceivedRow; - private int callsBytesSentRow; - private int callsBytesReceivedRow; - private int callsTotalTimeRow; - private int callsSection2Row; - - private int totalSectionRow; - private int totalBytesSentRow; - private int totalBytesReceivedRow; - private int totalSection2Row; - - private int resetRow; - private int resetSection2Row; - - private int rowCount; - - public DataUsageActivity(int type) { - super(); - currentType = type; + public ViewPage(Context context) { + super(context); + } } - @Override - public boolean onFragmentCreate() { - super.onFragmentCreate(); + private ListAdapter mobileAdapter; + private ListAdapter roamingAdapter; + private ListAdapter wifiAdapter; - rowCount = 0; + private Paint backgroundPaint = new Paint(); + private ScrollSlidingTextTabStrip scrollSlidingTextTabStrip; + private ViewPage[] viewPages = new ViewPage[2]; + private AnimatorSet tabsAnimation; + private boolean tabsAnimationInProgress; + private boolean animatingForward; + private boolean backAnimation; + private int maximumVelocity; + private static final Interpolator interpolator = t -> { + --t; + return t * t * t * t * t + 1.0F; + }; - photosSectionRow = rowCount++; - photosSentRow = rowCount++; - photosReceivedRow = rowCount++; - photosBytesSentRow = rowCount++; - photosBytesReceivedRow = rowCount++; - photosSection2Row = rowCount++; - - videosSectionRow = rowCount++; - videosSentRow = rowCount++; - videosReceivedRow = rowCount++; - videosBytesSentRow = rowCount++; - videosBytesReceivedRow = rowCount++; - videosSection2Row = rowCount++; - - audiosSectionRow = rowCount++; - audiosSentRow = rowCount++; - audiosReceivedRow = rowCount++; - audiosBytesSentRow = rowCount++; - audiosBytesReceivedRow = rowCount++; - audiosSection2Row = rowCount++; - - filesSectionRow = rowCount++; - filesSentRow = rowCount++; - filesReceivedRow = rowCount++; - filesBytesSentRow = rowCount++; - filesBytesReceivedRow = rowCount++; - filesSection2Row = rowCount++; - - callsSectionRow = rowCount++; - callsSentRow = rowCount++; - callsReceivedRow = rowCount++; - callsBytesSentRow = rowCount++; - callsBytesReceivedRow = rowCount++; - callsTotalTimeRow = rowCount++; - callsSection2Row = rowCount++; - - messagesSectionRow = rowCount++; - /*if (BuildVars.DEBUG_VERSION) { - messagesSentRow = rowCount++; - messagesReceivedRow = rowCount++; - }*/ - messagesBytesSentRow = rowCount++; - messagesBytesReceivedRow = rowCount++; - messagesSection2Row = rowCount++; - - totalSectionRow = rowCount++; - totalBytesSentRow = rowCount++; - totalBytesReceivedRow = rowCount++; - totalSection2Row = rowCount++; - - resetRow = rowCount++; - resetSection2Row = rowCount++; - - return true; + public DataUsageActivity() { + super(); } @Override public View createView(Context context) { actionBar.setBackButtonImage(R.drawable.ic_ab_back); - if (currentType == 0) { - actionBar.setTitle(LocaleController.getString("MobileUsage", R.string.MobileUsage)); - } else if (currentType == 1) { - actionBar.setTitle(LocaleController.getString("WiFiUsage", R.string.WiFiUsage)); - } else if (currentType == 2) { - actionBar.setTitle(LocaleController.getString("RoamingUsage", R.string.RoamingUsage)); - } + actionBar.setTitle(LocaleController.getString("NetworkUsage", R.string.NetworkUsage)); if (AndroidUtilities.isTablet()) { actionBar.setOccupyStatusBar(false); } + actionBar.setExtraHeight(AndroidUtilities.dp(44)); + actionBar.setAllowOverlayTitle(false); + actionBar.setAddToContainer(false); + actionBar.setClipContent(true); actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @Override public void onItemClick(int id) { @@ -180,43 +99,485 @@ public class DataUsageActivity extends BaseFragment { } } }); + hasOwnBackground = true; - listAdapter = new ListAdapter(context); + mobileAdapter = new ListAdapter(context, 0); + wifiAdapter = new ListAdapter(context, 1); + roamingAdapter = new ListAdapter(context, 2); - fragmentView = new FrameLayout(context); - fragmentView.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundGray)); - FrameLayout frameLayout = (FrameLayout) fragmentView; - - listView = new RecyclerListView(context); - listView.setVerticalScrollBarEnabled(false); - listView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)); - frameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT)); - listView.setAdapter(listAdapter); - listView.setOnItemClickListener((view, position) -> { - if (getParentActivity() == null) { - return; + scrollSlidingTextTabStrip = new ScrollSlidingTextTabStrip(context); + scrollSlidingTextTabStrip.setUseSameWidth(true); + actionBar.addView(scrollSlidingTextTabStrip, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 44, Gravity.LEFT | Gravity.BOTTOM)); + scrollSlidingTextTabStrip.setDelegate(new ScrollSlidingTextTabStrip.ScrollSlidingTabStripDelegate() { + @Override + public void onPageSelected(int id, boolean forward) { + if (viewPages[0].selectedType == id) { + return; + } + swipeBackEnabled = id == scrollSlidingTextTabStrip.getFirstTabId(); + viewPages[1].selectedType = id; + viewPages[1].setVisibility(View.VISIBLE); + switchToCurrentSelectedMode(true); + animatingForward = forward; } - if (position == resetRow) { - AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); - builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); - builder.setMessage(LocaleController.getString("ResetStatisticsAlert", R.string.ResetStatisticsAlert)); - builder.setPositiveButton(LocaleController.getString("Reset", R.string.Reset), (dialogInterface, i) -> { - StatsController.getInstance(currentAccount).resetStats(currentType); - listAdapter.notifyDataSetChanged(); - }); - builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showDialog(builder.create()); + + @Override + public void onPageScrolled(float progress) { + if (progress == 1 && viewPages[1].getVisibility() != View.VISIBLE) { + return; + } + if (animatingForward) { + viewPages[0].setTranslationX(-progress * viewPages[0].getMeasuredWidth()); + viewPages[1].setTranslationX(viewPages[0].getMeasuredWidth() - progress * viewPages[0].getMeasuredWidth()); + } else { + viewPages[0].setTranslationX(progress * viewPages[0].getMeasuredWidth()); + viewPages[1].setTranslationX(progress * viewPages[0].getMeasuredWidth() - viewPages[0].getMeasuredWidth()); + } + if (progress == 1) { + ViewPage tempPage = viewPages[0]; + viewPages[0] = viewPages[1]; + viewPages[1] = tempPage; + viewPages[1].setVisibility(View.GONE); + } } }); + ViewConfiguration configuration = ViewConfiguration.get(context); + maximumVelocity = configuration.getScaledMaximumFlingVelocity(); + + FrameLayout frameLayout; + fragmentView = frameLayout = new FrameLayout(context) { + + private int startedTrackingPointerId; + private boolean startedTracking; + private boolean maybeStartTracking; + private int startedTrackingX; + private int startedTrackingY; + private VelocityTracker velocityTracker; + private boolean globalIgnoreLayout; + + private boolean prepareForMoving(MotionEvent ev, boolean forward) { + int id = scrollSlidingTextTabStrip.getNextPageId(forward); + if (id < 0) { + return false; + } + getParent().requestDisallowInterceptTouchEvent(true); + maybeStartTracking = false; + startedTracking = true; + startedTrackingX = (int) ev.getX(); + actionBar.setEnabled(false); + scrollSlidingTextTabStrip.setEnabled(false); + viewPages[1].selectedType = id; + viewPages[1].setVisibility(View.VISIBLE); + animatingForward = forward; + switchToCurrentSelectedMode(true); + if (forward) { + viewPages[1].setTranslationX(viewPages[0].getMeasuredWidth()); + } else { + viewPages[1].setTranslationX(-viewPages[0].getMeasuredWidth()); + } + return true; + } + + @Override + public void forceHasOverlappingRendering(boolean hasOverlappingRendering) { + super.forceHasOverlappingRendering(hasOverlappingRendering); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int widthSize = MeasureSpec.getSize(widthMeasureSpec); + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + + setMeasuredDimension(widthSize, heightSize); + + measureChildWithMargins(actionBar, widthMeasureSpec, 0, heightMeasureSpec, 0); + int actionBarHeight = actionBar.getMeasuredHeight(); + globalIgnoreLayout = true; + for (int a = 0; a < viewPages.length; a++) { + if (viewPages[a] == null) { + continue; + } + if (viewPages[a].listView != null) { + viewPages[a].listView.setPadding(0, actionBarHeight, 0, AndroidUtilities.dp(4)); + } + } + globalIgnoreLayout = false; + + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + if (child == null || child.getVisibility() == GONE || child == actionBar) { + continue; + } + measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); + } + } + + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + if (parentLayout != null) { + parentLayout.drawHeaderShadow(canvas, actionBar.getMeasuredHeight() + (int) actionBar.getTranslationY()); + } + } + + @Override + public void requestLayout() { + if (globalIgnoreLayout) { + return; + } + super.requestLayout(); + } + + public boolean checkTabsAnimationInProgress() { + if (tabsAnimationInProgress) { + boolean cancel = false; + if (backAnimation) { + if (Math.abs(viewPages[0].getTranslationX()) < 1) { + viewPages[0].setTranslationX(0); + viewPages[1].setTranslationX(viewPages[0].getMeasuredWidth() * (animatingForward ? 1 : -1)); + cancel = true; + } + } else if (Math.abs(viewPages[1].getTranslationX()) < 1) { + viewPages[0].setTranslationX(viewPages[0].getMeasuredWidth() * (animatingForward ? -1 : 1)); + viewPages[1].setTranslationX(0); + cancel = true; + } + if (cancel) { + if (tabsAnimation != null) { + tabsAnimation.cancel(); + tabsAnimation = null; + } + tabsAnimationInProgress = false; + } + return tabsAnimationInProgress; + } + return false; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + return checkTabsAnimationInProgress() || scrollSlidingTextTabStrip.isAnimatingIndicator() || onTouchEvent(ev); + } + + @Override + protected void onDraw(Canvas canvas) { + backgroundPaint.setColor(Theme.getColor(Theme.key_windowBackgroundGray)); + canvas.drawRect(0, actionBar.getMeasuredHeight() + actionBar.getTranslationY(), getMeasuredWidth(), getMeasuredHeight(), backgroundPaint); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + if (!parentLayout.checkTransitionAnimation() && !checkTabsAnimationInProgress()) { + if (ev != null && ev.getAction() == MotionEvent.ACTION_DOWN && !startedTracking && !maybeStartTracking) { + startedTrackingPointerId = ev.getPointerId(0); + maybeStartTracking = true; + startedTrackingX = (int) ev.getX(); + startedTrackingY = (int) ev.getY(); + if (velocityTracker != null) { + velocityTracker.clear(); + } + } else if (ev != null && ev.getAction() == MotionEvent.ACTION_MOVE && ev.getPointerId(0) == startedTrackingPointerId) { + if (velocityTracker == null) { + velocityTracker = VelocityTracker.obtain(); + } + int dx = (int) (ev.getX() - startedTrackingX); + int dy = Math.abs((int) ev.getY() - startedTrackingY); + velocityTracker.addMovement(ev); + if (startedTracking && (animatingForward && dx > 0 || !animatingForward && dx < 0)) { + if (!prepareForMoving(ev, dx < 0)) { + maybeStartTracking = true; + startedTracking = false; + } + } + if (maybeStartTracking && !startedTracking) { + float touchSlop = AndroidUtilities.getPixelsInCM(0.3f, true); + if (Math.abs(dx) >= touchSlop && Math.abs(dx) / 3 > dy) { + prepareForMoving(ev, dx < 0); + } + } else if (startedTracking) { + if (animatingForward) { + viewPages[0].setTranslationX(dx); + viewPages[1].setTranslationX(viewPages[0].getMeasuredWidth() + dx); + } else { + viewPages[0].setTranslationX(dx); + viewPages[1].setTranslationX(dx - viewPages[0].getMeasuredWidth()); + } + float scrollProgress = Math.abs(dx) / (float) viewPages[0].getMeasuredWidth(); + scrollSlidingTextTabStrip.selectTabWithId(viewPages[1].selectedType, scrollProgress); + } + } else if (ev != null && ev.getPointerId(0) == startedTrackingPointerId && (ev.getAction() == MotionEvent.ACTION_CANCEL || ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_POINTER_UP)) { + if (velocityTracker == null) { + velocityTracker = VelocityTracker.obtain(); + } + velocityTracker.computeCurrentVelocity(1000, maximumVelocity); + if (!startedTracking) { + float velX = velocityTracker.getXVelocity(); + float velY = velocityTracker.getYVelocity(); + if (Math.abs(velX) >= 3000 && Math.abs(velX) > Math.abs(velY)) { + prepareForMoving(ev, velX < 0); + } + } + if (startedTracking) { + float x = viewPages[0].getX(); + tabsAnimation = new AnimatorSet(); + float velX = velocityTracker.getXVelocity(); + float velY = velocityTracker.getYVelocity(); + backAnimation = Math.abs(x) < viewPages[0].getMeasuredWidth() / 3.0f && (Math.abs(velX) < 3500 || Math.abs(velX) < Math.abs(velY)); + float distToMove; + float dx; + if (backAnimation) { + dx = Math.abs(x); + if (animatingForward) { + tabsAnimation.playTogether( + ObjectAnimator.ofFloat(viewPages[0], View.TRANSLATION_X, 0), + ObjectAnimator.ofFloat(viewPages[1], View.TRANSLATION_X, viewPages[1].getMeasuredWidth()) + ); + } else { + tabsAnimation.playTogether( + ObjectAnimator.ofFloat(viewPages[0], View.TRANSLATION_X, 0), + ObjectAnimator.ofFloat(viewPages[1], View.TRANSLATION_X, -viewPages[1].getMeasuredWidth()) + ); + } + } else { + dx = viewPages[0].getMeasuredWidth() - Math.abs(x); + if (animatingForward) { + tabsAnimation.playTogether( + ObjectAnimator.ofFloat(viewPages[0], View.TRANSLATION_X, -viewPages[0].getMeasuredWidth()), + ObjectAnimator.ofFloat(viewPages[1], View.TRANSLATION_X, 0) + ); + } else { + tabsAnimation.playTogether( + ObjectAnimator.ofFloat(viewPages[0], View.TRANSLATION_X, viewPages[0].getMeasuredWidth()), + ObjectAnimator.ofFloat(viewPages[1], View.TRANSLATION_X, 0) + ); + } + } + tabsAnimation.setInterpolator(interpolator); + + int width = getMeasuredWidth(); + int halfWidth = width / 2; + float distanceRatio = Math.min(1.0f, 1.0f * dx / (float) width); + float distance = (float) halfWidth + (float) halfWidth * AndroidUtilities.distanceInfluenceForSnapDuration(distanceRatio); + velX = Math.abs(velX); + int duration; + if (velX > 0) { + duration = 4 * Math.round(1000.0f * Math.abs(distance / velX)); + } else { + float pageDelta = dx / getMeasuredWidth(); + duration = (int) ((pageDelta + 1.0f) * 100.0f); + } + duration = Math.max(150, Math.min(duration, 600)); + + tabsAnimation.setDuration(duration); + tabsAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animator) { + tabsAnimation = null; + if (backAnimation) { + viewPages[1].setVisibility(View.GONE); + } else { + ViewPage tempPage = viewPages[0]; + viewPages[0] = viewPages[1]; + viewPages[1] = tempPage; + viewPages[1].setVisibility(View.GONE); + swipeBackEnabled = viewPages[0].selectedType == scrollSlidingTextTabStrip.getFirstTabId(); + scrollSlidingTextTabStrip.selectTabWithId(viewPages[0].selectedType, 1.0f); + } + tabsAnimationInProgress = false; + maybeStartTracking = false; + startedTracking = false; + actionBar.setEnabled(true); + scrollSlidingTextTabStrip.setEnabled(true); + } + }); + tabsAnimation.start(); + tabsAnimationInProgress = true; + } else { + maybeStartTracking = false; + startedTracking = false; + actionBar.setEnabled(true); + scrollSlidingTextTabStrip.setEnabled(true); + } + if (velocityTracker != null) { + velocityTracker.recycle(); + velocityTracker = null; + } + } + return startedTracking; + } + return false; + } + }; + frameLayout.setWillNotDraw(false); + + int scrollToPositionOnRecreate = -1; + int scrollToOffsetOnRecreate = 0; + + for (int a = 0; a < viewPages.length; a++) { + if (a == 0) { + if (viewPages[a] != null && viewPages[a].layoutManager != null) { + scrollToPositionOnRecreate = viewPages[a].layoutManager.findFirstVisibleItemPosition(); + if (scrollToPositionOnRecreate != viewPages[a].layoutManager.getItemCount() - 1) { + RecyclerListView.Holder holder = (RecyclerListView.Holder) viewPages[a].listView.findViewHolderForAdapterPosition(scrollToPositionOnRecreate); + if (holder != null) { + scrollToOffsetOnRecreate = holder.itemView.getTop(); + } else { + scrollToPositionOnRecreate = -1; + } + } else { + scrollToPositionOnRecreate = -1; + } + } + } + final ViewPage ViewPage = new ViewPage(context) { + @Override + public void setTranslationX(float translationX) { + super.setTranslationX(translationX); + if (tabsAnimationInProgress) { + if (viewPages[0] == this) { + float scrollProgress = Math.abs(viewPages[0].getTranslationX()) / (float) viewPages[0].getMeasuredWidth(); + scrollSlidingTextTabStrip.selectTabWithId(viewPages[1].selectedType, scrollProgress); + } + } + } + }; + frameLayout.addView(ViewPage, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + viewPages[a] = ViewPage; + + final LinearLayoutManager layoutManager = viewPages[a].layoutManager = new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) { + @Override + public boolean supportsPredictiveItemAnimations() { + return false; + } + }; + RecyclerListView listView = new RecyclerListView(context); + viewPages[a].listView = listView; + viewPages[a].listView.setItemAnimator(null); + viewPages[a].listView.setClipToPadding(false); + viewPages[a].listView.setSectionsType(2); + viewPages[a].listView.setLayoutManager(layoutManager); + viewPages[a].addView(viewPages[a].listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + viewPages[a].listView.setOnItemClickListener((view, position) -> { + if (getParentActivity() == null) { + return; + } + ListAdapter adapter = (ListAdapter) listView.getAdapter(); + if (position == adapter.resetRow) { + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); + builder.setMessage(LocaleController.getString("ResetStatisticsAlert", R.string.ResetStatisticsAlert)); + builder.setPositiveButton(LocaleController.getString("Reset", R.string.Reset), (dialogInterface, i) -> { + StatsController.getInstance(currentAccount).resetStats(adapter.currentType); + adapter.notifyDataSetChanged(); + }); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + showDialog(builder.create()); + } + }); + viewPages[a].listView.setOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + + } + + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + if (recyclerView == viewPages[0].listView) { + float currentTranslation = actionBar.getTranslationY(); + float newTranslation = currentTranslation - dy; + if (newTranslation < -ActionBar.getCurrentActionBarHeight()) { + newTranslation = -ActionBar.getCurrentActionBarHeight(); + } else if (newTranslation > 0) { + newTranslation = 0; + } + if (newTranslation != currentTranslation) { + setScrollY(newTranslation); + } + } + } + }); + if (a == 0 && scrollToPositionOnRecreate != -1) { + layoutManager.scrollToPositionWithOffset(scrollToPositionOnRecreate, scrollToOffsetOnRecreate); + } + if (a != 0) { + viewPages[a].setVisibility(View.GONE); + } + } + + frameLayout.addView(actionBar, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + updateTabs(); + switchToCurrentSelectedMode(false); + swipeBackEnabled = scrollSlidingTextTabStrip.getCurrentTabId() == scrollSlidingTextTabStrip.getFirstTabId(); + return fragmentView; } @Override public void onResume() { super.onResume(); - if (listAdapter != null) { - listAdapter.notifyDataSetChanged(); + if (mobileAdapter != null) { + mobileAdapter.notifyDataSetChanged(); + } + if (wifiAdapter != null) { + wifiAdapter.notifyDataSetChanged(); + } + if (roamingAdapter != null) { + roamingAdapter.notifyDataSetChanged(); + } + } + + private void setScrollY(float value) { + actionBar.setTranslationY(value); + for (int a = 0; a < viewPages.length; a++) { + viewPages[a].listView.setPinnedSectionOffsetY((int) value); + } + fragmentView.invalidate(); + } + + private void updateTabs() { + if (scrollSlidingTextTabStrip == null) { + return; + } + scrollSlidingTextTabStrip.addTextTab(0, LocaleController.getString("NetworkUsageMobile", R.string.NetworkUsageMobile)); + scrollSlidingTextTabStrip.addTextTab(1, LocaleController.getString("NetworkUsageWiFi", R.string.NetworkUsageWiFi)); + scrollSlidingTextTabStrip.addTextTab(2, LocaleController.getString("NetworkUsageRoaming", R.string.NetworkUsageRoaming)); + scrollSlidingTextTabStrip.setVisibility(View.VISIBLE); + actionBar.setExtraHeight(AndroidUtilities.dp(44)); + int id = scrollSlidingTextTabStrip.getCurrentTabId(); + if (id >= 0) { + viewPages[0].selectedType = id; + } + scrollSlidingTextTabStrip.finishAddingTabs(); + } + + private void switchToCurrentSelectedMode(boolean animated) { + for (int a = 0; a < viewPages.length; a++) { + viewPages[a].listView.stopScroll(); + } + int a = animated ? 1 : 0; + RecyclerView.Adapter currentAdapter = viewPages[a].listView.getAdapter(); + viewPages[a].listView.setPinnedHeaderShadowDrawable(null); + + if (viewPages[a].selectedType == 0) { + if (currentAdapter != mobileAdapter) { + viewPages[a].listView.setAdapter(mobileAdapter); + } + } else if (viewPages[a].selectedType == 1) { + if (currentAdapter != wifiAdapter) { + viewPages[a].listView.setAdapter(wifiAdapter); + } + } else if (viewPages[a].selectedType == 2) { + if (currentAdapter != roamingAdapter) { + viewPages[a].listView.setAdapter(roamingAdapter); + } + } + viewPages[a].listView.setVisibility(View.VISIBLE); + + if (actionBar.getTranslationY() != 0) { + viewPages[a].layoutManager.scrollToPositionWithOffset(0, (int) actionBar.getTranslationY()); } } @@ -224,8 +585,117 @@ public class DataUsageActivity extends BaseFragment { private Context mContext; - public ListAdapter(Context context) { + private int currentType; + + private int messagesSectionRow; + private int messagesSentRow; + private int messagesReceivedRow; + private int messagesBytesSentRow; + private int messagesBytesReceivedRow; + private int messagesSection2Row; + + private int photosSectionRow; + private int photosSentRow; + private int photosReceivedRow; + private int photosBytesSentRow; + private int photosBytesReceivedRow; + private int photosSection2Row; + + private int videosSectionRow; + private int videosSentRow; + private int videosReceivedRow; + private int videosBytesSentRow; + private int videosBytesReceivedRow; + private int videosSection2Row; + + private int audiosSectionRow; + private int audiosSentRow; + private int audiosReceivedRow; + private int audiosBytesSentRow; + private int audiosBytesReceivedRow; + private int audiosSection2Row; + + private int filesSectionRow; + private int filesSentRow; + private int filesReceivedRow; + private int filesBytesSentRow; + private int filesBytesReceivedRow; + private int filesSection2Row; + + private int callsSectionRow; + private int callsSentRow; + private int callsReceivedRow; + private int callsBytesSentRow; + private int callsBytesReceivedRow; + private int callsTotalTimeRow; + private int callsSection2Row; + + private int totalSectionRow; + private int totalBytesSentRow; + private int totalBytesReceivedRow; + private int totalSection2Row; + + private int resetRow; + private int resetSection2Row; + + private int rowCount; + + public ListAdapter(Context context, int type) { mContext = context; + currentType = type; + + rowCount = 0; + + photosSectionRow = rowCount++; + photosSentRow = rowCount++; + photosReceivedRow = rowCount++; + photosBytesSentRow = rowCount++; + photosBytesReceivedRow = rowCount++; + photosSection2Row = rowCount++; + + videosSectionRow = rowCount++; + videosSentRow = rowCount++; + videosReceivedRow = rowCount++; + videosBytesSentRow = rowCount++; + videosBytesReceivedRow = rowCount++; + videosSection2Row = rowCount++; + + audiosSectionRow = rowCount++; + audiosSentRow = rowCount++; + audiosReceivedRow = rowCount++; + audiosBytesSentRow = rowCount++; + audiosBytesReceivedRow = rowCount++; + audiosSection2Row = rowCount++; + + filesSectionRow = rowCount++; + filesSentRow = rowCount++; + filesReceivedRow = rowCount++; + filesBytesSentRow = rowCount++; + filesBytesReceivedRow = rowCount++; + filesSection2Row = rowCount++; + + callsSectionRow = rowCount++; + callsSentRow = rowCount++; + callsReceivedRow = rowCount++; + callsBytesSentRow = rowCount++; + callsBytesReceivedRow = rowCount++; + callsTotalTimeRow = rowCount++; + callsSection2Row = rowCount++; + + messagesSectionRow = rowCount++; + messagesSentRow = -1; + messagesReceivedRow = -1; + messagesBytesSentRow = rowCount++; + messagesBytesReceivedRow = rowCount++; + messagesSection2Row = rowCount++; + + totalSectionRow = rowCount++; + totalBytesSentRow = rowCount++; + totalBytesReceivedRow = rowCount++; + totalSection2Row = rowCount++; + + resetRow = rowCount++; + resetSection2Row = rowCount++; } @Override @@ -372,30 +842,38 @@ public class DataUsageActivity extends BaseFragment { @Override public ThemeDescription[] getThemeDescriptions() { - return new ThemeDescription[]{ - new ThemeDescription(listView, ThemeDescription.FLAG_CELLBACKGROUNDCOLOR, new Class[]{TextSettingsCell.class, HeaderCell.class}, null, null, null, Theme.key_windowBackgroundWhite), - new ThemeDescription(fragmentView, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundGray), + ArrayList arrayList = new ArrayList<>(); - new ThemeDescription(actionBar, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_actionBarDefault), - new ThemeDescription(listView, ThemeDescription.FLAG_LISTGLOWCOLOR, null, null, null, null, Theme.key_actionBarDefault), - new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_ITEMSCOLOR, null, null, null, null, Theme.key_actionBarDefaultIcon), - new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_TITLECOLOR, null, null, null, null, Theme.key_actionBarDefaultTitle), - new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SELECTORCOLOR, null, null, null, null, Theme.key_actionBarDefaultSelector), + arrayList.add(new ThemeDescription(fragmentView, 0, null, null, null, null, Theme.key_windowBackgroundGray)); + arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_actionBarDefault)); + arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_ITEMSCOLOR, null, null, null, null, Theme.key_actionBarDefaultIcon)); + arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_TITLECOLOR, null, null, null, null, Theme.key_actionBarDefaultTitle)); + arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SELECTORCOLOR, null, null, null, null, Theme.key_actionBarDefaultSelector)); - new ThemeDescription(listView, ThemeDescription.FLAG_SELECTOR, null, null, null, null, Theme.key_listSelector), + arrayList.add(new ThemeDescription(scrollSlidingTextTabStrip.getTabsContainer(), ThemeDescription.FLAG_TEXTCOLOR | ThemeDescription.FLAG_CHECKTAG, new Class[]{TextView.class}, null, null, null, Theme.key_actionBarDefaultTitle)); + arrayList.add(new ThemeDescription(scrollSlidingTextTabStrip.getTabsContainer(), ThemeDescription.FLAG_TEXTCOLOR | ThemeDescription.FLAG_CHECKTAG, new Class[]{TextView.class}, null, null, null, Theme.key_actionBarDefaultSubtitle)); + arrayList.add(new ThemeDescription(scrollSlidingTextTabStrip.getTabsContainer(), ThemeDescription.FLAG_BACKGROUNDFILTER | ThemeDescription.FLAG_DRAWABLESELECTEDSTATE, new Class[]{TextView.class}, null, null, null, Theme.key_actionBarDefaultSelector)); + arrayList.add(new ThemeDescription(null, 0, null, scrollSlidingTextTabStrip.getRectPaint(), null, null, Theme.key_actionBarDefaultTitle)); - new ThemeDescription(listView, 0, new Class[]{View.class}, Theme.dividerPaint, null, null, Theme.key_divider), + for (int a = 0; a < viewPages.length; a++) { + arrayList.add(new ThemeDescription(viewPages[a].listView, ThemeDescription.FLAG_CELLBACKGROUNDCOLOR, new Class[]{TextSettingsCell.class, HeaderCell.class}, null, null, null, Theme.key_windowBackgroundWhite)); + arrayList.add(new ThemeDescription(viewPages[a].listView, ThemeDescription.FLAG_LISTGLOWCOLOR, null, null, null, null, Theme.key_actionBarDefault)); + arrayList.add(new ThemeDescription(viewPages[a].listView, ThemeDescription.FLAG_SELECTOR, null, null, null, null, Theme.key_listSelector)); - new ThemeDescription(listView, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{ShadowSectionCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow), + arrayList.add(new ThemeDescription(viewPages[a].listView, 0, new Class[]{View.class}, Theme.dividerPaint, null, null, Theme.key_divider)); - new ThemeDescription(listView, 0, new Class[]{HeaderCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueHeader), + arrayList.add(new ThemeDescription(viewPages[a].listView, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{ShadowSectionCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow)); - new ThemeDescription(listView, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{TextInfoPrivacyCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow), - new ThemeDescription(listView, 0, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText4), + arrayList.add(new ThemeDescription(viewPages[a].listView, 0, new Class[]{HeaderCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueHeader)); - new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText), - new ThemeDescription(listView, 0, new Class[]{TextSettingsCell.class}, new String[]{"valueTextView"}, null, null, null, Theme.key_windowBackgroundWhiteValueText), - new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText2), - }; + arrayList.add(new ThemeDescription(viewPages[a].listView, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{TextInfoPrivacyCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow)); + arrayList.add(new ThemeDescription(viewPages[a].listView, 0, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText4)); + + arrayList.add(new ThemeDescription(viewPages[a].listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); + arrayList.add(new ThemeDescription(viewPages[a].listView, 0, new Class[]{TextSettingsCell.class}, new String[]{"valueTextView"}, null, null, null, Theme.key_windowBackgroundWhiteValueText)); + arrayList.add(new ThemeDescription(viewPages[a].listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText2)); + } + + return arrayList.toArray(new ThemeDescription[0]); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java index cbecabb89..a5b06cb5f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java @@ -167,6 +167,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. private long openedDialogId; private boolean cantSendToChannels; private boolean allowSwitchAccount; + private boolean checkCanWrite; private DialogsActivityDelegate delegate; @@ -190,6 +191,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. selectAlertStringGroup = arguments.getString("selectAlertStringGroup"); addToGroupAlertString = arguments.getString("addToGroupAlertString"); allowSwitchAccount = arguments.getBoolean("allowSwitchAccount"); + checkCanWrite = arguments.getBoolean("checkCanWrite", true); } if (dialogsType == 0) { @@ -844,7 +846,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. return false; } final TLRPC.TL_dialog dialog; - ArrayList dialogs = getDialogsArray(); + ArrayList dialogs = getDialogsArray(dialogsType, currentAccount); if (position < 0 || position >= dialogs.size()) { return false; } @@ -946,7 +948,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. } else { user = !isChat && lower_id > 0 && high_id != 1 ? MessagesController.getInstance(currentAccount).getUser(lower_id) : null; } - final boolean isBot = user != null && user.bot; + final boolean isBot = user != null && user.bot && !MessagesController.isSupportUser(user); builder.setItems(new CharSequence[]{ dialog.pinned ? LocaleController.getString("UnpinFromTop", R.string.UnpinFromTop) : LocaleController.getString("PinToTop", R.string.PinToTop), @@ -1164,7 +1166,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. } }); } - unreadFloatingButtonCounter.setTextColor(Theme.getColor(Theme.key_chat_goDownButtonCounter)); + unreadFloatingButtonCounter.setColors(Theme.getColor(Theme.key_chat_goDownButtonCounter)); unreadFloatingButtonCounter.setGravity(Gravity.CENTER); unreadFloatingButtonCounter.setBackgroundDrawable(Theme.createRoundRectDrawable(AndroidUtilities.dp(11.5f), Theme.getColor(Theme.key_chat_goDownButtonCounterBackground))); unreadFloatingButtonCounter.setMinWidth(AndroidUtilities.dp(23)); @@ -1200,7 +1202,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. return; } if (visibleItemCount > 0) { - if (layoutManager.findLastVisibleItemPosition() >= getDialogsArray().size() - 10) { + if (layoutManager.findLastVisibleItemPosition() >= getDialogsArray(dialogsType, currentAccount).size() - 10) { boolean fromCache = !MessagesController.getInstance(currentAccount).dialogsEndReached; if (fromCache || !MessagesController.getInstance(currentAccount).serverDialogsEndReached) { MessagesController.getInstance(currentAccount).loadDialogs(-1, 100, fromCache); @@ -1943,19 +1945,21 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. } } - private ArrayList getDialogsArray() { + public static ArrayList getDialogsArray(int dialogsType, int currentAccount) { if (dialogsType == 0) { return MessagesController.getInstance(currentAccount).dialogs; } else if (dialogsType == 1) { return MessagesController.getInstance(currentAccount).dialogsServerOnly; } else if (dialogsType == 2) { - return MessagesController.getInstance(currentAccount).dialogsGroupsOnly; + return MessagesController.getInstance(currentAccount).dialogsCanAddUsers; } else if (dialogsType == 3) { return MessagesController.getInstance(currentAccount).dialogsForward; } else if (dialogsType == 4) { return MessagesController.getInstance(currentAccount).dialogsUsersOnly; } else if (dialogsType == 5) { return MessagesController.getInstance(currentAccount).dialogsChannelsOnly; + } else if (dialogsType == 6) { + return MessagesController.getInstance(currentAccount).dialogsGroupsOnly; } return null; } @@ -2049,7 +2053,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. } private void didSelectResult(final long dialog_id, boolean useAlert, final boolean param) { - if (addToGroupAlertString == null) { + if (addToGroupAlertString == null && checkCanWrite) { if ((int) dialog_id < 0) { TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-(int) dialog_id); if (ChatObject.isChannel(chat) && !chat.megagroup && (cantSendToChannels || !ChatObject.isCanWriteToChannel(-(int) dialog_id, currentAccount))) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java index 215ad87eb..af4671c08 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java @@ -192,8 +192,13 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { try { setTaskDescription(new ActivityManager.TaskDescription(null, null, Theme.getColor(Theme.key_actionBarDefault) | 0xff000000)); - } catch (Exception e) { - // + } catch (Exception ignore) { + + } + try { + getWindow().setNavigationBarColor(0xff000000); + } catch (Exception ignore) { + } } @@ -1733,7 +1738,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa } if (error == null && actionBarLayout != null) { TLRPC.ChatInvite invite = (TLRPC.ChatInvite) response; - if (invite.chat != null && !ChatObject.isLeftFromChat(invite.chat)) { + if (invite.chat != null && (!ChatObject.isLeftFromChat(invite.chat) || !invite.chat.kicked && !TextUtils.isEmpty(invite.chat.username))) { MessagesController.getInstance(intentAccount).putChat(invite.chat, false); ArrayList chats = new ArrayList<>(); chats.add(invite.chat); @@ -3101,6 +3106,20 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa return false; } + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + int keyCode = event.getKeyCode(); + if (!mainFragmentsStack.isEmpty() && (!PhotoViewer.hasInstance() || !PhotoViewer.getInstance().isVisible()) && event.getRepeatCount() == 0 && event.getAction() == KeyEvent.ACTION_DOWN && (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP || event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN)) { + BaseFragment fragment = mainFragmentsStack.get(mainFragmentsStack.size() - 1); + if (fragment instanceof ChatActivity) { + if (((ChatActivity) fragment).maybePlayVisibleVideo()) { + return true; + } + } + } + return super.dispatchKeyEvent(event); + } + @Override public boolean onKeyUp(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_MENU && !SharedConfig.isWaitingForPasscodeEnter) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LogoutActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LogoutActivity.java new file mode 100644 index 000000000..173727321 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/LogoutActivity.java @@ -0,0 +1,306 @@ +/* + * This is the source code of Telegram for Android v. 5.x.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2018. + */ + +package org.telegram.ui; + +import android.animation.AnimatorSet; +import android.app.Dialog; +import android.content.Context; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.DownloadController; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.R; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.support.widget.LinearLayoutManager; +import org.telegram.messenger.support.widget.RecyclerView; +import org.telegram.ui.ActionBar.ActionBar; +import org.telegram.ui.ActionBar.AlertDialog; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.ActionBar.ThemeDescription; +import org.telegram.ui.Cells.HeaderCell; +import org.telegram.ui.Cells.ShadowSectionCell; +import org.telegram.ui.Cells.TextDetailSettingsCell; +import org.telegram.ui.Cells.TextInfoPrivacyCell; +import org.telegram.ui.Cells.TextSettingsCell; +import org.telegram.ui.Components.AlertsCreator; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.RecyclerListView; + +public class LogoutActivity extends BaseFragment { + + private ListAdapter listAdapter; + private RecyclerListView listView; + private AnimatorSet animatorSet; + + private int alternativeHeaderRow; + private int addAccountRow; + private int passcodeRow; + private int cacheRow; + private int phoneRow; + private int supportRow; + private int alternativeSectionRow; + private int logoutRow; + private int logoutSectionRow; + private int rowCount; + + @Override + public boolean onFragmentCreate() { + super.onFragmentCreate(); + + rowCount = 0; + alternativeHeaderRow = rowCount++; + if (UserConfig.getActivatedAccountsCount() < UserConfig.MAX_ACCOUNT_COUNT) { + addAccountRow = rowCount++; + } else { + addAccountRow = -1; + } + if (SharedConfig.passcodeHash.length() <= 0) { + passcodeRow = rowCount++; + } else { + passcodeRow = -1; + } + cacheRow = rowCount++; + phoneRow = rowCount++; + supportRow = rowCount++; + alternativeSectionRow = rowCount++; + logoutRow = rowCount++; + logoutSectionRow = rowCount++; + + return true; + } + + @Override + public View createView(Context context) { + actionBar.setBackButtonImage(R.drawable.ic_ab_back); + actionBar.setTitle(LocaleController.getString("LogOutTitle", R.string.LogOutTitle)); + if (AndroidUtilities.isTablet()) { + actionBar.setOccupyStatusBar(false); + } + actionBar.setAllowOverlayTitle(true); + actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { + @Override + public void onItemClick(int id) { + if (id == -1) { + finishFragment(); + } + } + }); + + listAdapter = new ListAdapter(context); + + fragmentView = new FrameLayout(context); + fragmentView.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundGray)); + FrameLayout frameLayout = (FrameLayout) fragmentView; + + listView = new RecyclerListView(context); + listView.setVerticalScrollBarEnabled(false); + listView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)); + frameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT)); + listView.setAdapter(listAdapter); + listView.setOnItemClickListener((view, position, x, y) -> { + if (position == addAccountRow) { + int freeAccount = -1; + for (int a = 0; a < UserConfig.MAX_ACCOUNT_COUNT; a++) { + if (!UserConfig.getInstance(a).isClientActivated()) { + freeAccount = a; + break; + } + } + if (freeAccount >= 0) { + presentFragment(new LoginActivity(freeAccount)); + } + } else if (position == passcodeRow) { + presentFragment(new PasscodeActivity(0)); + } else if (position == cacheRow) { + presentFragment(new CacheControlActivity()); + } else if (position == phoneRow) { + presentFragment(new ChangePhoneHelpActivity()); + } else if (position == supportRow) { + showDialog(AlertsCreator.createSupportAlert(LogoutActivity.this)); + } else if (position == logoutRow) { + if (getParentActivity() == null) { + return; + } + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + builder.setMessage(LocaleController.getString("AreYouSureLogout", R.string.AreYouSureLogout)); + builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); + builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), (dialogInterface, i) -> MessagesController.getInstance(currentAccount).performLogout(1)); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + showDialog(builder.create()); + } + }); + + return fragmentView; + } + + @Override + protected void onDialogDismiss(Dialog dialog) { + DownloadController.getInstance(currentAccount).checkAutodownloadSettings(); + } + + @Override + public void onResume() { + super.onResume(); + if (listAdapter != null) { + listAdapter.notifyDataSetChanged(); + } + } + + private class ListAdapter extends RecyclerListView.SelectionAdapter { + + private Context mContext; + + public ListAdapter(Context context) { + mContext = context; + } + + @Override + public int getItemCount() { + return rowCount; + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + switch (holder.getItemViewType()) { + case 0: { + HeaderCell view = (HeaderCell) holder.itemView; + if (position == alternativeHeaderRow) { + view.setText(LocaleController.getString("AlternativeOptions", R.string.AlternativeOptions)); + } + break; + } + case 1: { + TextDetailSettingsCell view = (TextDetailSettingsCell) holder.itemView; + if (position == addAccountRow) { + view.setTextAndValueAndIcon(LocaleController.getString("AddAnotherAccount", R.string.AddAnotherAccount), LocaleController.getString("AddAnotherAccountInfo", R.string.AddAnotherAccountInfo), R.drawable.actions_addmember2, true); + } else if (position == passcodeRow) { + view.setTextAndValueAndIcon(LocaleController.getString("SetPasscode", R.string.SetPasscode), LocaleController.getString("SetPasscodeInfo", R.string.SetPasscodeInfo), R.drawable.menu_passcode, true); + } else if (position == cacheRow) { + view.setTextAndValueAndIcon(LocaleController.getString("ClearCache", R.string.ClearCache), LocaleController.getString("ClearCacheInfo", R.string.ClearCacheInfo), R.drawable.menu_clearcache, true); + } else if (position == phoneRow) { + view.setTextAndValueAndIcon(LocaleController.getString("ChangePhoneNumber", R.string.ChangePhoneNumber), LocaleController.getString("ChangePhoneNumberInfo", R.string.ChangePhoneNumberInfo), R.drawable.menu_newphone, true); + } else if (position == supportRow) { + view.setTextAndValueAndIcon(LocaleController.getString("ContactSupport", R.string.ContactSupport), LocaleController.getString("ContactSupportInfo", R.string.ContactSupportInfo), R.drawable.menu_support, false); + } + break; + } + case 3: { + TextSettingsCell view = (TextSettingsCell) holder.itemView; + if (position == logoutRow) { + view.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText5)); + view.setText(LocaleController.getString("LogOutTitle", R.string.LogOutTitle), false); + } + break; + } + case 4: { + TextInfoPrivacyCell view = (TextInfoPrivacyCell) holder.itemView; + if (position == logoutSectionRow) { + view.setText(LocaleController.getString("LogOutInfo", R.string.LogOutInfo)); + } + break; + } + } + } + + @Override + public boolean isEnabled(RecyclerView.ViewHolder holder) { + int position = holder.getAdapterPosition(); + return position == addAccountRow || position == passcodeRow || position == cacheRow || position == phoneRow || position == supportRow || position == logoutRow; + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view; + switch (viewType) { + case 0: { + view = new HeaderCell(mContext); + view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + break; + } + case 1: { + TextDetailSettingsCell cell = new TextDetailSettingsCell(mContext); + cell.setMultilineDetail(true); + cell.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + view = cell; + break; + } + case 2: { + view = new ShadowSectionCell(mContext); + break; + } + case 3: { + view = new TextSettingsCell(mContext); + view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + break; + } + case 4: + default: { + view = new TextInfoPrivacyCell(mContext); + view.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + break; + } + } + view.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT)); + return new RecyclerListView.Holder(view); + } + + @Override + public int getItemViewType(int position) { + if (position == alternativeHeaderRow) { + return 0; + } else if (position == addAccountRow || position == passcodeRow || position == cacheRow || position == phoneRow || position == supportRow) { + return 1; + } else if (position == alternativeSectionRow) { + return 2; + } else if (position == logoutRow) { + return 3; + } else { + return 4; + } + } + } + + @Override + public ThemeDescription[] getThemeDescriptions() { + return new ThemeDescription[]{ + new ThemeDescription(listView, ThemeDescription.FLAG_CELLBACKGROUNDCOLOR, new Class[]{TextSettingsCell.class, HeaderCell.class, TextDetailSettingsCell.class}, null, null, null, Theme.key_windowBackgroundWhite), + new ThemeDescription(fragmentView, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundGray), + + new ThemeDescription(actionBar, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_actionBarDefault), + new ThemeDescription(listView, ThemeDescription.FLAG_LISTGLOWCOLOR, null, null, null, null, Theme.key_actionBarDefault), + new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_ITEMSCOLOR, null, null, null, null, Theme.key_actionBarDefaultIcon), + new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_TITLECOLOR, null, null, null, null, Theme.key_actionBarDefaultTitle), + new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SELECTORCOLOR, null, null, null, null, Theme.key_actionBarDefaultSelector), + + new ThemeDescription(listView, ThemeDescription.FLAG_SELECTOR, null, null, null, null, Theme.key_listSelector), + + new ThemeDescription(listView, 0, new Class[]{View.class}, Theme.dividerPaint, null, null, Theme.key_divider), + + new ThemeDescription(listView, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{ShadowSectionCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow), + + new ThemeDescription(listView, 0, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteRedText5), + + new ThemeDescription(listView, 0, new Class[]{HeaderCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueHeader), + + new ThemeDescription(listView, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{TextInfoPrivacyCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow), + new ThemeDescription(listView, 0, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText4), + + new ThemeDescription(listView, 0, new Class[]{TextDetailSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText), + new ThemeDescription(listView, 0, new Class[]{TextDetailSettingsCell.class}, new String[]{"valueTextView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText2), + new ThemeDescription(listView, 0, new Class[]{TextDetailSettingsCell.class}, new String[]{"imageView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayIcon), + }; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/MediaActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/MediaActivity.java index 2c167bb2c..ea6494d51 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/MediaActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/MediaActivity.java @@ -190,7 +190,7 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No private PhotoViewer.PhotoViewerProvider provider = new PhotoViewer.EmptyPhotoViewerProvider() { @Override - public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index) { + public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index, boolean needPreview) { if (messageObject == null || mediaPages[0].selectedType != 0 && mediaPages[0].selectedType != 1) { return null; } @@ -1086,7 +1086,7 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No int width = getMeasuredWidth(); int halfWidth = width / 2; float distanceRatio = Math.min(1.0f, 1.0f * dx / (float) width); - float distance = (float) halfWidth + (float) halfWidth * distanceInfluenceForSnapDuration(distanceRatio); + float distance = (float) halfWidth + (float) halfWidth * AndroidUtilities.distanceInfluenceForSnapDuration(distanceRatio); velX = Math.abs(velX); int duration; if (velX > 0) { @@ -1352,12 +1352,6 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No fragmentView.invalidate(); } - private float distanceInfluenceForSnapDuration(float f) { - f -= 0.5F; - f *= 0.47123894F; - return (float) Math.sin((double) f); - } - private void resetScroll() { if (actionBar.getTranslationY() == 0) { return; @@ -2890,7 +2884,7 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No boolean result = MediaController.getInstance().playMessage(messageObject); MediaController.getInstance().setVoiceMessagesPlaylist(result ? searchResult : null, false); if (messageObject.isRoundVideo()) { - MediaController.getInstance().setCurrentRoundVisible(false); + MediaController.getInstance().setCurrentVideoVisible(false); } return result; } else if (messageObject.isMusic()) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/NotificationsCustomSettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/NotificationsCustomSettingsActivity.java index 04ae03a79..b796fb710 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/NotificationsCustomSettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/NotificationsCustomSettingsActivity.java @@ -240,8 +240,9 @@ public class NotificationsCustomSettingsActivity extends BaseFragment { if (position == exceptionsAddRow) { Bundle args = new Bundle(); args.putBoolean("onlySelect", true); + args.putBoolean("checkCanWrite", false); if (currentType == NotificationsController.TYPE_GROUP) { - args.putInt("dialogsType", 2); + args.putInt("dialogsType", 6); } else if (currentType == NotificationsController.TYPE_CHANNEL) { args.putInt("dialogsType", 5); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PassportActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PassportActivity.java index d1b8db27f..17b437a5c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PassportActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PassportActivity.java @@ -347,7 +347,7 @@ public class PassportActivity extends BaseFragment implements NotificationCenter private PhotoViewer.PhotoViewerProvider provider = new PhotoViewer.EmptyPhotoViewerProvider() { @Override - public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index) { + public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index, boolean needPreview) { if (index < 0 || index >= currentPhotoViewerLayout.getChildCount()) { return null; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoPickerActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoPickerActivity.java index 7a336222d..e29cd7e9f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PhotoPickerActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoPickerActivity.java @@ -129,7 +129,7 @@ public class PhotoPickerActivity extends BaseFragment implements NotificationCen } @Override - public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index) { + public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index, boolean needPreview) { PhotoPickerPhotoCell cell = getCellForIndex(index); if (cell != null) { int coords[] = new int[2]; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java index dd22f7219..a241d02f8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java @@ -35,6 +35,7 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.media.MediaCodecInfo; +import android.media.MediaPlayer; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -270,10 +271,26 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private boolean doneButtonPressed; + private Runnable setLoadingRunnable = new Runnable() { + @Override + public void run() { + if (currentMessageObject == null) { + return; + } + FileLoader.getInstance(currentMessageObject.currentAccount).setLoadingVideo(currentMessageObject.getDocument(), true, false); + } + }; + private AspectRatioFrameLayout aspectRatioFrameLayout; private TextureView videoTextureView; private VideoPlayer videoPlayer; + private VideoPlayer injectingVideoPlayer; + private SurfaceTexture injectingVideoPlayerSurface; + private boolean playerInjected; + private boolean skipFirstBufferingProgress; + private boolean playerWasReady; private float seekToProgressPending; + private float seekToProgressPending2; private boolean streamingAlertShown; private long startedPlayTime; private boolean keepScreenOnFlagSet; @@ -506,7 +523,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } }; - private float animationValues[][] = new float[2][8]; + private float animationValues[][] = new float[2][10]; private ChatActivity parentChatActivity; private MentionsAdapter mentionsAdapter; @@ -941,7 +958,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat public static class EmptyPhotoViewerProvider implements PhotoViewerProvider { @Override - public PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index) { + public PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index, boolean needPreview) { return null; } @@ -1057,7 +1074,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } public interface PhotoViewerProvider { - PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index); + PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index, boolean needPreview); ImageReceiver.BitmapHolder getThumbForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index); @@ -1736,7 +1753,12 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { - boolean result = super.drawChild(canvas, child, drawingTime); + boolean result; + try { + result = super.drawChild(canvas, child, drawingTime); + } catch (Throwable ignore) { + result = false; + } if (Build.VERSION.SDK_INT >= 21 && child == animatingImageView && lastInsets != null) { WindowInsets insets = (WindowInsets) lastInsets; canvas.drawRect(0, getMeasuredHeight(), getMeasuredWidth(), getMeasuredHeight() + insets.getSystemWindowInsetBottom(), blackPaint); @@ -3209,6 +3231,23 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat isVisible = false; if (currentPlaceObject != null) { currentPlaceObject.imageReceiver.setVisible(true, true); + AnimatedFileDrawable animation = currentPlaceObject.imageReceiver.getAnimation(); + if (animation != null) { + Bitmap bitmap = animation.getAnimatedBitmap(); + if (bitmap != null) { + try { + Bitmap src = videoTextureView.getBitmap(bitmap.getWidth(), bitmap.getHeight()); + Canvas canvas = new Canvas(bitmap); + canvas.drawBitmap(src, 0, 0, null); + src.recycle(); + } catch (Throwable e) { + FileLog.e(e); + } + } + animation.seekTo(videoPlayer.getCurrentPosition(), true); + currentPlaceObject.imageReceiver.setAllowStartAnimation(true); + currentPlaceObject.imageReceiver.startAnimation(); + } } if (Build.VERSION.SDK_INT >= 21) { pipAnimationInProgress = true; @@ -3462,7 +3501,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat videoEditedInfo.originalHeight = originalHeight; videoEditedInfo.bitrate = bitrate; videoEditedInfo.originalPath = currentPlayingVideoFile.getPath(); - videoEditedInfo.estimatedSize = estimatedSize; + videoEditedInfo.estimatedSize = estimatedSize != 0 ? estimatedSize : 1; videoEditedInfo.estimatedDuration = estimatedDuration; videoEditedInfo.framerate = videoFramerate; @@ -3577,6 +3616,126 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } } + public void injectVideoPlayer(VideoPlayer player) { + injectingVideoPlayer = player; + } + + public void injectVideoPlayerSurface(SurfaceTexture surface) { + injectingVideoPlayerSurface = surface; + } + + public boolean isInjectingVideoPlayer() { + return injectingVideoPlayer != null; + } + + private void updatePlayerState(boolean playWhenReady, int playbackState) { + if (videoPlayer == null) { + return; + } + if (isStreaming) { + if (playbackState == ExoPlayer.STATE_BUFFERING && skipFirstBufferingProgress) { + if (playWhenReady) { + skipFirstBufferingProgress = false; + } + } else { + toggleMiniProgress(seekToProgressPending != 0 || playbackState == ExoPlayer.STATE_BUFFERING, true); + } + } + if (playWhenReady && (playbackState != ExoPlayer.STATE_ENDED && playbackState != ExoPlayer.STATE_IDLE)) { + try { + parentActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + keepScreenOnFlagSet = true; + } catch (Exception e) { + FileLog.e(e); + } + } else { + try { + parentActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + keepScreenOnFlagSet = false; + } catch (Exception e) { + FileLog.e(e); + } + } + if (seekToProgressPending != 0 && (playbackState == ExoPlayer.STATE_READY || playbackState == ExoPlayer.STATE_IDLE)) { + int seekTo = (int) (videoPlayer.getDuration() * seekToProgressPending); + videoPlayer.seekTo(seekTo); + seekToProgressPending = 0; + if (currentMessageObject != null && !FileLoader.getInstance(currentMessageObject.currentAccount).isLoadingVideoAny(currentMessageObject.getDocument())) { + skipFirstBufferingProgress = true; + } + } + if (playbackState == ExoPlayer.STATE_READY) { + if (aspectRatioFrameLayout.getVisibility() != View.VISIBLE) { + aspectRatioFrameLayout.setVisibility(View.VISIBLE); + } + if (!pipItem.isEnabled()) { + pipAvailable = true; + pipItem.setEnabled(true); + pipItem.setAlpha(1.0f); + } + playerWasReady = true; + if (currentMessageObject != null && currentMessageObject.isVideo()) { + AndroidUtilities.cancelRunOnUIThread(setLoadingRunnable); + FileLoader.getInstance(currentMessageObject.currentAccount).removeLoadingVideo(currentMessageObject.getDocument(), true, false); + } + } else if (playbackState == ExoPlayer.STATE_BUFFERING) { + if (playWhenReady && currentMessageObject != null && currentMessageObject.isVideo()) { + if (playerWasReady) { + setLoadingRunnable.run(); + } else { + AndroidUtilities.runOnUIThread(setLoadingRunnable, 1000); + } + } + } + + if (videoPlayer.isPlaying() && playbackState != ExoPlayer.STATE_ENDED) { + if (!isPlaying) { + isPlaying = true; + videoPlayButton.setImageResource(R.drawable.inline_video_pause); + AndroidUtilities.runOnUIThread(updateProgressRunnable); + } + } else if (isPlaying) { + isPlaying = false; + videoPlayButton.setImageResource(R.drawable.inline_video_play); + AndroidUtilities.cancelRunOnUIThread(updateProgressRunnable); + if (playbackState == ExoPlayer.STATE_ENDED) { + if (isCurrentVideo) { + if (!videoTimelineView.isDragging()) { + videoTimelineView.setProgress(0.0f); + if (!inPreview && videoTimelineView.getVisibility() == View.VISIBLE) { + videoPlayer.seekTo((int) (videoTimelineView.getLeftProgress() * videoPlayer.getDuration())); + } else { + videoPlayer.seekTo(0); + } + videoPlayer.pause(); + containerView.invalidate(); + } + } else { + if (!isActionBarVisible) { + toggleActionBar(true, true); + } + if (!videoPlayerSeekbar.isDragging()) { + videoPlayerSeekbar.setProgress(0.0f); + videoPlayerControlFrameLayout.invalidate(); + if (!inPreview && videoTimelineView.getVisibility() == View.VISIBLE) { + videoPlayer.seekTo((int) (videoTimelineView.getLeftProgress() * videoPlayer.getDuration())); + } else { + videoPlayer.seekTo(0); + } + videoPlayer.pause(); + } + } + if (pipVideoView != null) { + pipVideoView.onVideoCompleted(); + } + } + } + if (pipVideoView != null) { + pipVideoView.updatePlayButton(); + } + updateVideoPlayerTime(); + } + private void preparePlayer(Uri uri, boolean playWhenReady, boolean preview) { if (!preview) { currentPlayingVideoFile = uri; @@ -3588,7 +3747,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat startedPlayTime = SystemClock.elapsedRealtime(); currentVideoFinishedLoading = false; lastBufferedPositionCheck = 0; - seekToProgressPending = 0; firstAnimationDelay = true; inPreview = preview; releasePlayer(); @@ -3608,6 +3766,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat containerView.addView(aspectRatioFrameLayout, 0, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.CENTER)); videoTextureView = new TextureView(parentActivity); + if (injectingVideoPlayerSurface != null) { + videoTextureView.setSurfaceTexture(injectingVideoPlayerSurface); + textureUploaded = true; + injectingVideoPlayerSurface = null; + } videoTextureView.setPivotX(0); videoTextureView.setPivotY(0); videoTextureView.setOpaque(false); @@ -3625,94 +3788,23 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat videoCrossfadeStarted = false; videoTextureView.setAlpha(videoCrossfadeAlpha = 0.0f); videoPlayButton.setImageResource(R.drawable.inline_video_play); + boolean newPlayerCreated = false; + playerWasReady = false; if (videoPlayer == null) { - videoPlayer = new VideoPlayer(); + if (injectingVideoPlayer != null) { + videoPlayer = injectingVideoPlayer; + injectingVideoPlayer = null; + playerInjected = true; + updatePlayerState(videoPlayer.getPlayWhenReady(), videoPlayer.getPlaybackState()); + } else { + videoPlayer = new VideoPlayer(); + newPlayerCreated = true; + } videoPlayer.setTextureView(videoTextureView); videoPlayer.setDelegate(new VideoPlayer.VideoPlayerDelegate() { @Override public void onStateChanged(boolean playWhenReady, int playbackState) { - if (videoPlayer == null) { - return; - } - if (isStreaming) { - toggleMiniProgress(playbackState == ExoPlayer.STATE_BUFFERING, true); - } - if (playWhenReady && (playbackState != ExoPlayer.STATE_ENDED && playbackState != ExoPlayer.STATE_IDLE)) { - try { - parentActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - keepScreenOnFlagSet = true; - } catch (Exception e) { - FileLog.e(e); - } - } else { - try { - parentActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - keepScreenOnFlagSet = false; - } catch (Exception e) { - FileLog.e(e); - } - } - if (seekToProgressPending != 0 && (playbackState == ExoPlayer.STATE_READY || playbackState == ExoPlayer.STATE_IDLE)) { - int seekTo = (int) (videoPlayer.getDuration() * seekToProgressPending); - videoPlayer.seekTo(seekTo); - seekToProgressPending = 0; - } - if (playbackState == ExoPlayer.STATE_READY) { - if (aspectRatioFrameLayout.getVisibility() != View.VISIBLE) { - aspectRatioFrameLayout.setVisibility(View.VISIBLE); - } - if (!pipItem.isEnabled()) { - pipAvailable = true; - pipItem.setEnabled(true); - pipItem.setAlpha(1.0f); - } - } - if (videoPlayer.isPlaying() && playbackState != ExoPlayer.STATE_ENDED) { - if (!isPlaying) { - isPlaying = true; - videoPlayButton.setImageResource(R.drawable.inline_video_pause); - AndroidUtilities.runOnUIThread(updateProgressRunnable); - } - } else if (isPlaying) { - isPlaying = false; - videoPlayButton.setImageResource(R.drawable.inline_video_play); - AndroidUtilities.cancelRunOnUIThread(updateProgressRunnable); - if (playbackState == ExoPlayer.STATE_ENDED) { - if (isCurrentVideo) { - if (!videoTimelineView.isDragging()) { - videoTimelineView.setProgress(0.0f); - if (!inPreview && videoTimelineView.getVisibility() == View.VISIBLE) { - videoPlayer.seekTo((int) (videoTimelineView.getLeftProgress() * videoPlayer.getDuration())); - } else { - videoPlayer.seekTo(0); - } - videoPlayer.pause(); - containerView.invalidate(); - } - } else { - if (!isActionBarVisible) { - toggleActionBar(true, true); - } - if (!videoPlayerSeekbar.isDragging()) { - videoPlayerSeekbar.setProgress(0.0f); - videoPlayerControlFrameLayout.invalidate(); - if (!inPreview && videoTimelineView.getVisibility() == View.VISIBLE) { - videoPlayer.seekTo((int) (videoTimelineView.getLeftProgress() * videoPlayer.getDuration())); - } else { - videoPlayer.seekTo(0); - } - videoPlayer.pause(); - } - } - if (pipVideoView != null) { - pipVideoView.onVideoCompleted(); - } - } - } - if (pipVideoView != null) { - pipVideoView.updatePlayButton(); - } - updateVideoPlayerTime(); + updatePlayerState(playWhenReady, playbackState); } @Override @@ -3828,14 +3920,22 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat }); } - videoPlayer.preparePlayer(uri, "other"); - videoPlayerSeekbar.setProgress(0); - videoTimelineView.setProgress(0); - videoPlayerSeekbar.setBufferedProgress(0); + if (newPlayerCreated) { + seekToProgressPending = seekToProgressPending2; + videoPlayer.preparePlayer(uri, "other"); + videoPlayerSeekbar.setProgress(0); + videoTimelineView.setProgress(0); + videoPlayerSeekbar.setBufferedProgress(0); + videoPlayer.setPlayWhenReady(playWhenReady); + } if (currentBotInlineResult != null && (currentBotInlineResult.type.equals("video") || MessageObject.isVideoDocument(currentBotInlineResult.document))) { bottomLayout.setVisibility(View.VISIBLE); - bottomLayout.setTranslationY(-AndroidUtilities.dp(48)); + bottomLayout.setPadding(0, 0, AndroidUtilities.dp(84), 0); + pickerView.setVisibility(View.GONE); + //bottomLayout.setTranslationY(-AndroidUtilities.dp(48)); + } else { + bottomLayout.setPadding(0, 0, 0, 0); } videoPlayerControlFrameLayout.setVisibility(isCurrentVideo ? View.GONE : View.VISIBLE); @@ -3845,18 +3945,18 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat shareButton.setVisibility(View.GONE); menuItem.showSubItem(gallery_menu_share); } - - videoPlayer.setPlayWhenReady(playWhenReady); inPreview = preview; } private void releasePlayer() { if (videoPlayer != null) { - videoPlayer.releasePlayer(); + AndroidUtilities.cancelRunOnUIThread(setLoadingRunnable); + videoPlayer.releasePlayer(true); videoPlayer = null; } toggleMiniProgress(false, false); pipAvailable = false; + playerInjected = false; if (pipItem.isEnabled()) { pipItem.setEnabled(false); pipItem.setAlpha(0.5f); @@ -3870,7 +3970,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } } if (aspectRatioFrameLayout != null) { - containerView.removeView(aspectRatioFrameLayout); + try { + containerView.removeView(aspectRatioFrameLayout); + } catch (Throwable ignore) { + + } aspectRatioFrameLayout = null; } if (videoTextureView != null) { @@ -4780,7 +4884,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat TLRPC.PhotoSize sizeFull = FileLoader.getClosestPhotoSizeWithSize(botInlineResult.photo.sizes, AndroidUtilities.getPhotoSize()); return FileLoader.getAttachFileName(sizeFull); } else if (botInlineResult.content instanceof TLRPC.TL_webDocument) { - return Utilities.MD5(botInlineResult.content.url) + "." + ImageLoader.getHttpUrlExtension(botInlineResult.content.url, FileLoader.getExtensionByMime(botInlineResult.content.mime_type)); + return Utilities.MD5(botInlineResult.content.url) + "." + ImageLoader.getHttpUrlExtension(botInlineResult.content.url, FileLoader.getMimeTypePart(botInlineResult.content.mime_type)); } } } @@ -5321,6 +5425,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } } else if (isInvoice) { actionBar.setTitle(newMessageObject.messageOwner.media.title); + } else if (newMessageObject.isVideo()) { + actionBar.setTitle(LocaleController.getString("AttachVideo", R.string.AttachVideo)); } else if (newMessageObject.getDocument() != null) { actionBar.setTitle(LocaleController.getString("AttachDocument", R.string.AttachDocument)); } @@ -5594,7 +5700,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat showAfterAnimation = currentPlaceObject; } } - currentPlaceObject = placeProvider.getPlaceForPhoto(currentMessageObject, currentFileLocation, currentIndex); + currentPlaceObject = placeProvider.getPlaceForPhoto(currentMessageObject, currentFileLocation, currentIndex, false); if (currentPlaceObject != null) { if (animationInProgress == 0) { currentPlaceObject.imageReceiver.setVisible(false, true); @@ -5734,6 +5840,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } catch (Exception e) { FileLog.e(e); } + captionTextView.setScrollY(0); captionTextView.setTextColor(0xffffffff); boolean visible = isActionBarVisible && (bottomLayout.getVisibility() == View.VISIBLE || pickerView.getVisibility() == View.VISIBLE); if (visible) { @@ -6123,14 +6230,22 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat public static boolean isShowingImage(MessageObject object) { boolean result = false; if (Instance != null) { - result = !Instance.pipAnimationInProgress && Instance.isVisible && !Instance.disableShowCheck && object != null && Instance.currentMessageObject != null && Instance.currentMessageObject.getId() == object.getId(); + result = !Instance.pipAnimationInProgress && Instance.isVisible && !Instance.disableShowCheck && object != null && Instance.currentMessageObject != null && Instance.currentMessageObject.getId() == object.getId() && Instance.currentMessageObject.getDialogId() == object.getDialogId(); } if (!result && PipInstance != null) { - result = PipInstance.isVisible && !PipInstance.disableShowCheck && object != null && PipInstance.currentMessageObject != null && PipInstance.currentMessageObject.getId() == object.getId(); + result = PipInstance.isVisible && !PipInstance.disableShowCheck && object != null && PipInstance.currentMessageObject != null && PipInstance.currentMessageObject.getId() == object.getId() && PipInstance.currentMessageObject.getDialogId() == object.getDialogId(); } return result; } + public static boolean isPlayingMessageInPip(MessageObject object) { + return PipInstance != null && object != null && PipInstance.currentMessageObject != null && PipInstance.currentMessageObject.getId() == object.getId() && PipInstance.currentMessageObject.getDialogId() == object.getDialogId(); + } + + public static boolean isPlayingMessage(MessageObject object) { + return Instance != null && !Instance.pipAnimationInProgress && Instance.isVisible && object != null && Instance.currentMessageObject != null && Instance.currentMessageObject.getId() == object.getId() && Instance.currentMessageObject.getDialogId() == object.getDialogId(); + } + public static boolean isShowingImage(TLRPC.FileLocation object) { boolean result = false; if (Instance != null) { @@ -6288,7 +6403,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat return false; } - final PlaceProviderObject object = provider.getPlaceForPhoto(messageObject, fileLocation, index); + final PlaceProviderObject object = provider.getPlaceForPhoto(messageObject, fileLocation, index, true); if (object == null && photos == null) { return false; } @@ -6354,11 +6469,23 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat toggleActionBar(true, false); } + seekToProgressPending2 = 0; + skipFirstBufferingProgress = false; + playerInjected = false; if (object != null) { disableShowCheck = true; animationInProgress = 1; if (messageObject != null) { currentAnimation = object.imageReceiver.getAnimation(); + if (currentAnimation != null && messageObject.isVideo()) { + object.imageReceiver.setAllowStartAnimation(false); + object.imageReceiver.stopAnimation(); + if (MediaController.getInstance().isPlayingMessage(messageObject)) { + seekToProgressPending2 = messageObject.audioProgress; + } + skipFirstBufferingProgress = injectingVideoPlayer == null && !FileLoader.getInstance(messageObject.currentAccount).isLoadingVideo(messageObject.getDocument(), true) && (currentAnimation.hasBitmap() || !FileLoader.getInstance(messageObject.currentAccount).isLoadingVideo(messageObject.getDocument(), false)); + currentAnimation = null; + } } onPhotoShow(messageObject, fileLocation, messages, documents, photos, index, object); @@ -6455,6 +6582,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat animationValues[0][5] = clipTop * object.scale; animationValues[0][6] = clipBottom * object.scale; animationValues[0][7] = animatingImageView.getRadius(); + animationValues[0][8] = clipVertical * object.scale; + animationValues[0][9] = clipHorizontal * object.scale; animationValues[1][0] = scale; animationValues[1][1] = scale; @@ -6464,6 +6593,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat animationValues[1][5] = 0; animationValues[1][6] = 0; animationValues[1][7] = 0; + animationValues[1][8] = 0; + animationValues[1][9] = 0; animatingImageView.setAnimationProgress(0); backgroundDrawable.setAlpha(0); @@ -6622,7 +6753,28 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (captionEditText.hideActionMode() && !fromEditMode) { return; } - + final PlaceProviderObject object = placeProvider.getPlaceForPhoto(currentMessageObject, currentFileLocation, currentIndex, true); + if (videoPlayer != null && object != null) { + AnimatedFileDrawable animation = object.imageReceiver.getAnimation(); + if (animation != null) { + if (textureUploaded) { + Bitmap bitmap = animation.getAnimatedBitmap(); + if (bitmap != null) { + try { + Bitmap src = videoTextureView.getBitmap(bitmap.getWidth(), bitmap.getHeight()); + Canvas canvas = new Canvas(bitmap); + canvas.drawBitmap(src, 0, 0, null); + src.recycle(); + } catch (Throwable e) { + FileLog.e(e); + } + } + } + animation.seekTo(videoPlayer.getCurrentPosition(), !FileLoader.getInstance(currentMessageObject.currentAccount).isLoadingVideo(currentMessageObject.getDocument(), true)); + object.imageReceiver.setAllowStartAnimation(true); + object.imageReceiver.startAnimation(); + } + } releasePlayer(); captionEditText.onDestroy(); parentChatActivity = null; @@ -6635,8 +6787,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat velocityTracker = null; } - final PlaceProviderObject object = placeProvider.getPlaceForPhoto(currentMessageObject, currentFileLocation, currentIndex); - if (isInline) { isInline = false; animationInProgress = 0; @@ -6713,6 +6863,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat animationValues[0][5] = 0; animationValues[0][6] = 0; animationValues[0][7] = 0; + animationValues[0][8] = 0; + animationValues[0][9] = 0; animationValues[1][0] = object.scale; animationValues[1][1] = object.scale; @@ -6722,6 +6874,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat animationValues[1][5] = clipTop * object.scale; animationValues[1][6] = clipBottom * object.scale; animationValues[1][7] = object.radius; + animationValues[1][8] = clipVertical * object.scale; + animationValues[1][9] = clipHorizontal * object.scale; ArrayList animators = new ArrayList<>(sendPhotoType == SELECT_TYPE_AVATAR ? 4 : 3); animators.add(ObjectAnimator.ofFloat(animatingImageView, AnimationProperties.CLIPPING_IMAGE_VIEW_PROGRESS, 0.0f, 1.0f)); @@ -7120,7 +7274,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat moveStartX = ev.getX(); moveStartY = ev.getY(); updateMinMax(scale); - if (translationX < minX && (currentEditMode != 0 || !rightImage.hasImage()) || translationX > maxX && (currentEditMode != 0 || !leftImage.hasImage())) { + if (translationX < minX && (currentEditMode != 0 || !rightImage.hasImageSet()) || translationX > maxX && (currentEditMode != 0 || !leftImage.hasImageSet())) { moveDx /= 3.0f; } if (maxY == 0 && minY == 0 && currentEditMode == 0 && sendPhotoType != SELECT_TYPE_AVATAR) { @@ -7199,11 +7353,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } if (currentEditMode == 0 && sendPhotoType != SELECT_TYPE_AVATAR) { - if ((translationX < minX - getContainerViewWidth() / 3 || velocity < -AndroidUtilities.dp(650)) && rightImage.hasImage()) { + if ((translationX < minX - getContainerViewWidth() / 3 || velocity < -AndroidUtilities.dp(650)) && rightImage.hasImageSet()) { goToNext(); return true; } - if ((translationX > maxX + getContainerViewWidth() / 3 || velocity > AndroidUtilities.dp(650)) && leftImage.hasImage()) { + if ((translationX > maxX + getContainerViewWidth() / 3 || velocity > AndroidUtilities.dp(650)) && leftImage.hasImageSet()) { goToPrev(); return true; } @@ -7535,21 +7689,20 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat translateX = maxX; } boolean drawTextureView = aspectRatioFrameLayout != null && aspectRatioFrameLayout.getVisibility() == View.VISIBLE; - if (centerImage.hasBitmapImage()) { + if (centerImage.hasBitmapImage() || drawTextureView && textureUploaded) { canvas.save(); canvas.translate(getContainerViewWidth() / 2 + getAdditionX(), getContainerViewHeight() / 2 + getAdditionY()); canvas.translate(translateX, currentTranslationY); canvas.scale(currentScale - scaleDiff, currentScale - scaleDiff); - int bitmapWidth = centerImage.getBitmapWidth(); - int bitmapHeight = centerImage.getBitmapHeight(); + int bitmapWidth; + int bitmapHeight; if (drawTextureView && textureUploaded) { - float scale1 = bitmapWidth / (float) bitmapHeight; - float scale2 = videoTextureView.getMeasuredWidth() / (float) videoTextureView.getMeasuredHeight(); - if (Math.abs(scale1 - scale2) > 0.01f) { - bitmapWidth = videoTextureView.getMeasuredWidth(); - bitmapHeight = videoTextureView.getMeasuredHeight(); - } + bitmapWidth = videoTextureView.getMeasuredWidth(); + bitmapHeight = videoTextureView.getMeasuredHeight(); + } else { + bitmapWidth = centerImage.getBitmapWidth(); + bitmapHeight = centerImage.getBitmapHeight(); } float scaleX = (float) getContainerViewWidth() / (float) bitmapWidth; @@ -7567,7 +7720,6 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat scaleX = (float) canvas.getWidth() / (float) bitmapWidth; scaleY = (float) canvas.getHeight() / (float) bitmapHeight; scale = scaleX > scaleY ? scaleY : scaleX; - //width = (int) (bitmapWidth * scale); height = (int) (bitmapHeight * scale); if (!videoCrossfadeStarted && textureUploaded) { videoCrossfadeStarted = true; @@ -7581,7 +7733,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat long newUpdateTime = System.currentTimeMillis(); long dt = newUpdateTime - videoCrossfadeAlphaLastTime; videoCrossfadeAlphaLastTime = newUpdateTime; - videoCrossfadeAlpha += dt / 200.0f; + videoCrossfadeAlpha += dt / (playerInjected ? 100.0f : 200.0f); containerView.invalidate(); if (videoCrossfadeAlpha > 1.0f) { videoCrossfadeAlpha = 1.0f; @@ -8441,6 +8593,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat audioFramesSize = 0; videoFramesSize = 0; + videoDuration = 0; for (int b = 0; b < boxes.size(); b++) { if (currentLoadingVideoRunnable != this) { return; @@ -8463,8 +8616,37 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } sampleSizes += sizes[a]; } - videoDuration = (float) mediaHeaderBox.getDuration() / (float) mediaHeaderBox.getTimescale(); - trackBitrate = (int) (sampleSizes * 8 / videoDuration); + + if (videoDuration == 0) { + videoDuration = (float) mediaHeaderBox.getDuration() / (float) mediaHeaderBox.getTimescale(); + if (videoDuration == 0) { + MediaPlayer player = null; + try { + player = new MediaPlayer(); + player.setDataSource(videoPath); + player.prepare(); + videoDuration = player.getDuration() / 1000.0f; + if (videoDuration < 0) { + videoDuration = 0; + } + } catch (Throwable ignore) { + + } finally { + try { + if (player != null) { + player.release(); + } + } catch (Throwable ignore) { + + } + } + } + } + if (videoDuration != 0) { + trackBitrate = (int) (sampleSizes * 8 / videoDuration); + } else { + trackBitrate = 400000; + } } catch (Exception e) { FileLog.e(e); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PollCreateActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PollCreateActivity.java index dbe0db44d..63cebe576 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PollCreateActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PollCreateActivity.java @@ -160,7 +160,7 @@ public class PollCreateActivity extends BaseFragment { TLRPC.TL_pollAnswer answer = new TLRPC.TL_pollAnswer(); answer.text = getFixedString(answers[a]); answer.option = new byte[1]; - answer.option[0] = (byte) poll.poll.answers.size(); + answer.option[0] = (byte) (48 + poll.poll.answers.size()); poll.poll.answers.add(answer); } poll.results = new TLRPC.TL_pollResults(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java index 0a60d747f..24235bee9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java @@ -59,7 +59,6 @@ import org.telegram.messenger.SecretChatHelper; import org.telegram.messenger.SendMessagesHelper; import org.telegram.messenger.UserObject; import org.telegram.messenger.ApplicationLoader; -import org.telegram.messenger.browser.Browser; import org.telegram.messenger.support.widget.LinearLayoutManager; import org.telegram.messenger.support.widget.RecyclerView; import org.telegram.tgnet.ConnectionsManager; @@ -218,7 +217,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. private PhotoViewer.PhotoViewerProvider provider = new PhotoViewer.EmptyPhotoViewerProvider() { @Override - public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index) { + public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index, boolean needPreview) { if (fileLocation == null) { return null; } @@ -447,7 +446,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. if (user == null) { return; } - if (!isBot) { + if (!isBot || MessagesController.isSupportUser(user)) { AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); if (!userBlocked) { builder.setMessage(LocaleController.getString("AreYouSureBlockContact", R.string.AreYouSureBlockContact)); @@ -598,6 +597,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. final AlertDialog progressDialog[] = new AlertDialog[] {new AlertDialog(getParentActivity(), 3)}; TLRPC.TL_messages_getStatsURL req = new TLRPC.TL_messages_getStatsURL(); req.peer = MessagesController.getInstance(currentAccount).getInputPeer(did); + req.params = ""; int requestId = ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { try { progressDialog[0].dismiss(); @@ -607,7 +607,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. progressDialog[0] = null; if (response != null) { TLRPC.TL_statsURL url = (TLRPC.TL_statsURL) response; - Browser.openUrl(getParentActivity(), url.url); + presentFragment(new WebviewActivity(url.url, -chat_id)); } })); if (progressDialog[0] == null) { @@ -2597,8 +2597,10 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. if (user.id == UserConfig.getInstance(currentAccount).getClientUserId()) { newString2 = LocaleController.getString("ChatYourSelf", R.string.ChatYourSelf); newString = LocaleController.getString("ChatYourSelfName", R.string.ChatYourSelfName); - } else if (user.id == 333000 || user.id == 777000) { + } else if (user.id == 333000 || user.id == 777000 || user.id == 42777) { newString2 = LocaleController.getString("ServiceNotifications", R.string.ServiceNotifications); + } else if (MessagesController.isSupportUser(user)) { + newString2 = LocaleController.getString("SupportStatus", R.string.SupportStatus); } else if (isBot) { newString2 = LocaleController.getString("Bot", R.string.Bot); } else { @@ -2753,31 +2755,36 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. ActionBarMenuItem item = null; if (user_id != 0) { if (UserConfig.getInstance(currentAccount).getClientUserId() != user_id) { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(user_id); + if (user == null) { + return; + } if (userInfo != null && userInfo.phone_calls_available) { callItem = menu.addItem(call_item, R.drawable.ic_call_white_24dp); } - if (ContactsController.getInstance(currentAccount).contactsDict.get(user_id) == null) { - TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(user_id); - if (user == null) { - return; - } + if (isBot || ContactsController.getInstance(currentAccount).contactsDict.get(user_id) == null) { item = menu.addItem(10, R.drawable.ic_ab_other); - if (isBot) { - if (!user.bot_nochats) { - item.addSubItem(invite_to_group, LocaleController.getString("BotInvite", R.string.BotInvite)); + if (MessagesController.isSupportUser(user)) { + if (userBlocked) { + item.addSubItem(block_contact, LocaleController.getString("Unblock", R.string.Unblock)); } - item.addSubItem(share, LocaleController.getString("BotShare", R.string.BotShare)); - } - - if (user.phone != null && user.phone.length() != 0) { - item.addSubItem(add_contact, LocaleController.getString("AddContact", R.string.AddContact)); - item.addSubItem(share_contact, LocaleController.getString("ShareContact", R.string.ShareContact)); - item.addSubItem(block_contact, !userBlocked ? LocaleController.getString("BlockContact", R.string.BlockContact) : LocaleController.getString("Unblock", R.string.Unblock)); } else { if (isBot) { - item.addSubItem(block_contact, !userBlocked ? LocaleController.getString("BotStop", R.string.BotStop) : LocaleController.getString("BotRestart", R.string.BotRestart)); - } else { + if (!user.bot_nochats) { + item.addSubItem(invite_to_group, LocaleController.getString("BotInvite", R.string.BotInvite)); + } + item.addSubItem(share, LocaleController.getString("BotShare", R.string.BotShare)); + } + if (user.phone != null && user.phone.length() != 0) { + item.addSubItem(add_contact, LocaleController.getString("AddContact", R.string.AddContact)); + item.addSubItem(share_contact, LocaleController.getString("ShareContact", R.string.ShareContact)); item.addSubItem(block_contact, !userBlocked ? LocaleController.getString("BlockContact", R.string.BlockContact) : LocaleController.getString("Unblock", R.string.Unblock)); + } else { + if (isBot) { + item.addSubItem(block_contact, !userBlocked ? LocaleController.getString("BotStop", R.string.BotStop) : LocaleController.getString("BotRestart", R.string.BotRestart)); + } else { + item.addSubItem(block_contact, !userBlocked ? LocaleController.getString("BlockContact", R.string.BlockContact) : LocaleController.getString("Unblock", R.string.Unblock)); + } } } } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SecretMediaViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/SecretMediaViewer.java index 5a89bed7b..0e5822c30 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SecretMediaViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SecretMediaViewer.java @@ -319,6 +319,8 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD private boolean disableShowCheck; private PhotoViewer.PhotoViewerProvider currentProvider; + private int playerRetryPlayCount; + private boolean textureUploaded; private boolean videoCrossfadeStarted; private float videoCrossfadeAlpha; @@ -523,7 +525,12 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD @Override public void onError(Exception e) { - FileLog.e(e); + if (playerRetryPlayCount > 0) { + playerRetryPlayCount--; + AndroidUtilities.runOnUIThread(() -> preparePlayer(file), 100); + } else { + FileLog.e(e); + } } @Override @@ -563,7 +570,8 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD private void releasePlayer() { if (videoPlayer != null) { - videoPlayer.releasePlayer(); + playerRetryPlayCount = 0; + videoPlayer.releasePlayer(true); videoPlayer = null; } try { @@ -719,7 +727,7 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD if (parentActivity == null || messageObject == null || !messageObject.needDrawBluredPreview() || provider == null) { return; } - final PhotoViewer.PlaceProviderObject object = provider.getPlaceForPhoto(messageObject, null, 0); + final PhotoViewer.PlaceProviderObject object = provider.getPlaceForPhoto(messageObject, null, 0, true); if (object == null) { return; } @@ -815,6 +823,7 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD centerImage.setImage(document, null, currentThumb != null ? new BitmapDrawable(currentThumb.bitmap) : null, -1, null, messageObject, 1); secretDeleteTimer.setDestroyTime((long) messageObject.messageOwner.destroyTime * 1000, messageObject.messageOwner.ttl, false); } else { + playerRetryPlayCount = 1; actionBar.setTitle(LocaleController.getString("DisappearingVideo", R.string.DisappearingVideo)); File f = new File(messageObject.messageOwner.attachPath); if (f.exists()) { @@ -1177,7 +1186,7 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD if (currentProvider == null || currentMessageObject.messageOwner.media.photo instanceof TLRPC.TL_photoEmpty || currentMessageObject.messageOwner.media.document instanceof TLRPC.TL_documentEmpty) { object = null; } else { - object = currentProvider.getPlaceForPhoto(currentMessageObject, null, 0); + object = currentProvider.getPlaceForPhoto(currentMessageObject, null, 0, true); } if (videoPlayer != null) { videoPlayer.pause(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java index 6001e2081..d32d3e70a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java @@ -30,16 +30,9 @@ import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.content.FileProvider; -import android.text.Html; -import android.text.Spannable; -import android.text.SpannableString; import android.text.TextUtils; -import android.text.method.LinkMovementMethod; -import android.text.style.URLSpan; -import android.util.Base64; import android.util.TypedValue; import android.view.Gravity; -import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewOutlineProvider; @@ -69,7 +62,6 @@ import org.telegram.messenger.browser.Browser; import org.telegram.messenger.support.widget.LinearLayoutManager; import org.telegram.messenger.support.widget.RecyclerView; import org.telegram.tgnet.ConnectionsManager; -import org.telegram.tgnet.SerializedData; import org.telegram.tgnet.TLRPC; import org.telegram.messenger.FileLog; import org.telegram.messenger.MessagesController; @@ -90,6 +82,7 @@ import org.telegram.ui.Cells.TextInfoPrivacyCell; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; +import org.telegram.ui.Components.AlertsCreator; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.ImageUpdater; import org.telegram.ui.Components.BackupImageView; @@ -99,7 +92,6 @@ import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.RadialProgressView; import org.telegram.ui.Components.RecyclerListView; -import org.telegram.ui.Components.URLSpanNoUnderline; import org.telegram.ui.Components.voip.VoIPHelper; import java.io.BufferedInputStream; @@ -160,7 +152,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter private PhotoViewer.PhotoViewerProvider provider = new PhotoViewer.EmptyPhotoViewerProvider() { @Override - public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index) { + public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index, boolean needPreview) { if (fileLocation == null) { return null; } @@ -192,18 +184,6 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter } }; - private static class LinkMovementMethodMy extends LinkMovementMethod { - @Override - public boolean onTouchEvent(@NonNull TextView widget, @NonNull Spannable buffer, @NonNull MotionEvent event) { - try { - return super.onTouchEvent(widget, buffer, event); - } catch (Exception e) { - FileLog.e(e); - } - return false; - } - } - @Override public boolean onFragmentCreate() { super.onFragmentCreate(); @@ -269,15 +249,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter } else if (id == edit_name) { presentFragment(new ChangeNameActivity()); } else if (id == logout) { - if (getParentActivity() == null) { - return; - } - AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); - builder.setMessage(LocaleController.getString("AreYouSureLogout", R.string.AreYouSureLogout)); - builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); - builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), (dialogInterface, i) -> MessagesController.getInstance(currentAccount).performLogout(1)); - builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showDialog(builder.create()); + presentFragment(new LogoutActivity()); } } }); @@ -409,40 +381,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter Integer tag = (Integer) v2.getTag(); switch (tag) { case 0: { - if (getParentActivity() == null) { - return; - } - final TextView message = new TextView(getParentActivity()); - Spannable spanned = new SpannableString(Html.fromHtml(LocaleController.getString("AskAQuestionInfo", R.string.AskAQuestionInfo).replace("\n", "
    "))); - URLSpan[] spans = spanned.getSpans(0, spanned.length(), URLSpan.class); - for (int i = 0; i < spans.length; i++) { - URLSpan span = spans[i]; - int start = spanned.getSpanStart(span); - int end = spanned.getSpanEnd(span); - spanned.removeSpan(span); - span = new URLSpanNoUnderline(span.getURL()) { - @Override - public void onClick(View widget) { - dismissCurrentDialig(); - super.onClick(widget); - } - }; - spanned.setSpan(span, start, end, 0); - } - message.setText(spanned); - message.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); - message.setLinkTextColor(Theme.getColor(Theme.key_dialogTextLink)); - message.setHighlightColor(Theme.getColor(Theme.key_dialogLinkSelection)); - message.setPadding(AndroidUtilities.dp(23), 0, AndroidUtilities.dp(23), 0); - message.setMovementMethod(new LinkMovementMethodMy()); - message.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); - - AlertDialog.Builder builder1 = new AlertDialog.Builder(getParentActivity()); - builder1.setView(message); - builder1.setTitle(LocaleController.getString("AskAQuestion", R.string.AskAQuestion)); - builder1.setPositiveButton(LocaleController.getString("AskButton", R.string.AskButton), (dialogInterface, i) -> performAskAQuestion()); - builder1.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showDialog(builder1.create()); + showDialog(AlertsCreator.createSupportAlert(SettingsActivity.this)); break; } case 1: @@ -536,6 +475,8 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter SharedConfig.toggleInappCamera(); } else if (which == 6) { MessagesStorage.getInstance(currentAccount).clearSentMedia(); + SharedPreferences.Editor editor = MessagesController.getGlobalMainSettings().edit(); + SharedConfig.setNoSoundHintShowed(false); } else if (which == 7) { VoIPHelper.showCallDebugSettings(getParentActivity()); } else if (which == 8) { @@ -830,80 +771,6 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter } } - private void performAskAQuestion() { - final SharedPreferences preferences = MessagesController.getMainSettings(currentAccount); - int uid = preferences.getInt("support_id", 0); - TLRPC.User supportUser = null; - if (uid != 0) { - supportUser = MessagesController.getInstance(currentAccount).getUser(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.TLdeserialize(data, data.readInt32(false), false); - if (supportUser != null && supportUser.id == 333000) { - supportUser = null; - } - data.cleanup(); - } - } catch (Exception e) { - FileLog.e(e); - supportUser = null; - } - } - } - } - if (supportUser == null) { - final AlertDialog progressDialog = new AlertDialog(getParentActivity(), 3); - progressDialog.setCanCacnel(false); - progressDialog.show(); - TLRPC.TL_help_getSupport req = new TLRPC.TL_help_getSupport(); - ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> { - if (error == null) { - - final TLRPC.TL_help_support res = (TLRPC.TL_help_support) response; - AndroidUtilities.runOnUIThread(() -> { - 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(); - data.cleanup(); - try { - progressDialog.dismiss(); - } catch (Exception e) { - FileLog.e(e); - } - ArrayList users = new ArrayList<>(); - users.add(res.user); - MessagesStorage.getInstance(currentAccount).putUsersAndChats(users, null, true, true); - MessagesController.getInstance(currentAccount).putUser(res.user, false); - Bundle args = new Bundle(); - args.putInt("user_id", res.user.id); - presentFragment(new ChatActivity(args)); - }); - } else { - AndroidUtilities.runOnUIThread(() -> { - try { - progressDialog.dismiss(); - } catch (Exception e) { - FileLog.e(e); - } - }); - } - }); - } else { - MessagesController.getInstance(currentAccount).putUser(supportUser, true); - Bundle args = new Bundle(); - args.putInt("user_id", supportUser.id); - presentFragment(new ChatActivity(args)); - } - } - @Override public void onActivityResultFragment(int requestCode, int resultCode, Intent data) { imageUpdater.onActivityResult(requestCode, resultCode, data); @@ -1062,6 +929,9 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter private void updateUserData() { TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(UserConfig.getInstance(currentAccount).getClientUserId()); + if (user == null) { + return; + } TLRPC.FileLocation photo = null; TLRPC.FileLocation photoBig = null; if (user.photo != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ThemeActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ThemeActivity.java index 48f9a8ac7..d53b16c23 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ThemeActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ThemeActivity.java @@ -110,7 +110,6 @@ public class ThemeActivity extends BaseFragment implements NotificationCenter.No private int directShareRow; private int raiseToSpeakRow; private int sendByEnterRow; - private int autoplayGifsRow; private int saveToGalleryRow; private int enableAnimationsRow; private int settings2Row; @@ -338,7 +337,7 @@ public class ThemeActivity extends BaseFragment implements NotificationCenter.No message.to_id = new TLRPC.TL_peerUser(); message.to_id.user_id = UserConfig.getInstance(currentAccount).getClientUserId(); MessageObject message2 = new MessageObject(currentAccount, message, true); - message2.customReplyName = "Lucio"; + message2.customReplyName = LocaleController.getString("FontSizePreviewName", R.string.FontSizePreviewName); message2.eventId = 1; message2.resetLayout(); message2.replyMessageObject = replyMessageObject; @@ -428,7 +427,6 @@ public class ThemeActivity extends BaseFragment implements NotificationCenter.No enableAnimationsRow = -1; raiseToSpeakRow = -1; sendByEnterRow = -1; - autoplayGifsRow = -1; saveToGalleryRow = -1; settings2Row = -1; stickersRow = -1; @@ -452,7 +450,6 @@ public class ThemeActivity extends BaseFragment implements NotificationCenter.No enableAnimationsRow = rowCount++; raiseToSpeakRow = rowCount++; sendByEnterRow = rowCount++; - autoplayGifsRow = rowCount++; saveToGalleryRow = rowCount++; settings2Row = rowCount++; stickersRow = rowCount++; @@ -632,11 +629,6 @@ public class ThemeActivity extends BaseFragment implements NotificationCenter.No if (view instanceof TextCheckCell) { ((TextCheckCell) view).setChecked(SharedConfig.raiseToSpeak); } - } else if (position == autoplayGifsRow) { - SharedConfig.toggleAutoplayGifs(); - if (view instanceof TextCheckCell) { - ((TextCheckCell) view).setChecked(SharedConfig.autoplayGifs); - } } else if (position == saveToGalleryRow) { SharedConfig.toggleSaveToGallery(); if (view instanceof TextCheckCell) { @@ -1336,8 +1328,6 @@ public class ThemeActivity extends BaseFragment implements NotificationCenter.No textCheckCell.setTextAndCheck(LocaleController.getString("SendByEnter", R.string.SendByEnter), preferences.getBoolean("send_by_enter", false), true); } else if (position == saveToGalleryRow) { textCheckCell.setTextAndCheck(LocaleController.getString("SaveToGallerySettings", R.string.SaveToGallerySettings), SharedConfig.saveToGallery, false); - } else if (position == autoplayGifsRow) { - textCheckCell.setTextAndCheck(LocaleController.getString("AutoplayGifs", R.string.AutoplayGifs), SharedConfig.autoplayGifs, true); } else if (position == raiseToSpeakRow) { textCheckCell.setTextAndCheck(LocaleController.getString("RaiseToSpeak", R.string.RaiseToSpeak), SharedConfig.raiseToSpeak, true); } else if (position == customTabsRow) { @@ -1382,8 +1372,8 @@ public class ThemeActivity extends BaseFragment implements NotificationCenter.No } else if (position == automaticBrightnessRow) { return 6; } else if (position == scheduleLocationRow || position == enableAnimationsRow || position == sendByEnterRow || - position == saveToGalleryRow || position == autoplayGifsRow || position == raiseToSpeakRow || - position == customTabsRow || position == directShareRow) { + position == saveToGalleryRow || position == raiseToSpeakRow || position == customTabsRow || + position == directShareRow) { return 7; } else if (position == textSizeRow) { return 8; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/WebviewActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/WebviewActivity.java index 5a17ce03d..ed96ab12b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/WebviewActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/WebviewActivity.java @@ -40,7 +40,9 @@ import org.telegram.messenger.MessagesController; import org.telegram.messenger.R; import org.telegram.messenger.Utilities; import org.telegram.messenger.browser.Browser; +import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.SerializedData; +import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; @@ -60,15 +62,23 @@ public class WebviewActivity extends BaseFragment { private ContextProgressView progressView; private String currentUrl; + private long currentDialogId; private String currentBot; private String currentGame; private String linkToCopy; private MessageObject currentMessageObject; private String short_param; + private boolean loadStats; + private final static int share = 1; private final static int open_in = 2; + private static final int TYPE_GAME = 0; + private static final int TYPE_STAT = 1; + + private int type; + private class TelegramWebviewProxy { @JavascriptInterface public void postEvent(final String eventName, final String eventData) { @@ -111,12 +121,28 @@ public class WebviewActivity extends BaseFragment { currentMessageObject = messageObject; short_param = startParam; linkToCopy = "https://" + MessagesController.getInstance(currentAccount).linkPrefix + "/" + currentBot + (TextUtils.isEmpty(startParam) ? "" : "?game=" + startParam); + type = TYPE_GAME; } + public WebviewActivity(String statUrl, long did) { + super(); + currentUrl = statUrl; + currentDialogId = did; + type = TYPE_STAT; + } + + /*@Override + protected void onTransitionAnimationStart(boolean isOpen, boolean backward) { + if (!isOpen) { + + } + }*/ + @Override public void onFragmentDestroy() { super.onFragmentDestroy(); AndroidUtilities.cancelRunOnUIThread(typingRunnable); + webView.setLayerType(View.LAYER_TYPE_NONE, null); typingRunnable = null; try { ViewParent parent = webView.getParent(); @@ -138,8 +164,7 @@ public class WebviewActivity extends BaseFragment { swipeBackEnabled = false; actionBar.setBackButtonImage(R.drawable.ic_ab_back); actionBar.setAllowOverlayTitle(true); - actionBar.setTitle(currentGame); - actionBar.setSubtitle("@" + currentBot); + actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @Override public void onItemClick(int id) { @@ -155,15 +180,35 @@ public class WebviewActivity extends BaseFragment { }); ActionBarMenu menu = actionBar.createMenu(); progressItem = menu.addItemWithWidth(share, R.drawable.share, AndroidUtilities.dp(54)); - progressView = new ContextProgressView(context, 1); - progressView.setAlpha(0.0f); - progressView.setScaleX(0.1f); - progressView.setScaleY(0.1f); - progressView.setVisibility(View.INVISIBLE); - progressItem.addView(progressView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + if (type == TYPE_GAME) { + ActionBarMenuItem menuItem = menu.addItem(0, R.drawable.ic_ab_other); + menuItem.addSubItem(open_in, LocaleController.getString("OpenInExternalApp", R.string.OpenInExternalApp)); - ActionBarMenuItem menuItem = menu.addItem(0, R.drawable.ic_ab_other); - menuItem.addSubItem(open_in, LocaleController.getString("OpenInExternalApp", R.string.OpenInExternalApp)); + actionBar.setTitle(currentGame); + actionBar.setSubtitle("@" + currentBot); + + progressView = new ContextProgressView(context, 1); + progressItem.addView(progressView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + progressView.setAlpha(0.0f); + progressView.setScaleX(0.1f); + progressView.setScaleY(0.1f); + progressView.setVisibility(View.INVISIBLE); + } else if (type == TYPE_STAT) { + actionBar.setBackgroundColor(Theme.getColor(Theme.key_player_actionBar)); + actionBar.setItemsColor(Theme.getColor(Theme.key_player_actionBarItems), false); + actionBar.setItemsBackgroundColor(Theme.getColor(Theme.key_player_actionBarSelector), false); + actionBar.setTitleColor(Theme.getColor(Theme.key_player_actionBarTitle)); + actionBar.setSubtitleColor(Theme.getColor(Theme.key_player_actionBarSubtitle)); + actionBar.setTitle(LocaleController.getString("Statistics", R.string.Statistics)); + + progressView = new ContextProgressView(context, 3); + progressItem.addView(progressView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + progressView.setAlpha(1.0f); + progressView.setScaleX(1.0f); + progressView.setScaleY(1.0f); + progressView.setVisibility(View.VISIBLE); + progressItem.getImageView().setVisibility(View.GONE); + } webView = new WebView(context); webView.getSettings().setJavaScriptEnabled(true); @@ -171,12 +216,17 @@ public class WebviewActivity extends BaseFragment { fragmentView = new FrameLayout(context); FrameLayout frameLayout = (FrameLayout) fragmentView; + if (Build.VERSION.SDK_INT >= 19) { + webView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + } if (Build.VERSION.SDK_INT >= 21) { webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); CookieManager cookieManager = CookieManager.getInstance(); cookieManager.setAcceptThirdPartyCookies(webView, true); - webView.addJavascriptInterface(new TelegramWebviewProxy(), "TelegramWebviewProxy"); + if (type == TYPE_GAME) { + webView.addJavascriptInterface(new TelegramWebviewProxy(), "TelegramWebviewProxy"); + } } webView.setWebViewClient(new WebViewClient() { @@ -187,15 +237,24 @@ public class WebviewActivity extends BaseFragment { } Uri uri = Uri.parse(url); if ("tg".equals(uri.getScheme())) { - finishFragment(false); - try { - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - ComponentName componentName = new ComponentName(ApplicationLoader.applicationContext.getPackageName(), LaunchActivity.class.getName()); - intent.setComponent(componentName); - intent.putExtra(android.provider.Browser.EXTRA_APPLICATION_ID, ApplicationLoader.applicationContext.getPackageName()); - ApplicationLoader.applicationContext.startActivity(intent); - } catch (Exception e) { - FileLog.e(e); + if (type == TYPE_STAT) { + try { + uri = Uri.parse(url.replace("tg:statsrefresh", "tg://telegram.org")); + reloadStats(uri.getQueryParameter("params")); + } catch (Throwable e) { + FileLog.e(e); + } + } else { + finishFragment(false); + try { + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + ComponentName componentName = new ComponentName(ApplicationLoader.applicationContext.getPackageName(), LaunchActivity.class.getName()); + intent.setComponent(componentName); + intent.putExtra(android.provider.Browser.EXTRA_APPLICATION_ID, ApplicationLoader.applicationContext.getPackageName()); + ApplicationLoader.applicationContext.startActivity(intent); + } catch (Exception e) { + FileLog.e(e); + } } return true; } @@ -218,25 +277,37 @@ public class WebviewActivity extends BaseFragment { @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); - - progressItem.getImageView().setVisibility(View.VISIBLE); - progressItem.setEnabled(true); - AnimatorSet animatorSet = new AnimatorSet(); - animatorSet.playTogether( - ObjectAnimator.ofFloat(progressView, "scaleX", 1.0f, 0.1f), - ObjectAnimator.ofFloat(progressView, "scaleY", 1.0f, 0.1f), - ObjectAnimator.ofFloat(progressView, "alpha", 1.0f, 0.0f), - ObjectAnimator.ofFloat(progressItem.getImageView(), "scaleX", 0.0f, 1.0f), - ObjectAnimator.ofFloat(progressItem.getImageView(), "scaleY", 0.0f, 1.0f), - ObjectAnimator.ofFloat(progressItem.getImageView(), "alpha", 0.0f, 1.0f)); - animatorSet.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animator) { - progressView.setVisibility(View.INVISIBLE); + if (progressView != null && progressView.getVisibility() == View.VISIBLE) { + AnimatorSet animatorSet = new AnimatorSet(); + if (type == TYPE_GAME) { + progressItem.getImageView().setVisibility(View.VISIBLE); + progressItem.setEnabled(true); + animatorSet.playTogether( + ObjectAnimator.ofFloat(progressView, "scaleX", 1.0f, 0.1f), + ObjectAnimator.ofFloat(progressView, "scaleY", 1.0f, 0.1f), + ObjectAnimator.ofFloat(progressView, "alpha", 1.0f, 0.0f), + ObjectAnimator.ofFloat(progressItem.getImageView(), "scaleX", 0.0f, 1.0f), + ObjectAnimator.ofFloat(progressItem.getImageView(), "scaleY", 0.0f, 1.0f), + ObjectAnimator.ofFloat(progressItem.getImageView(), "alpha", 0.0f, 1.0f)); + } else { + animatorSet.playTogether( + ObjectAnimator.ofFloat(progressView, "scaleX", 1.0f, 0.1f), + ObjectAnimator.ofFloat(progressView, "scaleY", 1.0f, 0.1f), + ObjectAnimator.ofFloat(progressView, "alpha", 1.0f, 0.0f)); } - }); - animatorSet.setDuration(150); - animatorSet.start(); + animatorSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animator) { + if (type == TYPE_STAT) { + progressItem.setVisibility(View.GONE); + } else { + progressView.setVisibility(View.INVISIBLE); + } + } + }); + animatorSet.setDuration(150); + animatorSet.start(); + } } }); @@ -270,6 +341,23 @@ public class WebviewActivity extends BaseFragment { return true; } + private void reloadStats(String params) { + if (loadStats) { + return; + } + loadStats = true; + TLRPC.TL_messages_getStatsURL req = new TLRPC.TL_messages_getStatsURL(); + req.peer = MessagesController.getInstance(currentAccount).getInputPeer((int) currentDialogId); + req.params = params != null ? params : ""; + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + loadStats = false; + if (response != null) { + TLRPC.TL_statsURL url = (TLRPC.TL_statsURL) response; + webView.loadUrl(currentUrl = url.url); + } + })); + } + public static void openGameInBrowser(String urlStr, MessageObject messageObject, Activity parentActivity, String short_name, String username) { try { String url = urlStr; @@ -315,18 +403,35 @@ public class WebviewActivity extends BaseFragment { @Override public ThemeDescription[] getThemeDescriptions() { - return new ThemeDescription[]{ - new ThemeDescription(fragmentView, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundWhite), + if (type == TYPE_GAME) { + return new ThemeDescription[]{ + new ThemeDescription(fragmentView, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundWhite), - new ThemeDescription(actionBar, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_actionBarDefault), - new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_ITEMSCOLOR, null, null, null, null, Theme.key_actionBarDefaultIcon), - new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_TITLECOLOR, null, null, null, null, Theme.key_actionBarDefaultTitle), - new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SELECTORCOLOR, null, null, null, null, Theme.key_actionBarDefaultSelector), - new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SUBMENUBACKGROUND, null, null, null, null, Theme.key_actionBarDefaultSubmenuBackground), - new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SUBMENUITEM, null, null, null, null, Theme.key_actionBarDefaultSubmenuItem), + new ThemeDescription(actionBar, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_actionBarDefault), + new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_ITEMSCOLOR, null, null, null, null, Theme.key_actionBarDefaultIcon), + new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_TITLECOLOR, null, null, null, null, Theme.key_actionBarDefaultTitle), + new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SELECTORCOLOR, null, null, null, null, Theme.key_actionBarDefaultSelector), + new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SUBMENUBACKGROUND, null, null, null, null, Theme.key_actionBarDefaultSubmenuBackground), + new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SUBMENUITEM, null, null, null, null, Theme.key_actionBarDefaultSubmenuItem), - new ThemeDescription(progressView, 0, null, null, null, null, Theme.key_contextProgressInner2), - new ThemeDescription(progressView, 0, null, null, null, null, Theme.key_contextProgressOuter2), - }; + new ThemeDescription(progressView, 0, null, null, null, null, Theme.key_contextProgressInner2), + new ThemeDescription(progressView, 0, null, null, null, null, Theme.key_contextProgressOuter2), + }; + } else { + return new ThemeDescription[]{ + new ThemeDescription(fragmentView, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundWhite), + + new ThemeDescription(actionBar, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_player_actionBar), + new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_ITEMSCOLOR, null, null, null, null, Theme.key_player_actionBarItems), + new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_TITLECOLOR, null, null, null, null, Theme.key_player_actionBarTitle), + new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SUBTITLECOLOR, null, null, null, null, Theme.key_player_actionBarTitle), + new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SELECTORCOLOR, null, null, null, null, Theme.key_player_actionBarSelector), + new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SUBMENUBACKGROUND, null, null, null, null, Theme.key_actionBarDefaultSubmenuBackground), + new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SUBMENUITEM, null, null, null, null, Theme.key_actionBarDefaultSubmenuItem), + + new ThemeDescription(progressView, 0, null, null, null, null, Theme.key_contextProgressInner4), + new ThemeDescription(progressView, 0, null, null, null, null, Theme.key_contextProgressOuter4), + }; + } } } diff --git a/TMessagesProj/src/main/res/drawable-hdpi/menu_clearcache.png b/TMessagesProj/src/main/res/drawable-hdpi/menu_clearcache.png new file mode 100644 index 000000000..1035425cf Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/menu_clearcache.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/menu_newphone.png b/TMessagesProj/src/main/res/drawable-hdpi/menu_newphone.png new file mode 100644 index 000000000..2e8b1c890 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/menu_newphone.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/menu_passcode.png b/TMessagesProj/src/main/res/drawable-hdpi/menu_passcode.png new file mode 100644 index 000000000..d10eb14b2 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/menu_passcode.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/menu_support.png b/TMessagesProj/src/main/res/drawable-hdpi/menu_support.png new file mode 100644 index 000000000..f645eaffa Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/menu_support.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/tooltip_arrow.png b/TMessagesProj/src/main/res/drawable-hdpi/tooltip_arrow.png new file mode 100644 index 000000000..588efcfd5 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/tooltip_arrow.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/tooltip_sound.png b/TMessagesProj/src/main/res/drawable-hdpi/tooltip_sound.png new file mode 100644 index 000000000..639b9eab6 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/tooltip_sound.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/video_muted.png b/TMessagesProj/src/main/res/drawable-hdpi/video_muted.png new file mode 100644 index 000000000..121038220 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/video_muted.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/menu_clearcache.png b/TMessagesProj/src/main/res/drawable-mdpi/menu_clearcache.png new file mode 100644 index 000000000..db2e4a600 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/menu_clearcache.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/menu_newphone.png b/TMessagesProj/src/main/res/drawable-mdpi/menu_newphone.png new file mode 100644 index 000000000..fedd598d0 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/menu_newphone.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/menu_passcode.png b/TMessagesProj/src/main/res/drawable-mdpi/menu_passcode.png new file mode 100644 index 000000000..179c5ff1b Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/menu_passcode.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/menu_support.png b/TMessagesProj/src/main/res/drawable-mdpi/menu_support.png new file mode 100644 index 000000000..4c08b8f86 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/menu_support.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/tooltip_arrow.png b/TMessagesProj/src/main/res/drawable-mdpi/tooltip_arrow.png new file mode 100644 index 000000000..9fe5b5b5c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/tooltip_arrow.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/tooltip_sound.png b/TMessagesProj/src/main/res/drawable-mdpi/tooltip_sound.png new file mode 100644 index 000000000..fedce0179 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/tooltip_sound.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/video_muted.png b/TMessagesProj/src/main/res/drawable-mdpi/video_muted.png new file mode 100644 index 000000000..6db8f9a12 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/video_muted.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/menu_clearcache.png b/TMessagesProj/src/main/res/drawable-xhdpi/menu_clearcache.png new file mode 100644 index 000000000..8047f6565 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/menu_clearcache.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/menu_newphone.png b/TMessagesProj/src/main/res/drawable-xhdpi/menu_newphone.png new file mode 100644 index 000000000..c1bfe53ac Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/menu_newphone.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/menu_passcode.png b/TMessagesProj/src/main/res/drawable-xhdpi/menu_passcode.png new file mode 100644 index 000000000..016267096 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/menu_passcode.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/menu_support.png b/TMessagesProj/src/main/res/drawable-xhdpi/menu_support.png new file mode 100644 index 000000000..bc8e90fc6 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/menu_support.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/tooltip_arrow.png b/TMessagesProj/src/main/res/drawable-xhdpi/tooltip_arrow.png new file mode 100644 index 000000000..c6fc8bacd Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/tooltip_arrow.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/tooltip_sound.png b/TMessagesProj/src/main/res/drawable-xhdpi/tooltip_sound.png new file mode 100644 index 000000000..a120bba45 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/tooltip_sound.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/video_muted.png b/TMessagesProj/src/main/res/drawable-xhdpi/video_muted.png new file mode 100644 index 000000000..5fcc6b0ed Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/video_muted.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/menu_clearcache.png b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_clearcache.png new file mode 100644 index 000000000..077284a15 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_clearcache.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/menu_newphone.png b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_newphone.png new file mode 100644 index 000000000..0a79e34d6 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_newphone.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/menu_passcode.png b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_passcode.png new file mode 100644 index 000000000..3c9a00d71 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_passcode.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/menu_support.png b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_support.png new file mode 100644 index 000000000..273c37d65 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_support.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/tooltip_arrow.png b/TMessagesProj/src/main/res/drawable-xxhdpi/tooltip_arrow.png new file mode 100644 index 000000000..0adbe4b60 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/tooltip_arrow.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/tooltip_sound.png b/TMessagesProj/src/main/res/drawable-xxhdpi/tooltip_sound.png new file mode 100644 index 000000000..624f09326 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/tooltip_sound.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/video_muted.png b/TMessagesProj/src/main/res/drawable-xxhdpi/video_muted.png new file mode 100644 index 000000000..ca726a405 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/video_muted.png differ diff --git a/TMessagesProj/src/main/res/drawable/photoview_placeholder.png b/TMessagesProj/src/main/res/drawable/photoview_placeholder.png index 979ea46d1..b97c375ae 100644 Binary files a/TMessagesProj/src/main/res/drawable/photoview_placeholder.png and b/TMessagesProj/src/main/res/drawable/photoview_placeholder.png differ diff --git a/TMessagesProj/src/main/res/values-ar/strings.xml b/TMessagesProj/src/main/res/values-ar/strings.xml index 78ae90347..90653ebf8 100644 --- a/TMessagesProj/src/main/res/values-ar/strings.xml +++ b/TMessagesProj/src/main/res/values-ar/strings.xml @@ -227,7 +227,7 @@ تمّت ترقية هذه المجموعة إلى مجموعة خارقة تمت ترقية %1$s إلى مجموعة خارقة لن يتمكن المستخدمون الذين أزالهم المشرفون من المجموعة إعادة الإنضمام عبر روابط الدعوة. - لن يتمكن المستخدمون الذين أزالهم المشرفون من القناة إعادة الإنضمام عبر روابط الدعوة. + لن يتمكن المستخدمون الذين أزالهم المشرفون من القناة من الانضمام إليها حتى عبر روابط الدعوة الخاصة. قناة جديدة اسم القناة إضافة جهات اتصالك لقناتك @@ -465,7 +465,7 @@ **لا توجد أحداث هنا بعد**\n\nلم يَقم أعضاءُ المجموعة أو\nمشرفوها باتخاذ أيِّ إجراءات\nخلال الـ 48 ساعة الماضية. **ما من أحداث هنا بعد**\n\nلم يقم مشرفو القناة\nباتخاذ أي إجراءات خلال\nالـ 48 ساعة الماضية. **لم يتم العثور على أحداث**\n\nلا توجد نتائج تطابق ما بحثت عنه\nضمن الأحداث الأخيرة. - لا توجد أحداث حصلت مؤخرًأ تحوي \'**%1$s**\' تم العثور عليها. + لم يتم العثور على أحداث أخيرة تحوي «**%1$s**». ما هي آخر الأحداث؟ هي قائمة تحوي جميع الأحداث التي قام بها أعضاء المجموعة ومشرفوها خلال آخِر 48 ساعة. هي قائمة تحوي جميع الأحداث التي قام بها مشرفو القناة خلال آخِر 48 ساعة. @@ -579,6 +579,7 @@ حاول مرة أخرى أو اختر من القائمة أدناه مخفي + الدعم يكتب... "يكتب… " "يكتبون… " @@ -1022,7 +1023,7 @@ النمط الوضع الليلي الآلي - Off + معطّل معطّل مجدول تلقائي @@ -1045,13 +1046,13 @@ حفظ النمط نمط جديد لون النمط - CREATE THEME + إنشاء نمط تطبيق معاينة النمط اختر لونًا - Create new theme + إنشاء نمط جديد اضغط على أيقونة الألوان لاستعراض قائمة الخيارات في كل شاشة - ثم قم بتعديلها. - You can create your own theme by changing colors within the app.\n\nYou can always switch back to the default Telegram theme here. + يمكنك إنشاء النمط الخاص بك عبر تغيير الألوان داخل التطبيق.\n\nيمكنك العودة إلى نمط تيليجرام الافتراضي في أي وقت من هنا. تم تعيين كل الإشعارات إلى الوضع الافتراضي هل ترغب حقًا في إعادة تعيين جميع إعدادات الإشعارات إلى الوضع الافتراضي؟ @@ -1119,26 +1120,27 @@ إعادة تعيين الخلفيات لإزالة جميع الخلفيات المرفوعة وإستعادة الخلفيات الإفتراضية لجميع الأنماط. هل ترغب حقًا في إعادة تعيين كافة خلفيات المحادثة؟ - Are you sure you want to delete the selected backgrounds? + هل ترغب حقًا في حذف الخلفيات المحددة؟ معاينة الخلفية ملحوظة: بعض الخلفيات تصبح أجمل عند إضافة التأثير الضبابي. - شكرًا لك! + وااو! شكرًا لك اسحب لليمين أو لليسار لرؤية المزيد من الألوان. يابدع الورد .. يا جمال الورد - Reinhardt, we need to find you some new tunes 🎶. - Ah, you kids today with techno music! You should enjoy the classics, like Hasselhoff! - I can\'t even take you seriously right now. - Press \'Set\' to apply the background. - Enjoy the view. + أيمن + أحمد، هل أحضرت كافة الأشياء المطلوبة... + بلى، أنا جاهز للانطلاق في أي وقت! هل سنخبرهم بقدومنا أم نجعلها مفاجأة؟ + سنخبرهم بالطبع! هل تتذكر كيف انتهت آخر مفاجأة؟ + اضغط على \'تعيين\' لتطبيق الخلفية. + طلعت يا محلا نورها يا شمس الشموسة تعيين كخلفية بحث بواسطة اللون البحث عن خلفيات اللون: - Brightness - Hex color code - Color + السطوع + رمز اللون Hex + لون نقش - Intensity + الشدة الأزرق الأحمر البرتقالي @@ -1206,13 +1208,50 @@ طويل تنزيل الوسائط تلقائيًا تنزيل الوسائط تلقائيًا + مفعّل في جميع المحادثات + حتى %1$s في جميع المحادثات + معطّل + حتى %1$s في %2$s + مفعّل في %1$s + جهات الاتصال + القنوات + منخفض + متوسط + عالٍ + مخصص + الرسائل الخاصة + المجموعات + الصور + المقاطع المرئية + الملفات + تنزيل الملفات والمقاطع الصوتية تلقائيًا + تنزيل الصور تلقائيًا + تنزيل المقاطع المرئية والصور المتحركة تلقائيًا + الحجم الأقصى للمقطع المرئي + الحجم الأقصى للملف + تجهيز المقطع التالي + Start downloading the next track while you are listening to an audio file. + تجهيز المرئيات الكبيرة + تحميل الثواني الأولى (1-2 م.ب) من المقاطع المرئية الأكبر من %1$s لمشاهدتها فورَ الضغط عليها. + قم بتفعيل هذا الخيار إذا كنت تريد أن تتحمل الوسائط تلقائيًا على البيانات الخلوية. + قم بتفعيل هذا الخيار إذا كنت تريد أن تتحمل الوسائط تلقائيًا عند التجوال. + Turn this ON if you want media to be automatically downloaded on Wi-Fi. + على البيانات الخلوية + عند التجوال + على الـ Wi-Fi + استهلاك البيانات + أنواع الوسائط + الرسائل الصوتية صغيرة للغاية، لذا يتم تنزيلها دائمًا. + لا وسائط "إعادة تعيين إعدادات التنزيل التلقائي " "هل ترغب حقًا في إعادة تعيين إعدادات التنزيل التلقائي؟ " عند استخدام البيانات الخلوية عند الاتصال بالـ Wi-Fi - عند تواجدك خارج البلاد - لا وسائط - تشغيل الصور المتحركة تلقائيًا + عند التجوال + الصور المتحركة + المقاطع المرئية + Press the volume buttons to turn sound on. + تشغيل الوسائط تلقائيًا الرفع للتحدث حفظ في المعرض تعديل الاسم @@ -1244,6 +1283,8 @@ افتراضي إشعارات ذكية الاستثناءات + إضافة استثناء + استثناء جديد "يحوي هذا القسم على جميع المحادثات التي تملك إعدادات إشعارات مخصصة.\n\nيمكنك تخصيص إشعارات المحادثات عبر فتح صفحاتها واختيار \'الإشعارات\'. " لا شيء %1$d / %2$s @@ -1328,6 +1369,19 @@ إشعارات المحادثات الخاصة إشعارات القنوات انقر للتغيير + خيارات بديلة + إضافة حساب آخر + "إضافة حسابات تيليجرام أخرى والتبديل بينها بسهولة. " + تعيين رمز قفل + "قفل التطبيق برمز لمنع الآخرين من فتحه. " + مسح الذاكرة المؤقتة + "تفريغ بعض المساحة من جهازك؛ ستبقى وسائطك في السحابة. " + تغيير رقم الهاتف + "نقل حسابك ومحادثاتك مع وسائطها لرقمٍ آخر. " + التواصل مع الدعم + أخبرنا عن أي مشاكل تواجهك لأن تسجيل الخروج غالبًا لا ينفع. + تسجيل الخروج + لا تنسَ أن تسجيل الخروج سيحذف كل محادثاتك السرية. قاعدة البيانات على الجهاز هل ترغب في مسح الرسائل النصية من الذاكرة المؤقتة؟ @@ -1778,11 +1832,12 @@ لقد قمت بتفعيل التحقق بخطوتين.\nلتسجيل الدخول إلى حسابك هذا من أجهزة جديدةٍ ستحتاج إلى كلمة المرور التي قمت بتعيينها. البيانات والتخزين - استخدام الشبكة والتخزين + استخدام التخزين والشبكة استخدام التخزين - استخدام بيانات الجوال - استخدام بيانات التجوال - استخدام بيانات Wi-Fi + استهلاك البيانات + الخلوية + WI-FI + التجوال الرسائل والبيانات الأخرى المرسلة الواردة @@ -1795,8 +1850,8 @@ الوقت الإجمالي الإجمالي إعادة تعيين الإحصائيات - إحصاءات الاستخدام منذ %1$s - هل ترغب في مسح إحصائيات استخدامك؟ + إحصاءات الاستهلاك منذ %1$s + هل ترغب في إعادة تعيين إحصائيات استهلاكك؟ الخصوصية والأمان الخصوصية @@ -2050,7 +2105,7 @@ المعذرة، لا يمكنك حظر هذا المستخدم لأنه مشرف في هذه المجموعة ولا يمكنك تغيير ذلك. المعذرة، مشرفو هذه المجموعة قيدوك من إرسال الملصقات. المعذرة، قام مشرفو هذه المجموعة بتقييدك من إرسال الوسائط. - Sorry, the admins of this group have restricted you from sending polls. + المعذرة، قام مشرفو هذه المجموعة بتقييدك من إرسال الاستفتاءات. المعذرة، ليس من المسموح إرسال الملصقات إلى هذه المجموعة. المعذرة، ليس من المسموح إرسال الوسائط إلى هذه المجموعة. المعذرة، ليس من المسموح إرسال الاستفتاءات إلى هذه المجموعة. @@ -2115,6 +2170,7 @@ مطلقًا فقط على البيانات الخلوية دائمًا + فقط عند التجوال إجابة رفض أنت غير متصل حاليًا، قم بالاتصال بالإنترنت لتتمكن من إجراء المكالمات. @@ -2479,7 +2535,7 @@ dd MMM, HH:mm LLLL yyyy LLLL - MMM dd + dd MMM yy.MM.dd yyyy.MM.dd d MMMM diff --git a/TMessagesProj/src/main/res/values-de/strings.xml b/TMessagesProj/src/main/res/values-de/strings.xml index e4f883032..2e23384b1 100644 --- a/TMessagesProj/src/main/res/values-de/strings.xml +++ b/TMessagesProj/src/main/res/values-de/strings.xml @@ -579,6 +579,7 @@ Erneut probieren oder unten auswählen unsichtbar + Support tippt… tippt... tippen… @@ -1125,6 +1126,7 @@ Juhu, danke. Das wird mein Leben verändern. Für mehr Farben, nach links oder rechts wischen. Lachs ist ein Fisch, keine Farbe. + Lucio Reinhardt, wir müssen dir unbedingt... Oh je, ihr mit eurer Techno-Musik! Genießt lieber Klassiker, wie Hasselhoff! Nicht wirklich dein Ernst, oder? @@ -1206,13 +1208,50 @@ Lang Automatischer Mediendownload Medien Auto-Download + Für alle Chats eingeschaltet + Bis zu %1$s in allen Chats + Aus + Bis zu %1$s für %2$s + Ein für %1$s + Kontakte + Kanäle + Niedrig + Mittel + Hoch + Angepasst + PM + Gruppen + Bilder + Videos + Dateien + Auto-Download (Dateien und Musik) + Auto-Download (Bilder) + Auto-Download (Videos und GIFs) + Maximale Videogrösse + Maximale Dateigröße + Nächstes Lied vorladen + Nächsten Titel während der Wiedergabe herunterladen. + Größere Videos vorladen + Die ersten Sekunden (1-2 MB) von Videos vorladen, die größer als %1$s sind. + EINSCHALTEN, wenn du möchtest, dass Medien automatisch heruntergeladen werden sollen, wenn du per Mobilfunk verbunden bist. + EINSCHALTEN, wenn du möchtest, dass Medien automatisch heruntergeladen werden sollen, wenn du Roaming benutzt. + EINSCHALTEN, wenn du möchtest, dass Medien automatisch heruntergeladen werden sollen, wenn du per WLAN verbunden bist. + Über Mobilfunk + Über Roaming + Über WLAN + Datennutzung + Medienarten + Sprachnachrichten sind winzig, sie werden immer automatisch heruntergeladen. + kein automatischer Download Einstellungen zurücksetzen Möchtest du die Einstellungen für automatische Downloads wirklich zurücksetzen? Über Mobilfunk Über WLAN Bei Roaming - kein automatischer Download - GIFs autom. Abspielen + GIFs + Videos + Press the volume buttons to turn sound on. + Automatische Medienwiedergabe Zum Sprechen ans Ohr In der Galerie speichern Name bearbeiten @@ -1244,6 +1283,8 @@ Standard Schlaue Benachrichtigungen Ausnahmen + Eine Ausnahme hinzufügen + Neue Ausnahme Deine angepassten Benachrichtigungen werden in diesem Bereich angezeigt.\n\nUm Benachrichtigungen anzupassen, Chat mit dem Gesprächspartner öffnen, Profilbild antippen, dann \'Mitteilungen\' und \'Anpassen\'. Keine %1$d / %2$s @@ -1328,6 +1369,19 @@ Benachrichtigungen für private Chats Kanalbenachrichtigungen Antippen zum Ändern + Alternative Optionen + Weiteres Konto hinzufügen + Richte mehrere Telefonnummern ein und wechsle jederzeit die Konten. + Pincode einrichten + Sperre die App mit einem lokalen Pincode, so dass andere sie nicht öffnen können. + Cache leeren + Gebe jederzeit Speicherplatz von deinem Gerät frei. Deine Medien bleiben in der Cloud. + Nummer ändern + Verschiebe deine Kontakte, Gruppen, Nachrichten und Medien auf eine neue Nummer. + Support kontaktieren + Melde uns jederzeit Probleme. Die Abmeldung löst sie normalerweise nicht. + Abmelden + Wichtig: Die Abmeldung in der App beendet alle deine Geheimen Chats. Lokale Datenbank Textnachrichten-Cache leeren? @@ -1780,9 +1834,10 @@ Daten und Speicher Daten- und Speichernutzung Speichernutzung - Mobile Datennutzung - Roaming Datennutzung - WLAN Datennutzung + Datenbenutzung + MOBIL + WLAN + ROAMING Nachrichten und sonstige Daten Gesendet Empfangen @@ -2115,6 +2170,7 @@ Niemals Nur über Mobilfunk Immer + Nur bei Roaming Annehmen Ablehnen Du bist derzeit offline. Bitte verbinde dich mit dem Internet und versuche es erneut. @@ -2337,12 +2393,12 @@ %1$d Ausnahmen %1$d Ausnahmen %1$d Ausnahmen - %1$d hat Nutzer entfernt - %1$d hat Nutzer entfernt - %1$d hat Nutzer entfernt - %1$d hat Nutzer entfernt - %1$d hat Nutzer entfernt - %1$d hat Nutzer entfernt + %1$d entfernte Nutzer + %1$d entfernter Nutzer + %1$d entfernte Nutzer + %1$d entfernte Nutzer + %1$d entfernte Nutzer + %1$d entfernte Nutzer %1$d Punkte %1$d Punkt %1$d Punkte diff --git a/TMessagesProj/src/main/res/values-es/strings.xml b/TMessagesProj/src/main/res/values-es/strings.xml index cd6f06fa3..2ca3ab8b2 100644 --- a/TMessagesProj/src/main/res/values-es/strings.xml +++ b/TMessagesProj/src/main/res/values-es/strings.xml @@ -579,6 +579,7 @@ Reinténtalo o elige uno de la lista de abajo invisible + soporte escribiendo... está escribiendo... están escribiendo... @@ -1124,7 +1125,8 @@ Pista: algunas imágenes de fondo se ven mejor difuminadas. ¡Woo-hoo! ¡Gracias! Desliza a la izquierda o derecha para ver más colores - Es salmón es un pez, no un color. + El salmón es un pez, no un color. + Lúcio Reinhardt, necesitamos encontrarte algunas nuevas canciones 🎶. ¡Ah! Los niños de hoy en día con la música tecno. Disfruten los clásicos, ¡como Hasselhoff! Con eso ya no puedo tomarte en serio. @@ -1206,13 +1208,50 @@ Largas Descarga automática de multimedia Autodescargar multimedia + Activada en todos los chats + Hasta %1$s para todos los chats + Desactivada + Hasta %1$s para %2$s + Activada para %1$s + Contactos + Canales + Bajo + Medio + Alto + Otro + MP + Grupos + Fotos + Videos + Archivos + Autodescarga de archivos y música + Autodescargar fotos + Autodescarga de videos y GIF + Tamaño máximo de video + Tamaño máximo de archivo + Precargar siguiente audio + Comienza a descargar el siguiente tema mientras estás escuchando un archivo de audio. + Precargar videos grandes + Precarga los primeros segundos (1-2 MB) de videos que superan los %1$s para su reproducción instantánea. + Actívalo si quieres que la multimedia se descargue automáticamente con datos móviles. + Actívalo si quieres que la multimedia se descargue automáticamente con itinerancia de datos. + Actívalo si quieres que la multimedia se descargue automáticamente con Wi-Fi. + Con datos móviles + Con itinerancia de datos + Con Wi-Fi + Uso de datos + Tipos de multimedia + Los mensajes de voz son pequeños, siempre se descargan automáticamente. + Ningún contenido multimedia Restablecer ajustes de autodescarga ¿Quieres restablecer los ajustes de autodescarga? - Con uso de datos móviles - Con conexión a Wi-Fi + Con datos móviles + Con Wi-Fi Con itinerancia de datos - Ningún contenido multimedia - Autorreproducción de GIF + GIF + Videos + Press the volume buttons to turn sound on. + Autorreproducir multimedia Elevar para hablar Guardar en galería Editar nombre @@ -1244,6 +1283,8 @@ Por defecto Notificaciones inteligentes Excepciones + Añadir una excepción + Nueva excepción Aquí aparecerán los chats que no tengan los ajustes por defecto para las notificaciones.\n\nPuedes personalizar las notificaciones para un chat abriendo su información y seleccionando “Notificaciones”. Ninguna %1$d / %2$s @@ -1328,6 +1369,19 @@ Notificaciones para chats privados Notificaciones para canales Toca para cambiar + Opciones alternativas + Añadir otra cuenta + Conecta otras cuentas de Telegram y cambia entre ellas fácilmente. + Poner un código de acceso + Bloquea la app con un código para que otros no puedan abrirla. + Eliminar caché + Libera espacio en tu dispositivo; tu multimedia permanecerá en la nube. + Cambiar número de teléfono + Mueve tus cuenta, chats y multimedia a un nuevo número. + Contactar al soporte + Dinos cualquier problema; cerrar sesión generalmente no ayuda. + Cerrar sesión + Cerrar sesión desactiva los chats secretos. Base de datos local ¿Eliminar los mensajes en la caché? @@ -1780,9 +1834,10 @@ Datos y almacenamiento Uso de almacenamiento y red Uso de almacenamiento - Uso de datos móviles - Uso de datos de roaming - Uso de datos Wi-Fi + Uso de datos + MÓVIL + WI-FI + ITINERANCIA Mensajes y otros datos Enviados Recibidos @@ -2046,7 +2101,7 @@ No puedes añadir a este usuario o bot al grupo porque lo has bloqueado. Por favor, desbloquéalo para continuar. UNIRME AL GRUPO No puedes añadir a este usuario como administrador porque no es un miembro de este grupo y no tienes permitido añadirlo. - No puedes añadir a este usuario como administrador porque está en la lista de suspendidos y no puedes quitar su suspensión. + No puedes añadir a este usuario como administrador porque está en la lista de usuarios eliminados y no puedes quitar su suspensión. No puedes suspender a este usuario porque es un administrador del grupo y no tienes permitido degradarlo. Los administradores del grupo han restringido que envíes stickers. Lo sentimos, los administradores del grupo han restringido que envíes multimedia. @@ -2113,8 +2168,9 @@ Puedes restringir quién puede llamarte. Estos usuarios podrán o no podrán llamarte, sin importar los ajustes de arriba. Nunca - Sólo datos móviles + En datos móviles Siempre + En itinerancia de datos Contestar Rechazar Estás sin conexión. Por favor, conéctate a internet para realizar llamadas. diff --git a/TMessagesProj/src/main/res/values-it/strings.xml b/TMessagesProj/src/main/res/values-it/strings.xml index 0fe41f933..fd0f6ed49 100644 --- a/TMessagesProj/src/main/res/values-it/strings.xml +++ b/TMessagesProj/src/main/res/values-it/strings.xml @@ -579,6 +579,7 @@ Riprova o scegli dalla lista seguente invisibile + assistenza sta scrivendo... sta scrivendo... stanno scrivendo... @@ -1125,7 +1126,8 @@ Woo-hoo! Grazie! Scorri a sinistra o destra per vedere altri colori. Il salmone è un pesce, non un colore. - Reinhart, dobbiamo trovarti qualche nuova canzone 🎶. + Lucio + Reinhardt, dobbiamo trovarti qualche nuova canzone 🎶. Ah, i ragazzi di oggi con la musica techno! Godetevi i classici, come Hasselhoff! Non posso prenderti sul serio adesso. Premi \'Imposta\' per applicare lo sfondo. @@ -1206,13 +1208,50 @@ Lunga Download automatico media Download automatico + Attivo per tutte le chat + Fino a %1$s in tutte le chat + Disattivato + Fino a %1$s per %2$s + Attivo per %1$s + Contatti + Canali + Basso + Medio + Alto + Personalizzato + MP + Gruppi + Foto + Video + File + Download automatico file e musica + Download automatico foto + Download automatico video e GIF + Dimensione massima video + Dimensione massima file + Precarica traccia successiva + Inizia a scaricare la traccia successiva mentre stai ascoltando un file audio. + Precarica i video più grandi + Precarica i primi secondi (1-2 MB) dei video più grandi di %1$s per una riproduzione istantanea. + Attiva questo se vuoi che i media siano scaricati automaticamente con la rete cellulare. + Attiva questo se vuoi che i media siano scaricati automaticamente in roaming. + Attiva questo se vuoi che i media siano scaricati automaticamente con la rete Wi-Fi. + Con la rete cellulare + In roaming + Con il Wi-Fi + Utilizzo dati + Tipi di media + I messaggi vocali sono leggeri, sono sempre scaricati automaticamente. + Nessun media Ripristina download automatico Sei sicuro di voler ripristinare le impostazioni di download automatico? Quando utilizzi la rete cellulare Quando connesso tramite Wi-Fi In roaming - Nessun media - Autoriproduzione GIF + GIF + Video + Press the volume buttons to turn sound on. + Autoriproduzione media Alza per registrare Salva nella galleria Modifica nome @@ -1244,6 +1283,8 @@ Default Notifiche intelligenti Eccezioni + Aggiungi eccezione + Nuova eccezione Questa sezione mostrerà tutte le chat con impostazioni di notifica non predefinite.\n\nPuoi personalizzare le notifiche per una chat aprendo il suo profilo e scegliendo \'Notifiche\'. Nessuna %1$d / %2$s @@ -1328,6 +1369,19 @@ Notifiche per le chat private Notifiche per i canali Tocca per cambiare + Opzioni alternative + Aggiungi un altro account + Connetti altri account Telegram e passa facilmente da uno all\'altro. + Imposta un codice di blocco + Blocca l\'app con un codice, così gli altri non potranno aprirla. + Vuota la cache + Libera spazio sul tuo dispositivo; i tuoi media rimarranno nel cloud. + Cambia numero di telefono + Sposta il tuo account, le chat e i media su un nuovo numero. + Contatta l\'assistenza + Segnalaci qualsiasi problema; disconnettersi solitamente non aiuta. + Esci + Uscendo disattiverai tutte le chat segrete. Database locale Cancellare i messaggi salvati nella cache? @@ -1579,7 +1633,7 @@ Conferma impronta digitale per continuare Sensore touch Impronta digitale non riconosciuta. Riprova. - Mosta contenuto nelle app recenti + Mostra contenuto nelle app recenti Se disattivata, non potrai fare screenshot nell\'app, ma il sistema nasconderà il contenuto delle chat nella lista delle app recenti. Questo nasconderà il contenuto delle chat o dell\'elenco chat dalla lista della app recenti, ma non sarà possibile effettuare screenshot su Telegram.\n\nPotrebbe essere necessario riavviare l\'applicazione affinché questo abbia effetto. Troppi tentativi.\nPer favore riprova tra %1$s. @@ -1780,9 +1834,10 @@ Dati e archivio Utilizzo disco e rete Utilizzo archivio - Utilizzo dati cellulare - Utilizzo dati roaming - Utilizzo dati Wi-Fi + Utilizzo dati + RETE CELLULARE + WI-FI + ROAMING Messaggi e altri dati Inviati Ricevuti @@ -2000,7 +2055,7 @@ Inviare i messaggi a %1$s? Condividere il gioco con %1$s? Inviare contatto a %1$s? - Sei sicuro di voler uscire?\n\nNota che puoi usare Telegram su tutti i tuoi dispositivi contemporaneamente.\n\nRicorda, uscendo eliminerai tutte le Chat Segrete. + Sei sicuro di voler uscire?\n\nNota che puoi usare Telegram su tutti i tuoi dispositivi contemporaneamente.\n\nRicorda, uscendo eliminerai tutte le chat segrete. Sei sicuro di voler uscire ed eliminare il gruppo? Sei sicuro di voler eliminare e lasciare il gruppo **%1$s**? Sei sicuro di voler eliminare questa chat? @@ -2115,6 +2170,7 @@ Mai Solo con i dati cellulare Sempre + Solo in roaming Rispondi Rifiuta Al momento non sei in linea. Per favore connettiti a Internet per chiamare. diff --git a/TMessagesProj/src/main/res/values-ko/strings.xml b/TMessagesProj/src/main/res/values-ko/strings.xml index 1c9acd2d8..ae598cc87 100644 --- a/TMessagesProj/src/main/res/values-ko/strings.xml +++ b/TMessagesProj/src/main/res/values-ko/strings.xml @@ -13,7 +13,7 @@ 국가를 선택하세요 올바른 국가번호를 입력하세요 이미 이 계정으로 앱에 로그인되었습니다. - 바꾸기 + 교체 연락처 동기화 가지고 계신 연락처가 텔레그램 서버에서 제거됩니다. \'연락처 동기화\'를 켜시면, 연락처가 다시 동기화됩니다. 사용중인 계정에 기기에 있는 연락처가 추가가 될 예정입니다. @@ -579,6 +579,7 @@ 다시 시도하거나 아래 목록에서 고르세요 숨김 + 지원 입력 중... 님이 입력 중... 님이 입력 중... @@ -614,7 +615,7 @@ 메시지가 없습니다... 전달된 메시지 보낸 사람: - %1$s 님이 보냄 + %1$s 보냄 보낸 사람: 최근 이모지 없음 메시지 @@ -887,9 +888,9 @@ 링크를 클립보드에 복사했습니다 전화번호를 클립보드에 복사했습니다 이메일을 클립보드에 복사했습니다 - 링크로 그룹에 초대하기 + 링크로 그룹에 초대 초대 링크 - 정말 이 초대 링크를 폐기할까요? 앞으로 이 링크를 통하여 아무도 들어올 수 없습니다. + 정말 이 초대 링크를 폐기할까요? 폐기하면 누구도 이 링크로 들어올 수 없습니다. 이전 초대 링크는 현재 비활성화됐습니다. 새 초대 링크가 만들어졌습니다. 폐기 링크 폐기 @@ -897,7 +898,7 @@ **%1$s** 링크를 폐지하시겠습니까?\n\n \"**%2$s**\" 채널이 비공개로 바뀔 것입니다. 링크 복사 링크 공유 - 누구나 텔레그램을 설치했다면 위 링크를 따라 그룹에 참여할 수 있습니다. + 누구나 텔레그램을 설치했다면 위 링크를 따라 그룹에 들어올 수 있습니다. 참가자 공유된 미디어 @@ -1048,7 +1049,7 @@ 새로운 테마 적용 테마 미리보기 - 색깔 선택 + 색 선택 새로운 테마 만들기 각 화면의 구성 요소 목록을 보려면 팔레트 아이콘을 누르세요. 이들을 수정하실 수 있습니다. 앱 안에서 색들을 바꾸어 회원님만의 테마를 만드실 수 있습니다.\n\n여기서 언제든지 기본 텔레그램 테마로 돌리실 수 있습니다. @@ -1113,7 +1114,7 @@ 기본 지원 음소거일 때만 - 흐림 효과가 적용됨 + 흐림 모션 효과 대화방 배경 대화방 배경 초기화 @@ -1122,9 +1123,10 @@ 정말 선택한 배경을 삭제하시겠습니까? 배경 미리보기 팁: 몇몇 배경에는 흐림 효과가 잘 어울립니다. - 야호! 감사합니다 + 야호! 고마워요 좌우로 넘겨 다양한 색을 입혀 보세요 Salmon is a fish, not a color. + 루시우 라인하르트, 새 음악도 좀 들어보시지 그래요🎶? 요즘 젊은이들은 테크노인지 뭔지 하는 것만 좋아한다니까! 핫셀호프 같은 고전 음악을 들어 보라고! 이게 진심으로 하는 말이라니. 아으! @@ -1135,7 +1137,7 @@ 배경 검색 색: 밝기 - Hex 색깔 코드 + Hex 색 코드 무늬 세기 @@ -1206,13 +1208,50 @@ 길게 미디어 자동 다운로드 미디어 자동 다운로드 + 모든 대화방에 켬 + 모든 대화방에 최대 %1$s + + %2$s에 최대 %1$s + %1$s에 켬 + 연락처 + 채널 + 낮음 + 중간 + 높음 + 보통 + 개인 + 그룹 + 사진 + 동영상 + 파일 + 파일과 음악 자동 다운로드 + 사진 자동 다운로드 + 동영상과 GIF 자동 다운로드 + 최대 동영상 크기 + 최대 파일 크기 + 다음 트랙 미리 불러오기 + 오디오 파일을 듣고 계시는 동안 그다음 트랙을 다운로드합니다. + 큰 동영상 미리 불러오기 + %1$s보다 큰 동영상은 바로 재생하기 위해 처음 몇 초를 미리 불러옵니다. + 모바일 데이터를 사용할 때 미디어를 자동으로 다운로드하려면 이 설정을 켜세요. + 로밍했을 때 미디어를 자동으로 다운로드하려면 이 설정을 켜세요. + Wi-Fi에 연결했을 때 미디어를 자동으로 다운로드하려면 이 설정을 켜세요. + 모바일 데이터 사용 시 + 로밍 시 + Wi-Fi 연결 시 + 데이터 사용량 + 미디어 종류 + 음성 메시지는 용량이 적어 항상 자동으로 다운로드됩니다. + 다운로드하지 않음 자동 다운로드 설정 초기화 정말 자동 다운로드 설정을 초기화하시겠습니까? 모바일 데이터를 사용할 때 Wi-Fi에 연결할 때 로밍할 때 - 다운로드하지 않음 - GIF 자동 재생 + GIF + 동영상 + Press the volume buttons to turn sound on. + 미디어 자동 재생 기기 들어 말하기 갤러리에 저장 이름 수정 @@ -1244,6 +1283,8 @@ 기본 스마트 알림 예외 + 예외 추가 + 새로운 예외 기본과 다르게 알림 설정이 된 대화방이 이 부분에 모두 나열됩니다.\n\n대화방의 프로필을 열고 \'알림\'을 선택하여 알림 설정을 맞춤하실 수 있습니다. 없음 %1$d / %2$s @@ -1328,6 +1369,19 @@ 개인 대화방 알림 채널 알림 바꾸려면 누르세요 + 대체 옵션 + 다른 계정 추가 + 다른 텔레그램 계정을 연결하여 두 계정 간 손쉽게 전환하세요. + 암호 설정 + 앱을 암호로 잠그시면 다른 사람이 열 수 없습니다. + 캐시 비우기 + 기기의 저장 공간을 확보하세요. 미디어는 클라우드에 보존됩니다. + 전화번호 바꾸기 + 연락처와 그룹, 메시지, 미디어를 새 번호로 이동하세요. + 지원 문의 + 문제가 있으면 알려 주세요. 로그아웃은 대개 도움이 되지 않습니다. + 로그아웃 + 로그아웃하시면 비밀 대화가 모두 사라집니다. 로컬 데이터베이스 캐시가 된 문자 메시지를 비울까요? @@ -1621,8 +1675,8 @@ 혼합 m 떨어짐 km 떨어짐 - 현재 위치 보내기 - 다음과 나의 실시간 위치 공유... + 내 현재 위치 보내기 + 내 실시간 위치 공유... 위치 공유 중단 정말로 %1$s 님과 실시간 위치 공유를 중단하시겠습니까? 정말로 %1$s 님과 실시간 위치 공유를 중단하시겠습니까? @@ -1638,8 +1692,8 @@ 15분 동안 1시간 동안 8시간 동안 - 업데이트됨 - %1$s 전 업데이트됨 + 갱신됨 + %1$s 전 갱신됨 방금 갱신됨 회원님과 %1$s 님 %1$s를 %2$s와 공유 중 @@ -1780,9 +1834,10 @@ 데이터 및 저장소 디스크 및 네트워크 사용량 저장소 사용량 - 모바일 데이터 사용량 - 로밍 데이터 사용량 - WIFI 데이터 사용량 + 데이터 사용량 + 모바일 + 와이파이 + 로밍 메시지 및 기타 데이터 보냄 받음 @@ -2115,6 +2170,7 @@ 안 함 모바일 데이터로만 가능 항상 + 로밍 중에만 받기 거부 회원님은 현재 오프라인이십니다. 전화를 걸려면 인터넷에 연결해 주세요. @@ -2150,7 +2206,7 @@ 기술적 정보 포함 대화 내용이 공개되지 않으며 빠른 시일 내로 문제를 해결해 드리는 데 도움이 됩니다. 텔레그램 전화를 개선할 수 있도록 도와주셔서 감사합니다. - %s(으)로서 수신 중 + %s 님으로 수신 중 문자로 응답 이 빠른 응답 문구들을 전화가 걸려올 때 텔레그램 메시지로 보내실 수 있습니다. 무엇이든지 원하는 말로 바꾸세요. 지금 대화할 수 없어요. 무슨 일인가요? @@ -2349,12 +2405,12 @@ %1$d %1$d %1$d - %1$d분 전 업데이트됨 - %1$d분 전 업데이트됨 - %1$d분 전 업데이트됨 - %1$d분 전 업데이트됨 - %1$d분 전 업데이트됨 - %1$d분 전 업데이트됨 + %1$d분 전 갱신됨 + %1$d분 전 갱신됨 + %1$d분 전 갱신됨 + %1$d분 전 갱신됨 + %1$d분 전 갱신됨 + %1$d분 전 갱신됨 **%1$d**초 **%1$d**초 **%1$d**초 diff --git a/TMessagesProj/src/main/res/values-nl/strings.xml b/TMessagesProj/src/main/res/values-nl/strings.xml index 2b1176e06..d70ac9231 100644 --- a/TMessagesProj/src/main/res/values-nl/strings.xml +++ b/TMessagesProj/src/main/res/values-nl/strings.xml @@ -579,6 +579,7 @@ Probeer opnieuw of kies uit de lijst onzichtbaar + ondersteuning aan het typen is aan het typen zijn aan het typen @@ -1022,7 +1023,7 @@ Thema Automatisch nachtthema - Off + Uit Uitgeschakeld Geplande tijd Automatisch @@ -1043,7 +1044,7 @@ Geef een naam aan je thema BEWERKER SLUITEN THEME OPSLAAN - Nieuw Thema + Nieuw kleurenthema Kleurenthema THEMA MAKEN TOEPASSEN @@ -1125,11 +1126,12 @@ Bedankt! Veeg naar links of rechts om meer kleuren te zien Zalm is een vis, geen kleur. + Michiel Godfried, ben jij het echt? Ik zit mij voor het vensterglas onnoemlijk te vervelen. Ik wou dat ik twee hondjes was, dan kon ik samen spelen - Press \'Set\' to apply the background. - Enjoy the view. + Tik op stel in om de achtergrond toe te passen + Geniet van het uitzicht ACHTERGROND INSTELLEN Zoek op kleur Zoek Achtergronden @@ -1206,13 +1208,50 @@ Lang Automatisch media downloaden Automatisch downloaden + Aan voor alle chats + Tot %1$s in alle chats + Uit + Tot %1$s voor %2$s + Aan voor %1$s + Contacten + Kanalen + Laag + Middel + Hoog + Aangepast + Privéberichten + Groepen + Foto\'s + Video\'s + Bestanden + Autom bestanden en muziek downloaden + Autom. Foto\'s downloaden + Autom. video\'s en GIF\'s downloaden + Maximale videogrootte + Maximale bestandsgrootte + Volgend nummer voorladen + Begin met het downloaden van het volgende nummer tijdens het luisteren. + Grotere video\'s voorladen + Laad de eerste paar seconden van video\'s groter dan %1$s voor direct afspelen. + Zet dit aan als je media automatisch wilt download via je mobiele netwerk + Zet dit aan als je media automatisch wilt downloaden bij roamen. + Ze dit aan als je media automatisch wilt downloaden via Wi-Fi + Mobiele data + Tijdens roamen + Wi-Fi + Datagebruik + Soorten media + Spraakberichten zijn miniscuul, ze worden altijd automatisch gedownload. + Geen media Autom. downloaden resetten Instellingen voor automatisch downloaden echt resetten? Bij mobiele verbinding Bij Wi-Fi-verbinding Bij roaming - Geen media - GIF\'s automatisch afspelen + GIF\'s + Video\'s + Press the volume buttons to turn sound on. + Autom. media afspelen Houd bij oor Opslaan in galerij Naam wijzigen @@ -1244,6 +1283,8 @@ Standaard Slimme meldingen Uitzonderingen + Uitzondering toevoegen + Nieuwe uitzondering Hier vind je alle chats met niet-standaard berichtgevingsinstellingen.\n\nJe kunt de instellingen van een chat wijzigen door het profiel te openen en naar ‘Berichtgeving’ te gaan. Geen %1$d / %2$s @@ -1327,7 +1368,20 @@ Berichtmeldingen voor groepen Berichtmeldingen voor privégesprekken Berichtmeldingen voor kanalen - Tik om te wijzen + Tik om te wijzigen + Andere opties + Voeg een account toe + Telegram-accounts verbinden en makkelijk wisselen. + Stel een toegangscode in + Vergrendel de App met een toegangscode ter beveiliging. + Cache Wissen + Schoon de opslag op je apparaat op; je media blijft bewaard in de Cloud. + Telefoonnummer wijzigen + Verplaats je account, chats en media naar een nieuw nummer. + Om hulp vragen + Vertel ons over eventuele problemen; uitloggen helpt meestal niet. + Uitloggen + Uitloggen deactiveert al je geheime chats. Lokale database Gecachet tekstberichten wissen? @@ -1584,18 +1638,18 @@ Hiermee verberg je de inhoud van je chats of chatoverzicht in je taakbeheer, ook kun je geen schermafdrukken maken in Telegram.\n\nHet kan zijn dat je de app moet herstarten voordat dit actief is. Teveel pogingen.\nProbeer het opnieuw over %1$s. - January - February - March - April - May - June - July - August - September - October - November - December + januari + februari + maart + april + mei + juni + juli + augustus + september + oktober + november + december BESTANDEN MEDIA LINKS @@ -1780,9 +1834,10 @@ Data en opslag Opslag- en datagebruik Opslaggebruik - Mobiel datagebruik - "Roamen datagebruik " - Wi-Fi datagebruik + Datagebruik + MOBIEL + WI-FI + ROAMEN Berichten en andere gegevens Verzonden Ontvangen @@ -2115,6 +2170,7 @@ Nooit Alleen bij mobiele verbinding Altijd + Alleen tijdens roamen Opnemen Weigeren Je bent offline, maak verbinding met het internet om een oproep te starten. diff --git a/TMessagesProj/src/main/res/values-pt-rBR/strings.xml b/TMessagesProj/src/main/res/values-pt-rBR/strings.xml index bfe52e5d2..cf93c145b 100644 --- a/TMessagesProj/src/main/res/values-pt-rBR/strings.xml +++ b/TMessagesProj/src/main/res/values-pt-rBR/strings.xml @@ -16,11 +16,11 @@ Alternar Sincronizar Contatos Isso removerá os seus contatos dos servidores do Telegram. Se \"Contatos Sincronizados\" estiver ativa, os contatos serão sincronizados novamente. - Contatos deste dispositivo serão adicionados a sua conta. - Contatos deste dispositivo não serão adicionados a sua conta. - Contatos deste dispositivo serão adicionados a sua conta. + Contatos deste dispositivo serão adicionados à sua conta. + Contatos deste dispositivo não serão adicionados à sua conta. + Contatos deste dispositivo serão adicionados à sua conta. Ative para sincronizar continuamente contatos deste dispositivos com a sua conta. - Contatos deste dispositivo foram adicionados a sua conta. + Contatos deste dispositivo foram adicionados à sua conta. Verificar Número Enviamos um SMS com um código de ativação para **%1$s**. @@ -307,7 +307,7 @@ Convidar via Link Dispensar admin Editar permissões - Somente os administradores do canal podem ver essa lista. + Somente os administradores do canal podem ver esta lista. Qualquer pessoa que tenha o Telegram instalado poderá participar do seu canal seguindo esse link. Você pode adicionar administradores para ajudar você a gerenciar seu canal. Aperte e segure para removê-los. Você deseja entrar no canal \'%1$s\'? @@ -324,7 +324,7 @@ Você entrou neste canal Você entrou neste grupo Remover do canal - Desculp, você não pode enviar mensagens para esse canal. + Desculpe, você não pode enviar mensagens para esse canal. %1$s adicionou você ao canal %2$s Canal %1$s atualizou a foto %1$s postou uma mensagem @@ -400,7 +400,7 @@ Gerenciar canal Histórico do chat para novos membros Visível - Novos membros verão mensagens que foram enviadas antes deles entrarem. + Novos membros verão mensagens que foram enviadas antes de eles entrarem. Oculto Novos membros não verão mensagens anteriores. Novos membros não verão mais de 100 mensagens anteriores. @@ -539,7 +539,7 @@ Mensagens apagadas Mensagens editadas Mensagens fixadas - Membros saindo do grupo + Pessoas que saíram Nova Lista de Transmissão Digite o nome da lista @@ -579,6 +579,7 @@ Tente novamente para escolher através da lista invisível + suporte escrevendo... está escrevendo... estão escrevendo... @@ -890,7 +891,7 @@ Convidar para o Grupo via Link Link de Convite Você tem certeza de que deseja desativar o link de convite? Se fizer isso, ninguém conseguirá usá-lo. - Este link de convite está inativo. Um novo link foi gerado. + O link de convite anterior foi desativado. Um novo link foi gerado. Desativar Desativar Link Você tem certeza de que deseja desativar o link **%1$s**?\n\nO grupo \"**%2$s**\" se tornará privado. @@ -1054,7 +1055,7 @@ Você pode criar seu próprio tema alterando as cores dentro do aplicativo.\n\nVocê sempre pode voltar para o tema padrão do Telegram aqui. Restaurar todas as configurações de notificação - Deseja mesmo resetar as configurações de notificação para o padrão? + Tem certeza de que deseja restaurar todas as configurações de notificação para o padrão? Tamanho do texto das mensagens Fazer uma Pergunta Permitir Animações @@ -1125,11 +1126,12 @@ Valeu! Deslize para os lados para ver mais cores Salmão é um peixe, não uma cor. + Lúcio Reinhardt, temos que achar umas músicas novas pra você. 🎶. Ahh, vocês crianças de hoje em dia com essas músicas eletrônicas! Ouçam os clássicos, como Hasselhoff! Não consigo nem te levar a sério agora. - Press \'Set\' to apply the background. - Enjoy the view. + Toque em \'Definir\' para aplicar a imagem. + Aprecie a vista. DEFINIR PAPEL DE PAREDE Buscar por cor Buscar Papéis de Parede @@ -1206,13 +1208,50 @@ Longa Download automático de mídia Baixar Automaticamente + Sim, em todos os chats + Até %1$s em todos os chats + Não + Até %1$s para %2$s + Sim, para %1$s + Contatos + Canais + Baixo + Médio + Alto + Pessoal + PV + Grupos + Fotos + Vídeos + Arquivos + Download automático de arquivos e músicas + Download automático de fotos + Download automático de vídeos e GIFs + Tamanho máximo do vídeo + Tamanho máximo do arquivo + Pré-carregar próxima faixa + Baixa a próxima faixa enquanto você escuta um arquivo de áudio. + Pré-carregar vídeos grandes + Baixa os primeiros segundos (1-2 MB) de vídeos mais pesados do que %1$s para reprodução instantânea. + Ative esta opção se você quiser que mídias sejam baixadas automaticamente ao usar os dados móveis. + Ative esta opção se você quiser que mídias sejam baixadas automaticamente quando em roaming. + Ative esta opção se você quiser que mídias sejam baixadas automaticamente ao usar a conexão Wi-Fi. + Nos Dados Móveis + Em Roaming + No Wi-Fi + Uso de dados + Tipos de mídia + Mensagens de voz são bastante leves, portanto são sempre baixadas automaticamente. + Sem mídia Apagar Configurações de Download Tem certeza de que deseja apagar as configurações de download automático? Ao usar dados móveis Quando conectado ao Wi-Fi Quando em roaming - Sem mídia - GIFs Automáticos + GIFs + Vídeos + Press the volume buttons to turn sound on. + Reproduzir automaticamente Levantar para Falar Salvar na Galeria Editar nome @@ -1244,6 +1283,8 @@ Padrão Notificações Inteligentes Exceções + Adicionar Exceção + Nova Exceção Esta seção irá listar todos os chats que possuem configurações não-padrão.\n\nVocê pode personalizar notificações para um chat abrindo o perfil do mesmo e escolhendo \'Notificações\'. Nenhum %1$d / %2$s @@ -1328,6 +1369,19 @@ Notificações para chats privados Notificações para canais Toque para alterar + Opções alternativas + Adicionar outra conta + Conecte outras contas do Telegram e alterne facilmente entre elas. + Definir Senha de Bloqueio + Bloqueie o aplicativo com uma senha para que outras pessoas não possam abri-lo. + Limpar Cache + Libere espaço em disco no seu dispositivo; sua mídia ficará na nuvem. + Alterar Número de Telefone + Mova sua conta, chats e mídias para um novo número. + Contatar Suporte + Conte-nos sobre quaisquer problemas; sair geralmente não ajuda. + Sair + Sair encerrará todos os Chats Secretos. Banco de Dados Local Limpar todos os textos em cache? @@ -1538,7 +1592,7 @@ Sessões Ativas Sessão atual Nenhuma outra sessão ativa - Você pode entrar no Telegram a partir de outro celular, tablet ou computador usando o mesmo número de telefone. Todos os seus dados serão sincronizados instantaneamente. + Você pode entrar no Telegram em outros celulares, tablets e computadores usando o mesmo número de telefone. Todos os seus dados serão sincronizados. Sessões Ativas Controle suas sessões em outros aparelhos. Toque em uma sessão para terminá-la. @@ -1579,7 +1633,7 @@ Confirme a impressão digital para continuar Toque o sensor Digital não reconhecida, tente de novo. - Mostrar Conteúdo do App nos Recentes + Mostrar Conteúdo no Multitarefas Se desativado, você não poderá fazer capturas de tela no aplicativo, mas o conteúdo do chat ficará oculto no multitarefas. Isso ocultará o conteúdo de seus chats ou lista de chats do multitarefas, mas você não poderá tirar capturas de tela no Telegram.\n\nPode ser necessário reiniciar o aplicativo para que isso entre em vigor. Muitas tentativas.\nPor favor, tente de novo em %1$s. @@ -1778,11 +1832,12 @@ Você ativou a Verificação em Duas Etapas.\nToda vez que você entrar na sua conta em um novo aparelho, será preciso digitar a senha que você configurar aqui. Dados e Armazenamento - Uso do disco e da rede - Uso do Armazenamento - Uso de Dados Móveis - Uso de Dados em Roaming - Uso de Dados no Wi-Fi + Uso de disco e de rede + Uso de Armazenamento + Uso de Dados + DADOS MÓVEIS + WI-FI + ROAMING Mensagens e outros dados Enviados Recebidos @@ -2000,7 +2055,7 @@ Enviar mensagens para %1$s? Compartilhar jogo com %1$s? Enviar contato para %1$s? - Você tem certeza de que desejar sair?\n\nSaiba que você pode usar o Telegram em todos os seus dispositivos ao mesmo tempo.\n\nLembre-se, sair apaga todos os seus Chats Secretos. + Você tem certeza de que desejar sair?\n\nNote que você pode usar o Telegram em todos os seus dispositivos ao mesmo tempo.\n\nLembre-se, sair apaga todos os seus Chats Secretos. Você tem certeza de que deseja apagar e sair do grupo? Tem certeza de que deseja apagar e sair do grupo **%1$s**? Você tem certeza de que deseja apagar essa conversa? @@ -2033,7 +2088,7 @@ Enviar mensagens para %1$s? Compartilhar jogo com %1$s? Enviar contato para %1$s? - Não há conta do Telegram com esse nome de usuário + Não existe conta no Telegram com esse nome de usuário. Esse bot não pode entrar em grupos. Você gostaria de ativar a prévia de links estendida em Chats Secretos? Note que a pré-visualização é gerada nos servidores do Telegram. Os bots inline são fornecidos por desenvolvedores terceiros. Para o bot funcionar, os símbolos que você digita depois do nome de usuário do bot são enviados para o respectivo desenvolvedor. @@ -2115,6 +2170,7 @@ Nunca Em redes móveis Sempre + Apenas em roaming Atender Rejeitar Você está offline. Conecte à internet para poder realizar chamadas. diff --git a/TMessagesProj/src/main/res/values/strings.xml b/TMessagesProj/src/main/res/values/strings.xml index aa4a239ac..36cf772bf 100644 --- a/TMessagesProj/src/main/res/values/strings.xml +++ b/TMessagesProj/src/main/res/values/strings.xml @@ -579,6 +579,7 @@ Try again or choose from the list below invisible + support typing... is typing... are typing... @@ -1125,6 +1126,7 @@ Woo-hoo! Thanks Swipe left or right to see more colors. Salmon is a fish, not a color. + Lucio Reinhardt, we need to find you some new tunes 🎶. Ah, you kids today with techno music! You should enjoy the classics, like Hasselhoff! I can\'t even take you seriously right now. @@ -1206,13 +1208,50 @@ Long Automatic media download Auto-Download Media + On in all chats + Up to %1$s in all chats + Off + Up to %1$s for %2$s + On for %1$s + Contacts + Channels + Low + Medium + High + Custom + PM + Groups + Photos + Videos + Files + Auto-download files and music + Auto-download photos + Auto-download videos and GIFs + Maximum video size + Maximum file size + Preload next track + Start downloading the next track while you are listening to an audio file. + Preload larger videos + Preload the first few seconds (1-2 MB) of videos larger than %1$s for instant playback. + Turn this ON if you want media to be automatically downloaded on mobile data. + Turn this ON if you want media to be automatically downloaded when roaming. + Turn this ON if you want media to be automatically downloaded on Wi-Fi. + On Mobile Data + When Roaming + On Wi-Fi + Data usage + Types of media + Voice messages are tiny, so they\'re always downloaded automatically. + No media Reset Auto-Download Settings Are you sure you want to reset auto-download settings? When using mobile data When connected on Wi-Fi When roaming - No media - Autoplay GIFs + GIFs + Videos + Press the volume buttons to turn sound on. + Auto-play media Raise to Speak Save to Gallery Edit name @@ -1330,6 +1369,19 @@ Notifications for private chats Notifications for channels Tap to change + Alternative options + Add another account + Connect your other Telegram accounts and easily switch between them. + Set a Passcode + Lock the app with a passcode so that others can\'t open it. + Clear Cache + Free up disk space on your device; your media will stay in the cloud. + Change Phone Number + Move your contacts, groups, messages and media to a new number. + Contact Support + Tell us about any issues; logging out doesn\'t usually help. + Log Out + Remember, logging out kills all your Secret Chats. Local Database Clear cached text messages? @@ -1782,9 +1834,10 @@ Data and Storage Disk and network usage Storage Usage - Mobile Data Usage - Roaming Data Usage - Wi-Fi Data Usage + Data Usage + MOBILE + WI-FI + ROAMING Messages and other data Sent Received @@ -2117,6 +2170,7 @@ Never Only on mobile data Always + Only while roaming Answer Decline You\'re currently offline. Please connect to the Internet in order to make calls. diff --git a/TMessagesProj/src/multidex/java/org/telegram/messenger/MultiDexApplicationLoader.java b/TMessagesProj/src/multidex/java/org/telegram/messenger/MultiDexApplicationLoader.java new file mode 100644 index 000000000..1fdb5a84a --- /dev/null +++ b/TMessagesProj/src/multidex/java/org/telegram/messenger/MultiDexApplicationLoader.java @@ -0,0 +1,13 @@ +package org.telegram.messenger; + +import android.content.Context; +import android.support.multidex.MultiDex; + +public class MultiDexApplicationLoader extends ApplicationLoader{ + + @Override + protected void attachBaseContext(Context base){ + super.attachBaseContext(base); + MultiDex.install(this); + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index a52999e7e..cd3647923 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.1.4' - classpath 'com.google.gms:google-services:4.0.2' + classpath 'com.google.gms:google-services:4.2.0' } } repositories {