Merge official v8.8.3

This commit is contained in:
luvletter2333 2022-06-30 19:16:32 +08:00
commit 73cb11f883
No known key found for this signature in database
GPG Key ID: A26A8880836E1978
1803 changed files with 39050 additions and 11317 deletions

View File

@ -10,8 +10,8 @@ if (System.getenv("DEBUG_BUILD") == "true") {
verName += "-" + RuntimeUtil.execForStr("git log --pretty=format:'%h' -n 1").trim()
}
def officialVer = "8.7.4"
def officialCode = 2636
def officialVer = "8.8.3"
def officialCode = 2705
def serviceAccountCredentialsFile = rootProject.file("service_account_credentials.json")

View File

@ -219,7 +219,7 @@ static int writeOggPage(ogg_page *page, FILE *os) {
return written;
}
const opus_int32 bitrate = 30 * 1024;
const opus_int32 bitrate = OPUS_BITRATE_MAX;
const opus_int32 frame_size = 960;
const int with_cvbr = 1;
const int max_ogg_delay = 0;
@ -322,7 +322,7 @@ int initRecorder(const char *path, opus_int32 sampleRate) {
header.nb_streams = 1;
int result = OPUS_OK;
_encoder = opus_encoder_create(coding_rate, 1, OPUS_APPLICATION_AUDIO, &result);
_encoder = opus_encoder_create(coding_rate, 1, OPUS_APPLICATION_VOIP, &result);
if (result != OPUS_OK) {
LOGE("Error cannot create encoder: %s", opus_strerror(result));
return 0;

View File

@ -39,6 +39,7 @@ typedef struct LottieInfo {
volatile uint32_t maxFrameSize = 0;
uint32_t imageSize = 0;
uint32_t fileOffset = 0;
uint32_t fileFrame = 0;
bool nextFrameIsCacheFrame = false;
FILE *precacheFile = nullptr;
@ -146,6 +147,7 @@ JNIEXPORT jlong Java_org_telegram_ui_Components_RLottieDrawable_create(JNIEnv *e
info->maxFrameSize = maxFrameSize;
fread(&(info->imageSize), sizeof(uint32_t), 1, precacheFile);
info->fileOffset = 9;
info->fileFrame = 0;
utimensat(0, info->cacheFile.c_str(), nullptr, 0);
}
fclose(precacheFile);
@ -162,6 +164,18 @@ JNIEXPORT jlong Java_org_telegram_ui_Components_RLottieDrawable_create(JNIEnv *e
return (jlong) (intptr_t) info;
}
JNIEXPORT jstring Java_org_telegram_ui_Components_RLottieDrawable_getCacheFile(JNIEnv *env, jclass clazz, jlong ptr) {
if (!ptr) {
return NULL;
}
auto info = (LottieInfo *) (intptr_t) ptr;
if (info->precache) {
return env->NewStringUTF(info->cacheFile.c_str());
}
return NULL;
}
JNIEXPORT jlong Java_org_telegram_ui_Components_RLottieDrawable_createWithJson(JNIEnv *env, jclass clazz, jstring json, jstring name, jintArray data, jintArray colorReplacement) {
std::map<int32_t, int32_t> *colors = nullptr;
if (colorReplacement != nullptr) {
@ -271,6 +285,7 @@ void CacheWriteThreadProc() {
if (task->firstFrame) {
task->firstFrameSize = size;
task->fileOffset = 9 + sizeof(uint32_t) + task->firstFrameSize;
task->fileFrame = 1;
}
task->maxFrameSize = MAX(task->maxFrameSize, size);
fwrite(&size, sizeof(uint32_t), 1, task->precacheFile);
@ -310,6 +325,7 @@ JNIEXPORT void Java_org_telegram_ui_Components_RLottieDrawable_createCache(JNIEn
info->precacheFile = fopen(info->cacheFile.c_str(), "w+");
if (info->precacheFile != nullptr) {
fseek(info->precacheFile, info->fileOffset = 9, SEEK_SET);
info->fileFrame = 0;
info->maxFrameSize = 0;
info->bufferSize = w * h * 4;
info->imageSize = (uint32_t) w * h * 4;
@ -393,18 +409,32 @@ JNIEXPORT jint Java_org_telegram_ui_Components_RLottieDrawable_getFrame(JNIEnv *
}
info->decompressBuffer = new uint8_t[info->decompressBufferSize];
}
int currentFrame = frame / framesPerUpdate;
if (info->fileFrame != frame) {
info->fileOffset = 9;
info->fileFrame = 0;
while (info->fileFrame != currentFrame) {
fseek(precacheFile, info->fileOffset, SEEK_SET);
uint32_t frameSize;
fread(&frameSize, sizeof(uint32_t), 1, precacheFile);
info->fileOffset += 4 + frameSize;
info->fileFrame++;
}
}
fseek(precacheFile, info->fileOffset, SEEK_SET);
uint32_t frameSize;
fread(&frameSize, sizeof(uint32_t), 1, precacheFile);
if (frameSize > 0 && frameSize <= info->decompressBufferSize) {
fread(info->decompressBuffer, sizeof(uint8_t), frameSize, precacheFile);
info->fileOffset += 4 + frameSize;
info->fileFrame = currentFrame + 1;
LZ4_decompress_safe((const char *) info->decompressBuffer, (char *) pixels, frameSize, w * h * 4);
loadedFromCache = true;
}
fclose(precacheFile);
if (frame + framesPerUpdate >= info->frameCount) {
info->fileOffset = 9;
info->fileFrame = 0;
}
}
}

View File

@ -55,6 +55,7 @@ void TL_dcOption::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool
tcpo_only = (flags & 4) != 0;
cdn = (flags & 8) != 0;
isStatic = (flags & 16) != 0;
thisPortOnly = (flags & 32) != 0;
id = stream->readInt32(&error);
ip_address = stream->readString(&error);
port = stream->readInt32(&error);
@ -70,6 +71,7 @@ void TL_dcOption::serializeToStream(NativeByteBuffer *stream) {
flags = tcpo_only ? (flags | 4) : (flags &~ 4);
flags = cdn ? (flags | 8) : (flags &~ 8);
flags = isStatic ? (flags | 16) : (flags &~ 16);
flags = thisPortOnly ? (flags | 32) : (flags &~ 32);
stream->writeInt32(flags);
stream->writeInt32(id);
stream->writeString(ip_address);

View File

@ -49,6 +49,7 @@ public:
bool tcpo_only;
bool cdn;
bool isStatic;
bool thisPortOnly;
int32_t id;
std::string ip_address;
int32_t port;

View File

@ -699,6 +699,10 @@ void Connection::onDisconnectedInternal(int32_t reason, int32_t error) {
isTryingNextPort = true;
if (failedConnectionCount > willRetryConnectCount || switchToNextPort) {
currentDatacenter->nextAddressOrPort(currentAddressFlags);
if (currentDatacenter->isRepeatCheckingAddresses() && (ConnectionsManager::getInstance(currentDatacenter->instanceNum).getIpStratagy() == USE_IPV4_ONLY || ConnectionsManager::getInstance(currentDatacenter->instanceNum).getIpStratagy() == USE_IPV6_ONLY)) {
if (LOGS_ENABLED) DEBUG_D("started retrying connection, set ipv4 ipv6 random strategy");
ConnectionsManager::getInstance(currentDatacenter->instanceNum).setIpStrategy(USE_IPV4_IPV6_RANDOM);
}
failedConnectionCount = 0;
}
}

View File

@ -3009,8 +3009,13 @@ void ConnectionsManager::updateDcSettings(uint32_t dcNum, bool workaround) {
if (dcOption->secret != nullptr) {
secret = std::string((const char *) dcOption->secret->bytes, dcOption->secret->length);
}
if (LOGS_ENABLED) DEBUG_D("getConfig add %s:%d to dc%d, flags %d, has secret = %d[%d]", dcOption->ip_address.c_str(), dcOption->port, dcOption->id, dcOption->flags, dcOption->secret != nullptr ? 1 : 0, dcOption->secret != nullptr ? dcOption->secret->length : 0);
addresses->push_back(TcpAddress(dcOption->ip_address, dcOption->port, dcOption->flags, secret));
if (LOGS_ENABLED) DEBUG_D("getConfig add %s:%d to dc%d, flags %d, has_secret = %d[%d], try_this_port_only = %d", dcOption->ip_address.c_str(), dcOption->port, dcOption->id, dcOption->flags, dcOption->secret != nullptr ? 1 : 0, dcOption->secret != nullptr ? dcOption->secret->length : 0, dcOption->thisPortOnly ? 1 : 0);
if (dcOption->thisPortOnly) {
addresses->insert(addresses->begin(), TcpAddress(dcOption->ip_address, dcOption->port, dcOption->flags, secret));
} else {
addresses->push_back(TcpAddress(dcOption->ip_address, dcOption->port, dcOption->flags, secret));
}
}
};

View File

@ -414,6 +414,7 @@ void Datacenter::nextAddressOrPort(uint32_t flags) {
if (currentAddressNum + 1 < addresses->size()) {
currentAddressNum++;
} else {
repeatCheckingAddresses = true;
currentAddressNum = 0;
}
currentPortNum = 0;
@ -1477,6 +1478,12 @@ void Datacenter::resetInitVersion() {
lastInitMediaVersion = 0;
}
bool Datacenter::isRepeatCheckingAddresses() {
bool b = repeatCheckingAddresses;
repeatCheckingAddresses = false;
return b;
}
TL_help_configSimple *Datacenter::decodeSimpleConfig(NativeByteBuffer *buffer) {
TL_help_configSimple *result = nullptr;

View File

@ -58,6 +58,7 @@ public:
bool isExportingAuthorization();
bool hasMediaAddress();
void resetInitVersion();
bool isRepeatCheckingAddresses();
Connection *getDownloadConnection(uint8_t num, bool create);
Connection *getProxyConnection(uint8_t num, bool create, bool connect);
@ -121,6 +122,7 @@ private:
int64_t authKeyMediaTempId = 0;
Config *config = nullptr;
bool isCdnDatacenter = false;
bool repeatCheckingAddresses = false;
std::vector<std::unique_ptr<Handshake>> handshakes;

View File

@ -39,13 +39,9 @@
#define NETWORK_TYPE_ROAMING 2
class TLObject;
class TL_error;
class Request;
class TL_message;
class TL_config;
class NativeByteBuffer;
class Handshake;

View File

@ -41,7 +41,7 @@ target_compile_options(tgvoip PUBLIC
set_target_properties(tgvoip PROPERTIES
ANDROID_ARM_MODE arm)
target_compile_definitions(tgvoip PUBLIC
HAVE_PTHREAD __STDC_LIMIT_MACROS BSD=1 USE_KISS_FFT TGVOIP_NO_VIDEO NULL=0 SOCKLEN_T=socklen_t LOCALE_NOT_USED _LARGEFILE_SOURCE=1 _FILE_OFFSET_BITS=64 restrict= __EMX__ OPUS_BUILD FIXED_POINT USE_ALLOCA HAVE_LRINT HAVE_LRINTF)
HAVE_PTHREAD __STDC_LIMIT_MACROS BSD=1 USE_KISS_FFT TGVOIP_NO_VIDEO NULL=0 SOCKLEN_T=socklen_t LOCALE_NOT_USED _LARGEFILE_SOURCE=1 _FILE_OFFSET_BITS=64 restrict= __EMX__ OPUS_BUILD FIXED_POINT USE_ALLOCA HAVE_LRINT HAVE_LRINTF TGVOIP_NO_DSP)
target_compile_definitions(tgvoip PUBLIC
RTC_DISABLE_TRACE_EVENTS WEBRTC_OPUS_SUPPORT_120MS_PTIME=1 BWE_TEST_LOGGING_COMPILE_TIME_ENABLE=0 ABSL_ALLOCATOR_NOTHROW=1 RTC_ENABLE_VP9 WEBRTC_POSIX WEBRTC_LINUX WEBRTC_ANDROID WEBRTC_USE_H264 NDEBUG WEBRTC_HAVE_USRSCTP WEBRTC_HAVE_SCTP WEBRTC_APM_DEBUG_DUMP=0 WEBRTC_USE_BUILTIN_ISAC_FLOAT WEBRTC_OPUS_VARIABLE_COMPLEXITY=0 HAVE_NETINET_IN_H WEBRTC_INCLUDE_INTERNAL_AUDIO_DEVICE __Userspace__ SCTP_SIMPLE_ALLOCATOR SCTP_PROCESS_LEVEL_LOCKS __Userspace_os_Linux)
target_include_directories(tgvoip PUBLIC
@ -404,7 +404,7 @@ target_compile_options(tgcalls_tp PUBLIC
set_target_properties(tgcalls_tp PROPERTIES
ANDROID_ARM_MODE arm)
target_compile_definitions(tgcalls_tp PUBLIC
RTC_DISABLE_TRACE_EVENTS WEBRTC_OPUS_SUPPORT_120MS_PTIME=1 BWE_TEST_LOGGING_COMPILE_TIME_ENABLE=0 ABSL_ALLOCATOR_NOTHROW=1 HAVE_PTHREAD RTC_ENABLE_VP9 WEBRTC_POSIX WEBRTC_LINUX WEBRTC_ANDROID WEBRTC_USE_H264 NDEBUG WEBRTC_HAVE_USRSCTP WEBRTC_HAVE_SCTP WEBRTC_APM_DEBUG_DUMP=0 WEBRTC_USE_BUILTIN_ISAC_FLOAT WEBRTC_OPUS_VARIABLE_COMPLEXITY=0 HAVE_NETINET_IN_H WEBRTC_INCLUDE_INTERNAL_AUDIO_DEVICE __Userspace__ SCTP_SIMPLE_ALLOCATOR SCTP_PROCESS_LEVEL_LOCKS __Userspace_os_Linux HAVE_WEBRTC_VIDEO __ANDROID__)
RTC_DISABLE_TRACE_EVENTS WEBRTC_OPUS_SUPPORT_120MS_PTIME=1 BWE_TEST_LOGGING_COMPILE_TIME_ENABLE=0 ABSL_ALLOCATOR_NOTHROW=1 HAVE_PTHREAD RTC_ENABLE_VP9 WEBRTC_POSIX WEBRTC_LINUX WEBRTC_ANDROID WEBRTC_USE_H264 NDEBUG WEBRTC_HAVE_USRSCTP WEBRTC_HAVE_SCTP WEBRTC_APM_DEBUG_DUMP=0 WEBRTC_USE_BUILTIN_ISAC_FLOAT WEBRTC_OPUS_VARIABLE_COMPLEXITY=0 HAVE_NETINET_IN_H WEBRTC_INCLUDE_INTERNAL_AUDIO_DEVICE __Userspace__ SCTP_SIMPLE_ALLOCATOR SCTP_PROCESS_LEVEL_LOCKS __Userspace_os_Linux HAVE_WEBRTC_VIDEO __ANDROID__ TGVOIP_NO_DSP)
target_include_directories(tgcalls_tp PUBLIC
./
voip

View File

@ -371,7 +371,12 @@ public class GcmPushListenerService extends FirebaseMessagingService {
if (loc_key.startsWith("REACT_") || loc_key.startsWith("CHAT_REACT_")) {
messageText = getReactedText(loc_key, args);
} else {switch (loc_key) {
case "MESSAGE_TEXT":
case "MESSAGE_RECURRING_PAY": {
messageText = LocaleController.formatString("NotificationMessageRecurringPay", R.string.NotificationMessageRecurringPay, args[0], args[1]);
message1 = LocaleController.getString("PaymentInvoice", R.string.PaymentInvoice);
break;
}
case "MESSAGE_TEXT":
case "CHANNEL_MESSAGE_TEXT": {
messageText = LocaleController.formatString("NotificationMessageText", R.string.NotificationMessageText, args[0], args[1]);
message1 = args[1];

View File

@ -97,6 +97,7 @@
<uses-permission android:name="com.oppo.launcher.permission.WRITE_SETTINGS"/>
<uses-permission android:name="me.everything.badger.permission.BADGE_COUNT_READ"/>
<uses-permission android:name="me.everything.badger.permission.BADGE_COUNT_WRITE"/>
<uses-permission android:name="com.android.vending.BILLING"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="replace" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
@ -119,6 +120,94 @@
android:allowAudioPlaybackCapture="true"
tools:replace="android:supportsRtl">
<activity-alias
android:enabled="true"
android:name="org.telegram.messenger.DefaultIcon"
android:targetActivity="org.telegram.ui.LaunchActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:enabled="false"
android:name="org.telegram.messenger.VintageIcon"
android:targetActivity="org.telegram.ui.LaunchActivity"
android:icon="@mipmap/icon_6_launcher"
android:roundIcon="@mipmap/icon_6_launcher_round"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:enabled="false"
android:name="org.telegram.messenger.AquaIcon"
android:targetActivity="org.telegram.ui.LaunchActivity"
android:icon="@mipmap/icon_4_launcher"
android:roundIcon="@mipmap/icon_4_launcher_round"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:enabled="false"
android:name="org.telegram.messenger.PremiumIcon"
android:targetActivity="org.telegram.ui.LaunchActivity"
android:icon="@mipmap/icon_3_launcher"
android:roundIcon="@mipmap/icon_3_launcher_round"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:enabled="false"
android:name="org.telegram.messenger.TurboIcon"
android:targetActivity="org.telegram.ui.LaunchActivity"
android:icon="@mipmap/icon_5_launcher"
android:roundIcon="@mipmap/icon_5_launcher_round"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:enabled="false"
android:name="org.telegram.messenger.NoxIcon"
android:targetActivity="org.telegram.ui.LaunchActivity"
android:icon="@mipmap/icon_2_launcher"
android:roundIcon="@mipmap/icon_2_launcher_round"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
</intent-filter>
</activity-alias>
<activity
android:name="org.telegram.ui.LaunchActivity"
android:theme="@style/Theme.TMessages.Start"
@ -128,8 +217,6 @@
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND"/>

File diff suppressed because one or more lines are too long

View File

@ -26,6 +26,7 @@ chat_inPreviewInstantSelectedText=-5648402
chat_attachAudioBackground=-626837
location_sendLocationBackground=-9919529
actionBarDefaultSubmenuBackground=-14075831
actionBarDefaultSubmenuSeparator=-14733761
switchTrackBlueThumb=-14866637
avatar_nameInMessageViolet=-6643205
emptyListPlaceholder=-8549479

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

View File

@ -33,7 +33,7 @@ chat_inAudioSelectedProgress=-1
chats_nameMessage=-1315861
chat_messagePanelShadow=2030043136
chat_inMediaIcon=-1
actionBarDefaultArchived=-14145495
actionBarDefaultArchived=-13882323
avatar_subtitleInProfileViolet=-7628894
chat_messagePanelCancelInlineBot=1694498815
dialogSearchBackground=-13816531
@ -107,6 +107,7 @@ dialogInputField=-8553091
windowBackgroundWhiteInputFieldActivated=-9522449
chat_attachGalleryBackground=-11692299
chat_outInstantSelected=-1
actionBarDefaultSubmenuSeparator=-14803425
chat_outSentCheck=-506859265
key_graySectionText=-8158332
player_placeholder=-10197916
@ -116,7 +117,7 @@ groupcreate_spanBackground=-13816531
dialogButton=-10177041
contextProgressInner1=1526726655
chat_inLoaderPhotoIconSelected=-1
actionBarDefaultSubtitle=1693643506
actionBarDefaultSubtitle=1945301746
chat_inContactPhoneText=-8486783
chat_inlineResultIcon=-8796932
chat_outBubbleGradientSelectedOverlay=352321535
@ -197,7 +198,7 @@ statisticChartSignatureAlpha=-1946157057
chat_emojiSearchIcon=-9211020
chat_emojiPanelTrendingDescription=-8553090
calls_callReceivedGreenIcon=-12001930
chats_pinnedOverlay=16777215
chats_pinnedOverlay=201326591
windowBackgroundWhiteInputField=-11513776
avatar_backgroundRed=-2326437
statisticChartLine_green=-12729793
@ -266,7 +267,13 @@ chat_outAudioDurationSelectedText=-7023626
avatar_backgroundArchivedHidden=-12279320
chat_attachCameraIcon1=-32171
undo_background=-181917656
premiumGradientBackground3=-16777216
premiumGradientBackground4=-16777216
premiumGradientBackground1=-16578545
avatar_actionBarSelectorPink=-12758164
premiumGradientBackground2=-16645625
premiumStarGradient1=-2342678
premiumStarGradient2=-9992961
dialogTextHint=-8553091
statisticChartLine_orange=-1457126
chat_topPanelTitle=-10440716
@ -324,7 +331,7 @@ avatar_actionBarSelectorViolet=-12758164
chat_attachPollBackground=-2183099
avatar_nameInMessageBlue=-8796932
dialogTextBlack=-592138
actionBarDefault=-14671838
actionBarDefault=-14474458
location_placeLocationBackground=-9919529
profile_actionIcon=-1
windowBackgroundUnchecked=-14803424
@ -344,6 +351,7 @@ chat_goDownButtonCounter=-1
switchTrackBlueSelectorChecked=848091135
chat_outFileBackground=520093695
chats_name=-921103
premiumStartSmallStarsColor=-8290049
switchTrackBlueSelector=431611386
dialogBadgeBackground=-10177041
chat_outBubbleSelected=-11829841
@ -494,4 +502,4 @@ chat_topPanelBackground=-15066597
chat_outSentClock=-6698513
dialogBackgroundGray=-14013910
chat_searchPanelText=-10767620
chat_inContactIcon=-1
chat_inContactIcon=-1

View File

@ -0,0 +1,68 @@
precision highp float;
uniform sampler2D u_Texture;
uniform sampler2D u_NormalMap;
uniform sampler2D u_BackgroundTexture;
uniform float f_xOffset;
uniform float f_alpha;
uniform mat4 world;
uniform vec3 modelViewVertex;
varying vec3 vNormal;
varying vec2 vUV;
vec3 cameraPosition = vec3(0, 0, 100);
vec4 a_Color = vec4(1);
uniform float spec1;
uniform float spec2;
uniform float u_diffuse;
uniform float normalSpec;
uniform vec3 gradientColor1;
uniform vec3 gradientColor2;
uniform vec3 normalSpecColor;
uniform vec3 specColor;
uniform vec2 resolution;
uniform vec4 gradientPosition;
void main() {
vec3 vLightPosition2 = vec3(-400,400,400);
vec3 vLightPosition3 = vec3(0,200,400);
vec3 vLightPosition4 = vec3(100,-200,400);
vec3 vNormalW = normalize(vec3(world * vec4(vNormal, 0.0)));
vec3 vTextureNormal = normalize(texture2D(u_NormalMap, vUV + vec2(-f_xOffset, f_xOffset)).xyz * 2.0 - 1.0);
vec3 finalNormal = normalize(vNormalW + vTextureNormal);
vec3 color = texture2D(u_Texture, vUV * vec2(0.8, 0.8) + vec2(0.2 * f_xOffset, 0)).xyz;
vec3 viewDirectionW = normalize(cameraPosition - modelViewVertex);
vec3 angleW = normalize(viewDirectionW + vLightPosition2);
float specComp2 = max(0., dot(vNormalW, angleW));
specComp2 = pow(specComp2, max(1., 128.)) * spec1;
angleW = normalize(viewDirectionW + vLightPosition4);
float specComp3 = max(0., dot(vNormalW, angleW));
specComp3 = pow(specComp3, max(1., 128.)) * spec2;
float diffuse = max(dot(vNormalW, viewDirectionW), (1.0 - u_diffuse));
float mixValue = distance(vUV,vec2(1,0));
vec4 gradientColorFinal = vec4(mix(gradientColor1,gradientColor2,mixValue), 1.0);
angleW = normalize(viewDirectionW + vLightPosition4);
float normalSpecComp = max(0., dot(finalNormal, angleW));
normalSpecComp = pow(normalSpecComp, max(1., 128.)) * normalSpec;
angleW = normalize(viewDirectionW + vLightPosition2);
float normalSpecComp2 = max(0., dot(finalNormal, angleW));
normalSpecComp2 = pow(normalSpecComp2, max(1., 128.)) * normalSpec;
vec4 normalSpecFinal = vec4(normalSpecColor, 0.0) * (normalSpecComp + normalSpecComp2);
vec4 specFinal = vec4(specColor, 0.0) * (specComp2 + specComp3);
vec4 fragColor = gradientColorFinal + specFinal + normalSpecFinal;
vec4 backgroundColor = texture2D(u_BackgroundTexture, vec2(gradientPosition.x + (gl_FragCoord.x / resolution.x) * gradientPosition.y, gradientPosition.z + (1.0 - (gl_FragCoord.y / resolution.y)) * gradientPosition.w));
gl_FragColor = mix(backgroundColor, fragColor, diffuse) * f_alpha;
}

View File

@ -0,0 +1,16 @@
uniform mat4 uMVPMatrix;
attribute vec2 a_TexCoordinate;
attribute vec3 a_Normal;
attribute vec4 vPosition;
varying vec3 vNormal;
varying vec2 vUV;
varying vec3 modelViewVertex;
void main() {
modelViewVertex = vec3(uMVPMatrix * vPosition);
vUV = a_TexCoordinate;
vNormal = a_Normal;
gl_Position = uMVPMatrix * vPosition;
}

View File

@ -761,6 +761,13 @@ public class ChatListItemAnimator extends DefaultItemAnimator {
recyclerListView.invalidate();
ValueAnimator valueAnimator = ValueAnimator.ofFloat(1f, 0);
if (moveInfoExtended.animateBackgroundOnly) {
params.toDeltaLeft = -moveInfoExtended.deltaLeft;
params.toDeltaRight = -moveInfoExtended.deltaRight;
} else {
params.toDeltaLeft = -moveInfoExtended.deltaLeft - chatMessageCell.getAnimationOffsetX();
params.toDeltaRight = -moveInfoExtended.deltaRight - chatMessageCell.getAnimationOffsetX();
}
valueAnimator.addUpdateListener(animation -> {
float v = (float) animation.getAnimatedValue();
if (moveInfoExtended.animateBackgroundOnly) {
@ -777,6 +784,9 @@ public class ChatListItemAnimator extends DefaultItemAnimator {
chatMessageCell.invalidate();
});
animatorSet.playTogether(valueAnimator);
} else {
params.toDeltaLeft = 0;
params.toDeltaRight = 0;
}
MessageObject.GroupedMessages group = chatMessageCell.getCurrentMessagesGroup();

View File

@ -21,6 +21,7 @@ import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.os.Build;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
import android.view.animation.Interpolator;
@ -223,7 +224,11 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
final View view = holder.itemView;
final ViewPropertyAnimator animation = view.animate();
mRemoveAnimations.add(holder);
animation.setDuration(getRemoveDuration()).alpha(0).setListener(
if (getRemoveDelay() > 0) {
// wanted to achieve an effect of next items covering current
((ViewGroup) view.getParent()).bringChildToFront(view);
}
animation.setDuration(getRemoveDuration()).setStartDelay(getRemoveDelay()).alpha(0).setListener(
new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
@ -256,7 +261,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
final View view = holder.itemView;
final ViewPropertyAnimator animation = view.animate();
mAddAnimations.add(holder);
animation.alpha(1).setDuration(getAddDuration())
animation.alpha(1).setDuration(getAddDuration()).setStartDelay(getAddDelay())
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
@ -331,30 +336,35 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
if (translationInterpolator != null) {
animation.setInterpolator(translationInterpolator);
}
animation.setDuration(getMoveDuration()).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
dispatchMoveStarting(holder);
}
@Override
public void onAnimationCancel(Animator animator) {
if (deltaX != 0) {
view.setTranslationX(0);
animation
.setDuration(getMoveDuration())
.setStartDelay(getMoveDelay())
.setInterpolator(getMoveInterpolator())
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
dispatchMoveStarting(holder);
}
if (deltaY != 0) {
view.setTranslationY(0);
}
}
@Override
public void onAnimationEnd(Animator animator) {
animation.setListener(null);
dispatchMoveFinished(holder);
mMoveAnimations.remove(holder);
dispatchFinishedWhenDone();
}
}).start();
@Override
public void onAnimationCancel(Animator animator) {
if (deltaX != 0) {
view.setTranslationX(0);
}
if (deltaY != 0) {
view.setTranslationY(0);
}
}
@Override
public void onAnimationEnd(Animator animator) {
animation.setListener(null);
dispatchMoveFinished(holder);
mMoveAnimations.remove(holder);
dispatchFinishedWhenDone();
}
})
.start();
}
@Override
@ -393,8 +403,7 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
final RecyclerView.ViewHolder newHolder = changeInfo.newHolder;
final View newView = newHolder != null ? newHolder.itemView : null;
if (view != null) {
final ViewPropertyAnimator oldViewAnim = view.animate().setDuration(
getChangeDuration());
final ViewPropertyAnimator oldViewAnim = view.animate().setDuration(getChangeRemoveDuration()).setStartDelay(getChangeDelay());
mChangeAnimations.add(changeInfo.oldHolder);
oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX);
oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY);
@ -419,7 +428,8 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
if (newView != null) {
final ViewPropertyAnimator newViewAnimation = newView.animate();
mChangeAnimations.add(changeInfo.newHolder);
newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration())
newViewAnimation.translationX(0).translationY(0).setDuration(getChangeAddDuration())
.setStartDelay(getChangeDelay() + (getChangeDuration() - getChangeAddDuration()))
.alpha(1).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animator) {
@ -713,6 +723,14 @@ public class DefaultItemAnimator extends SimpleItemAnimator {
}
}
public boolean isHolderRemoving(RecyclerView.ViewHolder holder) {
return mRemoveAnimations.contains(holder);
}
public boolean isHolderAdding(RecyclerView.ViewHolder holder) {
return mAddAnimations.contains(holder);
}
/**
* {@inheritDoc}
* <p>

View File

@ -21,6 +21,9 @@ import android.view.View;
import androidx.annotation.Nullable;
import androidx.core.os.TraceCompat;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.FileLog;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -274,6 +277,7 @@ final class GapWorker implements Runnable {
return false;
}
@SuppressLint("NotifyDataSetChanged")
private RecyclerView.ViewHolder prefetchPositionWithDeadline(RecyclerView view,
int position, long deadlineNs) {
if (isPrefetchPositionAttached(view, position)) {
@ -301,6 +305,14 @@ final class GapWorker implements Runnable {
recycler.addViewHolderToRecycledViewPool(holder, false);
}
}
} catch (Exception e) {
FileLog.e(e);
AndroidUtilities.runOnUIThread(() -> {
if (view.getAdapter() != null) {
view.getAdapter().notifyDataSetChanged();
}
});
return null;
} finally {
view.onExitLayoutOrScroll(false);
}

View File

@ -22,6 +22,7 @@ import static androidx.core.view.ViewCompat.TYPE_NON_TOUCH;
import static androidx.core.view.ViewCompat.TYPE_TOUCH;
import android.animation.LayoutTransition;
import android.animation.TimeInterpolator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
@ -12720,7 +12721,16 @@ public class RecyclerView extends ViewGroup implements ScrollingView,
private long mAddDuration = 120;
private long mRemoveDuration = 120;
private long mMoveDuration = 250;
private long mChangeDuration = 250;
private long mChangeAddDuration = 250;
private long mChangeRemoveDuration = 250;
private TimeInterpolator mMoveInterpolator = null;
private long mAddDelay = 0;
private long mRemoveDelay = 0;
private long mMoveDelay = 0;
private long mDelay = 0;
private long mChangeDelay = 0;
/**
* Gets the current duration for which all move animations will run.
@ -12776,13 +12786,31 @@ public class RecyclerView extends ViewGroup implements ScrollingView,
mRemoveDuration = removeDuration;
}
/**
* Gets the current duration for which all change animations of item appearance
*
* @return The current change duration
*/
public long getChangeAddDuration() {
return mChangeAddDuration;
}
/**
* Gets the current duration for which all change animations will run.
*
* @return The current change duration
*/
public long getChangeDuration() {
return mChangeDuration;
return Math.max(mChangeAddDuration, mChangeRemoveDuration);
}
/**
* Gets the current duration for which all change animations of item disappearance
*
* @return The current change duration
*/
public long getChangeRemoveDuration() {
return mChangeRemoveDuration;
}
/**
@ -12791,7 +12819,68 @@ public class RecyclerView extends ViewGroup implements ScrollingView,
* @param changeDuration The change duration
*/
public void setChangeDuration(long changeDuration) {
mChangeDuration = changeDuration;
mChangeAddDuration = changeDuration;
mChangeRemoveDuration = changeDuration;
}
/**
* Sets the duration for which all change animations will run.
*
* @param changeNewDuration The duration of showing new view of change
* @param changeOldDuration The duration of hiding old view of change
*/
public void setChangeDuration(long changeNewDuration, long changeOldDuration) {
mChangeAddDuration = changeNewDuration;
mChangeRemoveDuration = changeOldDuration;
}
public void setAddDelay(long addDelay) {
mAddDelay = addDelay;
}
public void setRemoveDelay(long removeDelay) {
mRemoveDelay = removeDelay;
}
public void setMoveDelay(long moveDelay) {
mMoveDelay = moveDelay;
}
public void setChangeDelay(long changeDelay) {
mChangeDelay = changeDelay;
}
public long getAddDelay() {
return mAddDelay;
}
public long getRemoveDelay() {
return mRemoveDelay;
}
public long getMoveDelay() {
return mMoveDelay;
}
public long getChangeDelay() {
return mChangeDelay;
}
public void setDurations(long duration) {
mAddDuration = duration;
mMoveDuration = duration;
mRemoveDuration = duration;
mChangeAddDuration = duration;
mChangeRemoveDuration = duration;
}
public void setMoveInterpolator(TimeInterpolator interpolator) {
mMoveInterpolator = interpolator;
}
public TimeInterpolator getMoveInterpolator() {
return mMoveInterpolator;
}
/**

View File

@ -40,6 +40,7 @@ public abstract class SimpleItemAnimator extends RecyclerView.ItemAnimator {
boolean mSupportsChangeAnimations = true;
protected boolean alwaysCreateMoveAnimationIfPossible;
protected boolean disabledMoveAnimations;
/**
* Returns whether this ItemAnimator supports animations of change events.
@ -93,7 +94,7 @@ public abstract class SimpleItemAnimator extends RecyclerView.ItemAnimator {
View disappearingItemView = viewHolder.itemView;
int newLeft = postLayoutInfo == null ? disappearingItemView.getLeft() : postLayoutInfo.left;
int newTop = postLayoutInfo == null ? disappearingItemView.getTop() : postLayoutInfo.top;
if (!viewHolder.isRemoved() && (oldLeft != newLeft || oldTop != newTop)) {
if (!disabledMoveAnimations && !viewHolder.isRemoved() && (oldLeft != newLeft || oldTop != newTop)) {
disappearingItemView.layout(newLeft, newTop,
newLeft + disappearingItemView.getWidth(),
newTop + disappearingItemView.getHeight());
@ -114,7 +115,7 @@ public abstract class SimpleItemAnimator extends RecyclerView.ItemAnimator {
@Override
public boolean animateAppearance(@NonNull RecyclerView.ViewHolder viewHolder,
@Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
if (preLayoutInfo != null && ((preLayoutInfo.left != postLayoutInfo.left
if (!disabledMoveAnimations && preLayoutInfo != null && ((preLayoutInfo.left != postLayoutInfo.left
|| preLayoutInfo.top != postLayoutInfo.top) || alwaysCreateMoveAnimationIfPossible)) {
// slide items in if before/after locations differ
if (DEBUG) {
@ -133,7 +134,7 @@ public abstract class SimpleItemAnimator extends RecyclerView.ItemAnimator {
@Override
public boolean animatePersistence(@NonNull RecyclerView.ViewHolder viewHolder,
@NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
if (preInfo.left != postInfo.left || preInfo.top != postInfo.top) {
if (!disabledMoveAnimations && (preInfo.left != postInfo.left || preInfo.top != postInfo.top)) {
if (DEBUG) {
Log.d(TAG, "PERSISTENT: " + viewHolder
+ " with view " + viewHolder.itemView);

View File

@ -49,6 +49,7 @@ import android.os.Build;
import android.os.Environment;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.Vibrator;
import android.provider.CallLog;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
@ -76,6 +77,7 @@ import android.view.MotionEvent;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowInsets;
@ -90,12 +92,14 @@ import android.view.inputmethod.InputMethodSubtype;
import android.webkit.MimeTypeMap;
import android.widget.EdgeEffect;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.SpringAnimation;
@ -162,6 +166,7 @@ import java.util.Hashtable;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -187,7 +192,10 @@ import static com.v2ray.ang.V2RayConfig.WS_PROTOCOL;
public class AndroidUtilities {
public final static int LIGHT_STATUS_BAR_OVERLAY = 0x0f000000, DARK_STATUS_BAR_OVERLAY = 0x33000000;
public final static String TYPEFACE_ROBOTO_MEDIUM = "fonts/rmedium.ttf";
private static final Hashtable<String, Typeface> typefaceCache = new Hashtable<>();
public static float touchSlop;
private static int prevOrientation = -10;
private static boolean waitingForSms = false;
private static boolean waitingForCall = false;
@ -215,6 +223,7 @@ public class AndroidUtilities {
public static OvershootInterpolator overshootInterpolator = new OvershootInterpolator();
private static AccessibilityManager accessibilityManager;
private static Vibrator vibrator;
private static Boolean isTablet = null, isSmallScreen = null;
private static int adjustOwnerClassGuid = 0;
@ -453,6 +462,23 @@ public class AndroidUtilities {
return spannableStringBuilder;
}
public static void recycleBitmaps(ArrayList<Bitmap> bitmapToRecycle) {
if (bitmapToRecycle != null && !bitmapToRecycle.isEmpty()) {
AndroidUtilities.runOnUIThread(() -> NotificationCenter.getInstance(UserConfig.selectedAccount).doOnIdle(() -> {
for (int i = 0; i < bitmapToRecycle.size(); i++) {
Bitmap bitmap = bitmapToRecycle.get(i);
if (bitmap != null && !bitmap.isRecycled()) {
try {
bitmap.recycle();
} catch (Exception e) {
FileLog.e(e);
}
}
}
}), 36);
}
}
private static class LinkSpec {
String url;
int start;
@ -572,7 +598,7 @@ public class AndroidUtilities {
Linkify.addLinks(text, Linkify.PHONE_NUMBERS);
}
if ((mask & Linkify.WEB_URLS) != 0) {
gatherLinks(links, text, LinkifyPort.WEB_URL, new String[]{"http://", "https://", "ton://", "tg://"}, sUrlMatchFilter, internalOnly);
gatherLinks(links, text, LinkifyPort.WEB_URL, new String[]{"http://", "https://", "tg://"}, sUrlMatchFilter, internalOnly);
}
pruneOverlaps(links);
if (links.size() == 0) {
@ -904,10 +930,17 @@ public class AndroidUtilities {
}
public static void requestAdjustResize(Activity activity, int classGuid) {
if (activity == null || isTablet()) {
if (activity == null) {
return;
}
activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
requestAdjustResize(activity.getWindow(), classGuid);
}
public static void requestAdjustResize(Window window, int classGuid) {
if (window == null || isTablet()) {
return;
}
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
adjustOwnerClassGuid = classGuid;
}
@ -1846,6 +1879,8 @@ public class AndroidUtilities {
}
FileLog.e("density = " + density + " display size = " + displaySize.x + " " + displaySize.y + " " + displayMetrics.xdpi + "x" + displayMetrics.ydpi + ", screen layout: " + configuration.screenLayout + ", statusbar height: " + statusBarHeight + ", navbar height: " + navigationBarHeight);
}
ViewConfiguration vc = ViewConfiguration.get(context);
touchSlop = vc.getScaledTouchSlop();
} catch (Exception e) {
FileLog.e(e);
}
@ -2638,8 +2673,13 @@ public class AndroidUtilities {
public static File generatePicturePath(boolean secretChat, String ext) {
try {
File storageDir = ApplicationLoader.applicationContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
return new File(storageDir, generateFileName(0, ext));
File publicDir = FileLoader.getDirectory(FileLoader.MEDIA_DIR_IMAGE_PUBLIC);
if (secretChat || publicDir == null) {
File storageDir = ApplicationLoader.applicationContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
return new File(storageDir, generateFileName(0, ext));
} else {
return new File(publicDir, generateFileName(0, ext));
}
} catch (Exception e) {
FileLog.e(e);
}
@ -2765,11 +2805,11 @@ public class AndroidUtilities {
return String.format("%.1f MB", value);
}
} else {
float value = size / 1024.0f / 1024.0f / 1024.0f;
float value = (int) (size / 1024L / 1024L) / 1000.0f;
if (removeZero && (value - (int) value) * 10 == 0) {
return String.format("%d GB", (int) value);
} else {
return String.format("%.1f GB", value);
return String.format("%.2f GB", value);
}
}
}
@ -3013,7 +3053,7 @@ public class AndroidUtilities {
f = new File(message.messageOwner.attachPath);
}
if (f == null || f != null && !f.exists()) {
f = FileLoader.getPathToMessage(message.messageOwner);
f = FileLoader.getInstance(UserConfig.selectedAccount).getPathToMessage(message.messageOwner);
}
if (f != null && f.exists()) {
if (parentFragment != null && f.getName().toLowerCase().endsWith("attheme")) {
@ -3136,7 +3176,7 @@ public class AndroidUtilities {
f = new File(message.messageOwner.attachPath);
}
if (f == null || !f.exists()) {
f = FileLoader.getPathToMessage(message.messageOwner);
f = FileLoader.getInstance(message.currentAccount).getPathToMessage(message.messageOwner);
}
String mimeType = message.type == 9 || message.type == 0 ? message.getMimeType() : null;
return openForView(f, message.getFileName(), mimeType, activity, resourcesProvider);
@ -3144,7 +3184,7 @@ public class AndroidUtilities {
public static boolean openForView(TLRPC.Document document, boolean forceCache, Activity activity) {
String fileName = FileLoader.getAttachFileName(document);
File f = FileLoader.getPathToAttach(document, true);
File f = FileLoader.getInstance(UserConfig.selectedAccount).getPathToAttach(document, true);
return openForView(f, fileName, document.mime_type, activity, null);
}
@ -3225,7 +3265,7 @@ public class AndroidUtilities {
return false;
}
String fileName = FileLoader.getAttachFileName(media);
File f = FileLoader.getPathToAttach(media, true);
File f = FileLoader.getInstance(UserConfig.selectedAccount).getPathToAttach(media, true);
if (f != null && f.exists()) {
String realMimeType = null;
Intent intent = new Intent(Intent.ACTION_VIEW);
@ -3331,6 +3371,13 @@ public class AndroidUtilities {
}
}
public static Vibrator getVibrator() {
if (vibrator == null) {
vibrator = (Vibrator) ApplicationLoader.applicationContext.getSystemService(Context.VIBRATOR_SERVICE);
}
return vibrator;
}
public static boolean isAccessibilityTouchExplorationEnabled() {
if (accessibilityManager == null) {
accessibilityManager = (AccessibilityManager) ApplicationLoader.applicationContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
@ -4414,6 +4461,10 @@ public class AndroidUtilities {
}
}
public static float computeDampingRatio(float tension /* stiffness */, float friction /* damping */, float mass) {
return friction / (2f * (float) Math.sqrt(mass * tension));
}
private static WeakReference<BaseFragment> flagSecureFragment;
public static boolean hasFlagSecureFragment() {
@ -4763,11 +4814,34 @@ public class AndroidUtilities {
}
}
public static void updateImageViewImageAnimated(ImageView imageView, int newIcon) {
updateImageViewImageAnimated(imageView, ContextCompat.getDrawable(imageView.getContext(), newIcon));
}
public static void updateImageViewImageAnimated(ImageView imageView, Drawable newIcon) {
ValueAnimator animator = ValueAnimator.ofFloat(0, 1).setDuration(150);
AtomicBoolean changed = new AtomicBoolean();
animator.addUpdateListener(animation -> {
float val = (float) animation.getAnimatedValue();
float scale = 0.5f + Math.abs(val - 0.5f);
imageView.setScaleX(scale);
imageView.setScaleY(scale);
if (val >= 0.5f && !changed.get()) {
changed.set(true);
imageView.setImageDrawable(newIcon);
}
});
animator.start();
}
public static void updateViewVisibilityAnimated(View view, boolean show) {
updateViewVisibilityAnimated(view, show, 1f, true);
}
public static void updateViewVisibilityAnimated(View view, boolean show, float scaleFactor, boolean animated) {
if (view == null) {
return;
}
if (view.getParent() == null) {
animated = false;
}
@ -4883,4 +4957,14 @@ public class AndroidUtilities {
return false;
}
}
public static boolean isAccessibilityScreenReaderEnabled() {
return false;
// try {
// AccessibilityManager am = (AccessibilityManager) ApplicationLoader.applicationContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
// return (am != null && am.isEnabled() && !am.isTouchExplorationEnabled());
// } catch (Exception ignroe) {
// return false;
// }
}
}

View File

@ -14,7 +14,7 @@ public class AnimatedFileDrawableStream implements FileLoadOperationStream {
private int currentAccount;
private volatile boolean canceled;
private final Object sync = new Object();
private int lastOffset;
private long lastOffset;
private boolean waitingForLoad;
private boolean preview;
private boolean finishedLoadingFile;
@ -48,10 +48,10 @@ public class AnimatedFileDrawableStream implements FileLoadOperationStream {
if (readLength == 0) {
return 0;
} else {
int availableLength = 0;
long availableLength = 0;
try {
while (availableLength == 0) {
int[] result = loadOperation.getDownloadedLengthFromOffset(offset, readLength);
long[] result = loadOperation.getDownloadedLengthFromOffset(offset, readLength);
availableLength = result[0];
if (!finishedLoadingFile && result[1] != 0) {
finishedLoadingFile = true;
@ -79,7 +79,7 @@ public class AnimatedFileDrawableStream implements FileLoadOperationStream {
} catch (Exception e) {
FileLog.e(e, false);
}
return availableLength;
return (int) availableLength;
}
}

View File

@ -41,6 +41,7 @@ import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Components.ForegroundDetector;
import org.telegram.ui.LauncherIconController;
import java.io.File;
import java.lang.reflect.Method;
@ -388,7 +389,7 @@ public class ApplicationLoader extends Application {
org.osmdroid.config.Configuration.getInstance().setOsmdroidBasePath(new File(ApplicationLoader.applicationContext.getCacheDir(), "osmdroid"));
startPushService();
LauncherIconController.tryFixLauncherIconIfNeeded();
}
public static void startPushService() {

View File

@ -4,7 +4,7 @@ import org.telegram.tgnet.ConnectionsManager;
public class BaseController {
public int currentAccount;
protected final int currentAccount;
private AccountInstance parentAccountInstance;
public BaseController(int num) {

View File

@ -0,0 +1,42 @@
package org.telegram.messenger;
import android.os.Build;
import android.os.VibrationEffect;
import androidx.annotation.RequiresApi;
public enum BotWebViewVibrationEffect {
IMPACT_LIGHT(new long[] {7}, new int[] {65}, new long[] {50}),
IMPACT_MEDIUM(new long[] {7}, new int[] {145}, new long[] {50}),
IMPACT_HEAVY(new long[] {7}, new int[] {255}, new long[] {50}),
IMPACT_RIGID(new long[] {3}, new int[] {225}, new long[] {50}),
IMPACT_SOFT(new long[] {10}, new int[] {175}, new long[] {50}),
NOTIFICATION_ERROR(new long[] {14,48,14,48,14,48,20}, new int[] {200,0,200,0,255,0,145}, new long[] {50}),
NOTIFICATION_SUCCESS(new long[] {14,65,14}, new int[] {175,0,255}, new long[] {50}),
NOTIFICATION_WARNING(new long[] {14,64,14}, new int[] {225,0,175}, new long[] {50}),
SELECTION_CHANGE(new long[] {1}, new int[] {65}, new long[] {50});
public final long[] timings;
public final int[] amplitudes;
public final long[] fallbackTimings;
private Object vibrationEffect;
BotWebViewVibrationEffect(long[] timings, int[] amplitudes, long[] fallbackTimings) {
this.timings = timings;
this.amplitudes = amplitudes;
this.fallbackTimings = fallbackTimings;
}
@RequiresApi(Build.VERSION_CODES.O)
public VibrationEffect getVibrationEffectForOreo() {
if (vibrationEffect == null) {
if (!AndroidUtilities.getVibrator().hasAmplitudeControl()) {
vibrationEffect = VibrationEffect.createWaveform(fallbackTimings, -1);
} else {
vibrationEffect = VibrationEffect.createWaveform(timings, amplitudes, -1);
}
}
return (VibrationEffect) vibrationEffect;
}
}

View File

@ -17,6 +17,7 @@ import android.os.Build;
@SuppressWarnings("ConstantConditions")
public class BuildVars {
public static final boolean IS_BILLING_UNAVAILABLE = false;
public static boolean DEBUG_VERSION = BuildConfig.BUILD_TYPE.equals("debug");
public static boolean DEBUG_PRIVATE_VERSION = DEBUG_VERSION;
public static boolean LOGS_ENABLED = DEBUG_PRIVATE_VERSION;

View File

@ -21,6 +21,8 @@ import android.view.View;
import android.widget.RemoteViews;
import android.widget.RemoteViewsService;
import androidx.collection.LongSparseArray;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Components.AvatarDrawable;
@ -30,8 +32,6 @@ import org.telegram.ui.EditWidgetActivity;
import java.io.File;
import java.util.ArrayList;
import androidx.collection.LongSparseArray;
public class ChatsWidgetService extends RemoteViewsService {
@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
@ -132,7 +132,7 @@ class ChatsRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
try {
Bitmap bitmap = null;
if (photoPath != null) {
File path = FileLoader.getPathToAttach(photoPath, true);
File path = FileLoader.getInstance(UserConfig.selectedAccount).getPathToAttach(photoPath, true);
bitmap = BitmapFactory.decodeFile(path.toString());
}

View File

@ -18,6 +18,8 @@ import android.view.View;
import android.widget.RemoteViews;
import android.widget.RemoteViewsService;
import androidx.collection.LongSparseArray;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Components.AvatarDrawable;
@ -26,8 +28,6 @@ import org.telegram.ui.EditWidgetActivity;
import java.io.File;
import java.util.ArrayList;
import androidx.collection.LongSparseArray;
public class ContactsWidgetService extends RemoteViewsService {
@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
@ -135,7 +135,7 @@ class ContactsRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactor
try {
Bitmap bitmap = null;
if (photoPath != null) {
File path = FileLoader.getPathToAttach(photoPath, true);
File path = FileLoader.getInstance(UserConfig.selectedAccount).getPathToAttach(photoPath, true);
bitmap = BitmapFactory.decodeFile(path.toString());
}

View File

@ -52,7 +52,7 @@ public class DispatchQueue extends Thread {
syncLatch.await();
handler.removeCallbacks(runnable);
} catch (Exception e) {
FileLog.e(e);
FileLog.e(e, false);
}
}
@ -63,7 +63,7 @@ public class DispatchQueue extends Thread {
handler.removeCallbacks(runnables[i]);
}
} catch (Exception e) {
FileLog.e(e);
FileLog.e(e, false);
}
}
@ -76,7 +76,7 @@ public class DispatchQueue extends Thread {
try {
syncLatch.await();
} catch (Exception e) {
FileLog.e(e);
FileLog.e(e, false);
}
if (delay <= 0) {
return handler.post(runnable);
@ -90,7 +90,7 @@ public class DispatchQueue extends Thread {
syncLatch.await();
handler.removeCallbacksAndMessages(null);
} catch (Exception e) {
FileLog.e(e);
FileLog.e(e, false);
}
}
@ -109,12 +109,10 @@ public class DispatchQueue extends Thread {
@Override
public void run() {
Looper.prepare();
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
DispatchQueue.this.handleMessage(msg);
}
};
handler = new Handler(Looper.myLooper(), msg -> {
DispatchQueue.this.handleMessage(msg);
return true;
});
syncLatch.countDown();
Looper.loop();
}
@ -122,4 +120,8 @@ public class DispatchQueue extends Thread {
public boolean isReady() {
return syncLatch.getCount() == 0;
}
public Handler getHandler() {
return handler;
}
}

View File

@ -0,0 +1,169 @@
package org.telegram.messenger;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import java.util.ArrayList;
public class DispatchQueueMainThreadSync extends Thread {
private volatile Handler handler = null;
private boolean isRunning;
private boolean isRecycled;
private long lastTaskTime;
private static int indexPointer = 0;
public final int index = indexPointer++;
private ArrayList<PostponedTask> postponedTasks = new ArrayList<>();
public DispatchQueueMainThreadSync(final String threadName) {
this(threadName, true);
}
public DispatchQueueMainThreadSync(final String threadName, boolean start) {
setName(threadName);
if (start) {
start();
}
}
public void sendMessage(Message msg, int delay) {
checkThread();
if (isRecycled) {
return;
}
if (!isRunning) {
postponedTasks.add(new PostponedTask(msg, delay));
return;
}
if (delay <= 0) {
handler.sendMessage(msg);
} else {
handler.sendMessageDelayed(msg, delay);
}
}
private void checkThread() {
if (BuildVars.DEBUG_PRIVATE_VERSION && Thread.currentThread() != ApplicationLoader.applicationHandler.getLooper().getThread()) {
throw new IllegalStateException("Disaptch thread");
}
}
public void cancelRunnable(Runnable runnable) {
checkThread();
if (isRunning) {
handler.removeCallbacks(runnable);
} else {
for (int i = 0; i < postponedTasks.size(); i++) {
if (postponedTasks.get(i).runnable == runnable) {
postponedTasks.remove(i);
i--;
}
}
}
}
public void cancelRunnables(Runnable[] runnables) {
checkThread();
for (int i = 0; i < runnables.length; i++) {
cancelRunnable(runnables[i]);
}
}
public boolean postRunnable(Runnable runnable) {
checkThread();
lastTaskTime = SystemClock.elapsedRealtime();
return postRunnable(runnable, 0);
}
public boolean postRunnable(Runnable runnable, long delay) {
checkThread();
if (isRecycled) {
return false;
}
if (!isRunning) {
postponedTasks.add(new PostponedTask(runnable, delay));
return true;
}
if (delay <= 0) {
return handler.post(runnable);
} else {
return handler.postDelayed(runnable, delay);
}
}
public void cleanupQueue() {
checkThread();
postponedTasks.clear();
handler.removeCallbacksAndMessages(null);
}
public void handleMessage(Message inputMessage) {
}
public long getLastTaskTime() {
return lastTaskTime;
}
public void recycle() {
checkThread();
postRunnable(() -> {
handler.getLooper().quit();
});
isRecycled = true;
}
@Override
public void run() {
Looper.prepare();
handler = new Handler(Looper.myLooper(), msg -> {
DispatchQueueMainThreadSync.this.handleMessage(msg);
return true;
});
AndroidUtilities.runOnUIThread(new Runnable() {
@Override
public void run() {
isRunning = true;
for (int i = 0; i < postponedTasks.size(); i++) {
postponedTasks.get(i).run();
}
postponedTasks.clear();
}
});
Looper.loop();
}
public boolean isReady() {
return isRunning;
}
public Handler getHandler() {
return handler;
}
private class PostponedTask {
Message message;
Runnable runnable;
long delay;
public PostponedTask(Message msg, int delay) {
this.message = msg;
this.delay = delay;
}
public PostponedTask(Runnable runnable, long delay) {
this.runnable = runnable;
this.delay = delay;
}
public void run() {
if (runnable != null) {
postRunnable(runnable, delay);
} else {
sendMessage(message, (int) delay);
}
}
}
}

View File

@ -0,0 +1,86 @@
package org.telegram.messenger;
import android.os.SystemClock;
import android.util.SparseIntArray;
import androidx.annotation.UiThread;
import java.util.LinkedList;
public class DispatchQueuePoolMainThreadSync {
private LinkedList<DispatchQueueMainThreadSync> queues = new LinkedList<>();
private SparseIntArray busyQueuesMap = new SparseIntArray();
private LinkedList<DispatchQueueMainThreadSync> busyQueues = new LinkedList<>();
private int maxCount;
private int createdCount;
private int guid;
private int totalTasksCount;
private boolean cleanupScheduled;
private Runnable cleanupRunnable = new Runnable() {
@Override
public void run() {
if (!queues.isEmpty()) {
long currentTime = SystemClock.elapsedRealtime();
for (int a = 0, N = queues.size(); a < N; a++) {
DispatchQueueMainThreadSync queue = queues.get(a);
if (queue.getLastTaskTime() < currentTime - 30000) {
queue.recycle();
queues.remove(a);
createdCount--;
a--;
N--;
}
}
}
if (!queues.isEmpty() || !busyQueues.isEmpty()) {
AndroidUtilities.runOnUIThread(this, 30000);
cleanupScheduled = true;
} else {
cleanupScheduled = false;
}
}
};
public DispatchQueuePoolMainThreadSync(int count) {
maxCount = count;
guid = Utilities.random.nextInt();
}
@UiThread
public void execute(Runnable runnable) {
DispatchQueueMainThreadSync queue;
if (!busyQueues.isEmpty() && (totalTasksCount / 2 <= busyQueues.size() || queues.isEmpty() && createdCount >= maxCount)) {
queue = busyQueues.remove(0);
} else if (queues.isEmpty()) {
queue = new DispatchQueueMainThreadSync("DispatchQueuePool" + guid + "_" + Utilities.random.nextInt());
queue.setPriority(Thread.MAX_PRIORITY);
createdCount++;
} else {
queue = queues.remove(0);
}
if (!cleanupScheduled) {
AndroidUtilities.runOnUIThread(cleanupRunnable, 30000);
cleanupScheduled = true;
}
totalTasksCount++;
busyQueues.add(queue);
int count = busyQueuesMap.get(queue.index, 0);
busyQueuesMap.put(queue.index, count + 1);
queue.postRunnable(() -> {
runnable.run();
AndroidUtilities.runOnUIThread(() -> {
totalTasksCount--;
int remainingTasksCount = busyQueuesMap.get(queue.index) - 1;
if (remainingTasksCount == 0) {
busyQueuesMap.delete(queue.index);
busyQueues.remove(queue);
queues.add(queue);
} else {
busyQueuesMap.put(queue.index, remainingTasksCount);
}
});
});
}
}

View File

@ -85,14 +85,14 @@ public class DownloadController extends BaseController implements NotificationCe
public static class Preset {
public int[] mask = new int[4];
public int[] sizes = new int[4];
public long[] sizes = new long[4];
public boolean preloadVideo;
public boolean preloadMusic;
public boolean lessCallData;
public boolean enabled;
public int maxVideoBitrate;
public Preset(int[] m, int p, int v, int f, boolean pv, boolean pm, boolean e, boolean l, int bitrate) {
public Preset(int[] m, long p, long v, long f, boolean pv, boolean pm, boolean e, boolean l, int bitrate) {
System.arraycopy(m, 0, mask, 0, mask.length);
sizes[PRESET_SIZE_NUM_PHOTO] = p;
sizes[PRESET_SIZE_NUM_VIDEO] = v;
@ -261,9 +261,9 @@ public class DownloadController extends BaseController implements NotificationCe
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];
long[] mobileMaxFileSize = new long[7];
long[] wifiMaxFileSize = new long[7];
long[] roamingMaxFileSize = new long[7];
for (int a = 0; a < 4; a++) {
String key = "mobileDataDownloadMask" + (a == 0 ? "" : a);
@ -278,12 +278,12 @@ public class DownloadController extends BaseController implements NotificationCe
}
}
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]);
mobileMaxFileSize[2] = preferences.getLong("mobileMaxDownloadSize" + 2, mediumPreset.sizes[PRESET_SIZE_NUM_VIDEO]);
mobileMaxFileSize[3] = preferences.getLong("mobileMaxDownloadSize" + 3, mediumPreset.sizes[PRESET_SIZE_NUM_DOCUMENT]);
wifiMaxFileSize[2] = preferences.getLong("wifiMaxDownloadSize" + 2, highPreset.sizes[PRESET_SIZE_NUM_VIDEO]);
wifiMaxFileSize[3] = preferences.getLong("wifiMaxDownloadSize" + 3, highPreset.sizes[PRESET_SIZE_NUM_DOCUMENT]);
roamingMaxFileSize[2] = preferences.getLong("roamingMaxDownloadSize" + 2, lowPreset.sizes[PRESET_SIZE_NUM_VIDEO]);
roamingMaxFileSize[3] = preferences.getLong("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, 100);
@ -596,7 +596,7 @@ public class DownloadController extends BaseController implements NotificationCe
return canDownloadMedia(messageObject.messageOwner) == 1;
}
public boolean canDownloadMedia(int type, int size) {
public boolean canDownloadMedia(int type, long size) {
Preset preset;
int networkType = ApplicationLoader.getAutodownloadNetworkType();
if (networkType == StatsController.TYPE_WIFI) {
@ -617,7 +617,7 @@ public class DownloadController extends BaseController implements NotificationCe
preset = getCurrentMobilePreset();
}
int mask = preset.mask[1];
int maxSize = preset.sizes[typeToIndex(type)];
long maxSize = preset.sizes[typeToIndex(type)];
return (type == AUTODOWNLOAD_TYPE_PHOTO || size != 0 && size <= maxSize) && (type == AUTODOWNLOAD_TYPE_AUDIO || (mask & type) != 0);
}
@ -688,13 +688,13 @@ public class DownloadController extends BaseController implements NotificationCe
preset = getCurrentMobilePreset();
}
int mask = preset.mask[index];
int maxSize;
long maxSize;
if (type == AUTODOWNLOAD_TYPE_AUDIO) {
maxSize = Math.max(512 * 1024, preset.sizes[typeToIndex(type)]);
} else {
maxSize = preset.sizes[typeToIndex(type)];
}
int size = MessageObject.getMessageSize(message);
long size = MessageObject.getMessageSize(message);
if (isVideo && preset.preloadVideo && size > maxSize && maxSize > 2 * 1024 * 1024) {
return (mask & type) != 0 ? 2 : 0;
} else {
@ -782,7 +782,7 @@ public class DownloadController extends BaseController implements NotificationCe
break;
}
}
req.settings.photo_size_max = photo ? preset.sizes[PRESET_SIZE_NUM_PHOTO] : 0;
req.settings.photo_size_max = photo ? (int) 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;
getConnectionsManager().sendRequest(req, (response, error) -> {
@ -1128,7 +1128,7 @@ public class DownloadController extends BaseController implements NotificationCe
boolean contains = false;
for (int i = 0; i < recentDownloadingFiles.size(); i++) {
if (recentDownloadingFiles.get(i).getDocument().id == parentObject.getDocument().id) {
if (recentDownloadingFiles.get(i).getDocument() != null && recentDownloadingFiles.get(i).getDocument().id == parentObject.getDocument().id) {
contains = true;
break;
}
@ -1136,7 +1136,7 @@ public class DownloadController extends BaseController implements NotificationCe
if (!contains) {
for (int i = 0; i < downloadingFiles.size(); i++) {
if (downloadingFiles.get(i).getDocument().id == parentObject.getDocument().id) {
if (downloadingFiles.get(i).getDocument() != null && downloadingFiles.get(i).getDocument().id == parentObject.getDocument().id) {
contains = true;
break;
}
@ -1318,7 +1318,7 @@ public class DownloadController extends BaseController implements NotificationCe
getMessagesStorage().getStorageQueue().postRunnable(() -> {
ArrayList<MessageObject> downloadingMessages = new ArrayList<>();
ArrayList<MessageObject> recentlyDownloadedMessages = new ArrayList<>();
ArrayList<MessageObject> newMessages = new ArrayList<>();
try {
SQLiteCursor cursor2 = getMessagesStorage().getDatabase().queryFinalized("SELECT data, state FROM downloading_documents ORDER BY date DESC");
while (cursor2.next()) {
@ -1328,10 +1328,11 @@ public class DownloadController extends BaseController implements NotificationCe
TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false);
if (message != null) {
message.readAttachPath(data, UserConfig.getInstance(currentAccount).clientUserId);
MessageObject messageObject = new MessageObject(currentAccount, message, false, true);
MessageObject messageObject = new MessageObject(currentAccount, message, false, false);
newMessages.add(messageObject);
if (state == 0) {
downloadingMessages.add(messageObject);
} else if (messageObject.mediaExists) {
} else {
recentlyDownloadedMessages.add(messageObject);
}
}
@ -1343,6 +1344,9 @@ public class DownloadController extends BaseController implements NotificationCe
FileLog.e(e);
}
getFileLoader().checkMediaExistance(downloadingMessages);
getFileLoader().checkMediaExistance(recentlyDownloadedMessages);
AndroidUtilities.runOnUIThread(() -> {
downloadingFiles.clear();
downloadingFiles.addAll(downloadingMessages);
@ -1400,7 +1404,7 @@ public class DownloadController extends BaseController implements NotificationCe
state.step();
try {
File file = FileLoader.getPathToMessage(messageObjects.get(i).messageOwner);
File file = FileLoader.getInstance(currentAccount).getPathToMessage(messageObjects.get(i).messageOwner);
file.delete();
} catch (Exception e) {
FileLog.e(e);

View File

@ -30,10 +30,12 @@ import android.widget.TextView;
import java.io.File;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicReference;
import java.io.File;
import java.io.FileInputStream;
@ -121,20 +123,6 @@ public class Emoji {
scale = 2.0f;
}
String imageName;
File imageFile;
try {
for (int a = 13; a < 16; a++) {
imageName = String.format(Locale.US, "v%d_emoji%.01fx_%d.png", a, scale, page);
imageFile = ApplicationLoader.applicationContext.getFileStreamPath(imageName);
if (imageFile.exists()) {
imageFile.delete();
}
}
} catch (Exception e) {
FileLog.e(e);
}
Bitmap bitmap = null;
try {
InputStream is;
@ -262,9 +250,10 @@ public class Emoji {
public static class EmojiDrawable extends Drawable {
private DrawableInfo info;
private boolean fullSize = false;
private static Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG);
private static Rect rect = new Rect();
private static TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
private static final Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG);
private static final Rect rect = new Rect();
private static final TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
public int placeholderColor = 0x20000000;
public EmojiDrawable(DrawableInfo i) {
info = i;
@ -288,7 +277,9 @@ public class Emoji {
public void draw(Canvas canvas) {
if (!isLoaded()) {
loadEmoji(info.page, info.page2);
canvas.drawRect(getBounds(), placeholderPaint);
placeholderPaint.setColor(placeholderColor);
Rect bounds = getBounds();
canvas.drawCircle(bounds.centerX(), bounds.centerY(), bounds.width() * .4f, placeholderPaint);
return;
}
@ -377,37 +368,41 @@ public class Emoji {
return false;
}
public static CharSequence replaceEmoji(CharSequence cs, Paint.FontMetricsInt fontMetrics, int size, boolean createNew) {
return replaceEmoji(cs, fontMetrics, size, createNew, null);
public static class EmojiSpanRange {
public EmojiSpanRange(int start, int end, CharSequence code) {
this.start = start;
this.end = end;
this.code = code;
}
int start;
int end;
CharSequence code;
}
public static CharSequence replaceEmoji(CharSequence cs, Paint.FontMetricsInt fontMetrics, int size, boolean createNew, int[] emojiOnly) {
if (cs == null || cs.length() == 0) {
return cs;
}
Spannable s;
if (!createNew && cs instanceof Spannable) {
s = (Spannable) cs;
} else {
s = Spannable.Factory.getInstance().newSpannable(cs.toString());
public static boolean fullyConsistsOfEmojis(CharSequence cs) {
int[] emojiOnly = new int[1];
parseEmojis(cs, emojiOnly);
return emojiOnly[0] > 0;
}
public static ArrayList<EmojiSpanRange> parseEmojis(CharSequence cs) {
return parseEmojis(cs, null);
}
public static ArrayList<EmojiSpanRange> parseEmojis(CharSequence cs, int[] emojiOnly) {
ArrayList<EmojiSpanRange> emojis = new ArrayList<>();
if (cs == null || cs.length() <= 0) {
return emojis;
}
long buf = 0;
int emojiCount = 0;
char c;
int startIndex = -1;
int startLength = 0;
int previousGoodIndex = 0;
StringBuilder emojiCode = new StringBuilder(16);
StringBuilder addionalCode = new StringBuilder(2);
boolean nextIsSkinTone;
EmojiDrawable drawable;
EmojiSpan span;
int length = cs.length();
boolean doneEmoji = false;
int nextValidLength;
boolean nextValid;
boolean notOnlyEmoji;
//s.setSpansCount(emojiCount);
try {
for (int i = 0; i < length; i++) {
@ -520,29 +515,65 @@ public class Emoji {
if (emojiOnly != null) {
emojiOnly[0]++;
}
CharSequence code = emojiCode.subSequence(0, emojiCode.length());
drawable = Emoji.getEmojiDrawable(code);
if (drawable != null) {
span = new EmojiSpan(drawable, DynamicDrawableSpan.ALIGN_BOTTOM, size, fontMetrics);
s.setSpan(span, startIndex, startIndex + startLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
emojiCount++;
}
emojis.add(new EmojiSpanRange(startIndex, startIndex + startLength, emojiCode.subSequence(0, emojiCode.length())));
startLength = 0;
startIndex = -1;
emojiCode.setLength(0);
doneEmoji = false;
}
if ((Build.VERSION.SDK_INT < 23 || Build.VERSION.SDK_INT >= 29) && !BuildVars.DEBUG_PRIVATE_VERSION && emojiCount >= 50) {
break;
}
}
} catch (Exception e) {
FileLog.e(e);
return cs;
}
if (emojiOnly != null && emojiCode.length() != 0) {
emojiOnly[0] = 0;
}
return emojis;
}
public static CharSequence replaceEmoji(CharSequence cs, Paint.FontMetricsInt fontMetrics, int size, boolean createNew) {
return replaceEmoji(cs, fontMetrics, size, createNew, null, false, null);
}
public static CharSequence replaceEmoji(CharSequence cs, Paint.FontMetricsInt fontMetrics, int size, boolean createNew, boolean allowAnimated, AtomicReference<WeakReference<View>> viewRef) {
return replaceEmoji(cs, fontMetrics, size, createNew, null, allowAnimated, viewRef);
}
public static CharSequence replaceEmoji(CharSequence cs, Paint.FontMetricsInt fontMetrics, int size, boolean createNew, int[] emojiOnly) {
return replaceEmoji(cs, fontMetrics, size, createNew, emojiOnly, false, null);
}
public static CharSequence replaceEmoji(CharSequence cs, Paint.FontMetricsInt fontMetrics, int size, boolean createNew, int[] emojiOnly, boolean allowAnimated, AtomicReference<WeakReference<View>> viewRef) {
allowAnimated = false;
if (NekoConfig.useSystemEmoji.Bool() || cs == null || cs.length() == 0) {
return cs;
}
Spannable s;
if (!createNew && cs instanceof Spannable) {
s = (Spannable) cs;
} else {
s = Spannable.Factory.getInstance().newSpannable(cs.toString());
}
ArrayList<EmojiSpanRange> emojis = parseEmojis(s, emojiOnly);
EmojiSpan span;
Drawable drawable;
for (int i = 0; i < emojis.size(); ++i) {
EmojiSpanRange emojiRange = emojis.get(i);
try {
drawable = Emoji.getEmojiDrawable(emojiRange.code);
if (drawable != null) {
span = new EmojiSpan(drawable, DynamicDrawableSpan.ALIGN_BOTTOM, size, fontMetrics);
s.setSpan(span, emojiRange.start, emojiRange.end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
} catch (Exception e) {
FileLog.e(e);
}
if ((Build.VERSION.SDK_INT < 23 || Build.VERSION.SDK_INT >= 29) && !BuildVars.DEBUG_PRIVATE_VERSION && (i + 1) >= 50) {
break;
}
}
return s;
}
@ -550,7 +581,7 @@ public class Emoji {
private Paint.FontMetricsInt fontMetrics;
private int size = AndroidUtilities.dp(20);
public EmojiSpan(EmojiDrawable d, int verticalAlignment, int s, Paint.FontMetricsInt original) {
public EmojiSpan(Drawable d, int verticalAlignment, int s, Paint.FontMetricsInt original) {
super(d, verticalAlignment);
fontMetrics = original;
if (original != null) {
@ -620,6 +651,14 @@ public class Emoji {
getDrawable().setAlpha(255);
}
}
@Override
public void updateDrawState(TextPaint ds) {
if (getDrawable() instanceof EmojiDrawable) {
((EmojiDrawable) getDrawable()).placeholderColor = 0x20ffffff & ds.getColor();
}
super.updateDrawState(ds);
}
}
public static void addRecentEmoji(String code) {

View File

@ -94,7 +94,7 @@ class FeedRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory, N
rv.setViewVisibility(R.id.feed_widget_item_image, View.GONE);
} else {
TLRPC.PhotoSize size = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, AndroidUtilities.getPhotoSize());
File f = FileLoader.getPathToAttach(size);
File f = FileLoader.getInstance(UserConfig.selectedAccount).getPathToAttach(size);
if (f.exists()) {
rv.setViewVisibility(R.id.feed_widget_item_image, View.VISIBLE);
Uri uri = FileProvider.getUriForFile(mContext, BuildConfig.APPLICATION_ID + ".provider", f);

View File

@ -8,9 +8,6 @@
package org.telegram.messenger;
import android.util.SparseArray;
import android.util.SparseIntArray;
import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.NativeByteBuffer;
import org.telegram.tgnet.TLObject;
@ -23,6 +20,7 @@ import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Scanner;
import java.util.concurrent.CountDownLatch;
import java.util.zip.GZIPInputStream;
@ -32,27 +30,27 @@ public class FileLoadOperation {
protected static class RequestInfo {
private int requestToken;
private int offset;
private long offset;
private TLRPC.TL_upload_file response;
private TLRPC.TL_upload_webFile responseWeb;
private TLRPC.TL_upload_cdnFile responseCdn;
}
public static class Range {
private int start;
private int end;
private long start;
private long end;
private Range(int s, int e) {
private Range(long s, long e) {
start = s;
end = e;
}
}
private static class PreloadRange {
private int fileOffset;
private int length;
private long fileOffset;
private long length;
private PreloadRange(int o, int l) {
private PreloadRange(long o, long l) {
fileOffset = o;
length = l;
}
@ -65,21 +63,22 @@ public class FileLoadOperation {
private final static int stateFailed = 2;
private final static int stateFinished = 3;
private final static int downloadChunkSize = 1024 * 32;
private final static int downloadChunkSizeBig = 1024 * 128;
private final static int cdnChunkCheckSize = 1024 * 128;
private final static int maxDownloadRequests = BuildVars.DEBUG_PRIVATE_VERSION ? 8 : 4;
private final static int maxDownloadRequestsBig = BuildVars.DEBUG_PRIVATE_VERSION ? 8 : 4;
private final static int bigFileSizeFrom = 1024 * 1024;
private final static int maxCdnParts = (int) (FileLoader.MAX_FILE_SIZE / downloadChunkSizeBig);
private int downloadChunkSize = 1024 * 32;
private int downloadChunkSizeBig = 1024 * 128;
private int cdnChunkCheckSize = 1024 * 128;
private int maxDownloadRequests = 4;
private int maxDownloadRequestsBig = 4;
private int bigFileSizeFrom = 1024 * 1024;
private int maxCdnParts = (int) (FileLoader.DEFAULT_MAX_FILE_SIZE / downloadChunkSizeBig);
private final static int preloadMaxBytes = 2 * 1024 * 1024;
private String fileName;
private String storeFileName;
private int currentQueueType;
private SparseArray<PreloadRange> preloadedBytesRanges;
private SparseIntArray requestedPreloadedBytesRanges;
private HashMap<Long, PreloadRange> preloadedBytesRanges;
private HashMap<Long, Integer> requestedPreloadedBytesRanges;
private RandomAccessFile preloadStream;
private int preloadStreamFileOffset;
private int totalPreloadedBytes;
@ -87,12 +86,12 @@ public class FileLoadOperation {
private boolean preloadFinished;
private File cacheFilePreload;
private boolean supportsPreloading;
private int nextPreloadDownloadOffset;
private int nextAtomOffset;
private int foundMoovSize;
private int preloadNotRequestedBytesCount;
private long nextPreloadDownloadOffset;
private long nextAtomOffset;
private long foundMoovSize;
private long preloadNotRequestedBytesCount;
private int moovFound;
private byte[] preloadTempBuffer = new byte[16];
private byte[] preloadTempBuffer = new byte[24];
private int preloadTempBufferCount;
private boolean nextPartWasPreloaded;
@ -103,7 +102,7 @@ public class FileLoadOperation {
private volatile ArrayList<Range> notLoadedBytesRangesCopy;
private ArrayList<Range> notRequestedBytesRanges;
private ArrayList<Range> notCheckedCdnRanges;
private int requestedBytesCount;
private long requestedBytesCount;
private int currentAccount;
private boolean started;
@ -114,11 +113,11 @@ public class FileLoadOperation {
private WebFile webFile;
private volatile int state = stateIdle;
private volatile boolean paused;
private int downloadedBytes;
private int totalBytesCount;
private int bytesCountPadding;
private int streamStartOffset;
private int streamPriorityStartOffset;
private long downloadedBytes;
private long totalBytesCount;
private long bytesCountPadding;
private long streamStartOffset;
private long streamPriorityStartOffset;
private RequestInfo priorityRequestInfo;
private FileLoadOperationDelegate delegate;
private byte[] key;
@ -133,7 +132,7 @@ public class FileLoadOperation {
public Object parentObject;
private SparseArray<TLRPC.TL_fileHash> cdnHashes;
private HashMap<Long, TLRPC.TL_fileHash> cdnHashes;
private boolean forceBig;
@ -172,14 +171,30 @@ public class FileLoadOperation {
private boolean ungzip;
private int currentType;
public FilePathDatabase.PathData pathSaveData;
public interface FileLoadOperationDelegate {
void didFinishLoadingFile(FileLoadOperation operation, File finalFile);
void didFailedLoadingFile(FileLoadOperation operation, int state);
void didChangedLoadProgress(FileLoadOperation operation, long uploadedSize, long totalSize);
void saveFilePath(FilePathDatabase.PathData pathSaveData, File cacheFileFinal);
}
public FileLoadOperation(ImageLocation imageLocation, Object parent, String extension, int size) {
private void updateParams() {
if (MessagesController.getInstance(currentAccount).getfileExperimentalParams) {
downloadChunkSizeBig = 1024 * 512;
maxDownloadRequests = 8;
maxDownloadRequestsBig = 8;
} else {
downloadChunkSizeBig = 1024 * 128;
maxDownloadRequests = 4;
maxDownloadRequestsBig = 4;
}
maxCdnParts = (int) (FileLoader.DEFAULT_MAX_FILE_SIZE / downloadChunkSizeBig);
}
public FileLoadOperation(ImageLocation imageLocation, Object parent, String extension, long size) {
updateParams();
parentObject = parent;
forceBig = imageLocation.imageType == FileLoader.IMAGE_TYPE_ANIMATION;
if (imageLocation.isEncrypted()) {
@ -251,6 +266,7 @@ public class FileLoadOperation {
}
public FileLoadOperation(SecureDocument secureDocument) {
updateParams();
location = new TLRPC.TL_inputSecureFileLocation();
location.id = secureDocument.secureFile.id;
location.access_hash = secureDocument.secureFile.access_hash;
@ -262,6 +278,7 @@ public class FileLoadOperation {
}
public FileLoadOperation(int instance, WebFile webDocument) {
updateParams();
currentAccount = instance;
webFile = webDocument;
webLocation = webDocument.location;
@ -282,6 +299,7 @@ public class FileLoadOperation {
}
public FileLoadOperation(TLRPC.Document documentLocation, Object parent) {
updateParams();
try {
parentObject = parent;
if (documentLocation instanceof TLRPC.TL_documentEncrypted) {
@ -369,11 +387,12 @@ public class FileLoadOperation {
return priority;
}
public void setPaths(int instance, String name, int queueType, File store, File temp) {
public void setPaths(int instance, String name, int queueType, File store, File temp, String finalName) {
storePath = store;
tempPath = temp;
currentAccount = instance;
fileName = name;
storeFileName = finalName;
currentQueueType = queueType;
}
@ -389,7 +408,7 @@ public class FileLoadOperation {
return currentType;
}
private void removePart(ArrayList<Range> ranges, int start, int end) {
private void removePart(ArrayList<Range> ranges, long start, long end) {
if (ranges == null || end < start) {
return;
}
@ -430,7 +449,7 @@ public class FileLoadOperation {
}
}
private void addPart(ArrayList<Range> ranges, int start, int end, boolean save) {
private void addPart(ArrayList<Range> ranges, long start, long end, boolean save) {
if (ranges == null || end < start) {
return;
}
@ -471,18 +490,13 @@ public class FileLoadOperation {
filePartsStream.writeInt(count);
for (int a = 0; a < count; a++) {
range = ranges.get(a);
filePartsStream.writeInt(range.start);
filePartsStream.writeInt(range.end);
filePartsStream.writeLong(range.start);
filePartsStream.writeLong(range.end);
}
} catch (Exception e) {
FileLog.e(e);
}
if (streamListeners != null) {
count = streamListeners.size();
for (int a = 0; a < count; a++) {
streamListeners.get(a).newDataAvailable();
}
}
notifyStreamListeners();
} else {
if (BuildVars.LOGS_ENABLED) {
FileLog.e(cacheFileFinal + " downloaded duplicate file part " + start + " - " + end);
@ -491,6 +505,15 @@ public class FileLoadOperation {
}
}
private void notifyStreamListeners() {
if (streamListeners != null) {
int count = streamListeners.size();
for (int a = 0; a < count; a++) {
streamListeners.get(a).newDataAvailable();
}
}
}
protected File getCacheFileFinal() {
return cacheFileFinal;
}
@ -514,10 +537,13 @@ public class FileLoadOperation {
return result[0];
}
private int getDownloadedLengthFromOffsetInternal(ArrayList<Range> ranges, final int offset, final int length) {
private long getDownloadedLengthFromOffsetInternal(ArrayList<Range> ranges, final long offset, final long length) {
if (ranges == null || state == stateFinished || ranges.isEmpty()) {
if (downloadedBytes == 0) {
if (state == stateFinished) {
return length;
}
if (downloadedBytes == 0) {
return 0;
} else {
return Math.min(length, Math.max(downloadedBytes - offset, 0));
}
@ -525,7 +551,7 @@ public class FileLoadOperation {
int count = ranges.size();
Range range;
Range minRange = null;
int availableLength = length;
long availableLength = length;
for (int a = 0; a < count; a++) {
range = ranges.get(a);
if (offset <= range.start && (minRange == null || range.start < minRange.start)) {
@ -554,9 +580,9 @@ public class FileLoadOperation {
return progress + getDownloadedLengthFromOffsetInternal(ranges, (int) (totalBytesCount * progress), totalBytesCount) / (float) totalBytesCount;
}
protected int[] getDownloadedLengthFromOffset(final int offset, final int length) {
protected long[] getDownloadedLengthFromOffset(final int offset, final long length) {
final CountDownLatch countDownLatch = new CountDownLatch(1);
final int[] result = new int[2];
final long[] result = new long[2];
Utilities.stageQueue.postRunnable(() -> {
result[0] = getDownloadedLengthFromOffsetInternal(notLoadedBytesRanges, offset, length);
if (state == stateFinished) {
@ -603,7 +629,8 @@ public class FileLoadOperation {
return start(null, 0, false);
}
public boolean start(final FileLoadOperationStream stream, final int streamOffset, final boolean steamPriority) {
public boolean start(final FileLoadOperationStream stream, final long streamOffset, final boolean steamPriority) {
updateParams();
if (currentDownloadChunkSize == 0) {
currentDownloadChunkSize = totalBytesCount >= bigFileSizeFrom || forceBig ? downloadChunkSizeBig : downloadChunkSize;
currentMaxDownloadRequests = totalBytesCount >= bigFileSizeFrom || forceBig ? maxDownloadRequestsBig : maxDownloadRequests;
@ -617,7 +644,7 @@ public class FileLoadOperation {
streamListeners = new ArrayList<>();
}
if (steamPriority) {
int offset = streamOffset / currentDownloadChunkSize * currentDownloadChunkSize;
long offset = (streamOffset / (long) currentDownloadChunkSize) * (long) currentDownloadChunkSize;
if (priorityRequestInfo != null && priorityRequestInfo.offset != offset) {
requestInfos.remove(priorityRequestInfo);
requestedBytesCount -= currentDownloadChunkSize;
@ -677,13 +704,13 @@ public class FileLoadOperation {
fileNameTemp = md5 + ".temp.enc";
fileNameFinal = md5 + "." + ext + ".enc";
if (key != null) {
fileNameIv = md5 + ".iv.enc";
fileNameIv = md5 + "_64.iv.enc";
}
} else {
fileNameTemp = md5 + ".temp";
fileNameFinal = md5 + "." + ext;
if (key != null) {
fileNameIv = md5 + ".iv";
fileNameIv = md5 + "_64.iv";
}
}
} else {
@ -697,18 +724,18 @@ public class FileLoadOperation {
fileNameTemp = location.volume_id + "_" + location.local_id + ".temp.enc";
fileNameFinal = location.volume_id + "_" + location.local_id + "." + ext + ".enc";
if (key != null) {
fileNameIv = location.volume_id + "_" + location.local_id + ".iv.enc";
fileNameIv = location.volume_id + "_" + location.local_id + "_64.iv.enc";
}
} else {
fileNameTemp = location.volume_id + "_" + location.local_id + ".temp";
fileNameFinal = location.volume_id + "_" + location.local_id + "." + ext;
if (key != null) {
fileNameIv = location.volume_id + "_" + location.local_id + ".iv";
fileNameIv = location.volume_id + "_" + location.local_id + "_64.iv";
}
if (notLoadedBytesRanges != null) {
fileNameParts = location.volume_id + "_" + location.local_id + ".pt";
fileNameParts = location.volume_id + "_" + location.local_id + "_64.pt";
}
fileNamePreload = location.volume_id + "_" + location.local_id + ".preload";
fileNamePreload = location.volume_id + "_" + location.local_id + "_64.preload";
}
} else {
if (datacenterId == 0 || location.id == 0) {
@ -719,18 +746,18 @@ public class FileLoadOperation {
fileNameTemp = datacenterId + "_" + location.id + ".temp.enc";
fileNameFinal = datacenterId + "_" + location.id + ext + ".enc";
if (key != null) {
fileNameIv = datacenterId + "_" + location.id + ".iv.enc";
fileNameIv = datacenterId + "_" + location.id + "_64.iv.enc";
}
} else {
fileNameTemp = datacenterId + "_" + location.id + ".temp";
fileNameFinal = datacenterId + "_" + location.id + ext;
if (key != null) {
fileNameIv = datacenterId + "_" + location.id + ".iv";
fileNameIv = datacenterId + "_" + location.id + "_64.iv";
}
if (notLoadedBytesRanges != null) {
fileNameParts = datacenterId + "_" + location.id + ".pt";
fileNameParts = datacenterId + "_" + location.id + "_64.pt";
}
fileNamePreload = datacenterId + "_" + location.id + ".preload";
fileNamePreload = datacenterId + "_" + location.id + "_64.preload";
}
}
}
@ -745,7 +772,7 @@ public class FileLoadOperation {
} else if (fileName != null && !encryptFile) {
cacheFileFinal = new File(storePath, fileName);
} else {
cacheFileFinal = new File(storePath, fileNameFinal);
cacheFileFinal = new File(storePath, storeFileName);
}
boolean finalFileExist = cacheFileFinal.exists();
if (finalFileExist && (parentObject instanceof TLRPC.TL_theme || totalBytesCount != 0 && totalBytesCount != cacheFileFinal.length())) {
@ -795,51 +822,51 @@ public class FileLoadOperation {
try {
preloadStream = new RandomAccessFile(cacheFilePreload, "rws");
long len = preloadStream.length();
int readOffset = 0;
long readOffset = 0;
preloadStreamFileOffset = 1;
if (len - readOffset > 1) {
preloaded[0] = preloadStream.readByte() != 0;
readOffset += 1;
while (readOffset < len) {
if (len - readOffset < 4) {
if (len - readOffset < 8) {
break;
}
int offset = preloadStream.readInt();
readOffset += 4;
if (len - readOffset < 4 || offset < 0 || offset > totalBytesCount) {
long offset = preloadStream.readLong();
readOffset += 8;
if (len - readOffset < 8 || offset < 0 || offset > totalBytesCount) {
break;
}
int size = preloadStream.readInt();
readOffset += 4;
long size = preloadStream.readLong();
readOffset += 8;
if (len - readOffset < size || size > currentDownloadChunkSize) {
break;
}
PreloadRange range = new PreloadRange(readOffset, size);
readOffset += size;
preloadStream.seek(readOffset);
if (len - readOffset < 12) {
if (len - readOffset < 24) {
break;
}
foundMoovSize = preloadStream.readInt();
foundMoovSize = preloadStream.readLong();
if (foundMoovSize != 0) {
moovFound = nextPreloadDownloadOffset > totalBytesCount / 2 ? 2 : 1;
preloadNotRequestedBytesCount = foundMoovSize;
}
nextPreloadDownloadOffset = preloadStream.readInt();
nextAtomOffset = preloadStream.readInt();
readOffset += 12;
nextPreloadDownloadOffset = preloadStream.readLong();
nextAtomOffset = preloadStream.readLong();
readOffset += 24;
if (preloadedBytesRanges == null) {
preloadedBytesRanges = new SparseArray<>();
preloadedBytesRanges = new HashMap<>();
}
if (requestedPreloadedBytesRanges == null) {
requestedPreloadedBytesRanges = new SparseIntArray();
requestedPreloadedBytesRanges = new HashMap<>();
}
preloadedBytesRanges.put(offset, range);
requestedPreloadedBytesRanges.put(offset, 1);
totalPreloadedBytes += size;
preloadStreamFileOffset += 20 + size;
preloadStreamFileOffset += 36 + size;
}
}
preloadStream.seek(preloadStreamFileOffset);
@ -874,8 +901,8 @@ public class FileLoadOperation {
int count = filePartsStream.readInt();
if (count <= len / 2) {
for (int a = 0; a < count; a++) {
int start = filePartsStream.readInt();
int end = filePartsStream.readInt();
long start = filePartsStream.readLong();
long end = filePartsStream.readLong();
notLoadedBytesRanges.add(new Range(start, end));
notRequestedBytesRanges.add(new Range(start, end));
}
@ -892,9 +919,9 @@ public class FileLoadOperation {
} else {
long totalDownloadedLen = cacheFileTemp.length();
if (fileNameIv != null && (totalDownloadedLen % currentDownloadChunkSize) != 0) {
requestedBytesCount = downloadedBytes = 0;
requestedBytesCount = 0;
} else {
requestedBytesCount = downloadedBytes = ((int) cacheFileTemp.length()) / currentDownloadChunkSize * currentDownloadChunkSize;
requestedBytesCount = downloadedBytes = (cacheFileTemp.length()) / ((long) currentDownloadChunkSize) * currentDownloadChunkSize;
}
if (notLoadedBytesRanges != null && notLoadedBytesRanges.isEmpty()) {
notLoadedBytesRanges.add(new Range(downloadedBytes, totalBytesCount));
@ -930,8 +957,8 @@ public class FileLoadOperation {
fiv = new RandomAccessFile(cacheIvTemp, "rws");
if (downloadedBytes != 0 && !newKeyGenerated) {
long len = cacheIvTemp.length();
if (len > 0 && len % 32 == 0) {
fiv.read(iv, 0, 32);
if (len > 0 && len % 64 == 0) {
fiv.read(iv, 0, 64);
} else {
requestedBytesCount = downloadedBytes = 0;
}
@ -951,7 +978,7 @@ public class FileLoadOperation {
fileOutputStream.seek(downloadedBytes);
}
} catch (Exception e) {
FileLog.e(e);
FileLog.e(e, false);
}
if (fileOutputStream == null) {
onFail(true, 0);
@ -973,6 +1000,9 @@ public class FileLoadOperation {
started = true;
try {
onFinishLoadingFile(false);
if (pathSaveData != null) {
delegate.saveFilePath(pathSaveData, null);
}
} catch (Exception e) {
onFail(true, 0);
}
@ -1175,6 +1205,7 @@ public class FileLoadOperation {
return;
}
state = stateFinished;
notifyStreamListeners();
cleanup();
if (isPreloadVideoOperation) {
preloadFinished = true;
@ -1223,6 +1254,21 @@ public class FileLoadOperation {
}
} else {
try {
if (pathSaveData != null) {
cacheFileFinal = new File(storePath, storeFileName);
int count = 1;
while (cacheFileFinal.exists()) {
int lastDotIndex = storeFileName.lastIndexOf('.');
String newFileName;
if (lastDotIndex > 0) {
newFileName = storeFileName.substring(0, lastDotIndex) + " (" + count + ")" + storeFileName.substring(lastDotIndex);
} else {
newFileName = storeFileName + " (" + count + ")";
}
cacheFileFinal = new File(storePath, newFileName);
count++;
}
}
renameResult = cacheFileTemp.renameTo(cacheFileFinal);
} catch (Exception e) {
renameResult = false;
@ -1251,6 +1297,9 @@ public class FileLoadOperation {
onFail(false, 0);
return;
}
if (pathSaveData != null && cacheFileFinal.exists()) {
delegate.saveFilePath(pathSaveData, cacheFileFinal);
}
}
if (BuildVars.LOGS_ENABLED) {
FileLog.d("finished downloading file to " + cacheFileFinal);
@ -1281,15 +1330,21 @@ public class FileLoadOperation {
}
}
private int findNextPreloadDownloadOffset(int atomOffset, int partOffset, NativeByteBuffer partBuffer) {
private long findNextPreloadDownloadOffset(long atomOffset, long 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);
long count = partOffset + partSize - atomOffset;
if (count > Integer.MAX_VALUE) {
throw new RuntimeException("!!!");
}
preloadTempBufferCount = (int) count;
long position = partBuffer.limit() - preloadTempBufferCount;
partBuffer.position((int) position);
partBuffer.readBytes(preloadTempBuffer, 0, preloadTempBufferCount, false);
return partOffset + partSize;
}
@ -1298,10 +1353,15 @@ public class FileLoadOperation {
partBuffer.readBytes(preloadTempBuffer, preloadTempBufferCount, 16 - preloadTempBufferCount, false);
preloadTempBufferCount = 0;
} else {
partBuffer.position(atomOffset - partOffset);
long count = atomOffset - partOffset;
if (count > Integer.MAX_VALUE) {
throw new RuntimeException("!!!");
}
partBuffer.position((int) count);
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) {
@ -1317,7 +1377,7 @@ public class FileLoadOperation {
}
}
private void requestFileOffsets(int offset) {
private void requestFileOffsets(long offset) {
if (requestingCdnOffsets) {
return;
}
@ -1333,7 +1393,7 @@ public class FileLoadOperation {
TLRPC.Vector vector = (TLRPC.Vector) response;
if (!vector.objects.isEmpty()) {
if (cdnHashes == null) {
cdnHashes = new SparseArray<>();
cdnHashes = new HashMap<>();
}
for (int a = 0; a < vector.objects.size(); a++) {
TLRPC.TL_fileHash hash = (TLRPC.TL_fileHash) vector.objects.get(a);
@ -1393,8 +1453,8 @@ public class FileLoadOperation {
}
int currentBytesSize = bytes.limit();
if (isCdn) {
int cdnCheckPart = requestInfo.offset / cdnChunkCheckSize;
int fileOffset = cdnCheckPart * cdnChunkCheckSize;
long cdnCheckPart = requestInfo.offset / cdnChunkCheckSize;
long fileOffset = cdnCheckPart * cdnChunkCheckSize;
TLRPC.TL_fileHash hash = cdnHashes != null ? cdnHashes.get(fileOffset) : null;
if (hash == null) {
delayRequestInfo(requestInfo);
@ -1404,7 +1464,7 @@ public class FileLoadOperation {
}
if (requestInfo.responseCdn != null) {
int offset = requestInfo.offset / 16;
long offset = requestInfo.offset / 16;
cdnIv[15] = (byte) (offset & 0xff);
cdnIv[14] = (byte) ((offset >> 8) & 0xff);
cdnIv[13] = (byte) ((offset >> 16) & 0xff);
@ -1414,16 +1474,16 @@ public class FileLoadOperation {
boolean finishedDownloading;
if (isPreloadVideoOperation) {
preloadStream.writeInt(requestInfo.offset);
preloadStream.writeInt(currentBytesSize);
preloadStreamFileOffset += 8;
preloadStream.writeLong(requestInfo.offset);
preloadStream.writeLong(currentBytesSize);
preloadStreamFileOffset += 16;
FileChannel channel = preloadStream.getChannel();
channel.write(bytes.buffer);
if (BuildVars.DEBUG_VERSION) {
FileLog.d("save preload file part " + cacheFilePreload + " offset " + requestInfo.offset + " size " + currentBytesSize);
}
if (preloadedBytesRanges == null) {
preloadedBytesRanges = new SparseArray<>();
preloadedBytesRanges = new HashMap<>();
}
preloadedBytesRanges.put(requestInfo.offset, new PreloadRange(preloadStreamFileOffset, currentBytesSize));
@ -1431,7 +1491,7 @@ public class FileLoadOperation {
preloadStreamFileOffset += currentBytesSize;
if (moovFound == 0) {
int offset = findNextPreloadDownloadOffset(nextAtomOffset, requestInfo.offset, bytes);
long offset = findNextPreloadDownloadOffset(nextAtomOffset, requestInfo.offset, bytes);
if (offset < 0) {
offset *= -1;
nextPreloadDownloadOffset += currentDownloadChunkSize;
@ -1444,14 +1504,14 @@ public class FileLoadOperation {
}
nextPreloadDownloadOffset = -1;
} else {
nextPreloadDownloadOffset = offset / currentDownloadChunkSize * currentDownloadChunkSize;
nextPreloadDownloadOffset += currentDownloadChunkSize;
}
nextAtomOffset = offset;
}
preloadStream.writeInt(foundMoovSize);
preloadStream.writeInt(nextPreloadDownloadOffset);
preloadStream.writeInt(nextAtomOffset);
preloadStreamFileOffset += 12;
preloadStream.writeLong(foundMoovSize);
preloadStream.writeLong(nextPreloadDownloadOffset);
preloadStream.writeLong(nextAtomOffset);
preloadStreamFileOffset += 24;
finishedDownloading = nextPreloadDownloadOffset == 0 || moovFound != 0 && foundMoovSize < 0 || totalPreloadedBytes > preloadMaxBytes || nextPreloadDownloadOffset >= totalBytesCount;
if (finishedDownloading) {
preloadStream.seek(0);
@ -1469,11 +1529,15 @@ public class FileLoadOperation {
if (key != null) {
Utilities.aesIgeEncryption(bytes.buffer, key, iv, false, true, 0, bytes.limit());
if (finishedDownloading && bytesCountPadding != 0) {
bytes.limit(bytes.limit() - bytesCountPadding);
long limit = bytes.limit() - bytesCountPadding;
if (BuildVars.DEBUG_VERSION && limit > Integer.MAX_VALUE) {
throw new RuntimeException("Out of limit" + limit);
}
bytes.limit((int) (limit));
}
}
if (encryptFile) {
int offset = requestInfo.offset / 16;
long offset = requestInfo.offset / 16;
encryptIv[15] = (byte) (offset & 0xff);
encryptIv[14] = (byte) ((offset >> 8) & 0xff);
encryptIv[13] = (byte) ((offset >> 16) & 0xff);
@ -1491,7 +1555,7 @@ public class FileLoadOperation {
channel.write(bytes.buffer);
addPart(notLoadedBytesRanges, requestInfo.offset, requestInfo.offset + currentBytesSize, true);
if (isCdn) {
int cdnCheckPart = requestInfo.offset / cdnChunkCheckSize;
long cdnCheckPart = requestInfo.offset / cdnChunkCheckSize;
int size = notCheckedCdnRanges.size();
Range range;
@ -1504,8 +1568,8 @@ public class FileLoadOperation {
}
}
if (!checked) {
int fileOffset = cdnCheckPart * cdnChunkCheckSize;
int availableSize = getDownloadedLengthFromOffsetInternal(notLoadedBytesRanges, fileOffset, cdnChunkCheckSize);
long fileOffset = cdnCheckPart * cdnChunkCheckSize;
long 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) {
@ -1513,10 +1577,13 @@ public class FileLoadOperation {
fileReadStream = new RandomAccessFile(cacheFileTemp, "r");
}
fileReadStream.seek(fileOffset);
fileReadStream.readFully(cdnCheckBytes, 0, availableSize);
if (BuildVars.DEBUG_VERSION && availableSize > Integer.MAX_VALUE) {
throw new RuntimeException("!!!");
}
fileReadStream.readFully(cdnCheckBytes, 0, (int) availableSize);
if (encryptFile) {
int offset = fileOffset / 16;
long offset = fileOffset / 16;
encryptIv[15] = (byte) (offset & 0xff);
encryptIv[14] = (byte) ((offset >> 8) & 0xff);
encryptIv[13] = (byte) ((offset >> 16) & 0xff);
@ -1639,12 +1706,12 @@ public class FileLoadOperation {
}
private void clearOperaion(RequestInfo currentInfo, boolean preloadChanged) {
int minOffset = Integer.MAX_VALUE;
long minOffset = Long.MAX_VALUE;
for (int a = 0; a < requestInfos.size(); a++) {
RequestInfo info = requestInfos.get(a);
minOffset = Math.min(info.offset, minOffset);
if (isPreloadVideoOperation) {
requestedPreloadedBytesRanges.delete(info.offset);
requestedPreloadedBytesRanges.remove(info.offset);
} else {
removePart(notRequestedBytesRanges, info.offset, info.offset + currentDownloadChunkSize);
}
@ -1659,7 +1726,7 @@ public class FileLoadOperation {
for (int a = 0; a < delayedRequestInfos.size(); a++) {
RequestInfo info = delayedRequestInfos.get(a);
if (isPreloadVideoOperation) {
requestedPreloadedBytesRanges.delete(info.offset);
requestedPreloadedBytesRanges.remove(info.offset);
} else {
removePart(notRequestedBytesRanges, info.offset, info.offset + currentDownloadChunkSize);
}
@ -1713,7 +1780,7 @@ public class FileLoadOperation {
}
for (int a = 0; a < count; a++) {
int downloadOffset;
long downloadOffset;
if (isPreloadVideoOperation) {
if (moovFound != 0 && preloadNotRequestedBytesCount <= 0) {
return;
@ -1723,7 +1790,7 @@ public class FileLoadOperation {
boolean found = false;
int tries = preloadMaxBytes / currentDownloadChunkSize + 2;
while (tries != 0) {
if (requestedPreloadedBytesRanges.get(downloadOffset, 0) == 0) {
if (!requestedPreloadedBytesRanges.containsKey(downloadOffset)) {
found = true;
break;
}
@ -1743,7 +1810,7 @@ public class FileLoadOperation {
downloadOffset = nextPreloadDownloadOffset;
}
if (requestedPreloadedBytesRanges == null) {
requestedPreloadedBytesRanges = new SparseIntArray();
requestedPreloadedBytesRanges = new HashMap<>();
}
requestedPreloadedBytesRanges.put(downloadOffset, 1);
if (BuildVars.DEBUG_VERSION) {
@ -1752,27 +1819,27 @@ public class FileLoadOperation {
preloadNotRequestedBytesCount -= currentDownloadChunkSize;
} else {
if (notRequestedBytesRanges != null) {
int sreamOffset = streamPriorityStartOffset != 0 ? streamPriorityStartOffset : streamStartOffset;
long streamOffset = streamPriorityStartOffset != 0 ? streamPriorityStartOffset : streamStartOffset;
int size = notRequestedBytesRanges.size();
int minStart = Integer.MAX_VALUE;
int minStreamStart = Integer.MAX_VALUE;
long minStart = Long.MAX_VALUE;
long minStreamStart = Long.MAX_VALUE;
for (int b = 0; b < size; b++) {
Range range = notRequestedBytesRanges.get(b);
if (sreamOffset != 0) {
if (range.start <= sreamOffset && range.end > sreamOffset) {
minStreamStart = sreamOffset;
minStart = Integer.MAX_VALUE;
if (streamOffset != 0) {
if (range.start <= streamOffset && range.end > streamOffset) {
minStreamStart = streamOffset;
minStart = Long.MAX_VALUE;
break;
}
if (sreamOffset < range.start && range.start < minStreamStart) {
if (streamOffset < range.start && range.start < minStreamStart) {
minStreamStart = range.start;
}
}
minStart = Math.min(minStart, range.start);
}
if (minStreamStart != Integer.MAX_VALUE) {
if (minStreamStart != Long.MAX_VALUE) {
downloadOffset = minStreamStart;
} else if (minStart != Integer.MAX_VALUE) {
} else if (minStart != Long.MAX_VALUE) {
downloadOffset = minStart;
} else {
break;
@ -1803,7 +1870,7 @@ public class FileLoadOperation {
if (webLocation != null) {
TLRPC.TL_upload_getWebFile req = new TLRPC.TL_upload_getWebFile();
req.location = webLocation;
req.offset = downloadOffset;
req.offset = (int) downloadOffset;
req.limit = currentDownloadChunkSize;
request = req;
} else {
@ -1825,7 +1892,10 @@ public class FileLoadOperation {
if (range != null) {
requestInfo.response = new TLRPC.TL_upload_file();
try {
NativeByteBuffer buffer = new NativeByteBuffer(range.length);
if (BuildVars.DEBUG_VERSION && range.length > Integer.MAX_VALUE) {
throw new RuntimeException("cast long to integer");
}
NativeByteBuffer buffer = new NativeByteBuffer((int) range.length);
preloadStream.seek(range.fileOffset);
preloadStream.getChannel().read(buffer.buffer);
buffer.buffer.position(0);
@ -1840,6 +1910,7 @@ public class FileLoadOperation {
}
}
}
if (streamPriorityStartOffset != 0) {
if (BuildVars.DEBUG_VERSION) {
FileLog.d("frame get offset = " + streamPriorityStartOffset);
@ -1882,7 +1953,7 @@ public class FileLoadOperation {
TLRPC.TL_upload_fileCdnRedirect res = (TLRPC.TL_upload_fileCdnRedirect) response;
if (!res.file_hashes.isEmpty()) {
if (cdnHashes == null) {
cdnHashes = new SparseArray<>();
cdnHashes = new HashMap<>();
}
for (int a1 = 0; a1 < res.file_hashes.size(); a1++) {
TLRPC.TL_fileHash hash = res.file_hashes.get(a1);
@ -1921,7 +1992,7 @@ public class FileLoadOperation {
TLRPC.Vector vector = (TLRPC.Vector) response1;
if (!vector.objects.isEmpty()) {
if (cdnHashes == null) {
cdnHashes = new SparseArray<>();
cdnHashes = new HashMap<>();
}
for (int a1 = 0; a1 < vector.objects.size(); a1++) {
TLRPC.TL_fileHash hash = (TLRPC.TL_fileHash) vector.objects.get(a1);

View File

@ -30,10 +30,15 @@ public class FileLoader extends BaseController {
public interface FileLoaderDelegate {
void fileUploadProgressChanged(FileUploadOperation operation, String location, long uploadedSize, long totalSize, boolean isEncrypted);
void fileDidUploaded(String location, TLRPC.InputFile inputFile, TLRPC.InputEncryptedFile inputEncryptedFile, byte[] key, byte[] iv, long totalFileSize);
void fileDidFailedUpload(String location, boolean isEncrypted);
void fileDidLoaded(String location, File finalFile, Object parentObject, int type);
void fileDidFailedLoad(String location, int state);
void fileLoadProgressChanged(FileLoadOperation operation, String location, long uploadedSize, long totalSize);
}
@ -42,6 +47,7 @@ public class FileLoader extends BaseController {
public static final int MEDIA_DIR_VIDEO = 2;
public static final int MEDIA_DIR_DOCUMENT = 3;
public static final int MEDIA_DIR_CACHE = 4;
public static final int MEDIA_DIR_FILES = 5;
public static final int MEDIA_DIR_IMAGE_PUBLIC = 100;
public static final int MEDIA_DIR_VIDEO_PUBLIC = 101;
@ -55,10 +61,15 @@ public class FileLoader extends BaseController {
public static final int QUEUE_TYPE_FILE = 0;
public static final int QUEUE_TYPE_IMAGE = 1;
public static final int QUEUE_TYPE_AUDIO = 2;
public static final int QUEUE_TYPE_PRELOAD = 3;
public final static long MAX_FILE_SIZE = 1024L * 1024L * 2000L;
public final static long DEFAULT_MAX_FILE_SIZE = 1024L * 1024L * 2000L;
public final static long DEFAULT_MAX_FILE_SIZE_PREMIUM = DEFAULT_MAX_FILE_SIZE * 2L;
public final static int PRELOAD_CACHE_TYPE = 11;
private volatile static DispatchQueue fileLoaderQueue = new DispatchQueue("fileUploadQueue");
private final FilePathDatabase filePathDatabase;
private LinkedList<FileUploadOperation> uploadOperationQueue = new LinkedList<>();
private LinkedList<FileUploadOperation> uploadSmallOperationQueue = new LinkedList<>();
@ -70,9 +81,11 @@ public class FileLoader extends BaseController {
private SparseArray<LinkedList<FileLoadOperation>> fileLoadOperationQueues = new SparseArray<>();
private SparseArray<LinkedList<FileLoadOperation>> audioLoadOperationQueues = new SparseArray<>();
private SparseArray<LinkedList<FileLoadOperation>> imageLoadOperationQueues = new SparseArray<>();
private SparseArray<LinkedList<FileLoadOperation>> preloadingLoadOperationQueues = new SparseArray<>();
private SparseIntArray fileLoadOperationsCount = new SparseIntArray();
private SparseIntArray audioLoadOperationsCount = new SparseIntArray();
private SparseIntArray imageLoadOperationsCount = new SparseIntArray();
private SparseIntArray preloadingLoadOperationsCount = new SparseIntArray();
private ConcurrentHashMap<String, FileLoadOperation> loadOperationPaths = new ConcurrentHashMap<>();
private ArrayList<FileLoadOperation> activeFileLoadOperation = new ArrayList<>();
@ -116,6 +129,7 @@ public class FileLoader extends BaseController {
public FileLoader(int instance) {
super(instance);
filePathDatabase = new FilePathDatabase(instance);
}
public static void setMediaDirs(SparseArray<File> dirs) {
@ -255,7 +269,7 @@ public class FileLoader extends BaseController {
uploadFile(location, encrypted, small, 0, type, false);
}
public void uploadFile(final String location, final boolean encrypted, final boolean small, final int estimatedSize, final int type, boolean forceSmallFile) {
public void uploadFile(final String location, final boolean encrypted, final boolean small, final long estimatedSize, final int type, boolean forceSmallFile) {
if (location == null) {
return;
}
@ -269,7 +283,7 @@ public class FileLoader extends BaseController {
return;
}
}
int esimated = estimatedSize;
long esimated = estimatedSize;
if (esimated != 0) {
Long finalSize = uploadSizes.get(location);
if (finalSize != null) {
@ -383,7 +397,9 @@ public class FileLoader extends BaseController {
private LinkedList<FileLoadOperation> getLoadOperationQueue(int datacenterId, int type) {
SparseArray<LinkedList<FileLoadOperation>> queues;
if (type == QUEUE_TYPE_AUDIO) {
if (type == QUEUE_TYPE_PRELOAD) {
queues = preloadingLoadOperationQueues;
} else if (type == QUEUE_TYPE_AUDIO) {
queues = audioLoadOperationQueues;
} else if (type == QUEUE_TYPE_IMAGE) {
queues = imageLoadOperationQueues;
@ -400,7 +416,9 @@ public class FileLoader extends BaseController {
private SparseIntArray getLoadOperationCount(int type) {
SparseArray<LinkedList<FileLoadOperation>> queues;
if (type == QUEUE_TYPE_AUDIO) {
if (type == QUEUE_TYPE_PRELOAD) {
return preloadingLoadOperationsCount;
} else if (type == QUEUE_TYPE_AUDIO) {
return audioLoadOperationsCount;
} else if (type == QUEUE_TYPE_IMAGE) {
return imageLoadOperationsCount;
@ -597,7 +615,7 @@ public class FileLoader extends BaseController {
}
}
private FileLoadOperation loadFileInternal(final TLRPC.Document document, final SecureDocument secureDocument, final WebFile webDocument, TLRPC.TL_fileLocationToBeDeprecated location, final ImageLocation imageLocation, Object parentObject, final String locationExt, final int locationSize, final int priority, final FileLoadOperationStream stream, final int streamOffset, boolean streamPriority, final int cacheType) {
private FileLoadOperation loadFileInternal(final TLRPC.Document document, final SecureDocument secureDocument, final WebFile webDocument, TLRPC.TL_fileLocationToBeDeprecated location, final ImageLocation imageLocation, Object parentObject, final String locationExt, final long locationSize, final int priority, final FileLoadOperationStream stream, final int streamOffset, boolean streamPriority, final int cacheType) {
String fileName;
if (location != null) {
fileName = getAttachFileName(location, locationExt);
@ -617,7 +635,7 @@ public class FileLoader extends BaseController {
loadOperationPathsUI.put(fileName, true);
}
if (document != null && parentObject instanceof MessageObject && ((MessageObject) parentObject).putInDownloadsStore) {
if (document != null && parentObject instanceof MessageObject && ((MessageObject) parentObject).putInDownloadsStore && !((MessageObject) parentObject).isAnyKindOfSticker()) {
getDownloadController().startDownloadFile(document, (MessageObject) parentObject);
}
@ -666,15 +684,22 @@ public class FileLoader extends BaseController {
File tempDir = getDirectory(MEDIA_DIR_CACHE);
File storeDir = tempDir;
int type = MEDIA_DIR_CACHE;
long documentId = 0;
int dcId = 0;
String lastKnownPath = null;
if (secureDocument != null) {
operation = new FileLoadOperation(secureDocument);
type = MEDIA_DIR_DOCUMENT;
} else if (location != null) {
documentId = location.volume_id;
dcId = location.dc_id;
operation = new FileLoadOperation(imageLocation, parentObject, locationExt, locationSize);
type = MEDIA_DIR_IMAGE;
} else if (document != null) {
operation = new FileLoadOperation(document, parentObject);
documentId = document.id;
dcId = document.dc_id;
if (MessageObject.isVoiceDocument(document)) {
type = MEDIA_DIR_AUDIO;
} else if (MessageObject.isVideoDocument(document) || MessageObject.isGifDocument(document)) {
@ -699,19 +724,67 @@ public class FileLoader extends BaseController {
}
}
int queueType;
if (type == MEDIA_DIR_AUDIO) {
if (cacheType == PRELOAD_CACHE_TYPE) {
queueType = QUEUE_TYPE_PRELOAD;
} else if (type == MEDIA_DIR_AUDIO) {
queueType = QUEUE_TYPE_AUDIO;
} else if (secureDocument != null || location != null && (imageLocation == null || imageLocation.imageType != IMAGE_TYPE_ANIMATION) || MessageObject.isImageWebDocument(webDocument)) {
} else if (secureDocument != null || location != null && (imageLocation == null || imageLocation.imageType != IMAGE_TYPE_ANIMATION) || MessageObject.isImageWebDocument(webDocument) || MessageObject.isStickerDocument(document)) {
queueType = QUEUE_TYPE_IMAGE;
} else {
queueType = QUEUE_TYPE_FILE;
}
String storeFileName = fileName;
if (cacheType == 0 || cacheType == 10) {
storeDir = getDirectory(type);
if (documentId != 0) {
String path = getFileDatabase().getPath(documentId, dcId, type, true);
boolean customPath = false;
if (path != null) {
File file = new File(path);
if (file.exists()) {
customPath = true;
storeFileName = file.getName();
storeDir = file.getParentFile();
}
}
if (!customPath) {
storeFileName = fileName;
storeDir = getDirectory(type);
boolean saveCustomPath = false;
if ((type == MEDIA_DIR_IMAGE || type == MEDIA_DIR_VIDEO) && canSaveToPublicStorage(parentObject)) {
File newDir;
if (type == MEDIA_DIR_IMAGE) {
newDir = getDirectory(MEDIA_DIR_IMAGE_PUBLIC);
} else {
newDir = getDirectory(MEDIA_DIR_VIDEO_PUBLIC);
}
if (newDir != null) {
storeDir = newDir;
saveCustomPath = true;
}
} else if (!TextUtils.isEmpty(getDocumentFileName(document)) && canSaveAsFile(parentObject)) {
storeFileName = getDocumentFileName(document);
File newDir = getDirectory(MEDIA_DIR_FILES);
if (newDir != null) {
storeDir = newDir;
saveCustomPath = true;
}
}
if (saveCustomPath) {
operation.pathSaveData = new FilePathDatabase.PathData(documentId, dcId, type);
}
}
} else {
storeDir = getDirectory(type);
}
} else if (cacheType == 2) {
operation.setEncryptFile(true);
}
operation.setPaths(currentAccount, fileName, queueType, storeDir, tempDir);
operation.setPaths(currentAccount, fileName, queueType, storeDir, tempDir, storeFileName);
if (cacheType == 10) {
operation.setIsPreloadVideoOperation(true);
}
@ -757,6 +830,11 @@ public class FileLoader extends BaseController {
delegate.fileLoadProgressChanged(operation, fileName, uploadedSize, totalSize);
}
}
@Override
public void saveFilePath(FilePathDatabase.PathData pathSaveData, File cacheFileFinal) {
getFileDatabase().putPath(pathSaveData.id, pathSaveData.dc, pathSaveData.type, cacheFileFinal != null ? cacheFileFinal.toString() : null);
}
};
operation.setDelegate(fileLoadOperationDelegate);
@ -766,7 +844,15 @@ public class FileLoader extends BaseController {
operation.setPriority(priority);
boolean started;
if (queueType == QUEUE_TYPE_AUDIO) {
if (queueType == QUEUE_TYPE_PRELOAD) {
int maxCount = priority > 0 ? 6 : 2;
int count = preloadingLoadOperationsCount.get(datacenterId);
if (started = (stream != null || count < maxCount)) {
if (operation.start(stream, streamOffset, streamPriority)) {
preloadingLoadOperationsCount.put(datacenterId, count + 1);
}
}
} else if (queueType == QUEUE_TYPE_AUDIO) {
int maxCount = priority > 0 ? 3 : 1;
int count = audioLoadOperationsCount.get(datacenterId);
if (started = (stream != null || count < maxCount)) {
@ -801,6 +887,45 @@ public class FileLoader extends BaseController {
return operation;
}
private boolean canSaveAsFile(Object parentObject) {
if (parentObject instanceof MessageObject) {
MessageObject messageObject = (MessageObject) parentObject;
if (!messageObject.isDocument()) {
return false;
}
return true;
}
return false;
}
private boolean canSaveToPublicStorage(Object parentObject) {
if (SharedConfig.saveToGalleryFlags == 0 || BuildVars.NO_SCOPED_STORAGE) {
return false;
}
if (parentObject instanceof MessageObject) {
MessageObject messageObject = (MessageObject) parentObject;
int flag;
long dialogId = messageObject.getDialogId();
if (messageObject.isAnyKindOfSticker() || getMessagesController().isChatNoForwards(getMessagesController().getChat(-dialogId)) || messageObject.messageOwner.noforwards) {
return false;
}
if (dialogId >= 0) {
flag = SharedConfig.SAVE_TO_GALLERY_FLAG_PEER;
} else {
if (ChatObject.isChannelAndNotMegaGroup(getMessagesController().getChat(-dialogId))) {
flag = SharedConfig.SAVE_TO_GALLERY_FLAG_CHANNELS;
} else {
flag = SharedConfig.SAVE_TO_GALLERY_FLAG_GROUP;
}
}
if ((SharedConfig.saveToGalleryFlags & flag) != 0) {
return true;
}
}
return false;
}
private void addOperationToQueue(FileLoadOperation operation, LinkedList<FileLoadOperation> queue) {
int priority = operation.getPriority();
if (priority > 0) {
@ -818,7 +943,7 @@ public class FileLoader extends BaseController {
}
}
private void loadFile(final TLRPC.Document document, final SecureDocument secureDocument, final WebFile webDocument, TLRPC.TL_fileLocationToBeDeprecated location, final ImageLocation imageLocation, final Object parentObject, final String locationExt, final int locationSize, final int priority, final int cacheType) {
private void loadFile(final TLRPC.Document document, final SecureDocument secureDocument, final WebFile webDocument, TLRPC.TL_fileLocationToBeDeprecated location, final ImageLocation imageLocation, final Object parentObject, final String locationExt, final long locationSize, final int priority, final int cacheType) {
String fileName;
if (location != null) {
fileName = getAttachFileName(location, locationExt);
@ -845,7 +970,7 @@ public class FileLoader extends BaseController {
try {
semaphore.await();
} catch (Exception e) {
FileLog.e(e);
FileLog.e(e, false);
}
return result[0];
}
@ -870,7 +995,9 @@ public class FileLoader extends BaseController {
while (!queue.isEmpty()) {
operation = queue.get(0);
int maxCount;
if (queueType == QUEUE_TYPE_AUDIO) {
if (queueType == QUEUE_TYPE_PRELOAD) {
maxCount = operation.getPriority() != 0 ? 6 : 2;
} else if (queueType == QUEUE_TYPE_AUDIO) {
maxCount = operation.getPriority() != 0 ? 3 : 1;
} else if (queueType == QUEUE_TYPE_IMAGE) {
maxCount = operation.getPriority() != 0 ? 6 : 2;
@ -942,7 +1069,11 @@ public class FileLoader extends BaseController {
return "";
}
public static File getPathToMessage(TLRPC.Message message) {
public File getPathToMessage(TLRPC.Message message) {
return getPathToMessage(message, true);
}
public File getPathToMessage(TLRPC.Message message, boolean useFileDatabaseQueue) {
if (message == null) {
return new File("");
}
@ -952,75 +1083,91 @@ public class FileLoader extends BaseController {
if (sizes.size() > 0) {
TLRPC.PhotoSize sizeFull = getClosestPhotoSizeWithSize(sizes, AndroidUtilities.getPhotoSize());
if (sizeFull != null) {
return getPathToAttach(sizeFull);
return getPathToAttach(sizeFull, null, false, useFileDatabaseQueue);
}
}
}
} else {
if (message.media instanceof TLRPC.TL_messageMediaDocument) {
return getPathToAttach(message.media.document, message.media.ttl_seconds != 0);
return getPathToAttach(message.media.document, null, message.media.ttl_seconds != 0, useFileDatabaseQueue);
} else if (message.media instanceof TLRPC.TL_messageMediaPhoto) {
ArrayList<TLRPC.PhotoSize> sizes = message.media.photo.sizes;
if (sizes.size() > 0) {
TLRPC.PhotoSize sizeFull = getClosestPhotoSizeWithSize(sizes, AndroidUtilities.getPhotoSize(), false, null, true);
if (sizeFull != null) {
return getPathToAttach(sizeFull, message.media.ttl_seconds != 0);
return getPathToAttach(sizeFull, null, message.media.ttl_seconds != 0, useFileDatabaseQueue);
}
}
} else if (message.media instanceof TLRPC.TL_messageMediaWebPage) {
if (message.media.webpage.document != null) {
return getPathToAttach(message.media.webpage.document);
return getPathToAttach(message.media.webpage.document, null, false, useFileDatabaseQueue);
} else if (message.media.webpage.photo != null) {
ArrayList<TLRPC.PhotoSize> sizes = message.media.webpage.photo.sizes;
if (sizes.size() > 0) {
TLRPC.PhotoSize sizeFull = getClosestPhotoSizeWithSize(sizes, AndroidUtilities.getPhotoSize());
if (sizeFull != null) {
return getPathToAttach(sizeFull);
return getPathToAttach(sizeFull, null, false, useFileDatabaseQueue);
}
}
}
} else if (message.media instanceof TLRPC.TL_messageMediaInvoice) {
return getPathToAttach(((TLRPC.TL_messageMediaInvoice) message.media).photo, true);
return getPathToAttach(((TLRPC.TL_messageMediaInvoice) message.media).photo, null, true, useFileDatabaseQueue);
}
}
return new File("");
}
public static File getPathToAttach(TLObject attach) {
public File getPathToAttach(TLObject attach) {
return getPathToAttach(attach, null, false);
}
public static File getPathToAttach(TLObject attach, boolean forceCache) {
public File getPathToAttach(TLObject attach, boolean forceCache) {
return getPathToAttach(attach, null, forceCache);
}
public static File getPathToAttach(TLObject attach, String ext, boolean forceCache) {
return getPathToAttach(attach, null, ext, forceCache);
public File getPathToAttach(TLObject attach, String ext, boolean forceCache) {
return getPathToAttach(attach, null, ext, forceCache, true);
}
public static File getPathToAttach(TLObject attach, String size, String ext, boolean forceCache) {
public File getPathToAttach(TLObject attach, String ext, boolean forceCache, boolean useFileDatabaseQueue) {
return getPathToAttach(attach, null, ext, forceCache, useFileDatabaseQueue);
}
/**
* Return real file name. Used before file.exist()
*/
public File getPathToAttach(TLObject attach, String size, String ext, boolean forceCache, boolean useFileDatabaseQueue) {
File dir = null;
long documentId = 0;
int dcId = 0;
int type = 0;
if (forceCache) {
dir = getDirectory(MEDIA_DIR_CACHE);
} else {
if (attach instanceof TLRPC.Document) {
TLRPC.Document document = (TLRPC.Document) attach;
if (!TextUtils.isEmpty(document.localPath)) {
return new File(document.localPath);
}
if (document.key != null) {
dir = getDirectory(MEDIA_DIR_CACHE);
type = MEDIA_DIR_CACHE;
} else {
if (MessageObject.isVoiceDocument(document)) {
dir = getDirectory(MEDIA_DIR_AUDIO);
type = MEDIA_DIR_AUDIO;
} else if (MessageObject.isVideoDocument(document) || MessageObject.isGifDocument(document)) {
dir = getDirectory(MEDIA_DIR_VIDEO);
type = MEDIA_DIR_VIDEO;
} else if (MessageObject.isStickerDocument(document)) {
dir = getDirectory(MEDIA_DIR_CACHE);
} else {
dir = getDirectory(MEDIA_DIR_DOCUMENT);
type = MEDIA_DIR_DOCUMENT;
}
}
documentId = document.id;
dcId = document.dc_id;
dir = getDirectory(type);
} else if (attach instanceof TLRPC.Photo) {
TLRPC.PhotoSize photoSize = getClosestPhotoSizeWithSize(((TLRPC.Photo) attach).sizes, AndroidUtilities.getPhotoSize());
return getPathToAttach(photoSize, ext, false);
return getPathToAttach(photoSize, ext, false, useFileDatabaseQueue);
} else if (attach instanceof TLRPC.PhotoSize) {
TLRPC.PhotoSize photoSize = (TLRPC.PhotoSize) attach;
if (photoSize instanceof TLRPC.TL_photoStrippedSize || photoSize instanceof TLRPC.TL_photoPathSize) {
@ -1042,7 +1189,9 @@ public class FileLoader extends BaseController {
if (fileLocation.key != null || fileLocation.volume_id == Integer.MIN_VALUE && fileLocation.local_id < 0) {
dir = getDirectory(MEDIA_DIR_CACHE);
} else {
dir = getDirectory(MEDIA_DIR_IMAGE);
documentId = fileLocation.volume_id;
dcId = fileLocation.dc_id;
dir = getDirectory(type = MEDIA_DIR_IMAGE);
}
} else if (attach instanceof TLRPC.UserProfilePhoto || attach instanceof TLRPC.ChatPhoto) {
if (size == null) {
@ -1071,9 +1220,19 @@ public class FileLoader extends BaseController {
if (dir == null) {
return new File("");
}
if (documentId != 0) {
String path = getInstance(UserConfig.selectedAccount).getFileDatabase().getPath(documentId, dcId, type, useFileDatabaseQueue);
if (path != null) {
return new File(path);
}
}
return new File(dir, getAttachFileName(attach, ext));
}
private FilePathDatabase getFileDatabase() {
return filePathDatabase;
}
public static TLRPC.PhotoSize getClosestPhotoSizeWithSize(ArrayList<TLRPC.PhotoSize> sizes, int side) {
return getClosestPhotoSizeWithSize(sizes, side, false);
}
@ -1215,6 +1374,9 @@ public class FileLoader extends BaseController {
return getAttachFileName(attach, null, ext);
}
/**
* file hash. contains docId, dcId, ext.
*/
public static String getAttachFileName(TLObject attach, String size, String ext) {
if (attach instanceof TLRPC.Document) {
TLRPC.Document document = (TLRPC.Document) attach;
@ -1441,7 +1603,7 @@ public class FileLoader extends BaseController {
public void checkCurrentDownloadsFiles() {
ArrayList<MessageObject> messagesToRemove = new ArrayList<>();
ArrayList<MessageObject> messageObjects = new ArrayList<>(getDownloadController().recentDownloadingFiles);
for (int i = 0 ; i < messageObjects.size(); i++) {
for (int i = 0; i < messageObjects.size(); i++) {
messageObjects.get(i).checkMediaExistance();
if (messageObjects.get(i).mediaExists) {
messagesToRemove.add(messageObjects.get(i));
@ -1453,10 +1615,28 @@ public class FileLoader extends BaseController {
getNotificationCenter().postNotificationName(NotificationCenter.onDownloadingFilesChanged);
});
}
}
/**
* optimezed for bulk messages
*/
public void checkMediaExistance(ArrayList<MessageObject> messageObjects) {
getFileDatabase().checkMediaExistance(messageObjects);
}
public interface FileResolver {
File getFile();
}
public void clearRecentDownloadedFiles() {
getDownloadController().clearRecentDownloadedFiles();
}
public static boolean checkUploadFileSize(int currentAccount, long length) {
boolean premium = AccountInstance.getInstance(currentAccount).getUserConfig().isPremium();
if (length < DEFAULT_MAX_FILE_SIZE || (length < DEFAULT_MAX_FILE_SIZE_PREMIUM && premium)) {
return true;
}
return false;
}
}

View File

@ -0,0 +1,238 @@
package org.telegram.messenger;
import android.os.Looper;
import org.telegram.SQLite.SQLiteCursor;
import org.telegram.SQLite.SQLiteDatabase;
import org.telegram.SQLite.SQLiteException;
import org.telegram.SQLite.SQLitePreparedStatement;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
public class FilePathDatabase {
private final DispatchQueue dispatchQueue;
private final int currentAccount;
private SQLiteDatabase database;
private File cacheFile;
private File shmCacheFile;
private final static int LAST_DB_VERSION = 1;
private final static String DATABASE_NAME = "file_to_path";
private final static String DATABASE_BACKUP_NAME = "file_to_path_backup";
public FilePathDatabase(int currentAccount) {
this.currentAccount = currentAccount;
dispatchQueue = new DispatchQueue("files_database_queue_" + currentAccount);
dispatchQueue.postRunnable(() -> {
createDatabase(0, false);
});
}
public void createDatabase(int tryCount, boolean fromBackup) {
File filesDir = ApplicationLoader.getFilesDirFixed();
if (currentAccount != 0) {
filesDir = new File(filesDir, "account" + currentAccount + "/");
filesDir.mkdirs();
}
cacheFile = new File(filesDir, DATABASE_NAME + ".db");
shmCacheFile = new File(filesDir, DATABASE_NAME + ".db-shm");
boolean createTable = false;
if (!cacheFile.exists()) {
createTable = true;
}
try {
database = new SQLiteDatabase(cacheFile.getPath());
database.executeFast("PRAGMA secure_delete = ON").stepThis().dispose();
database.executeFast("PRAGMA temp_store = MEMORY").stepThis().dispose();
if (createTable) {
database.executeFast("CREATE TABLE paths(document_id INTEGER, dc_id INTEGER, type INTEGER, path TEXT, PRIMARY KEY(document_id, dc_id, type));").stepThis().dispose();
database.executeFast("PRAGMA user_version = " + LAST_DB_VERSION).stepThis().dispose();
} else {
int version = database.executeInt("PRAGMA user_version");
if (BuildVars.LOGS_ENABLED) {
FileLog.d("current db version = " + version);
}
if (version == 0) {
throw new Exception("malformed");
}
//migration
}
if (!fromBackup) {
createBackup();
}
FileLog.d("files db created from_backup= " + fromBackup);
} catch (Exception e) {
if (tryCount < 4) {
if (!fromBackup && restoreBackup()) {
createDatabase(tryCount + 1, true);
return;
} else {
cacheFile.delete();
shmCacheFile.delete();
createDatabase(tryCount + 1, false);
}
}
if (BuildVars.DEBUG_VERSION) {
FileLog.e(e);
}
}
}
private void createBackup() {
File filesDir = ApplicationLoader.getFilesDirFixed();
if (currentAccount != 0) {
filesDir = new File(filesDir, "account" + currentAccount + "/");
filesDir.mkdirs();
}
File backupCacheFile = new File(filesDir, DATABASE_BACKUP_NAME + ".db");
try {
AndroidUtilities.copyFile(cacheFile, backupCacheFile);
FileLog.d("file db backup created " + backupCacheFile.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
private boolean restoreBackup() {
File filesDir = ApplicationLoader.getFilesDirFixed();
if (currentAccount != 0) {
filesDir = new File(filesDir, "account" + currentAccount + "/");
filesDir.mkdirs();
}
File backupCacheFile = new File(filesDir, DATABASE_BACKUP_NAME + ".db");
if (!backupCacheFile.exists()) {
return false;
}
try {
return AndroidUtilities.copyFile(backupCacheFile, cacheFile);
} catch (IOException e) {
FileLog.e(e);
}
return false;
}
public String getPath(long documentId, int dc, int type, boolean useQueue) {
if (useQueue) {
if (BuildVars.DEBUG_VERSION) {
if (dispatchQueue.getHandler() != null && Thread.currentThread() == dispatchQueue.getHandler().getLooper().getThread()) {
throw new RuntimeException("Error, lead to infinity loop");
}
}
CountDownLatch syncLatch = new CountDownLatch(1);
String[] res = new String[1];
long time = System.currentTimeMillis();
dispatchQueue.postRunnable(() -> {
SQLiteCursor cursor = null;
try {
cursor = database.queryFinalized("SELECT path FROM paths WHERE document_id = " + documentId + " AND dc_id = " + dc + " AND type = " + type);
if (cursor.next()) {
res[0] = cursor.stringValue(0);
}
cursor.dispose();
} catch (SQLiteException e) {
FileLog.e(e);
}
syncLatch.countDown();
});
try {
syncLatch.await();
} catch (Exception ignore) {
}
return res[0];
} else {
SQLiteCursor cursor = null;
String res = null;
try {
cursor = database.queryFinalized("SELECT path FROM paths WHERE document_id = " + documentId + " AND dc_id = " + dc + " AND type = " + type);
if (cursor.next()) {
res = cursor.stringValue(0);
}
cursor.dispose();
} catch (SQLiteException e) {
FileLog.e(e);
}
return res;
}
}
public void putPath(long id, int dc, int type, String path) {
dispatchQueue.postRunnable(() -> {
SQLitePreparedStatement state = null;
try {
if (path != null) {
state = database.executeFast("REPLACE INTO paths VALUES(?, ?, ?, ?)");
state.requery();
state.bindLong(1, id);
state.bindInteger(2, dc);
state.bindInteger(3, type);
state.bindString(4, path);
state.step();
} else {
database.executeFast("DELETE FROM paths WHERE document_id = " + id + " AND dc_id = " + dc + " AND type = " + type).stepThis().dispose();
}
} catch (SQLiteException e) {
FileLog.e(e);
}
});
}
public void checkMediaExistance(ArrayList<MessageObject> messageObjects) {
if (messageObjects.isEmpty()) {
return;
}
ArrayList<MessageObject> arrayListFinal = new ArrayList<>(messageObjects);
CountDownLatch syncLatch = new CountDownLatch(1);
long time = System.currentTimeMillis();
dispatchQueue.postRunnable(() -> {
try {
for (int i = 0; i < arrayListFinal.size(); i++) {
MessageObject messageObject = arrayListFinal.get(i);
messageObject.checkMediaExistance(false);
}
} catch (Exception e) {
e.printStackTrace();
}
syncLatch.countDown();
});
try {
syncLatch.await();
} catch (InterruptedException e) {
FileLog.e(e);
}
FileLog.d("checkMediaExistance size=" + messageObjects.size() + " time=" + (System.currentTimeMillis() - time));
if (BuildVars.DEBUG_VERSION) {
if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
FileLog.e(new Exception("warning, not allowed in main thread"));
}
}
}
public static class PathData {
public final long id;
public final int dc;
public final int type;
public PathData(long documentId, int dcId, int type) {
this.id = documentId;
this.dc = dcId;
this.type = type;
}
}
}

View File

@ -70,7 +70,16 @@ public class FileRefController extends BaseController {
}
public static String getKeyForParentObject(Object parentObject) {
if (parentObject instanceof MessageObject) {
if (parentObject instanceof TLRPC.TL_availableReaction) {
return "available_reaction_" + ((TLRPC.TL_availableReaction) parentObject).reaction;
} else if (parentObject instanceof TLRPC.BotInfo) {
TLRPC.BotInfo botInfo = (TLRPC.BotInfo) parentObject;
return "bot_info_" + botInfo.user_id;
} else if (parentObject instanceof TLRPC.TL_attachMenuBot) {
TLRPC.TL_attachMenuBot bot = (TLRPC.TL_attachMenuBot) parentObject;
long botId = bot.bot_id;
return "attach_menu_bot_" + botId;
} else if (parentObject instanceof MessageObject) {
MessageObject messageObject = (MessageObject) parentObject;
long channelId = messageObject.getChannelId();
return "message" + messageObject.getRealId() + "_" + channelId + "_" + messageObject.scheduled;
@ -309,7 +318,21 @@ public class FileRefController extends BaseController {
}
private void requestReferenceFromServer(Object parentObject, String locationKey, String parentKey, Object[] args) {
if (parentObject instanceof MessageObject) {
if (parentObject instanceof TLRPC.TL_availableReaction) {
TLRPC.TL_messages_getAvailableReactions req = new TLRPC.TL_messages_getAvailableReactions();
req.hash = 0;
getConnectionsManager().sendRequest(req, (response, error) -> onRequestComplete(locationKey, parentKey, response, true, false));
} else if (parentObject instanceof TLRPC.BotInfo) {
TLRPC.BotInfo botInfo = (TLRPC.BotInfo) parentObject;
TLRPC.TL_users_getFullUser req = new TLRPC.TL_users_getFullUser();
req.id = getMessagesController().getInputUser(botInfo.user_id);
getConnectionsManager().sendRequest(req, (response, error) -> onRequestComplete(locationKey, parentKey, response, true, false));
} else if (parentObject instanceof TLRPC.TL_attachMenuBot) {
TLRPC.TL_attachMenuBot bot = (TLRPC.TL_attachMenuBot) parentObject;
TLRPC.TL_messages_getAttachMenuBot req = new TLRPC.TL_messages_getAttachMenuBot();
req.bot = getMessagesController().getInputUser(bot.bot_id);
getConnectionsManager().sendRequest(req, (response, error) -> onRequestComplete(locationKey, parentKey, response, true, false));
} else if (parentObject instanceof MessageObject) {
MessageObject messageObject = (MessageObject) parentObject;
long channelId = messageObject.getChannelId();
if (messageObject.scheduled) {
@ -726,12 +749,84 @@ public class FileRefController extends BaseController {
}
}
if (result == null) {
getMessagesStorage().replaceMessageIfExists(res.messages.get(0), res.users, res.chats,true);
getMessagesStorage().replaceMessageIfExists(res.messages.get(0), res.users, res.chats, true);
if (BuildVars.DEBUG_VERSION) {
FileLog.d("file ref not found in messages, replacing message");
}
}
}
} else if (response instanceof TLRPC.TL_messages_availableReactions) {
TLRPC.TL_messages_availableReactions availableReactions = (TLRPC.TL_messages_availableReactions) response;
getMediaDataController().processLoadedReactions(availableReactions.reactions, availableReactions.hash, (int) (System.currentTimeMillis() / 1000), false);
for (TLRPC.TL_availableReaction reaction : availableReactions.reactions) {
result = getFileReference(reaction.static_icon, requester.location, needReplacement, locationReplacement);
if (result != null) {
break;
}
result = getFileReference(reaction.appear_animation, requester.location, needReplacement, locationReplacement);
if (result != null) {
break;
}
result = getFileReference(reaction.select_animation, requester.location, needReplacement, locationReplacement);
if (result != null) {
break;
}
result = getFileReference(reaction.activate_animation, requester.location, needReplacement, locationReplacement);
if (result != null) {
break;
}
result = getFileReference(reaction.effect_animation, requester.location, needReplacement, locationReplacement);
if (result != null) {
break;
}
result = getFileReference(reaction.around_animation, requester.location, needReplacement, locationReplacement);
if (result != null) {
break;
}
result = getFileReference(reaction.center_icon, requester.location, needReplacement, locationReplacement);
if (result != null) {
break;
}
}
} else if (response instanceof TLRPC.TL_users_userFull) {
TLRPC.TL_users_userFull usersFull = (TLRPC.TL_users_userFull) response;
getMessagesController().putUsers(usersFull.users, false);
getMessagesController().putChats(usersFull.chats, false);
TLRPC.UserFull userFull = usersFull.full_user;
TLRPC.BotInfo botInfo = userFull.bot_info;
if (botInfo != null) {
getMessagesStorage().updateUserInfo(userFull, true);
result = getFileReference(botInfo.description_document, requester.location, needReplacement, locationReplacement);
if (result != null) {
continue;
}
result = getFileReference(botInfo.description_photo, requester.location, needReplacement, locationReplacement);
}
} else if (response instanceof TLRPC.TL_attachMenuBotsBot) {
TLRPC.TL_attachMenuBot bot = ((TLRPC.TL_attachMenuBotsBot) response).bot;
for (TLRPC.TL_attachMenuBotIcon icon : bot.icons) {
result = getFileReference(icon.icon, requester.location, needReplacement, locationReplacement);
if (result != null) {
break;
}
}
if (cache) {
TLRPC.TL_attachMenuBots bots = getMediaDataController().getAttachMenuBots();
ArrayList<TLRPC.TL_attachMenuBot> newBotsList = new ArrayList<>(bots.bots);
for (int i = 0; i < newBotsList.size(); i++) {
TLRPC.TL_attachMenuBot wasBot = newBotsList.get(i);
if (wasBot.bot_id == bot.bot_id) {
newBotsList.set(i, bot);
break;
}
}
bots.bots = newBotsList;
getMediaDataController().processLoadedMenuBots(bots, bots.hash, (int) (System.currentTimeMillis() / 1000), false);
}
} else if (response instanceof TLRPC.TL_help_appUpdate) {
TLRPC.TL_help_appUpdate appUpdate = (TLRPC.TL_help_appUpdate) response;
result = getFileReference(appUpdate.document, requester.location, needReplacement, locationReplacement);

View File

@ -57,7 +57,7 @@ public class FileStreamLoadOperation extends BaseDataSource implements FileLoadO
document = new TLRPC.TL_document();
document.access_hash = Utilities.parseLong(uri.getQueryParameter("hash"));
document.id = Utilities.parseLong(uri.getQueryParameter("id"));
document.size = Utilities.parseInt(uri.getQueryParameter("size"));
document.size = Utilities.parseLong(uri.getQueryParameter("size"));
document.dc_id = Utilities.parseInt(uri.getQueryParameter("dc"));
document.mime_type = uri.getQueryParameter("mime");
document.file_reference = Utilities.hexToBytes(uri.getQueryParameter("reference"));
@ -96,7 +96,7 @@ public class FileStreamLoadOperation extends BaseDataSource implements FileLoadO
readLength = (int) bytesRemaining;
}
while (availableLength == 0 && opened) {
availableLength = loadOperation.getDownloadedLengthFromOffset(currentOffset, readLength)[0];
availableLength = (int) loadOperation.getDownloadedLengthFromOffset(currentOffset, readLength)[0];
if (availableLength == 0) {
FileLoader.getInstance(currentAccount).loadStreamFile(this, document, null, parentObject, currentOffset, false);
countDownLatch = new CountDownLatch(1);

View File

@ -43,7 +43,7 @@ public class FileUploadOperation {
private static final int initialRequestsSlowNetworkCount = 1;
private static final int maxUploadingKBytes = 1024 * 2;
private static final int maxUploadingSlowNetworkKBytes = 32;
private static final int maxUploadParts = (int) (FileLoader.MAX_FILE_SIZE / 1024 / 512);
private int maxRequestsCount;
private int uploadChunkSize = 64 * 1024;
private boolean slowNetwork;
@ -69,7 +69,7 @@ public class FileUploadOperation {
private boolean isBigFile;
private boolean forceSmallFile;
private String fileKey;
private int estimatedSize;
private long estimatedSize;
private int uploadStartTime;
private RandomAccessFile stream;
private boolean started;
@ -88,7 +88,7 @@ public class FileUploadOperation {
void didChangedUploadProgress(FileUploadOperation operation, long uploadedSize, long totalSize);
}
public FileUploadOperation(int instance, String location, boolean encrypted, int estimated, int type) {
public FileUploadOperation(int instance, String location, boolean encrypted, long estimated, int type) {
currentAccount = instance;
uploadingFilePath = location;
isEncrypted = encrypted;
@ -229,12 +229,12 @@ public class FileUploadOperation {
private void calcTotalPartsCount() {
if (uploadFirstPartLater) {
if (isBigFile) {
totalPartsCount = 1 + (int) ((totalFileSize - uploadChunkSize) + uploadChunkSize - 1) / uploadChunkSize;
totalPartsCount = 1 + (int) (((totalFileSize - uploadChunkSize) + uploadChunkSize - 1) / uploadChunkSize);
} else {
totalPartsCount = 1 + (int) ((totalFileSize - 1024) + uploadChunkSize - 1) / uploadChunkSize;
totalPartsCount = 1 + (int) (((totalFileSize - 1024) + uploadChunkSize - 1) / uploadChunkSize);
}
} else {
totalPartsCount = (int) (totalFileSize + uploadChunkSize - 1) / uploadChunkSize;
totalPartsCount = (int) ((totalFileSize + uploadChunkSize - 1) / uploadChunkSize);
}
}
@ -279,7 +279,11 @@ public class FileUploadOperation {
isBigFile = true;
}
uploadChunkSize = (int) Math.max(slowNetwork ? minUploadChunkSlowNetworkSize : minUploadChunkSize, (totalFileSize + 1024 * maxUploadParts - 1) / (1024 * maxUploadParts));
long maxUploadParts = MessagesController.getInstance(currentAccount).uploadMaxFileParts;
if (AccountInstance.getInstance(currentAccount).getUserConfig().isPremium() && totalFileSize > FileLoader.DEFAULT_MAX_FILE_SIZE) {
maxUploadParts = MessagesController.getInstance(currentAccount).uploadMaxFilePartsPremium;
}
uploadChunkSize = (int) Math.max(slowNetwork ? minUploadChunkSlowNetworkSize : minUploadChunkSize, (totalFileSize + 1024L * maxUploadParts - 1) / (1024L * maxUploadParts));
if (1024 % uploadChunkSize != 0) {
int chunkSize = 64;
while (uploadChunkSize > chunkSize) {

View File

@ -94,6 +94,7 @@ public class ImageLoader {
private LruCache<BitmapDrawable> memCache;
private LruCache<BitmapDrawable> wallpaperMemCache;
private LruCache<BitmapDrawable> lottieMemCache;
ArrayList<AnimatedFileDrawable> cachedAnimatedFileDrawables = new ArrayList<>();
private HashMap<String, CacheImage> imageLoadingByUrl = new HashMap<>();
private HashMap<String, CacheImage> imageLoadingByKeys = new HashMap<>();
private SparseArray<CacheImage> imageLoadingByTag = new SparseArray<>();
@ -460,13 +461,13 @@ public class ImageLoader {
private CacheImage cacheImage;
private RandomAccessFile fileOutputStream;
private int imageSize;
private long imageSize;
private long lastProgressTime;
private boolean canRetry = true;
private String overrideUrl;
private HttpURLConnection httpConnection;
public HttpImageTask(CacheImage cacheImage, int size) {
public HttpImageTask(CacheImage cacheImage, long size) {
this.cacheImage = cacheImage;
imageSize = size;
}
@ -812,7 +813,7 @@ public class ImageLoader {
if (cacheImage.imageLocation.photoSize instanceof TLRPC.TL_photoStrippedSize) {
TLRPC.TL_photoStrippedSize photoSize = (TLRPC.TL_photoStrippedSize) cacheImage.imageLocation.photoSize;
Bitmap bitmap = getStrippedPhotoBitmap(photoSize.bytes, cacheImage.filter);
Bitmap bitmap = getStrippedPhotoBitmap(photoSize.bytes, "b");
onPostExecute(bitmap != null ? new BitmapDrawable(bitmap) : null);
} else if (cacheImage.imageType == FileLoader.IMAGE_TYPE_THEME_PREVIEW) {
BitmapDrawable bitmapDrawable = null;
@ -823,8 +824,8 @@ public class ImageLoader {
}
onPostExecute(bitmapDrawable);
} else if (cacheImage.imageType == FileLoader.IMAGE_TYPE_SVG || cacheImage.imageType == FileLoader.IMAGE_TYPE_SVG_WHITE) {
int w = AndroidUtilities.dp(360);
int h = AndroidUtilities.dp(640);
int w = AndroidUtilities.displaySize.x;
int h = AndroidUtilities.displaySize.y;
if (cacheImage.filter != null) {
String[] args = cacheImage.filter.split("_");
if (args.length >= 2) {
@ -957,10 +958,22 @@ public class ImageLoader {
} else {
seekTo = 0;
}
if (AUTOPLAY_FILTER.equals(cacheImage.filter) && !(cacheImage.imageLocation.document instanceof TLRPC.TL_documentEncrypted)) {
boolean limitFps = false;
if (cacheImage.filter != null) {
String[] args = cacheImage.filter.split("_");
if (args.length >= 2) {
float w_filter = Float.parseFloat(args[0]);
float h_filter = Float.parseFloat(args[1]);
if (w_filter <= 90 && h_filter <= 90 && !cacheImage.filter.contains("nolimit")) {
limitFps = true;
}
}
}
if ((isAnimatedAvatar(cacheImage.filter) || AUTOPLAY_FILTER.equals(cacheImage.filter)) && !(cacheImage.imageLocation.document instanceof TLRPC.TL_documentEncrypted)) {
TLRPC.Document document = cacheImage.imageLocation.document instanceof TLRPC.Document ? cacheImage.imageLocation.document : null;
int size = document != null ? cacheImage.size : cacheImage.imageLocation.currentSize;
long size = document != null ? cacheImage.size : cacheImage.imageLocation.currentSize;
fileDrawable = new AnimatedFileDrawable(cacheImage.finalFilePath, false, size, document, document == null ? cacheImage.imageLocation : null, cacheImage.parentObject, seekTo, cacheImage.currentAccount, false);
fileDrawable.setIsWebmSticker(MessageObject.isWebM(document) || MessageObject.isVideoSticker(document) || isAnimatedAvatar(cacheImage.filter));
} else {
int w = 0;
@ -974,8 +987,10 @@ public class ImageLoader {
h = (int) (h_filter * AndroidUtilities.density);
}
}
fileDrawable = new AnimatedFileDrawable(cacheImage.finalFilePath, "d".equals(cacheImage.filter), 0, cacheImage.imageLocation.document, null, null, seekTo, cacheImage.currentAccount, false , w, h);
fileDrawable = new AnimatedFileDrawable(cacheImage.finalFilePath, "d".equals(cacheImage.filter), 0, cacheImage.imageLocation.document, null, null, seekTo, cacheImage.currentAccount, false, w, h);
fileDrawable.setIsWebmSticker(MessageObject.isWebM(cacheImage.imageLocation.document) || MessageObject.isVideoSticker(cacheImage.imageLocation.document) || isAnimatedAvatar(cacheImage.filter));
}
fileDrawable.setLimitFps(limitFps);
Thread.interrupted();
onPostExecute(fileDrawable);
} else {
@ -985,7 +1000,7 @@ public class ImageLoader {
boolean needInvert = false;
int orientation = 0;
File cacheFileFinal = cacheImage.finalFilePath;
boolean inEncryptedFile = cacheImage.secureDocument != null || cacheImage.encryptionKeyPath != null && cacheFileFinal != null && cacheFileFinal.getAbsolutePath().endsWith(".enc");
boolean inEncryptedFile = cacheImage.secureDocument != null || cacheImage.encryptionKeyPath != null && cacheFileFinal != null && (cacheFileFinal.getAbsolutePath().endsWith(".enc") || cacheFileFinal.getAbsolutePath().endsWith(".64enc"));
SecureDocumentKey secureDocumentKey;
byte[] secureDocumentHash;
if (cacheImage.secureDocument != null) {
@ -1502,7 +1517,7 @@ public class ImageLoader {
} else if (drawable instanceof AnimatedFileDrawable) {
AnimatedFileDrawable animatedFileDrawable = (AnimatedFileDrawable) drawable;
if (animatedFileDrawable.isWebmSticker) {
toSet = getFromLottieCahce(cacheImage.key);
toSet = getFromLottieCache(cacheImage.key);
if (toSet == null) {
lottieMemCache.put(cacheImage.key, animatedFileDrawable);
toSet = animatedFileDrawable;
@ -1557,6 +1572,10 @@ public class ImageLoader {
}
}
private boolean isAnimatedAvatar(String filter) {
return filter != null && filter.endsWith("avatar");
}
private BitmapDrawable getFromMemCache(String key) {
BitmapDrawable drawable = memCache.get(key);
if (drawable == null) {
@ -1566,7 +1585,7 @@ public class ImageLoader {
drawable = wallpaperMemCache.get(key);
}
if (drawable == null) {
drawable = getFromLottieCahce(key);
drawable = getFromLottieCache(key);
}
return drawable;
}
@ -1602,7 +1621,7 @@ public class ImageLoader {
protected SecureDocument secureDocument;
protected ImageLocation imageLocation;
protected Object parentObject;
protected int size;
protected long size;
protected int imageType;
protected int type;
@ -1728,7 +1747,7 @@ public class ImageLoader {
AnimatedFileDrawable fileDrawable = (AnimatedFileDrawable) image;
for (int a = 0; a < finalImageReceiverArray.size(); a++) {
ImageReceiver imgView = finalImageReceiverArray.get(a);
AnimatedFileDrawable toSet = (a == 0 ? fileDrawable : fileDrawable.makeCopy());
AnimatedFileDrawable toSet = fileDrawable;
if (imgView.setImageBitmapByKey(toSet, key, type, false, finalImageReceiverGuidsArray.get(a))) {
if (toSet == fileDrawable) {
imageSet = true;
@ -1813,7 +1832,9 @@ public class ImageLoader {
if (count == null || count == 0) {
Bitmap b = oldValue.getBitmap();
if (!b.isRecycled()) {
b.recycle();
ArrayList<Bitmap> bitmapToRecycle = new ArrayList<>();
bitmapToRecycle.add(b);
AndroidUtilities.recycleBitmaps(bitmapToRecycle);
}
}
}
@ -1833,7 +1854,9 @@ public class ImageLoader {
if (count == null || count == 0) {
Bitmap b = oldValue.getBitmap();
if (!b.isRecycled()) {
b.recycle();
ArrayList<Bitmap> bitmapToRecycle = new ArrayList<>();
bitmapToRecycle.add(b);
AndroidUtilities.recycleBitmaps(bitmapToRecycle);
}
}
}
@ -1846,14 +1869,26 @@ public class ImageLoader {
};
lottieMemCache = new LruCache<BitmapDrawable>(512 * 512 * 2 * 4 * 5) {
@Override
protected int sizeOf(String key, BitmapDrawable value) {
return value.getIntrinsicWidth() * value.getIntrinsicHeight() * 4 * 2;
}
@Override
public BitmapDrawable put(String key, BitmapDrawable value) {
if (value instanceof AnimatedFileDrawable) {
cachedAnimatedFileDrawables.add((AnimatedFileDrawable) value);
}
return super.put(key, value);
}
@Override
protected void entryRemoved(boolean evicted, String key, final BitmapDrawable oldValue, BitmapDrawable newValue) {
final Integer count = bitmapUseCounts.get(key);
if (oldValue instanceof AnimatedFileDrawable) {
cachedAnimatedFileDrawables.remove((AnimatedFileDrawable) oldValue);
}
if (count == null || count == 0) {
if (oldValue instanceof AnimatedFileDrawable) {
((AnimatedFileDrawable) oldValue).recycle();
@ -1911,11 +1946,22 @@ public class ImageLoader {
public void fileDidLoaded(final String location, final File finalFile, Object parentObject, final int type) {
fileProgresses.remove(location);
AndroidUtilities.runOnUIThread(() -> {
if (SharedConfig.saveToGallery && finalFile != null && (location.endsWith(".mp4") || location.endsWith(".jpg"))) {
if (SharedConfig.saveToGalleryFlags != 0 && finalFile != null && (location.endsWith(".mp4") || location.endsWith(".jpg"))) {
if (parentObject instanceof MessageObject) {
MessageObject messageObject = (MessageObject) parentObject;
// test add only for peer dialogs
if (messageObject.getDialogId() >= 0) {
long dialogId = messageObject.getDialogId();
int flag;
if (dialogId >= 0) {
flag = SharedConfig.SAVE_TO_GALLERY_FLAG_PEER;
} else {
if (ChatObject.isChannelAndNotMegaGroup(MessagesController.getInstance(currentAccount).getChat(-dialogId))) {
flag = SharedConfig.SAVE_TO_GALLERY_FLAG_CHANNELS;
} else {
flag = SharedConfig.SAVE_TO_GALLERY_FLAG_GROUP;
}
}
if ((SharedConfig.saveToGalleryFlags & flag) != 0) {
AndroidUtilities.addMediaToGallery(finalFile.toString());
}
}
@ -2094,7 +2140,7 @@ public class ImageLoader {
if (type == FileLoader.MEDIA_DIR_IMAGE) {
srcFile = new File(from, "000000000_999999_temp.f");
dstFile = new File(to, "000000000_999999.f");
} else if (type == FileLoader.MEDIA_DIR_DOCUMENT) {
} else if (type == FileLoader.MEDIA_DIR_DOCUMENT || type == FileLoader.MEDIA_DIR_FILES) {
srcFile = new File(from, "000000000_999999_temp.f");
dstFile = new File(to, "000000000_999999.f");
} else if (type == FileLoader.MEDIA_DIR_AUDIO) {
@ -2223,7 +2269,7 @@ public class ImageLoader {
public boolean isInMemCache(String key, boolean animated) {
if (animated) {
return getFromLottieCahce(key) != null;
return getFromLottieCache(key) != null;
} else {
return getFromMemCache(key) != null;
}
@ -2382,7 +2428,7 @@ public class ImageLoader {
imageLoadQueue.postRunnable(() -> forceLoadingImages.remove(key));
}
private void createLoadOperationForImageReceiver(final ImageReceiver imageReceiver, final String key, final String url, final String ext, final ImageLocation imageLocation, final String filter, final int size, final int cacheType, final int type, final int thumb, int guid) {
private void createLoadOperationForImageReceiver(final ImageReceiver imageReceiver, final String key, final String url, final String ext, final ImageLocation imageLocation, final String filter, final long size, final int cacheType, final int type, final int thumb, int guid) {
if (imageReceiver == null || url == null || key == null || imageLocation == null) {
return;
}
@ -2398,7 +2444,7 @@ public class ImageLoader {
final int finalTag = TAG;
final boolean finalIsNeedsQualityThumb = imageReceiver.isNeedsQualityThumb();
final Object parentObject = imageReceiver.getParentObject();
final TLRPC.Document qualityDocument = imageReceiver.getQulityThumbDocument();
final TLRPC.Document qualityDocument = imageReceiver.getQualityThumbDocument();
final boolean shouldGenerateQualityThumb = imageReceiver.isShouldGenerateQualityThumb();
final int currentAccount = imageReceiver.getCurrentAccount();
final boolean currentKeyQuality = type == ImageReceiver.TYPE_IMAGE && imageReceiver.isCurrentKeyQuality();
@ -2470,12 +2516,12 @@ public class ImageLoader {
MessageObject parentMessageObject = (MessageObject) parentObject;
parentDocument = parentMessageObject.getDocument();
localPath = parentMessageObject.messageOwner.attachPath;
cachePath = FileLoader.getPathToMessage(parentMessageObject.messageOwner);
cachePath = FileLoader.getInstance(currentAccount).getPathToMessage(parentMessageObject.messageOwner);
mediaType = parentMessageObject.getMediaType();
bigThumb = false;
} else if (qualityDocument != null) {
parentDocument = qualityDocument;
cachePath = FileLoader.getPathToAttach(parentDocument, true);
cachePath = FileLoader.getInstance(currentAccount).getPathToAttach(parentDocument, true);
if (MessageObject.isVideoDocument(parentDocument)) {
mediaType = FileLoader.MEDIA_DIR_VIDEO;
} else {
@ -2554,14 +2600,14 @@ public class ImageLoader {
}
if (cacheFile == null) {
int fileSize = 0;
long fileSize = 0;
if (imageLocation.photoSize instanceof TLRPC.TL_photoStrippedSize || imageLocation.photoSize instanceof TLRPC.TL_photoPathSize) {
onlyCache = true;
} else if (imageLocation.secureDocument != null) {
img.secureDocument = imageLocation.secureDocument;
onlyCache = img.secureDocument.secureFile.dc_id == Integer.MIN_VALUE;
cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), url);
} else if (!AUTOPLAY_FILTER.equals(filter) && (cacheType != 0 || size <= 0 || imageLocation.path != null || isEncrypted)) {
} else if (!(AUTOPLAY_FILTER.equals(filter) || isAnimatedAvatar(filter)) && (cacheType != 0 || size <= 0 || imageLocation.path != null || isEncrypted)) {
cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), url);
if (cacheFile.exists()) {
cacheFileExists = true;
@ -2600,7 +2646,7 @@ public class ImageLoader {
} else {
cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_DOCUMENT), url);
}
if (AUTOPLAY_FILTER.equals(filter) && !cacheFile.exists()) {
if ((isAnimatedAvatar(filter) || AUTOPLAY_FILTER.equals(filter)) && !cacheFile.exists()) {
cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), document.dc_id + "_" + document.id + ".temp");
}
if (document instanceof DocumentObject.ThemeDocument) {
@ -2631,11 +2677,11 @@ public class ImageLoader {
} else {
cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_IMAGE), url);
}
if (AUTOPLAY_FILTER.equals(filter) && imageLocation.location != null && !cacheFile.exists()) {
if (isAnimatedAvatar(filter) || AUTOPLAY_FILTER.equals(filter) && imageLocation.location != null && !cacheFile.exists()) {
cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), imageLocation.location.volume_id + "_" + imageLocation.location.local_id + ".temp");
}
}
if (AUTOPLAY_FILTER.equals(filter)) {
if (AUTOPLAY_FILTER.equals(filter) || isAnimatedAvatar(filter)) {
img.imageType = FileLoader.IMAGE_TYPE_ANIMATION;
img.size = fileSize;
onlyCache = true;
@ -2752,8 +2798,8 @@ public class ImageLoader {
if (mediaKey != null) {
ImageLocation mediaLocation = imageReceiver.getMediaLocation();
Drawable drawable;
if (useLottieMemChache(mediaLocation)) {
drawable = getFromLottieCahce(mediaKey);
if (useLottieMemCache(mediaLocation, mediaKey)) {
drawable = getFromLottieCache(mediaKey);
} else {
drawable = memCache.get(mediaKey);
if (drawable != null) {
@ -2785,8 +2831,8 @@ public class ImageLoader {
if (!imageSet && imageKey != null) {
ImageLocation imageLocation = imageReceiver.getImageLocation();
Drawable drawable = null;
if (useLottieMemChache(imageLocation)) {
drawable = getFromLottieCahce(imageKey);
if (useLottieMemCache(imageLocation, imageKey)) {
drawable = getFromLottieCache(imageKey);
}
if (drawable == null) {
drawable = memCache.get(imageKey);
@ -2820,8 +2866,8 @@ public class ImageLoader {
if (thumbKey != null) {
ImageLocation thumbLocation = imageReceiver.getThumbLocation();
Drawable drawable;
if (useLottieMemChache(thumbLocation)) {
drawable = getFromLottieCahce(thumbKey);
if (useLottieMemCache(thumbLocation, thumbKey)) {
drawable = getFromLottieCache(thumbKey);
} else {
drawable = memCache.get(thumbKey);
if (drawable != null) {
@ -2852,7 +2898,7 @@ public class ImageLoader {
boolean qualityThumb = false;
Object parentObject = imageReceiver.getParentObject();
TLRPC.Document qualityDocument = imageReceiver.getQulityThumbDocument();
TLRPC.Document qualityDocument = imageReceiver.getQualityThumbDocument();
ImageLocation thumbLocation = imageReceiver.getThumbLocation();
String thumbFilter = imageReceiver.getThumbFilter();
ImageLocation mediaLocation = imageReceiver.getMediaLocation();
@ -3034,7 +3080,7 @@ public class ImageLoader {
}
}
private BitmapDrawable getFromLottieCahce(String imageKey) {
private BitmapDrawable getFromLottieCache(String imageKey) {
BitmapDrawable drawable = lottieMemCache.get(imageKey);
if (drawable instanceof AnimatedFileDrawable) {
if (((AnimatedFileDrawable) drawable).isRecycled()) {
@ -3045,8 +3091,8 @@ public class ImageLoader {
return drawable;
}
private boolean useLottieMemChache(ImageLocation imageLocation) {
return imageLocation != null && (MessageObject.isAnimatedStickerDocument(imageLocation.document, true) || imageLocation.imageType == FileLoader.IMAGE_TYPE_LOTTIE || MessageObject.isVideoSticker(imageLocation.document));
private boolean useLottieMemCache(ImageLocation imageLocation, String key) {
return imageLocation != null && (MessageObject.isAnimatedStickerDocument(imageLocation.document, true) || imageLocation.imageType == FileLoader.IMAGE_TYPE_LOTTIE || MessageObject.isVideoSticker(imageLocation.document)) || isAnimatedAvatar(key);
}
private void httpFileLoadError(final String location) {
@ -3434,7 +3480,7 @@ public class ImageLoader {
if (photoSize == null || photoSize.bytes != null && photoSize.bytes.length != 0) {
return;
}
File file = FileLoader.getPathToAttach(photoSize, true);
File file = FileLoader.getInstance(UserConfig.selectedAccount).getPathToAttach(photoSize, true);
try {
RandomAccessFile f = new RandomAccessFile(file, "r");
int len = (int) f.length();
@ -3491,16 +3537,17 @@ public class ImageLoader {
fileDir = location.volume_id != Integer.MIN_VALUE ? FileLoader.getDirectory(FileLoader.MEDIA_DIR_IMAGE) : FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE);
}
final File cacheFile = new File(fileDir, fileName);
if (compressFormat == Bitmap.CompressFormat.JPEG && progressive && BuildVars.DEBUG_VERSION) {
photoSize.size = Utilities.saveProgressiveJpeg(scaledBitmap, scaledBitmap.getWidth(), scaledBitmap.getHeight(), scaledBitmap.getRowBytes(), quality, cacheFile.getAbsolutePath());
} else {
FileOutputStream stream = new FileOutputStream(cacheFile);
scaledBitmap.compress(compressFormat, quality, stream);
if (!cache) {
photoSize.size = (int) stream.getChannel().size();
}
stream.close();
//TODO was crash in DEBUG_PRIVATE
// if (compressFormat == Bitmap.CompressFormat.JPEG && progressive && BuildVars.DEBUG_VERSION) {
// photoSize.size = Utilities.saveProgressiveJpeg(scaledBitmap, scaledBitmap.getWidth(), scaledBitmap.getHeight(), scaledBitmap.getRowBytes(), quality, cacheFile.getAbsolutePath());
// } else {
FileOutputStream stream = new FileOutputStream(cacheFile);
scaledBitmap.compress(compressFormat, quality, stream);
if (!cache) {
photoSize.size = (int) stream.getChannel().size();
}
stream.close();
// }
if (cache) {
ByteArrayOutputStream stream2 = new ByteArrayOutputStream();
scaledBitmap.compress(compressFormat, quality, stream2);
@ -3604,7 +3651,7 @@ public class ImageLoader {
photoSize.location.volume_id = Integer.MIN_VALUE;
photoSize.location.local_id = SharedConfig.getLastLocalId();
}
File file = FileLoader.getPathToAttach(photoSize, true);
File file = FileLoader.getInstance(UserConfig.selectedAccount).getPathToAttach(photoSize, true);
boolean isEncrypted = false;
if (MessageObject.shouldEncryptPhotoOrVideo(message)) {
file = new File(file.getAbsolutePath() + ".enc");
@ -3718,7 +3765,7 @@ public class ImageLoader {
TLRPC.PhotoSize photoSize = findPhotoCachedSize(message);
if (photoSize != null && photoSize.bytes != null && photoSize.bytes.length != 0) {
File file = FileLoader.getPathToAttach(photoSize, true);
File file = FileLoader.getInstance(UserConfig.selectedAccount).getPathToAttach(photoSize, true);
TLRPC.TL_photoSize newPhotoSize = new TLRPC.TL_photoSize_layer127();
newPhotoSize.w = photoSize.w;
@ -3767,7 +3814,7 @@ public class ImageLoader {
}
Point point = ChatMessageCell.getMessageSize(w, h);
String key = String.format(Locale.US, "%s_false@%d_%d_b", ImageLocation.getStippedKey(message, message, size), (int) (point.x / AndroidUtilities.density), (int) (point.y / AndroidUtilities.density));
String key = String.format(Locale.US, "%s_false@%d_%d_b", ImageLocation.getStrippedKey(message, message, size), (int) (point.x / AndroidUtilities.density), (int) (point.y / AndroidUtilities.density));
if (!getInstance().isInMemCache(key, false)) {
Bitmap b = getStrippedPhotoBitmap(size.bytes, null);
if (b != null) {
@ -3786,6 +3833,12 @@ public class ImageLoader {
return null;
}
public void onFragmentStackChanged() {
for (int i = 0; i < cachedAnimatedFileDrawables.size(); i++) {
cachedAnimatedFileDrawables.get(i).repeatCount = 0;
}
}
public DispatchQueue getCacheOutQueue() {
return cacheOutQueue;
}

View File

@ -30,7 +30,7 @@ public class ImageLocation {
public int thumbVersion;
public int currentSize;
public long currentSize;
public long photoId;
public long documentId;
@ -107,6 +107,7 @@ public class ImageLocation {
public static final int TYPE_BIG = 0;
public static final int TYPE_SMALL = 1;
public static final int TYPE_STRIPPED = 2;
public static final int TYPE_VIDEO_THUMB = 3;
public static ImageLocation getForUserOrChat(TLObject object, int type) {
if (object instanceof TLRPC.User) {
@ -129,6 +130,23 @@ public class ImageLocation {
if (user == null || user.access_hash == 0 || user.photo == null) {
return null;
}
if (type == TYPE_VIDEO_THUMB) {
int currentAccount = UserConfig.selectedAccount;
if (MessagesController.getInstance(currentAccount).isPremiumUser(user) && user.photo.has_video) {
final TLRPC.UserFull userFull = MessagesController.getInstance(currentAccount).getUserFull(user.id);
if (userFull != null && userFull.profile_photo !=null && userFull.profile_photo.video_sizes != null && !userFull.profile_photo.video_sizes.isEmpty()) {
TLRPC.VideoSize videoSize = userFull.profile_photo.video_sizes.get(0);
for (int i = 0; i < userFull.profile_photo.video_sizes.size(); i++) {
if ("p".equals(userFull.profile_photo.video_sizes.get(i).type)) {
videoSize = userFull.profile_photo.video_sizes.get(i);
break;
}
}
return ImageLocation.getForPhoto(videoSize, userFull.profile_photo);
}
}
return null;
}
if (type == TYPE_STRIPPED) {
if (user.photo.stripped_thumb == null) {
return null;
@ -223,7 +241,11 @@ public class ImageLocation {
return null;
}
ImageLocation location = getForPhoto(videoSize.location, videoSize.size, null, document, null, TYPE_SMALL, document.dc_id, null, videoSize.type);
location.imageType = FileLoader.IMAGE_TYPE_ANIMATION;
if ("f".equals(videoSize.type)) {
location.imageType = FileLoader.IMAGE_TYPE_LOTTIE;
} else {
location.imageType = FileLoader.IMAGE_TYPE_ANIMATION;
}
return location;
}
@ -301,7 +323,7 @@ public class ImageLocation {
return imageLocation;
}
public static String getStippedKey(Object parentObject, Object fullObject, Object strippedObject) {
public static String getStrippedKey(Object parentObject, Object fullObject, Object strippedObject) {
if (parentObject instanceof TLRPC.WebPage) {
if (fullObject instanceof ImageLocation) {
ImageLocation imageLocation = (ImageLocation) fullObject;
@ -341,7 +363,7 @@ public class ImageLocation {
return secureDocument.secureFile.dc_id + "_" + secureDocument.secureFile.id;
} else if (photoSize instanceof TLRPC.TL_photoStrippedSize || photoSize instanceof TLRPC.TL_photoPathSize) {
if (photoSize.bytes.length > 0) {
return getStippedKey(parentObject, fullObject, photoSize);
return getStrippedKey(parentObject, fullObject, photoSize);
}
} else if (location != null) {
return location.volume_id + "_" + location.local_id;
@ -365,7 +387,7 @@ public class ImageLocation {
return key != null;
}
public int getSize() {
public long getSize() {
if (photoSize != null) {
return photoSize.size;
} else if (secureDocument != null) {

View File

@ -20,6 +20,7 @@ import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
@ -132,7 +133,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
public ImageLocation mediaLocation;
public String mediaFilter;
public Drawable thumb;
public int size;
public long size;
public int cacheType;
public Object parentObject;
public String ext;
@ -221,7 +222,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
private int currentGuid;
private int currentSize;
private long currentSize;
private int currentCacheType;
private boolean allowStartAnimation = true;
private boolean allowStartLottieAnimation = true;
@ -278,6 +279,9 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
private String uniqKeyPrefix;
private ArrayList<Runnable> loadingOperations = new ArrayList<>();
private boolean attachedToWindow;
private boolean videoThumbIsSame;
public int animatedFileDrawableRepeatMaxCount;
public ImageReceiver() {
this(null);
@ -319,11 +323,11 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
setImage(imageLocation, imageFilter, null, null, thumb, 0, ext, parentObject, cacheType);
}
public void setImage(ImageLocation imageLocation, String imageFilter, Drawable thumb, int size, String ext, Object parentObject, int cacheType) {
public void setImage(ImageLocation imageLocation, String imageFilter, Drawable thumb, long size, String ext, Object parentObject, int cacheType) {
setImage(imageLocation, imageFilter, null, null, thumb, size, ext, parentObject, cacheType);
}
public void setImage(String imagePath, String imageFilter, Drawable thumb, String ext, int size) {
public void setImage(String imagePath, String imageFilter, Drawable thumb, String ext, long size) {
setImage(ImageLocation.getForPath(imagePath), imageFilter, null, null, thumb, size, ext, null, 1);
}
@ -331,26 +335,46 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
setImage(imageLocation, imageFilter, thumbLocation, thumbFilter, null, 0, ext, parentObject, cacheType);
}
public void setImage(ImageLocation imageLocation, String imageFilter, ImageLocation thumbLocation, String thumbFilter, int size, String ext, Object parentObject, int cacheType) {
public void setImage(ImageLocation imageLocation, String imageFilter, ImageLocation thumbLocation, String thumbFilter, long size, String ext, Object parentObject, int cacheType) {
setImage(imageLocation, imageFilter, thumbLocation, thumbFilter, null, size, ext, parentObject, cacheType);
}
public void setForUserOrChat(TLObject object, Drawable avatarDrawable) {
setForUserOrChat(object, avatarDrawable, null);
}
public void setForUserOrChat(TLObject object, Drawable avatarDrawable, Object parentObject) {
setForUserOrChat(object, avatarDrawable, null, false);
}
public void setForUserOrChat(TLObject object, Drawable avatarDrawable, Object parentObject, boolean animationEnabled) {
if (parentObject == null) {
parentObject = object;
}
setUseRoundForThumbDrawable(true);
BitmapDrawable strippedBitmap = null;
boolean hasStripped = false;
ImageLocation videoLocation = null;
if (object instanceof TLRPC.User) {
TLRPC.User user = (TLRPC.User) object;
if (user.photo != null) {
strippedBitmap = user.photo.strippedBitmap;
hasStripped = user.photo.stripped_thumb != null;
if (MessagesController.getInstance(currentAccount).isPremiumUser(user) && user.photo.has_video && animationEnabled) {
final TLRPC.UserFull userFull = MessagesController.getInstance(currentAccount).getUserFull(user.id);
if (userFull == null) {
MessagesController.getInstance(currentAccount).loadFullUser(user, currentGuid, false);
}
if (userFull != null && userFull.profile_photo != null && userFull.profile_photo.video_sizes != null && !userFull.profile_photo.video_sizes.isEmpty()) {
TLRPC.VideoSize videoSize = userFull.profile_photo.video_sizes.get(0);
for (int i = 0; i < userFull.profile_photo.video_sizes.size(); i++) {
if ("p".equals(userFull.profile_photo.video_sizes.get(i).type)) {
videoSize = userFull.profile_photo.video_sizes.get(i);
break;
}
}
videoLocation = ImageLocation.getForPhoto(videoSize, userFull.profile_photo);
}
}
}
} else if (object instanceof TLRPC.Chat) {
TLRPC.Chat chat = (TLRPC.Chat) object;
@ -359,24 +383,32 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
hasStripped = chat.photo.stripped_thumb != null;
}
}
if (strippedBitmap != null) {
setImage(ImageLocation.getForUserOrChat(object, ImageLocation.TYPE_SMALL), "50_50", strippedBitmap, null, parentObject, 0);
} else if (hasStripped) {
setImage(ImageLocation.getForUserOrChat(object, ImageLocation.TYPE_SMALL), "50_50", ImageLocation.getForUserOrChat(object, ImageLocation.TYPE_STRIPPED), "50_50_b", avatarDrawable, parentObject, 0);
ImageLocation location = ImageLocation.getForUserOrChat(object, ImageLocation.TYPE_SMALL);
String filter = "50_50";
if (videoLocation != null) {
setImage(videoLocation, "avatar", location, filter, null, null,strippedBitmap, 0, null, parentObject, 0);
animatedFileDrawableRepeatMaxCount = 3;
} else {
setImage(ImageLocation.getForUserOrChat(object, ImageLocation.TYPE_SMALL), "50_50", avatarDrawable, null, parentObject, 0);
if (strippedBitmap != null) {
setImage(location, filter, strippedBitmap, null, parentObject, 0);
} else if (hasStripped) {
setImage(location, filter, ImageLocation.getForUserOrChat(object, ImageLocation.TYPE_STRIPPED), "50_50_b", avatarDrawable, parentObject, 0);
} else {
setImage(location, filter, avatarDrawable, null, parentObject, 0);
}
}
}
public void setImage(ImageLocation fileLocation, String fileFilter, ImageLocation thumbLocation, String thumbFilter, Drawable thumb, Object parentObject, int cacheType) {
setImage(null, null, fileLocation, fileFilter, thumbLocation, thumbFilter, thumb, 0, null, parentObject, cacheType);
}
public void setImage(ImageLocation fileLocation, String fileFilter, ImageLocation thumbLocation, String thumbFilter, Drawable thumb, int size, String ext, Object parentObject, int cacheType) {
public void setImage(ImageLocation fileLocation, String fileFilter, ImageLocation thumbLocation, String thumbFilter, Drawable thumb, long size, String ext, Object parentObject, int cacheType) {
setImage(null, null, fileLocation, fileFilter, thumbLocation, thumbFilter, thumb, size, ext, parentObject, cacheType);
}
public void setImage(ImageLocation mediaLocation, String mediaFilter, ImageLocation imageLocation, String imageFilter, ImageLocation thumbLocation, String thumbFilter, Drawable thumb, int size, String ext, Object parentObject, int cacheType) {
public void setImage(ImageLocation mediaLocation, String mediaFilter, ImageLocation imageLocation, String imageFilter, ImageLocation thumbLocation, String thumbFilter, Drawable thumb, long size, String ext, Object parentObject, int cacheType) {
if (ignoreImageSet) {
return;
}
@ -445,6 +477,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
if (imageKey == null && imageLocation != null) {
imageLocation = null;
}
animatedFileDrawableRepeatMaxCount = 0;
currentKeyQuality = false;
if (imageKey == null && needsQualityThumb && (parentObject instanceof MessageObject || qulityThumbDocument != null)) {
TLRPC.Document document = qulityThumbDocument != null ? qulityThumbDocument : ((MessageObject) parentObject).getDocument();
@ -497,6 +530,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
if (currentMediaDrawable != null) {
if (currentMediaDrawable instanceof AnimatedFileDrawable) {
((AnimatedFileDrawable) currentMediaDrawable).stop();
((AnimatedFileDrawable) currentMediaDrawable).removeParent(this);
}
recycleBitmap(thumbKey, TYPE_THUMB);
recycleBitmap(null, TYPE_CROSSFDADE);
@ -712,11 +746,11 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
AnimatedFileDrawable fileDrawable = (AnimatedFileDrawable) bitmap;
fileDrawable.setParentView(parentView);
if (attachedToWindow) {
fileDrawable.addParent(parentView);
fileDrawable.addParent(this);
}
fileDrawable.setUseSharedQueue(useSharedAnimationQueue || fileDrawable.isWebmSticker);
if (allowStartAnimation && currentOpenedLayerFlags == 0) {
fileDrawable.start();
fileDrawable.checkRepeat();
}
fileDrawable.setAllowDecodeSingleFrame(allowDecodeSingleFrame);
} else if (bitmap instanceof RLottieDrawable) {
@ -733,6 +767,9 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
updateDrawableRadius(bitmap);
currentMediaLocation = null;
currentMediaFilter = null;
if (currentMediaDrawable instanceof AnimatedFileDrawable) {
((AnimatedFileDrawable) currentMediaDrawable).removeParent(this);
}
currentMediaDrawable = null;
currentMediaKey = null;
mediaShader = null;
@ -834,7 +871,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
AnimatedFileDrawable animatedFileDrawable = (AnimatedFileDrawable) drawable;
animatedFileDrawable.setRoundRadius(roundRadius);
} else if (bitmapDrawable.getBitmap() != null) {
setDrawableShader(drawable, new BitmapShader(bitmapDrawable.getBitmap(), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
setDrawableShader(drawable, new BitmapShader(bitmapDrawable.getBitmap(), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
}
} else {
setDrawableShader(drawable, null);
@ -882,7 +919,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
AnimatedFileDrawable animatedFileDrawable = getAnimation();
if (animatedFileDrawable != null) {
animatedFileDrawable.removeParent(parentView);
animatedFileDrawable.removeParent(this);
}
}
@ -918,10 +955,10 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
}
AnimatedFileDrawable animatedFileDrawable = getAnimation();
if (animatedFileDrawable != null && parentView != null) {
animatedFileDrawable.addParent(parentView);
animatedFileDrawable.addParent(this);
}
if (animatedFileDrawable != null && allowStartAnimation && currentOpenedLayerFlags == 0) {
animatedFileDrawable.start();
animatedFileDrawable.checkRepeat();
if (parentView != null) {
parentView.invalidate();
}
@ -1040,12 +1077,13 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
drawRegion.set(imageX + (imageW - bitmapW) / 2, imageY + (imageH - bitmapH) / 2, imageX + (imageW + bitmapW) / 2, imageY + (imageH + bitmapH) / 2);
if (isVisible) {
roundPaint.setShader(shader);
shaderMatrix.reset();
shaderMatrix.setTranslate(drawRegion.left, drawRegion.top);
shaderMatrix.setTranslate((int) drawRegion.left, (int) drawRegion.top);
float toScale = 1.0f / scale;
shaderMatrix.preScale(1.0f / scale, 1.0f / scale);
shader.setLocalMatrix(shaderMatrix);
roundPaint.setShader(shader);
roundPaint.setAlpha(alpha);
roundRect.set(drawRegion);
@ -1101,7 +1139,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
}
if (isVisible) {
shaderMatrix.reset();
shaderMatrix.setTranslate(drawRegion.left + sideClip, drawRegion.top + sideClip);
shaderMatrix.setTranslate((int) (drawRegion.left + sideClip), (int) (drawRegion.top + sideClip));
if (orientation == 90) {
shaderMatrix.preRotate(90);
shaderMatrix.preTranslate(0, -drawRegion.width());
@ -1789,6 +1827,15 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
imageH = height;
}
public void setImageCoords(Rect bounds) {
if (bounds != null) {
imageX = bounds.left;
imageY = bounds.top;
imageW = bounds.width();
imageH = bounds.height();
}
}
public void setSideClip(float value) {
sideClip = value;
}
@ -1857,7 +1904,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
return currentThumbKey;
}
public int getSize() {
public long getSize() {
return currentSize;
}
@ -1957,7 +2004,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
qulityThumbDocument = document;
}
public TLRPC.Document getQulityThumbDocument() {
public TLRPC.Document getQualityThumbDocument() {
return qulityThumbDocument;
}
@ -2109,6 +2156,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
if (!key.equals(currentImageKey)) {
return false;
}
boolean allowCrossFade = true;
if (!(drawable instanceof AnimatedFileDrawable)) {
ImageLoader.getInstance().incrementUseCount(currentImageKey);
} else {
@ -2117,6 +2165,9 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
if (animatedFileDrawable.isWebmSticker) {
ImageLoader.getInstance().incrementUseCount(currentImageKey);
}
if (videoThumbIsSame) {
allowCrossFade = !animatedFileDrawable.hasBitmap();
}
}
currentImageDrawable = drawable;
if (drawable instanceof ExtendedBitmapDrawable) {
@ -2124,14 +2175,14 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
}
updateDrawableRadius(drawable);
if (isVisible && (!memCache && !forcePreview || forceCrossfade) && crossfadeDuration != 0) {
boolean allowCorssfade = true;
if (allowCrossFade && isVisible && (!memCache && !forcePreview || forceCrossfade) && crossfadeDuration != 0) {
boolean allowCrossfade = true;
if (currentMediaDrawable instanceof AnimatedFileDrawable && ((AnimatedFileDrawable) currentMediaDrawable).hasBitmap()) {
allowCorssfade = false;
allowCrossfade = false;
} else if (currentImageDrawable instanceof RLottieDrawable) {
allowCorssfade = staticThumbDrawable instanceof LoadingStickerDrawable || staticThumbDrawable instanceof SvgHelper.SvgDrawable || staticThumbDrawable instanceof Emoji.EmojiDrawable;
allowCrossfade = staticThumbDrawable instanceof LoadingStickerDrawable || staticThumbDrawable instanceof SvgHelper.SvgDrawable || staticThumbDrawable instanceof Emoji.EmojiDrawable;
}
if (allowCorssfade && (currentThumbDrawable != null || staticThumbDrawable != null || forceCrossfade)) {
if (allowCrossfade && (currentThumbDrawable != null || staticThumbDrawable != null || forceCrossfade)) {
if (currentThumbDrawable != null && staticThumbDrawable != null) {
previousAlpha = currentAlpha;
} else {
@ -2155,7 +2206,16 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
AnimatedFileDrawable animatedFileDrawable = (AnimatedFileDrawable) drawable;
animatedFileDrawable.setStartEndTime(startTime, endTime);
if (animatedFileDrawable.isWebmSticker) {
ImageLoader.getInstance().incrementUseCount(currentImageKey);
ImageLoader.getInstance().incrementUseCount(currentMediaKey);
}
if (videoThumbIsSame && (currentThumbDrawable instanceof AnimatedFileDrawable || currentImageDrawable instanceof AnimatedFileDrawable)) {
long currentTimestamp = 0;
if (currentThumbDrawable instanceof AnimatedFileDrawable) {
currentTimestamp = ((AnimatedFileDrawable) currentThumbDrawable).getLastFrameTimestamp();
} else if (currentImageDrawable instanceof AnimatedFileDrawable) {
currentTimestamp = ((AnimatedFileDrawable) currentImageDrawable).getLastFrameTimestamp();
}
animatedFileDrawable.seekTo(currentTimestamp, true, true);
}
}
currentMediaDrawable = drawable;
@ -2225,10 +2285,10 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
AnimatedFileDrawable fileDrawable = (AnimatedFileDrawable) drawable;
fileDrawable.setUseSharedQueue(useSharedAnimationQueue);
if (attachedToWindow) {
fileDrawable.addParent(parentView);
fileDrawable.addParent(this);
}
if (allowStartAnimation && currentOpenedLayerFlags == 0) {
fileDrawable.start();
fileDrawable.checkRepeat();
}
fileDrawable.setAllowDecodeSingleFrame(allowDecodeSingleFrame);
animationReadySent = false;
@ -2291,8 +2351,8 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
lottieDrawable.removeParentView(parentView);
}
if (image instanceof AnimatedFileDrawable) {
AnimatedFileDrawable lottieDrawable = (AnimatedFileDrawable) image;
lottieDrawable.removeParent(parentView);
AnimatedFileDrawable animatedFileDrawable = (AnimatedFileDrawable) image;
animatedFileDrawable.removeParent(this);
}
if (key != null && (newKey == null || !newKey.equals(key)) && image != null) {
if (image instanceof RLottieDrawable) {
@ -2315,14 +2375,18 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
fileDrawable.stop();
}
} else {
fileDrawable.recycle();
if (fileDrawable.getParents().isEmpty()) {
fileDrawable.recycle();
}
}
} else if (image instanceof BitmapDrawable) {
Bitmap bitmap = ((BitmapDrawable) image).getBitmap();
boolean canDelete = ImageLoader.getInstance().decrementUseCount(key);
if (!ImageLoader.getInstance().isInMemCache(key, false)) {
if (canDelete) {
bitmap.recycle();
ArrayList<Bitmap> bitmapToRecycle = new ArrayList<>();
bitmapToRecycle.add(bitmap);
AndroidUtilities.recycleBitmaps(bitmapToRecycle);
}
}
}
@ -2400,7 +2464,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
}
AnimatedFileDrawable animatedFileDrawable = getAnimation();
if (allowStartAnimation && animatedFileDrawable != null) {
animatedFileDrawable.start();
animatedFileDrawable.checkRepeat();
if (parentView != null) {
parentView.invalidate();
}
@ -2440,4 +2504,17 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
ImageLoader.getInstance().moveToFront(currentImageKey);
ImageLoader.getInstance().moveToFront(currentThumbKey);
}
public View getParentView() {
return parentView;
}
public boolean isAttachedToWindow() {
return attachedToWindow;
}
public void setVideoThumbIsSame(boolean b) {
videoThumbIsSame = b;
}
}

View File

@ -1161,14 +1161,17 @@ public class LocaleController {
return getString(param, key + "_other", resourceId);
}
public static String formatPluralString(String key, int plural) {
public static String formatPluralString(String key, int plural, Object... args) {
if (key == null || key.length() == 0 || getInstance().currentPluralRules == null) {
return "LOC_ERR:" + key;
}
String param = getInstance().stringForQuantity(getInstance().currentPluralRules.quantityForNumber(plural));
param = key + "_" + param;
int resourceId = ApplicationLoader.applicationContext.getResources().getIdentifier(param, "string", ApplicationLoader.applicationContext.getPackageName());
return formatString(param, key + "_other", resourceId, plural);
Object[] argsWithPlural = new Object[args.length + 1];
argsWithPlural[0] = plural;
System.arraycopy(args, 0, argsWithPlural, 1, args.length);
return formatString(param, key + "_other", resourceId, argsWithPlural);
}
public static String formatPluralStringComma(String key, int plural) {
@ -1798,15 +1801,17 @@ public class LocaleController {
return text;
}
public static String formatDateOnline(long date) {
public static String formatDateOnline(long date, boolean[] madeShorter) {
try {
date *= 1000;
Calendar rightNow = Calendar.getInstance();
int day = rightNow.get(Calendar.DAY_OF_YEAR);
int year = rightNow.get(Calendar.YEAR);
int hour = rightNow.get(Calendar.HOUR_OF_DAY);
rightNow.setTimeInMillis(date);
int dateDay = rightNow.get(Calendar.DAY_OF_YEAR);
int dateYear = rightNow.get(Calendar.YEAR);
int dateHour = rightNow.get(Calendar.HOUR_OF_DAY);
PersianDate persianDate = null;
if (usePersianCalendar) {
persianDate = new PersianDate(date);
@ -1823,7 +1828,15 @@ public class LocaleController {
return LocaleController.formatPluralString("LastSeenHours", (int) Math.ceil(diff / 60.0f));
}*/
} else if (dateDay + 1 == day && year == dateYear) {
return LocaleController.formatString("LastSeenFormatted", R.string.LastSeenFormatted, LocaleController.formatString("YesterdayAtFormatted", R.string.YesterdayAtFormatted, getInstance().formatterDay.format(new Date(date))));
if (madeShorter != null) {
madeShorter[0] = true;
if (hour <= 6 && dateHour > 18 && is24HourFormat) {
return LocaleController.formatString("LastSeenFormatted", R.string.LastSeenFormatted, getInstance().formatterDay.format(new Date(date)));
}
return LocaleController.formatString("YesterdayAtFormatted", R.string.YesterdayAtFormatted, getInstance().formatterDay.format(new Date(date)));
} else {
return LocaleController.formatString("LastSeenFormatted", R.string.LastSeenFormatted, LocaleController.formatString("YesterdayAtFormatted", R.string.YesterdayAtFormatted, getInstance().formatterDay.format(new Date(date))));
}
} else if (Math.abs(System.currentTimeMillis() - date) < 31536000000L) {
if (usePersianCalendar) {
String format = LocaleController.formatString("formatDateAtTime", R.string.formatDateAtTime, persianDate.getPersianMonthDay(), getInstance().formatterDay.format(new Date(date)));
@ -2100,6 +2113,10 @@ public class LocaleController {
}
public static String formatUserStatus(int currentAccount, TLRPC.User user, boolean[] isOnline) {
return formatUserStatus(currentAccount, user, isOnline, null);
}
public static String formatUserStatus(int currentAccount, TLRPC.User user, boolean[] isOnline, boolean[] madeShorter) {
if (user != null && user.status != null && user.status.expires == 0) {
if (user.status instanceof TLRPC.TL_userStatusRecently) {
user.status.expires = -100;
@ -2136,7 +2153,7 @@ public class LocaleController {
} else if (user.status.expires == -102) {
return getString("WithinAMonth", R.string.WithinAMonth);
} else {
return formatDateOnline(user.status.expires);
return formatDateOnline(user.status.expires, madeShorter);
}
}
}
@ -2282,6 +2299,10 @@ public class LocaleController {
}
public void loadRemoteLanguages(final int currentAccount) {
loadRemoteLanguages(currentAccount, true);
}
public void loadRemoteLanguages(final int currentAccount, boolean applyCurrent) {
if (loadingRemoteLanguages) {
return;
}
@ -2349,7 +2370,9 @@ public class LocaleController {
}
saveOtherLanguages();
NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.suggestedLangpack);
applyLanguage(currentLocaleInfo, true, false, currentAccount);
if (applyCurrent) {
applyLanguage(currentLocaleInfo, true, false, currentAccount);
}
});
}
}, ConnectionsManager.RequestFlagWithoutLogin);

View File

@ -406,9 +406,9 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
@Override
public String getPath() {
if (photoSize != null) {
return FileLoader.getPathToAttach(photoSize, true).getAbsolutePath();
return FileLoader.getInstance(UserConfig.selectedAccount).getPathToAttach(photoSize, true).getAbsolutePath();
} else if (document != null) {
return FileLoader.getPathToAttach(document, true).getAbsolutePath();
return FileLoader.getInstance(UserConfig.selectedAccount).getPathToAttach(document, true).getAbsolutePath();
} else {
return ImageLoader.getHttpFilePath(imageUrl, "jpg").getAbsolutePath();
}
@ -430,9 +430,9 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
public String getPathToAttach() {
if (photoSize != null) {
return FileLoader.getPathToAttach(photoSize, true).getAbsolutePath();
return FileLoader.getInstance(UserConfig.selectedAccount).getPathToAttach(photoSize, true).getAbsolutePath();
} else if (document != null) {
return FileLoader.getPathToAttach(document, true).getAbsolutePath();
return FileLoader.getInstance(UserConfig.selectedAccount).getPathToAttach(document, true).getAbsolutePath();
} else {
return imageUrl;
}
@ -600,7 +600,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
private ArrayList<ByteBuffer> recordBuffers = new ArrayList<>();
private ByteBuffer fileBuffer;
public int recordBufferSize = 1280;
public int sampleRate = NekoConfig.increaseVoiceMessageQuality.Bool() ? 48000 : 16000;
public int sampleRate = 48000;
private int sendAfterDone;
private boolean sendAfterDoneNotify;
private int sendAfterDoneScheduleDate;
@ -905,7 +905,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
recordQueue.postRunnable(() -> {
try {
sampleRate = NekoConfig.increaseVoiceMessageQuality.Bool() ? 48000 : 16000;
sampleRate = 48000;
int minBuferSize = AudioRecord.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
if (minBuferSize <= 0) {
minBuferSize = 1280;
@ -1329,7 +1329,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
playMessage(playingMessageObject);
} else if (audioInfo == null) {
try {
File cacheFile = FileLoader.getPathToMessage(playingMessageObject.messageOwner);
File cacheFile = FileLoader.getInstance(UserConfig.selectedAccount).getPathToMessage(playingMessageObject.messageOwner);
audioInfo = AudioInfo.getAudioInfo(cacheFile);
} catch (Exception e) {
FileLog.e(e);
@ -2382,7 +2382,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
file = null;
}
}
final File cacheFile = file != null ? file : FileLoader.getPathToMessage(nextAudio.messageOwner);
final File cacheFile = file != null ? file : FileLoader.getInstance(currentAccount).getPathToMessage(nextAudio.messageOwner);
boolean exist = cacheFile.exists();
if (cacheFile != file && !cacheFile.exists()) {
FileLoader.getInstance(currentAccount).loadFile(nextAudio.getDocument(), nextAudio, 0, 0);
@ -2421,7 +2421,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
file = null;
}
}
final File cacheFile = file != null ? file : FileLoader.getPathToMessage(nextAudio.messageOwner);
final File cacheFile = file != null ? file : FileLoader.getInstance(currentAccount).getPathToMessage(nextAudio.messageOwner);
boolean exist = cacheFile.exists();
if (cacheFile != file && !cacheFile.exists() && nextAudio.isMusic()) {
FileLoader.getInstance(currentAccount).loadFile(nextAudio.getDocument(), nextAudio, 0, 0);
@ -2785,7 +2785,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
document.mime_type = "sound/ogg";
document.file_reference = sound.fileReference;
document.dc_id = accountInstance.getConnectionsManager().getCurrentDatacenterId();
File file = FileLoader.getPathToAttach(document, true);
File file = FileLoader.getInstance(accountInstance.getCurrentAccount()).getPathToAttach(document, true);
if (file.exists()) {
if (loadOnly) {
return;
@ -2859,10 +2859,28 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
});
}
private static long volumeBarLastTimeShown;
public void checkVolumeBarUI() {
try {
final long now = System.currentTimeMillis();
if (Math.abs(now - volumeBarLastTimeShown) < 5000) {
return;
}
AudioManager audioManager = (AudioManager) ApplicationLoader.applicationContext.getSystemService(Context.AUDIO_SERVICE);
int stream = useFrontSpeaker ? AudioManager.STREAM_VOICE_CALL : AudioManager.STREAM_MUSIC;
int volume = audioManager.getStreamVolume(stream);
if (volume == 0) {
audioManager.adjustStreamVolume(stream, volume, AudioManager.FLAG_SHOW_UI);
volumeBarLastTimeShown = now;
}
} catch (Exception ignore) {}
}
public boolean playMessage(final MessageObject messageObject) {
if (messageObject == null) {
return false;
}
checkVolumeBarUI();
if ((audioPlayer != null || videoPlayer != null) && isSamePlayingMessage(messageObject)) {
if (isPaused) {
resumeAudio(messageObject);
@ -2897,7 +2915,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
file = null;
}
}
final File cacheFile = file != null ? file : FileLoader.getPathToMessage(messageObject.messageOwner);
final File cacheFile = file != null ? file : FileLoader.getInstance(messageObject.currentAccount).getPathToMessage(messageObject.messageOwner);
boolean canStream = SharedConfig.streamMedia && (messageObject.isMusic() || messageObject.isRoundVideo() || messageObject.isVideo() && messageObject.canStreamVideo()) && !DialogObject.isEncryptedDialog(messageObject.getDialogId());
if (cacheFile != file && !(exists = cacheFile.exists()) && !canStream) {
FileLoader.getInstance(messageObject.currentAccount).loadFile(messageObject.getDocument(), messageObject, 0, 0);
@ -3515,7 +3533,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
recordingAudioFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), FileLoader.getAttachFileName(recordingAudio));
try {
if (startRecord(recordingAudioFile.getAbsolutePath(), NekoConfig.increaseVoiceMessageQuality.Bool() ? 48000 : 16000) == 0) {
if (startRecord(recordingAudioFile.getAbsolutePath(), sampleRate) == 0) {
AndroidUtilities.runOnUIThread(() -> {
recordStartRunnable = null;
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.recordStartError, guid);
@ -3564,7 +3582,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
public void generateWaveform(MessageObject messageObject) {
final String id = messageObject.getId() + "_" + messageObject.getDialogId();
final String path = FileLoader.getPathToMessage(messageObject.messageOwner).getAbsolutePath();
final String path = FileLoader.getInstance(messageObject.currentAccount).getPathToMessage(messageObject.messageOwner).getAbsolutePath();
if (generatingWaveform.containsKey(id)) {
return;
}
@ -3732,7 +3750,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
}
if (path == null || path.length() == 0) {
path = FileLoader.getPathToMessage(message.messageOwner).toString();
path = FileLoader.getInstance(currentAccount.getCurrentAccount()).getPathToMessage(message.messageOwner).toString();
}
File sourceFile = new File(path);
if (!sourceFile.exists()) {
@ -3786,7 +3804,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
}
if (path == null || path.length() == 0) {
path = FileLoader.getPathToMessage(message.messageOwner).toString();
path = FileLoader.getInstance(currentAccount.getCurrentAccount()).getPathToMessage(message.messageOwner).toString();
}
File sourceFile = new File(path);
if (!sourceFile.exists()) {
@ -4292,10 +4310,24 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
f = AndroidUtilities.getSharingDirectory();
f.mkdirs();
f = new File(f, name);
if (AndroidUtilities.isInternalUri(Uri.fromFile(f))) {
return null;
}
int count = 0;
do {
f = AndroidUtilities.getSharingDirectory();
if (count == 0) {
f = new File(f, name);
} else {
int lastDotIndex = name.lastIndexOf(".");
if (lastDotIndex > 0) {
f = new File(f, name.substring(0, lastDotIndex) + " (" + count + ")" + name.substring(lastDotIndex));
} else {
f = new File(f, name + " (" + count + ")");
}
}
count++;
} while (f.exists());
inputStream = ApplicationLoader.applicationContext.getContentResolver().openInputStream(uri);
if (inputStream instanceof FileInputStream) {
FileInputStream fileInputStream = (FileInputStream) inputStream;
@ -4833,7 +4865,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
resultWidth = temp;
}
// if (framerate > 40 && (Math.min(resultHeight, resultWidth) <= 480)) {
// if (!info.shouldLimitFps && framerate > 40 && (Math.min(resultHeight, resultWidth) <= 480)) {
// framerate = 30;
// }
//

View File

@ -32,6 +32,8 @@ import android.text.Spanned;
import android.text.SpannedString;
import android.text.TextUtils;
import android.text.style.CharacterStyle;
import android.text.style.URLSpan;
import android.text.util.Linkify;
import android.util.SparseArray;
import android.widget.Toast;
@ -57,8 +59,11 @@ import org.telegram.tgnet.SerializedData;
import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.BaseFragment;
import org.telegram.ui.ActionBar.EmojiThemes;
import org.telegram.ui.Components.AvatarDrawable;
import org.telegram.ui.Components.Bulletin;
import org.telegram.ui.Components.ChatThemeBottomSheet;
import org.telegram.ui.Components.Reactions.ReactionsEffectOverlay;
import org.telegram.ui.Components.StickerSetBulletinLayout;
import org.telegram.ui.Components.StickersArchiveAlert;
import org.telegram.ui.Components.TextStyleSpan;
@ -164,6 +169,7 @@ public class MediaDataController extends BaseController {
}
loadStickersByEmojiOrName(AndroidUtilities.STICKERS_PLACEHOLDER_PACK_NAME, false, true);
loadEmojiThemes();
ringtoneDataStore = new RingtoneDataStore(currentAccount);
}
@ -188,6 +194,10 @@ public class MediaDataController extends BaseController {
private boolean isLoadingReactions;
private int reactionsUpdateDate;
private TLRPC.TL_help_premiumPromo premiumPromo;
private boolean isLoadingPremiumPromo;
private int premiumPromoUpdateDate;
private ArrayList<TLRPC.TL_messages_stickerSet>[] stickerSets = new ArrayList[]{new ArrayList<>(), new ArrayList<>(), new ArrayList<>(0), new ArrayList<>(), new ArrayList<>()};
private LongSparseArray<TLRPC.Document>[] stickersByIds = new LongSparseArray[]{new LongSparseArray<>(), new LongSparseArray<>(), new LongSparseArray<>(), new LongSparseArray<>(), new LongSparseArray<>()};
private LongSparseArray<TLRPC.TL_messages_stickerSet> stickerSetsById = new LongSparseArray<>();
@ -223,6 +233,7 @@ public class MediaDataController extends BaseController {
private long loadFeaturedHash;
private int loadFeaturedDate;
public boolean loadFeaturedPremium;
private ArrayList<TLRPC.StickerSetCovered> featuredStickerSets = new ArrayList<>();
private LongSparseArray<TLRPC.StickerSetCovered> featuredStickerSetsById = new LongSparseArray<>();
private ArrayList<Long> unreadStickerSets = new ArrayList<>();
@ -232,6 +243,10 @@ public class MediaDataController extends BaseController {
private TLRPC.Document greetingsSticker;
public final RingtoneDataStore ringtoneDataStore;
public final ArrayList<ChatThemeBottomSheet.ChatThemeItem> defaultEmojiThemes = new ArrayList<>();
public final ArrayList<TLRPC.Document> premiumPreviewStickers = new ArrayList<>();
boolean previewStickersLoading;
public void cleanup() {
for (int a = 0; a < recentStickers.length; a++) {
@ -315,6 +330,16 @@ public class MediaDataController extends BaseController {
}
}
public void checkPremiumPromo() {
if (!isLoadingPremiumPromo && Math.abs(System.currentTimeMillis() / 1000 - premiumPromoUpdateDate) >= 60 * 60) {
loadPremiumPromo(true);
}
}
public TLRPC.TL_help_premiumPromo getPremiumPromo() {
return premiumPromo;
}
public TLRPC.TL_attachMenuBots getAttachMenuBots() {
return attachMenuBots;
}
@ -365,7 +390,7 @@ public class MediaDataController extends BaseController {
}
}
private void processLoadedMenuBots(TLRPC.TL_attachMenuBots bots, long hash, int date, boolean cache) {
public void processLoadedMenuBots(TLRPC.TL_attachMenuBots bots, long hash, int date, boolean cache) {
if (bots != null && date != 0) {
attachMenuBots = bots;
menuBotsUpdateHash = hash;
@ -411,6 +436,86 @@ public class MediaDataController extends BaseController {
});
}
public void loadPremiumPromo(boolean cache) {
isLoadingPremiumPromo = true;
if (cache) {
getMessagesStorage().getStorageQueue().postRunnable(() -> {
SQLiteCursor c = null;
int date = 0;
TLRPC.TL_help_premiumPromo premiumPromo = null;
try {
c = getMessagesStorage().getDatabase().queryFinalized("SELECT data, date FROM premium_promo");
if (c.next()) {
NativeByteBuffer data = c.byteBufferValue(0);
if (data != null) {
premiumPromo = TLRPC.TL_help_premiumPromo.TLdeserialize(data, data.readInt32(false), true);
data.reuse();
}
date = c.intValue(1);
}
} catch (Exception e) {
FileLog.e(e, false);
} finally {
if (c != null) {
c.dispose();
}
}
if (premiumPromo != null) {
processLoadedPremiumPromo(premiumPromo, date, true);
}
});
} else {
TLRPC.TL_help_getPremiumPromo req = new TLRPC.TL_help_getPremiumPromo();
getConnectionsManager().sendRequest(req, (response, error) -> {
int date = (int) (System.currentTimeMillis() / 1000);
if (response instanceof TLRPC.TL_help_premiumPromo) {
TLRPC.TL_help_premiumPromo r = (TLRPC.TL_help_premiumPromo) response;
processLoadedPremiumPromo(r, date, false);
}
});
}
}
private void processLoadedPremiumPromo(TLRPC.TL_help_premiumPromo premiumPromo, int date, boolean cache) {
this.premiumPromo = premiumPromo;
premiumPromoUpdateDate = date;
getMessagesController().putUsers(premiumPromo.users, cache);
AndroidUtilities.runOnUIThread(() -> getNotificationCenter().postNotificationName(NotificationCenter.premiumPromoUpdated));
if (!cache) {
putPremiumPromoToCache(premiumPromo, date);
} else if (Math.abs(System.currentTimeMillis() / 1000 - date) >= 60 * 60 * 24 || BuildVars.DEBUG_PRIVATE_VERSION) {
loadPremiumPromo(false);
}
}
private void putPremiumPromoToCache(TLRPC.TL_help_premiumPromo premiumPromo, int date) {
getMessagesStorage().getStorageQueue().postRunnable(() -> {
try {
if (premiumPromo != null) {
getMessagesStorage().getDatabase().executeFast("DELETE FROM premium_promo").stepThis().dispose();
SQLitePreparedStatement state = getMessagesStorage().getDatabase().executeFast("REPLACE INTO premium_promo VALUES(?, ?)");
state.requery();
NativeByteBuffer data = new NativeByteBuffer(premiumPromo.getObjectSize());
premiumPromo.serializeToStream(data);
state.bindByteBuffer(1, data);
state.bindInteger(2, date);
state.step();
data.reuse();
state.dispose();
} else {
SQLitePreparedStatement state = getMessagesStorage().getDatabase().executeFast("UPDATE premium_promo SET date = ?");
state.requery();
state.bindInteger(1, date);
state.step();
state.dispose();
}
} catch (Exception e) {
FileLog.e(e);
}
});
}
public List<TLRPC.TL_availableReaction> getReactionsList() {
return reactionsList;
}
@ -463,7 +568,7 @@ public class MediaDataController extends BaseController {
}
}
private void processLoadedReactions(List<TLRPC.TL_availableReaction> reactions, int hash, int date, boolean cache) {
public void processLoadedReactions(List<TLRPC.TL_availableReaction> reactions, int hash, int date, boolean cache) {
if (reactions != null && date != 0) {
reactionsList.clear();
reactionsMap.clear();
@ -484,29 +589,27 @@ public class MediaDataController extends BaseController {
for (int i = 0; i < reactions.size(); i++) {
ImageReceiver imageReceiver = new ImageReceiver();
TLRPC.TL_availableReaction reaction = reactions.get(i);
imageReceiver.setImage(ImageLocation.getForDocument(reaction.activate_animation), null, null, null, 0, 1);
imageReceiver.setImage(ImageLocation.getForDocument(reaction.activate_animation), null, null, null, 0, FileLoader.PRELOAD_CACHE_TYPE);
ImageLoader.getInstance().loadImageForImageReceiver(imageReceiver);
imageReceiver = new ImageReceiver();
imageReceiver.setImage(ImageLocation.getForDocument(reaction.appear_animation), "60_60_nolimit", null, null, 0, 1);
imageReceiver.setImage(ImageLocation.getForDocument(reaction.appear_animation), "60_60_nolimit", null, null, 0, FileLoader.PRELOAD_CACHE_TYPE);
ImageLoader.getInstance().loadImageForImageReceiver(imageReceiver);
int size = ReactionsEffectOverlay.sizeForBigReaction();
imageReceiver = new ImageReceiver();
imageReceiver.setImage(ImageLocation.getForDocument(reaction.around_animation), size + "_" + size, null, null, 0, FileLoader.PRELOAD_CACHE_TYPE);
ImageLoader.getInstance().loadImageForImageReceiver(imageReceiver);
imageReceiver = new ImageReceiver();
imageReceiver.setImage(ImageLocation.getForDocument(reaction.around_animation), null, null, null, 0, 1);
ImageLoader.getInstance().loadImageForImageReceiver(imageReceiver);
imageReceiver = new ImageReceiver();
imageReceiver.setImage(ImageLocation.getForDocument(reaction.center_icon), null, null, null, 0, 1);
ImageLoader.getInstance().loadImageForImageReceiver(imageReceiver);
imageReceiver = new ImageReceiver();
imageReceiver.setImage(ImageLocation.getForDocument(reaction.static_icon), null, null, null, 0, 1);
imageReceiver.setImage(ImageLocation.getForDocument(reaction.center_icon), null, null, null, 0, FileLoader.PRELOAD_CACHE_TYPE);
ImageLoader.getInstance().loadImageForImageReceiver(imageReceiver);
}
NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.reactionsDidLoad);
});
}
isLoadingReactions = false;
if (!cache) {
putReactionsToCache(reactions, hash, date);
} else if (Math.abs(System.currentTimeMillis() / 1000 - date) >= 60 * 60) {
@ -582,6 +685,24 @@ public class MediaDataController extends BaseController {
return false;
}
public void clearRecentStickers() {
TLRPC.TL_messages_clearRecentStickers req = new TLRPC.TL_messages_clearRecentStickers();
getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(()-> {
if (response instanceof TLRPC.TL_boolTrue) {
getMessagesStorage().getStorageQueue().postRunnable(() -> {
try {
getMessagesStorage().getDatabase().executeFast("DELETE FROM web_recent_v3 WHERE type = " + 3).stepThis().dispose();
} catch (Exception e) {
FileLog.e(e);
}
});
recentStickers[TYPE_IMAGE].clear();
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.recentDocumentsDidLoad, false, TYPE_IMAGE);
}
}));
}
public void addRecentSticker(int type, Object parentObject, TLRPC.Document document, int date, boolean remove) {
if (type == TYPE_GREETINGS || !MessageObject.isStickerDocument(document) && !MessageObject.isAnimatedStickerDocument(document, true)) {
return;
@ -606,7 +727,8 @@ public class MediaDataController extends BaseController {
if (remove) {
NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.showBulletin, Bulletin.TYPE_STICKER, document, StickerSetBulletinLayout.TYPE_REMOVED_FROM_FAVORITES);
} else {
NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.showBulletin, Bulletin.TYPE_STICKER, document, StickerSetBulletinLayout.TYPE_ADDED_TO_FAVORITES);
boolean replace = recentStickers[type].size() > getMessagesController().maxFaveStickersCount;
NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.showBulletin, Bulletin.TYPE_STICKER, document, replace ? StickerSetBulletinLayout.TYPE_REPLACED_TO_FAVORITES : StickerSetBulletinLayout.TYPE_ADDED_TO_FAVORITES);
}
TLRPC.TL_messages_faveSticker req = new TLRPC.TL_messages_faveSticker();
req.id = new TLRPC.TL_inputDocument();
@ -719,7 +841,7 @@ public class MediaDataController extends BaseController {
return false;
}
public void addRecentGif(TLRPC.Document document, int date) {
public void addRecentGif(TLRPC.Document document, int date, boolean showReplaceBulletin) {
if (document == null) {
return;
}
@ -736,7 +858,7 @@ public class MediaDataController extends BaseController {
if (!found) {
recentGifs.add(0, document);
}
if (recentGifs.size() > getMessagesController().maxRecentGifsCount) {
if ((recentGifs.size() > getMessagesController().savedGifsLimitDefault && !UserConfig.getInstance(currentAccount).isPremium()) || recentGifs.size() > getMessagesController().savedGifsLimitPremium) {
TLRPC.Document old = recentGifs.remove(recentGifs.size() - 1);
getMessagesStorage().getStorageQueue().postRunnable(() -> {
try {
@ -745,6 +867,11 @@ public class MediaDataController extends BaseController {
FileLog.e(e);
}
});
if (showReplaceBulletin) {
AndroidUtilities.runOnUIThread(() -> {
NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.showBulletin, Bulletin.TYPE_STICKER, document, StickerSetBulletinLayout.TYPE_REPLACED_TO_FAVORITES_GIFS);
});
}
}
ArrayList<TLRPC.Document> arrayList = new ArrayList<>();
arrayList.add(document);
@ -930,6 +1057,9 @@ public class MediaDataController extends BaseController {
}
public TLRPC.Document getEmojiAnimatedSticker(CharSequence message) {
if (message == null) {
return null;
}
String emoji = message.toString().replace("\uFE0F", "");
ArrayList<TLRPC.TL_messages_stickerSet> arrayList = getStickerSets(MediaDataController.TYPE_EMOJI);
for (int a = 0, N = arrayList.size(); a < N; a++) {
@ -999,6 +1129,34 @@ public class MediaDataController extends BaseController {
return value != null ? value : "";
}
public static boolean canShowAttachMenuBotForTarget(TLRPC.TL_attachMenuBot bot, String target) {
for (TLRPC.AttachMenuPeerType peerType : bot.peer_types) {
if ((peerType instanceof TLRPC.TL_attachMenuPeerTypeSameBotPM || peerType instanceof TLRPC.TL_attachMenuPeerTypeBotPM) && target.equals("bots") ||
peerType instanceof TLRPC.TL_attachMenuPeerTypeBroadcast && target.equals("channels") ||
peerType instanceof TLRPC.TL_attachMenuPeerTypeChat && target.equals("groups") ||
peerType instanceof TLRPC.TL_attachMenuPeerTypePM && target.equals("users")) {
return true;
}
}
return false;
}
public static boolean canShowAttachMenuBot(TLRPC.TL_attachMenuBot bot, TLObject peer) {
TLRPC.User user = peer instanceof TLRPC.User ? (TLRPC.User) peer : null;
TLRPC.Chat chat = peer instanceof TLRPC.Chat ? (TLRPC.Chat) peer : null;
for (TLRPC.AttachMenuPeerType peerType : bot.peer_types) {
if (peerType instanceof TLRPC.TL_attachMenuPeerTypeSameBotPM && user != null && user.bot && user.id == bot.bot_id ||
peerType instanceof TLRPC.TL_attachMenuPeerTypeBotPM && user != null && user.bot && user.id != bot.bot_id ||
peerType instanceof TLRPC.TL_attachMenuPeerTypePM && user != null && !user.bot ||
peerType instanceof TLRPC.TL_attachMenuPeerTypeChat && chat != null && !ChatObject.isChannelAndNotMegaGroup(chat) ||
peerType instanceof TLRPC.TL_attachMenuPeerTypeBroadcast && chat != null && ChatObject.isChannelAndNotMegaGroup(chat)) {
return true;
}
}
return false;
}
@Nullable
public static TLRPC.TL_attachMenuBotIcon getAnimatedAttachMenuBotIcon(@NonNull TLRPC.TL_attachMenuBot bot) {
for (TLRPC.TL_attachMenuBotIcon icon : bot.icons) {
@ -1391,9 +1549,10 @@ public class MediaDataController extends BaseController {
ArrayList<Long> unread = new ArrayList<>();
int date = 0;
long hash = 0;
boolean premium = false;
SQLiteCursor cursor = null;
try {
cursor = getMessagesStorage().getDatabase().queryFinalized("SELECT data, unread, date, hash FROM stickers_featured WHERE 1");
cursor = getMessagesStorage().getDatabase().queryFinalized("SELECT data, unread, date, hash, premium FROM stickers_featured WHERE 1");
if (cursor.next()) {
NativeByteBuffer data = cursor.byteBufferValue(0);
if (data != null) {
@ -1415,6 +1574,7 @@ public class MediaDataController extends BaseController {
}
date = cursor.intValue(2);
hash = calcFeaturedStickersHash(newStickerArray);
premium = cursor.intValue(4) == 1;
}
} catch (Throwable e) {
FileLog.e(e);
@ -1423,7 +1583,7 @@ public class MediaDataController extends BaseController {
cursor.dispose();
}
}
processLoadedFeaturedStickers(newStickerArray, unread, true, date, hash);
processLoadedFeaturedStickers(newStickerArray, unread, premium, true, date, hash);
});
} else {
TLRPC.TL_messages_getFeaturedStickers req = new TLRPC.TL_messages_getFeaturedStickers();
@ -1431,15 +1591,15 @@ public class MediaDataController extends BaseController {
getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> {
if (response instanceof TLRPC.TL_messages_featuredStickers) {
TLRPC.TL_messages_featuredStickers res = (TLRPC.TL_messages_featuredStickers) response;
processLoadedFeaturedStickers(res.sets, res.unread, false, (int) (System.currentTimeMillis() / 1000), res.hash);
processLoadedFeaturedStickers(res.sets, res.unread, res.premium,false, (int) (System.currentTimeMillis() / 1000), res.hash);
} else {
processLoadedFeaturedStickers(null, null, false, (int) (System.currentTimeMillis() / 1000), req.hash);
processLoadedFeaturedStickers(null, null, false, false, (int) (System.currentTimeMillis() / 1000), req.hash);
}
}));
}
}
private void processLoadedFeaturedStickers(ArrayList<TLRPC.StickerSetCovered> res, ArrayList<Long> unreadStickers, boolean cache, int date, long hash) {
private void processLoadedFeaturedStickers(ArrayList<TLRPC.StickerSetCovered> res, ArrayList<Long> unreadStickers, boolean premium, boolean cache, int date, long hash) {
AndroidUtilities.runOnUIThread(() -> {
loadingFeaturedStickers = false;
featuredStickersLoaded = true;
@ -1468,7 +1628,7 @@ public class MediaDataController extends BaseController {
}
if (!cache) {
putFeaturedStickersToCache(stickerSetsNew, unreadStickers, date, hash);
putFeaturedStickersToCache(stickerSetsNew, unreadStickers, date, hash, premium);
}
AndroidUtilities.runOnUIThread(() -> {
unreadStickerSets = unreadStickers;
@ -1476,6 +1636,7 @@ public class MediaDataController extends BaseController {
featuredStickerSets = stickerSetsNew;
loadFeaturedHash = hash;
loadFeaturedDate = date;
loadFeaturedPremium = premium;
loadStickers(TYPE_FEATURED, true, false);
getNotificationCenter().postNotificationName(NotificationCenter.featuredStickersDidLoad);
});
@ -1484,17 +1645,17 @@ public class MediaDataController extends BaseController {
}
} else {
AndroidUtilities.runOnUIThread(() -> loadFeaturedDate = date);
putFeaturedStickersToCache(null, null, date, 0);
putFeaturedStickersToCache(null, null, date, 0, premium);
}
});
}
private void putFeaturedStickersToCache(ArrayList<TLRPC.StickerSetCovered> stickers, ArrayList<Long> unreadStickers, int date, long hash) {
private void putFeaturedStickersToCache(ArrayList<TLRPC.StickerSetCovered> stickers, ArrayList<Long> unreadStickers, int date, long hash, boolean premium) {
ArrayList<TLRPC.StickerSetCovered> stickersFinal = stickers != null ? new ArrayList<>(stickers) : null;
getMessagesStorage().getStorageQueue().postRunnable(() -> {
try {
if (stickersFinal != null) {
SQLitePreparedStatement state = getMessagesStorage().getDatabase().executeFast("REPLACE INTO stickers_featured VALUES(?, ?, ?, ?, ?)");
SQLitePreparedStatement state = getMessagesStorage().getDatabase().executeFast("REPLACE INTO stickers_featured VALUES(?, ?, ?, ?, ?, ?)");
state.requery();
int size = 4;
for (int a = 0; a < stickersFinal.size(); a++) {
@ -1515,6 +1676,7 @@ public class MediaDataController extends BaseController {
state.bindByteBuffer(3, data2);
state.bindInteger(4, date);
state.bindLong(5, hash);
state.bindInteger(6, premium ? 1 : 0);
state.step();
data.reuse();
data2.reuse();
@ -1564,7 +1726,7 @@ public class MediaDataController extends BaseController {
unreadStickerSets.clear();
loadFeaturedHash = calcFeaturedStickersHash(featuredStickerSets);
getNotificationCenter().postNotificationName(NotificationCenter.featuredStickersDidLoad);
putFeaturedStickersToCache(featuredStickerSets, unreadStickerSets, loadFeaturedDate, loadFeaturedHash);
putFeaturedStickersToCache(featuredStickerSets, unreadStickerSets, loadFeaturedDate, loadFeaturedHash, loadFeaturedPremium);
if (query) {
TLRPC.TL_messages_readFeaturedStickers req = new TLRPC.TL_messages_readFeaturedStickers();
getConnectionsManager().sendRequest(req, (response, error) -> {
@ -1600,7 +1762,7 @@ public class MediaDataController extends BaseController {
readingStickerSets.remove(id);
loadFeaturedHash = calcFeaturedStickersHash(featuredStickerSets);
getNotificationCenter().postNotificationName(NotificationCenter.featuredStickersDidLoad);
putFeaturedStickersToCache(featuredStickerSets, unreadStickerSets, loadFeaturedDate, loadFeaturedHash);
putFeaturedStickersToCache(featuredStickerSets, unreadStickerSets, loadFeaturedDate, loadFeaturedHash, loadFeaturedPremium);
}, 1000);
}
@ -2058,7 +2220,7 @@ public class MediaDataController extends BaseController {
}
});
Utilities.stageQueue.postRunnable(() -> {
if (cache && (res == null || Math.abs(System.currentTimeMillis() / 1000 - date) >= 60 * 60) || !cache && res == null && hash == 0) {
if (cache && (res == null || BuildVars.DEBUG_PRIVATE_VERSION || Math.abs(System.currentTimeMillis() / 1000 - date) >= 60 * 60) || !cache && res == null && hash == 0) {
AndroidUtilities.runOnUIThread(() -> {
if (res != null && hash != 0) {
loadHash[type] = hash;
@ -2989,10 +3151,11 @@ public class MediaDataController extends BaseController {
continue;
}
MessageObject messageObject = new MessageObject(currentAccount, message, usersDict, true, true);
MessageObject messageObject = new MessageObject(currentAccount, message, usersDict, true, false);
messageObject.createStrippedThumb();
objects.add(messageObject);
}
getFileLoader().checkMediaExistance(objects);
AndroidUtilities.runOnUIThread(() -> {
int totalCount = res.count;
@ -3428,7 +3591,7 @@ public class MediaDataController extends BaseController {
Bitmap bitmap = null;
if (photo != null) {
try {
File path = FileLoader.getPathToAttach(photo, true);
File path = getFileLoader().getPathToAttach(photo, true);
bitmap = BitmapFactory.decodeFile(path.toString());
if (bitmap != null) {
int size = AndroidUtilities.dp(48);
@ -3902,7 +4065,7 @@ public class MediaDataController extends BaseController {
if (overrideAvatar || photo != null) {
try {
if (!overrideAvatar) {
File path = FileLoader.getPathToAttach(photo, true);
File path = getFileLoader().getPathToAttach(photo, true);
bitmap = BitmapFactory.decodeFile(path.toString());
}
if (overrideAvatar || bitmap != null) {
@ -5133,6 +5296,26 @@ public class MediaDataController extends BaseController {
}
}
}
if (spannable instanceof Spannable) {
AndroidUtilities.addLinks((Spannable) spannable, Linkify.WEB_URLS);
URLSpan[] spansUrl = spannable.getSpans(0, message[0].length(), URLSpan.class);
if (spansUrl != null && spansUrl.length > 0) {
if (entities == null) {
entities = new ArrayList<>();
}
for (int b = 0; b < spansUrl.length; b++) {
if (spansUrl[b] instanceof URLSpanReplacement || spansUrl[b] instanceof URLSpanUserMention) {
continue;
}
TLRPC.TL_messageEntityUrl entity = new TLRPC.TL_messageEntityUrl();
entity.offset = spannable.getSpanStart(spansUrl[b]);
entity.length = Math.min(spannable.getSpanEnd(spansUrl[b]), message[0].length()) - entity.offset;
entity.url = spansUrl[b].getURL();
entities.add(entity);
}
}
}
}
CharSequence cs = message[0];
@ -5153,12 +5336,23 @@ public class MediaDataController extends BaseController {
int offset = 0;
while (m.find()) {
String gr = m.group(1);
cs = cs.subSequence(0, m.start() - offset) + gr + cs.subSequence(m.end() - offset, cs.length());
boolean allowEntity = true;
if (cs instanceof Spannable) {
// check if it is inside a link: do not convert __ ** to styles inside links
URLSpan[] spansUrl = ((Spannable) cs).getSpans(m.start() - offset, m.end() - offset, URLSpan.class);
if (spansUrl != null && spansUrl.length > 0) {
allowEntity = false;
}
}
TLRPC.MessageEntity entity = entityProvider.provide(null);
entity.offset = m.start() - offset;
entity.length = gr.length();
entities.add(entity);
if (allowEntity) {
cs = cs.subSequence(0, m.start() - offset) + gr + cs.subSequence(m.end() - offset, cs.length());
TLRPC.MessageEntity entity = entityProvider.provide(null);
entity.offset = m.start() - offset;
entity.length = gr.length();
entities.add(entity);
}
offset += m.end() - m.start() - gr.length();
}
@ -5769,6 +5963,51 @@ public class MediaDataController extends BaseController {
return true;
}
public void preloadPremiumPreviewStickers() {
if (previewStickersLoading || !premiumPreviewStickers.isEmpty()) {
for (int i = 0; i < Math.min(premiumPreviewStickers.size(), 3); i++) {
TLRPC.Document document = premiumPreviewStickers.get(i == 2 ? premiumPreviewStickers.size() - 1 : i);
if (MessageObject.isPremiumSticker(document)) {
ImageReceiver imageReceiver = new ImageReceiver();
imageReceiver.setImage(ImageLocation.getForDocument(document), null, null, "webp", null, 1);
ImageLoader.getInstance().loadImageForImageReceiver(imageReceiver);
imageReceiver = new ImageReceiver();
imageReceiver.setImage(ImageLocation.getForDocument(MessageObject.getPremiumStickerAnimation(document), document), null, null, null, "tgs", null, 1);
ImageLoader.getInstance().loadImageForImageReceiver(imageReceiver);
}
}
return;
}
final TLRPC.TL_messages_getStickers req2 = new TLRPC.TL_messages_getStickers();
req2.emoticon = Emoji.fixEmoji("") + Emoji.fixEmoji("");
req2.hash = 0;
previewStickersLoading = true;
ConnectionsManager.getInstance(currentAccount).sendRequest(req2, (response, error) -> AndroidUtilities.runOnUIThread(() -> {
if (error != null) {
return;
}
previewStickersLoading = false;
TLRPC.TL_messages_stickers res = (TLRPC.TL_messages_stickers) response;
premiumPreviewStickers.clear();
premiumPreviewStickers.addAll(res.stickers);
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.premiumStickersPreviewLoaded);
}));
}
public void chekAllMedia(boolean force) {
if (force) {
reactionsUpdateDate = 0;
loadFeaturedDate = 0;
}
loadRecents(MediaDataController.TYPE_FAVE, false, true, false);
loadRecents(MediaDataController.TYPE_GREETINGS, false, true, false);
checkFeaturedStickers();
checkReactions();
checkMenuBots();
checkPremiumPromo();
}
//---------------- BOT END ----------------
//---------------- EMOJI START ----------------
@ -6048,5 +6287,81 @@ public class MediaDataController extends BaseController {
}
}
public void loadEmojiThemes() {
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("emojithemes_config_" + currentAccount, Context.MODE_PRIVATE);
int count = preferences.getInt("count", 0);
ArrayList<ChatThemeBottomSheet.ChatThemeItem> previewItems = new ArrayList<>();
previewItems.add(new ChatThemeBottomSheet.ChatThemeItem(EmojiThemes.createHomePreviewTheme()));
for (int i = 0; i < count; ++i) {
String value = preferences.getString("theme_" + i, "");
SerializedData serializedData = new SerializedData(Utilities.hexToBytes(value));
try {
TLRPC.TL_theme theme = TLRPC.Theme.TLdeserialize(serializedData, serializedData.readInt32(true), true);
EmojiThemes fullTheme = EmojiThemes.createPreviewFullTheme(theme);
if (fullTheme.items.size() >= 4) {
previewItems.add(new ChatThemeBottomSheet.ChatThemeItem(fullTheme));
}
ChatThemeController.chatThemeQueue.postRunnable(new Runnable() {
@Override
public void run() {
for (int i = 0; i < previewItems.size(); i++) {
previewItems.get(i).chatTheme.loadPreviewColors(0);
}
AndroidUtilities.runOnUIThread(() -> {
defaultEmojiThemes.clear();
defaultEmojiThemes.addAll(previewItems);
});
}
});
} catch (Throwable e) {
FileLog.e(e);
}
}
}
public void generateEmojiPreviewThemes(final ArrayList<TLRPC.TL_theme> emojiPreviewThemes, int currentAccount) {
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("emojithemes_config_" + currentAccount, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = preferences.edit();
editor.putInt("count", emojiPreviewThemes.size());
for (int i = 0; i < emojiPreviewThemes.size(); ++i) {
TLRPC.TL_theme tlChatTheme = emojiPreviewThemes.get(i);
SerializedData data = new SerializedData(tlChatTheme.getObjectSize());
tlChatTheme.serializeToStream(data);
editor.putString("theme_" + i, Utilities.bytesToHex(data.toByteArray()));
}
editor.apply();
if (!emojiPreviewThemes.isEmpty()) {
final ArrayList<ChatThemeBottomSheet.ChatThemeItem> previewItems = new ArrayList<>();
previewItems.add(new ChatThemeBottomSheet.ChatThemeItem(EmojiThemes.createHomePreviewTheme()));
for (int i = 0; i < emojiPreviewThemes.size(); i++) {
TLRPC.TL_theme theme = emojiPreviewThemes.get(i);
EmojiThemes chatTheme = EmojiThemes.createPreviewFullTheme(theme);
ChatThemeBottomSheet.ChatThemeItem item = new ChatThemeBottomSheet.ChatThemeItem(chatTheme);
if (chatTheme.items.size() >= 4) {
previewItems.add(item);
}
}
ChatThemeController.chatThemeQueue.postRunnable(new Runnable() {
@Override
public void run() {
for (int i = 0; i < previewItems.size(); i++) {
previewItems.get(i).chatTheme.loadPreviewColors(currentAccount);
}
AndroidUtilities.runOnUIThread(() -> {
defaultEmojiThemes.clear();
defaultEmojiThemes.addAll(previewItems);
NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.emojiPreviewThemesChanged);
});
}
});
} else {
defaultEmojiThemes.clear();
NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.emojiPreviewThemesChanged);
}
}
//---------------- EMOJI END ----------------
}

View File

@ -0,0 +1,101 @@
package org.telegram.messenger;
import org.telegram.tgnet.AbstractSerializedData;
import org.telegram.tgnet.NativeByteBuffer;
import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC;
public class MessageCustomParamsHelper {
public static boolean isEmpty(TLRPC.Message message) {
return message.voiceTranscription == null &&
!message.voiceTranscriptionOpen &&
!message.voiceTranscriptionFinal &&
!message.voiceTranscriptionRated &&
message.voiceTranscriptionId == 0 &&
!message.premiumEffectWasPlayed;
}
public static void copyParams(TLRPC.Message fromMessage, TLRPC.Message toMessage) {
toMessage.voiceTranscription = fromMessage.voiceTranscription;
toMessage.voiceTranscriptionOpen = fromMessage.voiceTranscriptionOpen;
toMessage.voiceTranscriptionFinal = fromMessage.voiceTranscriptionFinal;
toMessage.voiceTranscriptionRated = fromMessage.voiceTranscriptionRated;
toMessage.voiceTranscriptionId = fromMessage.voiceTranscriptionId;
toMessage.premiumEffectWasPlayed = fromMessage.premiumEffectWasPlayed;
}
public static void readLocalParams(TLRPC.Message message, NativeByteBuffer byteBuffer) {
if (byteBuffer == null) {
return;
}
int version = byteBuffer.readInt32(true);
TLObject params;
switch (version) {
case 1:
params = new Params_v1(message);
break;
default:
throw new RuntimeException("can't read params version = " + version);
}
params.readParams(byteBuffer, true);
}
public static NativeByteBuffer writeLocalParams(TLRPC.Message message) {
if (isEmpty(message)) {
return null;
}
TLObject params = new Params_v1(message);
try {
NativeByteBuffer nativeByteBuffer = new NativeByteBuffer(params.getObjectSize());
params.serializeToStream(nativeByteBuffer);
return nativeByteBuffer;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static class Params_v1 extends TLObject {
private final static int VERSION = 1;
final TLRPC.Message message;
int flags = 0;
private Params_v1(TLRPC.Message message) {
this.message = message;
flags += message.voiceTranscription != null ? 1 : 0;
}
@Override
public void serializeToStream(AbstractSerializedData stream) {
stream.writeInt32(VERSION);
stream.writeInt32(flags);
if ((flags & 1) != 0) {
stream.writeString(message.voiceTranscription);
}
stream.writeBool(message.voiceTranscriptionOpen);
stream.writeBool(message.voiceTranscriptionFinal);
stream.writeBool(message.voiceTranscriptionRated);
stream.writeInt64(message.voiceTranscriptionId);
stream.writeBool(message.premiumEffectWasPlayed);
}
@Override
public void readParams(AbstractSerializedData stream, boolean exception) {
flags = stream.readInt32(true);
if ((flags & 1) != 0) {
message.voiceTranscription = stream.readString(exception);
}
message.voiceTranscriptionOpen = stream.readBool(exception);
message.voiceTranscriptionFinal = stream.readBool(exception);
message.voiceTranscriptionRated = stream.readBool(exception);
message.voiceTranscriptionId = stream.readInt64(exception);
message.premiumEffectWasPlayed = stream.readBool(exception);
}
}
}

View File

@ -8,11 +8,17 @@
package org.telegram.messenger;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.SystemClock;
import android.text.Layout;
import android.text.Spannable;
import android.text.SpannableString;
@ -21,11 +27,16 @@ import android.text.Spanned;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.style.CharacterStyle;
import android.text.style.ClickableSpan;
import android.text.style.ImageSpan;
import android.text.style.URLSpan;
import android.text.util.Linkify;
import android.util.Base64;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.collection.LongSparseArray;
import org.telegram.PhoneFormat.PhoneFormat;
@ -37,7 +48,9 @@ import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Cells.ChatMessageCell;
import org.telegram.ui.Components.RLottieDrawable;
import org.telegram.ui.Components.TextStyleSpan;
import org.telegram.ui.Components.TranscribeButton;
import org.telegram.ui.Components.TypefaceSpan;
import org.telegram.ui.Components.URLSpanBotCommand;
import org.telegram.ui.Components.URLSpanBrowser;
@ -51,6 +64,7 @@ import org.telegram.ui.Components.spoilers.SpoilerEffect;
import java.io.BufferedReader;
import java.io.File;
import java.io.StringReader;
import java.lang.ref.WeakReference;
import java.net.URLEncoder;
import java.util.AbstractMap;
import java.util.ArrayList;
@ -106,6 +120,7 @@ public class MessageObject {
public boolean isReactionPush;
public boolean putInDownloadsStore;
public boolean isDownloadingFile;
public boolean forcePlayEffect;
private int isRoundVideoCached;
public long eventId;
public int contentType;
@ -139,6 +154,8 @@ public class MessageObject {
public boolean isRestrictedMessage;
public long loadedFileSize;
public AtomicReference<WeakReference<View>> viewRef = new AtomicReference<>(null);
public boolean isSpoilersRevealed = NekoConfig.showSpoilersDirectly.Bool();
public byte[] sponsoredId;
public int sponsoredChannelPost;
@ -251,6 +268,18 @@ public class MessageObject {
return false;
}
public static boolean isPremiumSticker(TLRPC.Document document) {
if (document == null || document.thumbs == null) {
return false;
}
for (int i = 0; i < document.video_thumbs.size(); i++) {
if ("f".equals(document.video_thumbs.get(i).type)) {
return true;
}
}
return false;
}
public int getEmojiOnlyCount() {
return emojiOnlyCount;
}
@ -286,6 +315,29 @@ public class MessageObject {
}
}
public boolean isPremiumSticker() {
if (messageOwner.media != null && messageOwner.media.nopremium) {
return false;
}
return isPremiumSticker(getDocument());
}
public TLRPC.VideoSize getPremiumStickerAnimation() {
return getPremiumStickerAnimation(getDocument());
}
public static TLRPC.VideoSize getPremiumStickerAnimation(TLRPC.Document document) {
if (document == null || document.thumbs == null) {
return null;
}
for (int i = 0; i < document.video_thumbs.size(); i++) {
if ("f".equals(document.video_thumbs.get(i).type)) {
return document.video_thumbs.get(i);
}
}
return null;
}
public static class SendAnimationData {
public float x;
public float y;
@ -1034,7 +1086,7 @@ public class MessageObject {
paint = Theme.chat_msgTextPaint;
}
int[] emojiOnly = allowsBigEmoji() ? new int[1] : null;
messageText = Emoji.replaceEmoji(messageText, paint.getFontMetricsInt(), AndroidUtilities.dp(20), false, emojiOnly);
messageText = Emoji.replaceEmoji(messageText, paint.getFontMetricsInt(), AndroidUtilities.dp(20), false, emojiOnly, contentType == 0, viewRef);
checkEmojiOnly(emojiOnly);
emojiAnimatedSticker = null;
if (emojiOnlyCount == 1 && !(message.media instanceof TLRPC.TL_messageMediaWebPage) && !(message.media instanceof TLRPC.TL_messageMediaInvoice) && message.entities.isEmpty() && (message.media instanceof TLRPC.TL_messageMediaEmpty || message.media == null) && messageOwner.grouped_id == 0) {
@ -1944,9 +1996,15 @@ public class MessageObject {
}
} else if (event.action instanceof TLRPC.TL_channelAdminLogEventActionParticipantJoinByRequest) {
TLRPC.TL_channelAdminLogEventActionParticipantJoinByRequest action = (TLRPC.TL_channelAdminLogEventActionParticipantJoinByRequest) event.action;
messageText = replaceWithLink(LocaleController.getString("JoinedViaInviteLinkApproved", R.string.JoinedViaInviteLinkApproved), "un1", fromUser);
messageText = replaceWithLink(messageText, "un2", action.invite);
messageText = replaceWithLink(messageText, "un3", MessagesController.getInstance(currentAccount).getUser(action.approved_by));
if (action.invite instanceof TLRPC.TL_chatInviteExported && "https://t.me/+PublicChat".equals(((TLRPC.TL_chatInviteExported) action.invite).link) ||
action.invite instanceof TLRPC.TL_chatInvitePublicJoinRequests) {
messageText = replaceWithLink(LocaleController.getString("JoinedViaRequestApproved", R.string.JoinedViaRequestApproved), "un1", fromUser);
messageText = replaceWithLink(messageText, "un2", MessagesController.getInstance(currentAccount).getUser(action.approved_by));
} else {
messageText = replaceWithLink(LocaleController.getString("JoinedViaInviteLinkApproved", R.string.JoinedViaInviteLinkApproved), "un1", fromUser);
messageText = replaceWithLink(messageText, "un2", action.invite);
messageText = replaceWithLink(messageText, "un3", MessagesController.getInstance(currentAccount).getUser(action.approved_by));
}
} else if (event.action instanceof TLRPC.TL_channelAdminLogEventActionSendMessage) {
message = ((TLRPC.TL_channelAdminLogEventActionSendMessage) event.action).message;
messageText = replaceWithLink(LocaleController.getString("EventLogSendMessages", R.string.EventLogSendMessages), "un1", fromUser);
@ -2026,7 +2084,7 @@ public class MessageObject {
paint = Theme.chat_msgTextPaint;
}
int[] emojiOnly = allowsBigEmoji() ? new int[1] : null;
messageText = Emoji.replaceEmoji(messageText, paint.getFontMetricsInt(), AndroidUtilities.dp(20), false, emojiOnly);
messageText = Emoji.replaceEmoji(messageText, paint.getFontMetricsInt(), AndroidUtilities.dp(20), false, emojiOnly, contentType == 0, viewRef);
checkEmojiOnly(emojiOnly);
if (mediaController.isPlayingMessage(this)) {
MessageObject player = mediaController.getPlayingMessageObject();
@ -2083,7 +2141,11 @@ public class MessageObject {
}
public void applyNewText() {
if (TextUtils.isEmpty(messageOwner.message)) {
applyNewText(messageOwner.message);
}
public void applyNewText(CharSequence text) {
if (TextUtils.isEmpty(text)) {
return;
}
@ -2092,8 +2154,7 @@ public class MessageObject {
fromUser = MessagesController.getInstance(currentAccount).getUser(messageOwner.from_id.user_id);
}
messageText = messageOwner.message;
messageText = text;
TextPaint paint;
if (messageOwner.media instanceof TLRPC.TL_messageMediaGame) {
paint = Theme.chat_msgGameTextPaint;
@ -2101,7 +2162,7 @@ public class MessageObject {
paint = Theme.chat_msgTextPaint;
}
int[] emojiOnly = allowsBigEmoji() ? new int[1] : null;
messageText = Emoji.replaceEmoji(messageText, paint.getFontMetricsInt(), AndroidUtilities.dp(20), false, emojiOnly);
messageText = Emoji.replaceEmoji(messageText, paint.getFontMetricsInt(), AndroidUtilities.dp(20), false, emojiOnly, contentType == 0, viewRef);
checkEmojiOnly(emojiOnly);
generateLayout(fromUser);
}
@ -2155,10 +2216,25 @@ public class MessageObject {
} else {
name = "";
}
String currency;
try {
currency = LocaleController.getInstance().formatCurrencyString(messageOwner.action.total_amount, messageOwner.action.currency);
} catch (Exception e) {
currency = "<error>";
FileLog.e(e);
}
if (replyMessageObject != null && replyMessageObject.messageOwner.media instanceof TLRPC.TL_messageMediaInvoice) {
messageText = LocaleController.formatString("PaymentSuccessfullyPaid", R.string.PaymentSuccessfullyPaid, LocaleController.getInstance().formatCurrencyString(messageOwner.action.total_amount, messageOwner.action.currency), name, replyMessageObject.messageOwner.media.title);
if (messageOwner.action.recurring_init) {
messageText = LocaleController.formatString(R.string.PaymentSuccessfullyPaidRecurrent, currency, name, replyMessageObject.messageOwner.media.title);
} else {
messageText = LocaleController.formatString("PaymentSuccessfullyPaid", R.string.PaymentSuccessfullyPaid, currency, name, replyMessageObject.messageOwner.media.title);
}
} else {
messageText = LocaleController.formatString("PaymentSuccessfullyPaidNoItem", R.string.PaymentSuccessfullyPaidNoItem, LocaleController.getInstance().formatCurrencyString(messageOwner.action.total_amount, messageOwner.action.currency), name);
if (messageOwner.action.recurring_init) {
messageText = LocaleController.formatString(R.string.PaymentSuccessfullyPaidNoItemRecurrent, currency, name);
} else {
messageText = LocaleController.formatString("PaymentSuccessfullyPaidNoItem", R.string.PaymentSuccessfullyPaidNoItem, currency, name);
}
}
}
@ -2208,13 +2284,13 @@ public class MessageObject {
messageText = replaceWithLink(LocaleController.getString("ActionPinnedPhoto", R.string.ActionPinnedPhoto), "un1", fromUser != null ? fromUser : chat);
} else if (replyMessageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGame) {
messageText = replaceWithLink(LocaleController.formatString("ActionPinnedGame", R.string.ActionPinnedGame, "\uD83C\uDFAE " + replyMessageObject.messageOwner.media.game.title), "un1", fromUser != null ? fromUser : chat);
messageText = Emoji.replaceEmoji(messageText, Theme.chat_msgTextPaint.getFontMetricsInt(), AndroidUtilities.dp(20), false);
messageText = Emoji.replaceEmoji(messageText, Theme.chat_msgTextPaint.getFontMetricsInt(), AndroidUtilities.dp(20), false, contentType == 0, viewRef);
} else if (replyMessageObject.messageText != null && replyMessageObject.messageText.length() > 0) {
CharSequence mess = replyMessageObject.messageText;
if (mess.length() > 20) {
mess = mess.subSequence(0, 20) + "...";
}
mess = Emoji.replaceEmoji(mess, Theme.chat_msgTextPaint.getFontMetricsInt(), AndroidUtilities.dp(20), false);
mess = Emoji.replaceEmoji(mess, Theme.chat_msgTextPaint.getFontMetricsInt(), AndroidUtilities.dp(20), false, contentType == 0, viewRef);
MediaDataController.addTextStyleRuns(replyMessageObject, (Spannable) mess);
messageText = replaceWithLink(AndroidUtilities.formatSpannable(LocaleController.getString("ActionPinnedText", R.string.ActionPinnedText), mess), "un1", fromUser != null ? fromUser : chat);
} else {
@ -2552,7 +2628,7 @@ public class MessageObject {
if (str == null) {
str = "";
}
text = Emoji.replaceEmoji(str, Theme.chat_msgBotButtonPaint.getFontMetricsInt(), AndroidUtilities.dp(15), false);
text = Emoji.replaceEmoji(str, Theme.chat_msgBotButtonPaint.getFontMetricsInt(), AndroidUtilities.dp(15), false, contentType == 0, viewRef);
}
StaticLayout staticLayout = new StaticLayout(text, Theme.chat_msgBotButtonPaint, AndroidUtilities.dp(2000), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
if (staticLayout.getLineCount() > 0) {
@ -2572,7 +2648,7 @@ public class MessageObject {
TLRPC.TL_reactionCount reactionCount = messageOwner.reactions.results.get(a);
int maxButtonSize = 0;
botButtonsLayout.append(0).append(a);
CharSequence text = Emoji.replaceEmoji(String.format("%d %s", reactionCount.count, reactionCount.reaction), Theme.chat_msgBotButtonPaint.getFontMetricsInt(), AndroidUtilities.dp(15), false);
CharSequence text = Emoji.replaceEmoji(String.format("%d %s", reactionCount.count, reactionCount.reaction), Theme.chat_msgBotButtonPaint.getFontMetricsInt(), AndroidUtilities.dp(15), false, contentType == 0, viewRef);
StaticLayout staticLayout = new StaticLayout(text, Theme.chat_msgBotButtonPaint, AndroidUtilities.dp(2000), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
if (staticLayout.getLineCount() > 0) {
float width = staticLayout.getLineWidth(0);
@ -3322,7 +3398,7 @@ public class MessageObject {
paint = Theme.chat_msgTextPaint;
}
int[] emojiOnly = allowsBigEmoji() ? new int[1] : null;
messageText = Emoji.replaceEmoji(messageText, paint.getFontMetricsInt(), AndroidUtilities.dp(20), false, emojiOnly);
messageText = Emoji.replaceEmoji(messageText, paint.getFontMetricsInt(), AndroidUtilities.dp(20), false, emojiOnly, contentType == 0, viewRef);
checkEmojiOnly(emojiOnly);
generateLayout(fromUser);
return true;
@ -3831,7 +3907,7 @@ public class MessageObject {
FileLog.e(e);
}
}
linkDescription = Emoji.replaceEmoji(linkDescription, Theme.chat_msgTextPaint.getFontMetricsInt(), AndroidUtilities.dp(20), false);
linkDescription = Emoji.replaceEmoji(linkDescription, Theme.chat_msgTextPaint.getFontMetricsInt(), AndroidUtilities.dp(20), false, contentType == 0, viewRef);
if (hashtagsType != 0) {
if (!(linkDescription instanceof Spannable)) {
linkDescription = new SpannableStringBuilder(linkDescription);
@ -3841,6 +3917,51 @@ public class MessageObject {
}
}
public CharSequence getVoiceTranscription() {
if (messageOwner == null || messageOwner.voiceTranscription == null) {
return null;
}
if (TextUtils.isEmpty(messageOwner.voiceTranscription)) {
SpannableString ssb = new SpannableString(LocaleController.getString("NoWordsRecognized", R.string.NoWordsRecognized));
ssb.setSpan(new CharacterStyle() {
@Override
public void updateDrawState(TextPaint textPaint) {
textPaint.setTextSize(textPaint.getTextSize() * .8f);
textPaint.setColor(Theme.chat_timePaint.getColor());
}
}, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return ssb;
}
CharSequence text = messageOwner.voiceTranscription;
if (!TextUtils.isEmpty(text)) {
text = Emoji.replaceEmoji(text, Theme.chat_msgTextPaint.getFontMetricsInt(), AndroidUtilities.dp(20), false, contentType == 0, viewRef);
}
return text;
}
public float measureVoiceTranscriptionHeight() {
CharSequence voiceTranscription = getVoiceTranscription();
if (voiceTranscription == null) {
return 0;
}
int width = AndroidUtilities.displaySize.x - AndroidUtilities.dp(this.needDrawAvatar() ? 147 : 95);
StaticLayout captionLayout;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
captionLayout = StaticLayout.Builder.obtain(voiceTranscription, 0, voiceTranscription.length(), Theme.chat_msgTextPaint, width)
.setBreakStrategy(StaticLayout.BREAK_STRATEGY_HIGH_QUALITY)
.setHyphenationFrequency(StaticLayout.HYPHENATION_FREQUENCY_NONE)
.setAlignment(Layout.Alignment.ALIGN_NORMAL)
.build();
} else {
captionLayout = new StaticLayout(voiceTranscription, Theme.chat_msgTextPaint, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
}
return captionLayout.getHeight();
}
public boolean isVoiceTranscriptionOpen() {
return isVoice() && messageOwner != null && messageOwner.voiceTranscriptionOpen && messageOwner.voiceTranscription != null && (messageOwner.voiceTranscriptionFinal || TranscribeButton.isTranscribing(this)) && UserConfig.getInstance(currentAccount).isPremium();
}
public void generateCaption() {
if (caption != null || isRoundVideo()) {
return;
@ -3854,7 +3975,7 @@ public class MessageObject {
msg = messageOwner.message;
}
caption = Emoji.replaceEmoji(msg, Theme.chat_msgTextPaint.getFontMetricsInt(), AndroidUtilities.dp(20), false);
caption = Emoji.replaceEmoji(msg, Theme.chat_msgTextPaint.getFontMetricsInt(), AndroidUtilities.dp(20), false, contentType == 0, viewRef);
boolean hasEntities;
if (messageOwner.send_state != MESSAGE_SEND_STATE_SENT) {
@ -3864,15 +3985,16 @@ public class MessageObject {
}
boolean useManualParse = !hasEntities && (
eventId != 0 ||
messageOwner.media instanceof TLRPC.TL_messageMediaPhoto_old ||
messageOwner.media instanceof TLRPC.TL_messageMediaPhoto_layer68 ||
messageOwner.media instanceof TLRPC.TL_messageMediaPhoto_layer74 ||
messageOwner.media instanceof TLRPC.TL_messageMediaDocument_old ||
messageOwner.media instanceof TLRPC.TL_messageMediaDocument_layer68 ||
messageOwner.media instanceof TLRPC.TL_messageMediaDocument_layer74 ||
isOut() && messageOwner.send_state != MESSAGE_SEND_STATE_SENT ||
messageOwner.id < 0);
eventId != 0 ||
messageOwner.media instanceof TLRPC.TL_messageMediaPhoto_old ||
messageOwner.media instanceof TLRPC.TL_messageMediaPhoto_layer68 ||
messageOwner.media instanceof TLRPC.TL_messageMediaPhoto_layer74 ||
messageOwner.media instanceof TLRPC.TL_messageMediaDocument_old ||
messageOwner.media instanceof TLRPC.TL_messageMediaDocument_layer68 ||
messageOwner.media instanceof TLRPC.TL_messageMediaDocument_layer74 ||
isOut() && messageOwner.send_state != MESSAGE_SEND_STATE_SENT ||
messageOwner.id < 0
);
if (useManualParse) {
if (containsUrls(caption)) {
@ -3901,11 +4023,14 @@ public class MessageObject {
}
public static void addUrlsByPattern(boolean isOut, CharSequence charSequence, boolean botCommands, int patternType, int duration, boolean check) {
if (charSequence == null) {
return;
}
try {
Matcher matcher;
if (patternType == 3 || patternType == 4) {
if (videoTimeUrlPattern == null) {
videoTimeUrlPattern = Pattern.compile("\\b(?:(\\d{1,2}):)?(\\d{1,3}):([0-5][0-9])\\b");
videoTimeUrlPattern = Pattern.compile("\\b(?:(\\d{1,2}):)?(\\d{1,3}):([0-5][0-9])\\b([^\\n]*)");
}
matcher = videoTimeUrlPattern.matcher(charSequence);
} else if (patternType == 1) {
@ -3925,10 +4050,6 @@ public class MessageObject {
int end = matcher.end();
URLSpanNoUnderline url = null;
if (patternType == 3 || patternType == 4) {
URLSpan[] spans = spannable.getSpans(start, end, URLSpan.class);
if (spans != null && spans.length > 0) {
continue;
}
int count = matcher.groupCount();
int s1 = matcher.start(1);
int e1 = matcher.end(1);
@ -3936,9 +4057,19 @@ public class MessageObject {
int e2 = matcher.end(2);
int s3 = matcher.start(3);
int e3 = matcher.end(3);
int s4 = matcher.start(4);
int e4 = matcher.end(4);
int minutes = Utilities.parseInt(charSequence.subSequence(s2, e2));
int seconds = Utilities.parseInt(charSequence.subSequence(s3, e3));
int hours = s1 >= 0 && e1 >= 0 ? Utilities.parseInt(charSequence.subSequence(s1, e1)) : -1;
String label = s4 < 0 || e4 < 0 ? null : charSequence.subSequence(s4, e4).toString();
if (s4 >= 0 || e4 >= 0) {
end = e3;
}
URLSpan[] spans = spannable.getSpans(start, end, URLSpan.class);
if (spans != null && spans.length > 0) {
continue;
}
seconds += minutes * 60;
if (hours > 0) {
seconds += hours * 60 * 60;
@ -3951,6 +4082,7 @@ public class MessageObject {
} else {
url = new URLSpanNoUnderline("audio?" + seconds);
}
url.label = label;
} else {
char ch = charSequence.charAt(start);
if (patternType != 0) {
@ -4102,6 +4234,9 @@ public class MessageObject {
}
public boolean addEntitiesToText(CharSequence text, boolean photoViewer, boolean useManualParse) {
if (text == null) {
return false;
}
if (isRestrictedMessage) {
ArrayList<TLRPC.MessageEntity> entities = new ArrayList<>();
TLRPC.TL_messageEntityItalic entityItalic = new TLRPC.TL_messageEntityItalic();
@ -4225,6 +4360,9 @@ public class MessageObject {
for (int b = 0, N2 = runs.size(); b < N2; b++) {
TextStyleSpan.TextStyleRun run = runs.get(b);
if ((run.flags & TextStyleSpan.FLAG_STYLE_SPOILER) != 0 && newRun.start >= run.start && newRun.end <= run.end) {
continue;
}
if (newRun.start > run.start) {
if (newRun.start >= run.end) {
@ -4836,7 +4974,7 @@ public class MessageObject {
return messageOwner.realId != 0 ? messageOwner.realId : messageOwner.id;
}
public static int getMessageSize(TLRPC.Message message) {
public static long getMessageSize(TLRPC.Message message) {
TLRPC.Document document;
if (message.media instanceof TLRPC.TL_messageMediaWebPage) {
document = message.media.webpage.document;
@ -4851,7 +4989,7 @@ public class MessageObject {
return 0;
}
public int getSize() {
public long getSize() {
return getMessageSize(messageOwner);
}
@ -5841,7 +5979,7 @@ public class MessageObject {
&& !messageOwner.fwd_from.imported
&& (
messageOwner.fwd_from.saved_from_peer == null
|| messageOwner.fwd_from.from_id instanceof TLRPC.TL_peerChannel && messageOwner.fwd_from.saved_from_peer.channel_id != messageOwner.fwd_from.from_id.channel_id
|| !(messageOwner.fwd_from.from_id instanceof TLRPC.TL_peerChannel) || messageOwner.fwd_from.saved_from_peer.channel_id != messageOwner.fwd_from.from_id.channel_id
|| (((messageOwner.flags & TLRPC.MESSAGE_FLAG_REPLY) == 0) &&
(messageOwner.fwd_from.from_id instanceof TLRPC.TL_peerUser
|| messageOwner.fwd_from.from_id == null && messageOwner.fwd_from.from_name != null))
@ -6172,13 +6310,17 @@ public class MessageObject {
}
public void checkMediaExistance() {
checkMediaExistance(true);
}
public void checkMediaExistance(boolean useFileDatabaseQueue) {
File cacheFile = null;
attachPathExists = false;
mediaExists = false;
if (type == TYPE_PHOTO) {
TLRPC.PhotoSize currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(photoThumbs, AndroidUtilities.getPhotoSize());
if (currentPhotoObject != null) {
File file = FileLoader.getPathToMessage(messageOwner);
File file = FileLoader.getInstance(currentAccount).getPathToMessage(messageOwner, useFileDatabaseQueue);
if (needDrawBluredPreview()) {
mediaExists = new File(file.getAbsolutePath() + ".enc").exists();
}
@ -6193,7 +6335,7 @@ public class MessageObject {
attachPathExists = f.exists();
}
if (!attachPathExists) {
File file = FileLoader.getPathToMessage(messageOwner);
File file = FileLoader.getInstance(currentAccount).getPathToMessage(messageOwner, useFileDatabaseQueue);
if (type == 3 && needDrawBluredPreview()) {
mediaExists = new File(file.getAbsolutePath() + ".enc").exists();
}
@ -6206,22 +6348,22 @@ public class MessageObject {
TLRPC.Document document = getDocument();
if (document != null) {
if (isWallpaper()) {
mediaExists = FileLoader.getPathToAttach(document, true).exists();
mediaExists = FileLoader.getInstance(currentAccount).getPathToAttach(document, null, true, useFileDatabaseQueue).exists();
} else {
mediaExists = FileLoader.getPathToAttach(document).exists();
mediaExists = FileLoader.getInstance(currentAccount).getPathToAttach(document, null, false, useFileDatabaseQueue).exists();
}
} else if (type == 0) {
TLRPC.PhotoSize currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(photoThumbs, AndroidUtilities.getPhotoSize());
if (currentPhotoObject == null) {
return;
}
mediaExists = FileLoader.getPathToAttach(currentPhotoObject, true).exists();
mediaExists = FileLoader.getInstance(currentAccount).getPathToAttach(currentPhotoObject, null, true, useFileDatabaseQueue).exists();
} else if (type == 11) {
TLRPC.Photo photo = messageOwner.action.photo;
if (photo == null || photo.video_sizes.isEmpty()) {
return;
}
mediaExists = FileLoader.getPathToAttach(photo.video_sizes.get(0), true).exists();
mediaExists = FileLoader.getInstance(currentAccount).getPathToAttach(photo.video_sizes.get(0), null, true, useFileDatabaseQueue).exists();
}
}
}

View File

@ -28,6 +28,7 @@ import org.telegram.SQLite.SQLiteCursor;
import org.telegram.SQLite.SQLiteDatabase;
import org.telegram.SQLite.SQLiteException;
import org.telegram.SQLite.SQLitePreparedStatement;
import org.telegram.messenger.ringtone.RingtoneDataStore;
import org.telegram.messenger.support.LongSparseIntArray;
import org.telegram.tgnet.NativeByteBuffer;
import org.telegram.tgnet.RequestDelegate;
@ -56,7 +57,7 @@ import tw.nekomimi.nekogram.transtale.TranslateDb;
public class MessagesStorage extends BaseController {
private DispatchQueue storageQueue = new DispatchQueue("storageQueue");
private DispatchQueue storageQueue;
private SQLiteDatabase database;
private File cacheFile;
private File walCacheFile;
@ -88,7 +89,7 @@ public class MessagesStorage extends BaseController {
private CountDownLatch openSync = new CountDownLatch(1);
private static SparseArray<MessagesStorage> Instance = new SparseArray();
private final static int LAST_DB_VERSION = 93;
private final static int LAST_DB_VERSION = 98;
private boolean databaseMigrationInProgress;
public boolean showClearDatabaseAlert;
@ -194,6 +195,7 @@ public class MessagesStorage extends BaseController {
public MessagesStorage(int instance) {
super(instance);
storageQueue = new DispatchQueue("storageQueue_" + instance);
//storageQueue.setPriority(Thread.MAX_PRIORITY);
storageQueue.postRunnable(() -> openDatabase(1));
}
@ -291,7 +293,7 @@ public class MessagesStorage extends BaseController {
database.executeFast("CREATE INDEX IF NOT EXISTS uid_date_idx_scheduled_messages_v2 ON scheduled_messages_v2(uid, date);").stepThis().dispose();
database.executeFast("CREATE INDEX IF NOT EXISTS reply_to_idx_scheduled_messages_v2 ON scheduled_messages_v2(mid, reply_to_message_id);").stepThis().dispose();
database.executeFast("CREATE TABLE messages_v2(mid INTEGER, uid INTEGER, read_state INTEGER, send_state INTEGER, date INTEGER, data BLOB, out INTEGER, ttl INTEGER, media INTEGER, replydata BLOB, imp INTEGER, mention INTEGER, forwards INTEGER, replies_data BLOB, thread_reply_id INTEGER, is_channel INTEGER, reply_to_message_id INTEGER, PRIMARY KEY(mid, uid))").stepThis().dispose();
database.executeFast("CREATE TABLE messages_v2(mid INTEGER, uid INTEGER, read_state INTEGER, send_state INTEGER, date INTEGER, data BLOB, out INTEGER, ttl INTEGER, media INTEGER, replydata BLOB, imp INTEGER, mention INTEGER, forwards INTEGER, replies_data BLOB, thread_reply_id INTEGER, is_channel INTEGER, reply_to_message_id INTEGER, custom_params BLOB, PRIMARY KEY(mid, uid))").stepThis().dispose();
database.executeFast("CREATE INDEX IF NOT EXISTS uid_mid_read_out_idx_messages_v2 ON messages_v2(uid, mid, read_state, out);").stepThis().dispose();
database.executeFast("CREATE INDEX IF NOT EXISTS uid_date_mid_idx_messages_v2 ON messages_v2(uid, date, mid);").stepThis().dispose();
database.executeFast("CREATE INDEX IF NOT EXISTS mid_out_idx_messages_v2 ON messages_v2(mid, out);").stepThis().dispose();
@ -365,7 +367,7 @@ public class MessagesStorage extends BaseController {
database.executeFast("CREATE TABLE dialog_settings(did INTEGER PRIMARY KEY, flags INTEGER);").stepThis().dispose();
database.executeFast("CREATE TABLE web_recent_v3(id TEXT, type INTEGER, image_url TEXT, thumb_url TEXT, local_url TEXT, width INTEGER, height INTEGER, size INTEGER, date INTEGER, document BLOB, PRIMARY KEY (id, type));").stepThis().dispose();
database.executeFast("CREATE TABLE stickers_v2(id INTEGER PRIMARY KEY, data BLOB, date INTEGER, hash INTEGER);").stepThis().dispose();
database.executeFast("CREATE TABLE stickers_featured(id INTEGER PRIMARY KEY, data BLOB, unread BLOB, date INTEGER, hash INTEGER);").stepThis().dispose();
database.executeFast("CREATE TABLE stickers_featured(id INTEGER PRIMARY KEY, data BLOB, unread BLOB, date INTEGER, hash INTEGER, premium INTEGER);").stepThis().dispose();
database.executeFast("CREATE TABLE stickers_dice(emoji TEXT PRIMARY KEY, data BLOB, date INTEGER);").stepThis().dispose();
database.executeFast("CREATE TABLE hashtag_recent_v2(id TEXT PRIMARY KEY, date INTEGER);").stepThis().dispose();
database.executeFast("CREATE TABLE webpage_pending_v2(id INTEGER, mid INTEGER, uid INTEGER, PRIMARY KEY (id, mid, uid));").stepThis().dispose();
@ -402,6 +404,8 @@ public class MessagesStorage extends BaseController {
database.executeFast("CREATE TABLE downloading_documents(data BLOB, hash INTEGER, id INTEGER, state INTEGER, date INTEGER, PRIMARY KEY(hash, id));").stepThis().dispose();
database.executeFast("CREATE TABLE attach_menu_bots(data BLOB, hash INTEGER, date INTEGER);").stepThis().dispose();
database.executeFast("CREATE TABLE premium_promo(data BLOB, date INTEGER);").stepThis().dispose();
//version
database.executeFast("PRAGMA user_version = " + LAST_DB_VERSION).stepThis().dispose();
} else {
@ -1576,6 +1580,28 @@ public class MessagesStorage extends BaseController {
if (version == 92) {
database.executeFast("CREATE TABLE IF NOT EXISTS attach_menu_bots(data BLOB, hash INTEGER, date INTEGER);").stepThis().dispose();
database.executeFast("PRAGMA user_version = 93").stepThis().dispose();
version = 95;
}
if (version == 95 || version == 93) {
executeNoException("ALTER TABLE messages_v2 ADD COLUMN custom_params BLOB default NULL");
database.executeFast("PRAGMA user_version = 96").stepThis().dispose();
version = 96;
}
// skip 94, 95. private beta db rollback
if (version == 96) {
database.executeFast("CREATE TABLE IF NOT EXISTS premium_promo(data BLOB, date INTEGER);").stepThis().dispose();
database.executeFast("UPDATE stickers_v2 SET date = 0");
database.executeFast("PRAGMA user_version = 97").stepThis().dispose();
version = 97;
}
if (version == 97) {
database.executeFast("DROP TABLE IF EXISTS stickers_featured;").stepThis().dispose();
database.executeFast("CREATE TABLE stickers_featured(id INTEGER PRIMARY KEY, data BLOB, unread BLOB, date INTEGER, hash INTEGER, premium INTEGER);").stepThis().dispose();
database.executeFast("PRAGMA user_version = 98").stepThis().dispose();
version = 98;
}
FileLog.d("MessagesStorage db migration finished");
@ -2187,7 +2213,6 @@ public class MessagesStorage extends BaseController {
}
private static class ReadDialog {
public int lastMid;
public int date;
@ -2403,6 +2428,7 @@ public class MessagesStorage extends BaseController {
SQLiteCursor filtersCursor = database.queryFinalized("SELECT id, ord, unread_count, flags, title, emoticon FROM dialog_filter_neko WHERE 1");
boolean updateCounters = false;
boolean hasDefaultFilter = false;
while (filtersCursor.next()) {
MessagesController.DialogFilter filter = new MessagesController.DialogFilter();
filter.id = filtersCursor.intValue(0);
@ -2460,9 +2486,33 @@ public class MessagesStorage extends BaseController {
}
cursor2.dispose();
}
if (filter.id == 0) {
hasDefaultFilter = true;
}
}
filtersCursor.dispose();
if (!hasDefaultFilter) {
MessagesController.DialogFilter filter = new MessagesController.DialogFilter();
filter.id = 0;
filter.order = 0;
filter.name = "ALL_CHATS";
for (int i = 0; i < dialogFilters.size(); i++) {
dialogFilters.get(i).order++;
}
dialogFilters.add(filter);
dialogFiltersMap.put(filter.id, filter);
filtersById.put(filter.id, filter);
SQLitePreparedStatement state = database.executeFast("REPLACE INTO dialog_filter VALUES(?, ?, ?, ?, ?)");
state.bindInteger(1, filter.id);
state.bindInteger(2, filter.order);
state.bindInteger(3, filter.unreadCount);
state.bindInteger(4, filter.flags);
state.bindString(5, filter.name);
state.stepThis().dispose();
}
Collections.sort(dialogFilters, (o1, o2) -> {
if (o1.order > o2.order) {
return 1;
@ -2902,7 +2952,7 @@ public class MessagesStorage extends BaseController {
state.bindInteger(2, filter.order);
state.bindInteger(3, filter.unreadCount);
state.bindInteger(4, filter.flags);
state.bindString(5, filter.name);
state.bindString(5, filter.id == 0 ? "ALL_CHATS" : filter.name);
if (filter.emoticon != null) {
state.bindString(6, filter.emoticon);
} else {
@ -2971,7 +3021,7 @@ public class MessagesStorage extends BaseController {
HashMap<Integer, HashSet<Long>> filterDialogRemovals = new HashMap<>();
HashSet<Integer> filtersUnreadCounterReset = new HashSet<>();
for (int a = 0, N = vector.objects.size(); a < N; a++) {
TLRPC.TL_dialogFilter newFilter = (TLRPC.TL_dialogFilter) vector.objects.get(a);
TLRPC.DialogFilter newFilter = (TLRPC.DialogFilter) vector.objects.get(a);
filtersOrder.add(newFilter.id);
int newFlags = 0;
if (newFilter.contacts) {
@ -3366,7 +3416,17 @@ public class MessagesStorage extends BaseController {
}
public void saveDialogFiltersOrder() {
storageQueue.postRunnable(this::saveDialogFiltersOrderInternal);
ArrayList<MessagesController.DialogFilter> filtersFinal = new ArrayList<>(getMessagesController().dialogFilters);
storageQueue.postRunnable(() -> {
dialogFilters.clear();
dialogFiltersMap.clear();
dialogFilters.addAll(filtersFinal);
for (int i = 0; i < filtersFinal.size(); i++) {
filtersFinal.get(i).order = i;
dialogFiltersMap.put(filtersFinal.get(i).id, filtersFinal.get(i));
}
saveDialogFiltersOrderInternal();
});
}
protected static void addReplyMessages(TLRPC.Message message, LongSparseArray<SparseArray<ArrayList<TLRPC.Message>>> replyMessageOwners, LongSparseArray<ArrayList<Integer>> dialogReplyMessagesIds) {
@ -3781,6 +3841,9 @@ public class MessagesStorage extends BaseController {
if (document == null) {
return false;
}
if (getMediaDataController().ringtoneDataStore.contains(document.id)) {
return false;
}
id = document.id;
type = DownloadController.AUTODOWNLOAD_TYPE_AUDIO;
} else if (MessageObject.isStickerMessage(message) || MessageObject.isAnimatedStickerMessage(message)) {
@ -3796,6 +3859,9 @@ public class MessagesStorage extends BaseController {
id = document.id;
type = DownloadController.AUTODOWNLOAD_TYPE_VIDEO;
} else if (document != null) {
if (getMediaDataController().ringtoneDataStore.contains(document.id)) {
return false;
}
id = document.id;
type = DownloadController.AUTODOWNLOAD_TYPE_DOCUMENT;
} else if (photo != null) {
@ -3815,7 +3881,7 @@ public class MessagesStorage extends BaseController {
if (!TextUtils.isEmpty(name)) {
namesToDelete.add(name);
}
File file = FileLoader.getPathToAttach(photoSize, forceCache);
File file = getFileLoader().getPathToAttach(photoSize, forceCache);
if (file.toString().length() > 0) {
filesToDelete.add(file);
}
@ -3826,13 +3892,13 @@ public class MessagesStorage extends BaseController {
if (!TextUtils.isEmpty(name)) {
namesToDelete.add(name);
}
File file = FileLoader.getPathToAttach(document, forceCache);
File file = getFileLoader().getPathToAttach(document, forceCache);
if (file.toString().length() > 0) {
filesToDelete.add(file);
}
for (int a = 0, N = document.thumbs.size(); a < N; a++) {
TLRPC.PhotoSize photoSize = document.thumbs.get(a);
file = FileLoader.getPathToAttach(photoSize);
file = getFileLoader().getPathToAttach(photoSize);
if (file.toString().length() > 0) {
filesToDelete.add(file);
}
@ -4217,7 +4283,7 @@ public class MessagesStorage extends BaseController {
ArrayList<String> namesToDelete = new ArrayList<>();
ArrayList<Pair<Long, Integer>> idsToDelete = new ArrayList<>();
ArrayList<TLRPC.Message> messages = new ArrayList<>();
SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid, date, uid FROM messages_v2 WHERE mid IN (%s) AND uid = %d", TextUtils.join(",", mids), dialogId));
SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid, date, uid, custom_params FROM messages_v2 WHERE mid IN (%s) AND uid = %d", TextUtils.join(",", mids), dialogId));
while (cursor.next()) {
NativeByteBuffer data = cursor.byteBufferValue(0);
if (data != null) {
@ -4238,6 +4304,11 @@ public class MessagesStorage extends BaseController {
message.id = cursor.intValue(1);
message.date = cursor.intValue(2);
message.dialog_id = cursor.longValue(3);
NativeByteBuffer customParams = cursor.byteBufferValue(4);
if (customParams != null) {
MessageCustomParamsHelper.readLocalParams(message, customParams);
customParams.reuse();
}
messages.add(message);
}
}
@ -4245,7 +4316,8 @@ public class MessagesStorage extends BaseController {
cursor.dispose();
deleteFromDownloadQueue(idsToDelete, true);
if (!messages.isEmpty()) {
SQLitePreparedStatement state = database.executeFast("REPLACE INTO messages_v2 VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, 0)");
SQLitePreparedStatement state = database.executeFast("REPLACE INTO messages_v2 VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, 0, ?)");
for (int a = 0; a < messages.size(); a++) {
TLRPC.Message message = messages.get(a);
@ -4289,12 +4361,20 @@ public class MessagesStorage extends BaseController {
state.bindInteger(14, 0);
}
state.bindLong(15, MessageObject.getChannelId(message));
NativeByteBuffer customParams = MessageCustomParamsHelper.writeLocalParams(message);
if (customParams != null) {
state.bindByteBuffer(16, customParams);
} else {
state.bindNull(16);
}
state.step();
data.reuse();
if (repliesData != null) {
repliesData.reuse();
}
if (customParams != null) {
customParams.reuse();
}
}
state.dispose();
AndroidUtilities.runOnUIThread(() -> {
@ -4414,6 +4494,145 @@ public class MessagesStorage extends BaseController {
});
}
public void updateMessageVoiceTranscriptionOpen(long dialogId, int msgId, TLRPC.Message saveFromMessage) {
storageQueue.postRunnable(() -> {
try {
database.beginTransaction();
TLRPC.Message message = getMessageWithCustomParamsOnly(msgId, dialogId);
message.voiceTranscriptionOpen = saveFromMessage.voiceTranscriptionOpen;
message.voiceTranscriptionRated = saveFromMessage.voiceTranscriptionRated;
message.voiceTranscriptionFinal = saveFromMessage.voiceTranscriptionFinal;
message.voiceTranscriptionId = saveFromMessage.voiceTranscriptionId;
SQLitePreparedStatement state = database.executeFast("UPDATE messages_v2 SET custom_params = ? WHERE mid = ? AND uid = ?");
state.requery();
NativeByteBuffer nativeByteBuffer = MessageCustomParamsHelper.writeLocalParams(message);
if (nativeByteBuffer != null) {
state.bindByteBuffer(1, nativeByteBuffer);
} else {
state.bindNull(1);
}
state.bindInteger(2, msgId);
state.bindLong(3, dialogId);
state.step();
state.dispose();
if (nativeByteBuffer != null) {
nativeByteBuffer.reuse();
}
database.commitTransaction();
} catch (Exception e) {
FileLog.e(e);
}
});
}
public void updateMessageVoiceTranscription(long dialogId, int messageId, String text, long transcriptionId, boolean isFinal) {
storageQueue.postRunnable(() -> {
try {
database.beginTransaction();
TLRPC.Message message = getMessageWithCustomParamsOnly(messageId, dialogId);
message.voiceTranscriptionFinal = isFinal;
message.voiceTranscriptionId = transcriptionId;
message.voiceTranscription = text;
SQLitePreparedStatement state = database.executeFast("UPDATE messages_v2 SET custom_params = ? WHERE mid = ? AND uid = ?");
state.requery();
NativeByteBuffer nativeByteBuffer = MessageCustomParamsHelper.writeLocalParams(message);
if (nativeByteBuffer != null) {
state.bindByteBuffer(1, nativeByteBuffer);
} else {
state.bindNull(1);
}
state.bindInteger(2, messageId);
state.bindLong(3, dialogId);
state.step();
state.dispose();
database.commitTransaction();
if (nativeByteBuffer != null) {
nativeByteBuffer.reuse();
}
} catch (Exception e) {
FileLog.e(e);
}
});
}
public void updateMessageVoiceTranscription(long dialogId, int messageId, String text, TLRPC.Message saveFromMessage) {
storageQueue.postRunnable(() -> {
try {
database.beginTransaction();
TLRPC.Message message = getMessageWithCustomParamsOnly(messageId, dialogId);
message.voiceTranscriptionOpen = saveFromMessage.voiceTranscriptionOpen;
message.voiceTranscriptionRated = saveFromMessage.voiceTranscriptionRated;
message.voiceTranscriptionFinal = saveFromMessage.voiceTranscriptionFinal;
message.voiceTranscriptionId = saveFromMessage.voiceTranscriptionId;
message.voiceTranscription = text;
SQLitePreparedStatement state = database.executeFast("UPDATE messages_v2 SET custom_params = ? WHERE mid = ? AND uid = ?");
state.requery();
NativeByteBuffer nativeByteBuffer = MessageCustomParamsHelper.writeLocalParams(message);
if (nativeByteBuffer != null) {
state.bindByteBuffer(1, nativeByteBuffer);
} else {
state.bindNull(1);
}
state.bindInteger(2, messageId);
state.bindLong(3, dialogId);
state.step();
state.dispose();
database.commitTransaction();
if (nativeByteBuffer != null) {
nativeByteBuffer.reuse();
}
} catch (Exception e) {
FileLog.e(e);
}
});
}
public void updateMessageCustomParams(long dialogId, TLRPC.Message saveFromMessage) {
storageQueue.postRunnable(() -> {
try {
database.beginTransaction();
TLRPC.Message message = getMessageWithCustomParamsOnly(saveFromMessage.id, dialogId);
MessageCustomParamsHelper.copyParams(saveFromMessage, message);
SQLitePreparedStatement state = database.executeFast("UPDATE messages_v2 SET custom_params = ? WHERE mid = ? AND uid = ?");
state.requery();
NativeByteBuffer nativeByteBuffer = MessageCustomParamsHelper.writeLocalParams(message);
if (nativeByteBuffer != null) {
state.bindByteBuffer(1, nativeByteBuffer);
} else {
state.bindNull(1);
}
state.bindInteger(2, saveFromMessage.id);
state.bindLong(3, dialogId);
state.step();
state.dispose();
database.commitTransaction();
if (nativeByteBuffer != null) {
nativeByteBuffer.reuse();
}
} catch (Exception e) {
FileLog.e(e);
}
});
}
private TLRPC.Message getMessageWithCustomParamsOnly(int messageId, long dialogId) {
TLRPC.Message message = new TLRPC.TL_message();
try {
SQLiteCursor cursor = database.queryFinalized("SELECT custom_params FROM messages_v2 WHERE mid = " + messageId + " AND uid = " + dialogId);
if (cursor.next()) {
MessageCustomParamsHelper.readLocalParams(message, cursor.byteBufferValue(0));
}
cursor.dispose();
} catch (SQLiteException e) {
FileLog.e(e);
}
return message;
}
public void getNewTask(LongSparseArray<ArrayList<Integer>> oldTask, LongSparseArray<ArrayList<Integer>> oldTaskMedia) {
storageQueue.postRunnable(() -> {
try {
@ -6901,7 +7120,7 @@ public class MessagesStorage extends BaseController {
LongSparseArray<ArrayList<TLRPC.Message>> replyMessageRandomOwners = new LongSparseArray<>();
ArrayList<Long> replyMessageRandomIds = new ArrayList<>();
SQLiteCursor cursor;
String messageSelect = "SELECT m.read_state, m.data, m.send_state, m.mid, m.date, r.random_id, m.replydata, m.media, m.ttl, m.mention, m.imp, m.forwards, m.replies_data FROM messages_v2 as m LEFT JOIN randoms_v2 as r ON r.mid = m.mid AND r.uid = m.uid";
String messageSelect = "SELECT m.read_state, m.data, m.send_state, m.mid, m.date, r.random_id, m.replydata, m.media, m.ttl, m.mention, m.imp, m.forwards, m.replies_data, m.custom_params FROM messages_v2 as m LEFT JOIN randoms_v2 as r ON r.mid = m.mid AND r.uid = m.uid";
if (scheduled) {
isEnd = true;
@ -7355,6 +7574,11 @@ public class MessagesStorage extends BaseController {
} else if ((flags & 2) != 0) {
message.stickerVerified = 2;
}
NativeByteBuffer customParams = cursor.byteBufferValue(13);
if (customParams != null) {
MessageCustomParamsHelper.readLocalParams(message, customParams);
customParams.reuse();
}
res.messages.add(message);
addUsersAndChatsFromMessage(message, usersToLoad, chatsToLoad);
@ -9068,7 +9292,7 @@ public class MessagesStorage extends BaseController {
LongSparseArray<ArrayList<Integer>> dialogMessagesIdsMap = new LongSparseArray<>();
LongSparseArray<ArrayList<Integer>> dialogMentionsIdsMap = new LongSparseArray<>();
SQLitePreparedStatement state_messages = database.executeFast("REPLACE INTO messages_v2 VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, 0)");
SQLitePreparedStatement state_messages = database.executeFast("REPLACE INTO messages_v2 VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, 0, ?)");
SQLitePreparedStatement state_media = null;
SQLitePreparedStatement state_randoms = database.executeFast("REPLACE INTO randoms_v2 VALUES(?, ?, ?)");
SQLitePreparedStatement state_download = database.executeFast("REPLACE INTO download_queue VALUES(?, ?, ?, ?, ?)");
@ -9341,6 +9565,12 @@ public class MessagesStorage extends BaseController {
state_messages.bindInteger(14, 0);
}
state_messages.bindLong(15, MessageObject.getChannelId(message));
NativeByteBuffer customParams = MessageCustomParamsHelper.writeLocalParams(message);
if (customParams != null) {
state_messages.bindByteBuffer(16, customParams);
} else {
state_messages.bindNull(16);
}
state_messages.step();
if (message.random_id != 0) {
@ -9398,6 +9628,9 @@ public class MessagesStorage extends BaseController {
if (repliesData != null) {
repliesData.reuse();
}
if (customParams != null) {
customParams.reuse();
}
data.reuse();
if (downloadMask != 0 && (message.peer_id.channel_id == 0 || message.post) && message.date >= getConnectionsManager().getCurrentTime() - 60 * 60 && getDownloadController().canDownloadMedia(message) == 1) {
@ -10852,12 +11085,14 @@ public class MessagesStorage extends BaseController {
try {
SQLiteCursor cursor = null;
int readState = 0;
NativeByteBuffer customParams = null;
try {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT uid, read_state FROM messages_v2 WHERE mid = %d AND uid = %d LIMIT 1", message.id, MessageObject.getDialogId(message)));
cursor = database.queryFinalized(String.format(Locale.US, "SELECT uid, read_state, custom_params FROM messages_v2 WHERE mid = %d AND uid = %d LIMIT 1", message.id, MessageObject.getDialogId(message)));
if (!cursor.next()) {
return;
}
readState = cursor.intValue(1);
customParams = cursor.byteBufferValue(2);
} catch (Exception e) {
FileLog.e(e);
} finally {
@ -10868,7 +11103,7 @@ public class MessagesStorage extends BaseController {
database.beginTransaction();
SQLitePreparedStatement state = database.executeFast("REPLACE INTO messages_v2 VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, 0)");
SQLitePreparedStatement state = database.executeFast("REPLACE INTO messages_v2 VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, 0, ?)");
SQLitePreparedStatement state2 = database.executeFast("REPLACE INTO media_v4 VALUES(?, ?, ?, ?, ?)");
if (message.dialog_id == 0) {
MessageObject.getDialogId(message);
@ -10914,6 +11149,11 @@ public class MessagesStorage extends BaseController {
state.bindInteger(14, 0);
}
state.bindLong(15, MessageObject.getChannelId(message));
if (customParams != null) {
state.bindByteBuffer(16, customParams);
} else {
state.bindNull(16);
}
state.step();
if (MediaDataController.canAddMessageToMedia(message)) {
@ -10928,6 +11168,9 @@ public class MessagesStorage extends BaseController {
if (repliesData != null) {
repliesData.reuse();
}
if (customParams != null) {
customParams.reuse();
}
data.reuse();
state.dispose();
@ -11026,7 +11269,7 @@ public class MessagesStorage extends BaseController {
ArrayList<String> namesToDelete = new ArrayList<>();
ArrayList<Pair<Long, Integer>> idsToDelete = new ArrayList<>();
SQLitePreparedStatement state_messages = database.executeFast("REPLACE INTO messages_v2 VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, 0)");
SQLitePreparedStatement state_messages = database.executeFast("REPLACE INTO messages_v2 VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, 0, ?)");
SQLitePreparedStatement state_media = database.executeFast("REPLACE INTO media_v4 VALUES(?, ?, ?, ?, ?)");
SQLitePreparedStatement state_polls = null;
SQLitePreparedStatement state_webpage = null;
@ -11042,7 +11285,7 @@ public class MessagesStorage extends BaseController {
}
if (load_type == -2) {
SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT mid, data, ttl, mention, read_state, send_state FROM messages_v2 WHERE mid = %d AND uid = %d", message.id, MessageObject.getDialogId(message)));
SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT mid, data, ttl, mention, read_state, send_state, custom_params FROM messages_v2 WHERE mid = %d AND uid = %d", message.id, MessageObject.getDialogId(message)));
boolean exist;
if (exist = cursor.next()) {
NativeByteBuffer data = cursor.byteBufferValue(1);
@ -11066,6 +11309,11 @@ public class MessagesStorage extends BaseController {
if (!sameMedia) {
addFilesToDelete(oldMessage, filesToDelete, idsToDelete, namesToDelete, false);
}
NativeByteBuffer customParams = cursor.byteBufferValue(6);
MessageCustomParamsHelper.readLocalParams(message, customParams);
if (customParams != null) {
customParams.reuse();
}
}
boolean oldMention = cursor.intValue(3) != 0;
int readState = cursor.intValue(4);
@ -11180,6 +11428,12 @@ public class MessagesStorage extends BaseController {
state_messages.bindInteger(14, 0);
}
state_messages.bindLong(15, MessageObject.getChannelId(message));
NativeByteBuffer customParams = MessageCustomParamsHelper.writeLocalParams(message);
if (customParams == null) {
state_messages.bindNull(16);
} else {
state_messages.bindByteBuffer(16, customParams);
}
state_messages.step();
if (MediaDataController.canAddMessageToMedia(message)) {
@ -11201,6 +11455,9 @@ public class MessagesStorage extends BaseController {
if (repliesData != null) {
repliesData.reuse();
}
if (customParams != null) {
customParams.reuse();
}
data.reuse();
if (message.ttl_period != 0 && message.id > 0) {
@ -11683,7 +11940,7 @@ public class MessagesStorage extends BaseController {
}
if (!dialogs.dialogs.isEmpty()) {
SQLitePreparedStatement state_messages = database.executeFast("REPLACE INTO messages_v2 VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, 0)");
SQLitePreparedStatement state_messages = database.executeFast("REPLACE INTO messages_v2 VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, 0, NULL)");
SQLitePreparedStatement state_dialogs = database.executeFast("REPLACE INTO dialogs VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
SQLitePreparedStatement state_media = database.executeFast("REPLACE INTO media_v4 VALUES(?, ?, ?, ?, ?)");
SQLitePreparedStatement state_settings = database.executeFast("REPLACE INTO dialog_settings VALUES(?, ?)");
@ -12297,6 +12554,8 @@ public class MessagesStorage extends BaseController {
}
String savedMessages = LocaleController.getString("SavedMessages", R.string.SavedMessages).toLowerCase();
String savedMessages2 = "saved messages";
String replies = LocaleController.getString("RepliesTitle", R.string.RepliesTitle).toLowerCase();
String replies2 = "replies";
String search2 = LocaleController.getInstance().getTranslitString(search1);
if (search1.equals(search2) || search2.length() == 0) {
search2 = null;
@ -12360,6 +12619,21 @@ public class MessagesStorage extends BaseController {
resultCount++;
}
if (dialogsType != 4 && (replies).startsWith(search1) || replies2.startsWith(search1)) {
TLRPC.User user = getMessagesController().getUser(708513L);
if (user == null) {
user = getMessagesController().getUser(1271266957L);
}
if (user != null) {
DialogsSearchAdapter.DialogSearchResult dialogSearchResult = new DialogsSearchAdapter.DialogSearchResult();
dialogSearchResult.date = Integer.MAX_VALUE;
dialogSearchResult.name = LocaleController.getString("RepliesTitle", R.string.RepliesTitle);
dialogSearchResult.object = user;
dialogsResult.put(user.id, dialogSearchResult);
resultCount++;
}
}
if (!usersToLoad.isEmpty()) {
cursor = getDatabase().queryFinalized(String.format(Locale.US, "SELECT data, status, name, uid FROM users WHERE uid IN(%s)", TextUtils.join(",", usersToLoad)));
while (cursor.next()) {

View File

@ -35,6 +35,8 @@ import android.os.SystemClock;
import android.service.media.MediaBrowserService;
import android.text.TextUtils;
import androidx.collection.LongSparseArray;
import org.telegram.SQLite.SQLiteCursor;
import org.telegram.messenger.audioinfo.AudioInfo;
import org.telegram.tgnet.NativeByteBuffer;
@ -47,8 +49,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import androidx.collection.LongSparseArray;
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class MusicBrowserService extends MediaBrowserService implements NotificationCenter.NotificationCenterDelegate {
@ -297,7 +297,7 @@ public class MusicBrowserService extends MediaBrowserService implements Notifica
}
Bitmap bitmap = null;
if (avatar != null) {
bitmap = createRoundBitmap(FileLoader.getPathToAttach(avatar, true));
bitmap = createRoundBitmap(FileLoader.getInstance(currentAccount).getPathToAttach(avatar, true));
if (bitmap != null) {
builder.setIconBitmap(bitmap);
}

View File

@ -241,7 +241,7 @@ public class MusicPlayerService extends Service implements NotificationCenter.No
albumArt = loadArtworkFromUrl(artworkUrlBig, false, !forBitmap);
}
} else {
loadingFilePath = FileLoader.getPathToAttach(messageObject.getDocument()).getAbsolutePath();
loadingFilePath = FileLoader.getInstance(UserConfig.selectedAccount).getPathToAttach(messageObject.getDocument()).getAbsolutePath();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

View File

@ -124,6 +124,7 @@ public class NotificationCenter {
public static final int stickersImportComplete = totalEvents++;
public static final int dialogDeleted = totalEvents++;
public static final int webViewResultSent = totalEvents++;
public static final int voiceTranscriptionUpdate = totalEvents++;
public static final int didGenerateFingerprintKeyPair = totalEvents++;
@ -246,6 +247,12 @@ public class NotificationCenter {
public static final int onActivityResultReceived = totalEvents++;
public static final int onRequestPermissionResultReceived = totalEvents++;
public static final int onUserRingtonesUpdated = totalEvents++;
public static final int currentUserPremiumStatusChanged = totalEvents++;
public static final int premiumPromoUpdated = totalEvents++;
public static final int premiumStatusChangedGlobal = totalEvents++;
public static final int currentUserShowLimitReachedDialog = totalEvents++;
public static final int billingProductDetailsUpdated = totalEvents++;
public static final int premiumStickersPreviewLoaded = totalEvents++;
// custom

View File

@ -34,8 +34,6 @@ import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.BitmapDrawable;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.media.SoundPool;
import android.net.Uri;
import android.os.Build;
@ -43,7 +41,6 @@ import android.os.PowerManager;
import android.os.SystemClock;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.text.TextUtils;
@ -3788,7 +3785,7 @@ public class NotificationsController extends BaseController {
mBuilder.setLargeIcon(img.getBitmap());
} else {
try {
File file = FileLoader.getPathToAttach(photoPath, true);
File file = getFileLoader().getPathToAttach(photoPath, true);
if (file.exists()) {
float scaleFactor = 160.0f / AndroidUtilities.dp(50);
BitmapFactory.Options options = new BitmapFactory.Options();
@ -4173,7 +4170,7 @@ public class NotificationsController extends BaseController {
}
if (photoPath != null) {
avatalFile = FileLoader.getPathToAttach(photoPath, true);
avatalFile = getFileLoader().getPathToAttach(photoPath, true);
if (Build.VERSION.SDK_INT < 28) {
BitmapDrawable img = ImageLoader.getInstance().getImageFromMemory(photoPath, null, "50_50");
if (img != null) {
@ -4245,7 +4242,7 @@ public class NotificationsController extends BaseController {
try {
if (sender != null && sender.photo != null && sender.photo.photo_small != null && sender.photo.photo_small.volume_id != 0 && sender.photo.photo_small.local_id != 0) {
Person.Builder personBuilder = new Person.Builder().setName(LocaleController.getString("FromYou", R.string.FromYou));
File avatar = FileLoader.getPathToAttach(sender.photo.photo_small, true);
File avatar = getFileLoader().getPathToAttach(sender.photo.photo_small, true);
loadRoundAvatar(avatar, personBuilder);
selfPerson = personBuilder.build();
personCache.put(selfUserId, selfPerson);
@ -4348,7 +4345,7 @@ public class NotificationsController extends BaseController {
}
}
if (sender != null && sender.photo != null && sender.photo.photo_small != null && sender.photo.photo_small.volume_id != 0 && sender.photo.photo_small.local_id != 0) {
avatar = FileLoader.getPathToAttach(sender.photo.photo_small, true);
avatar = getFileLoader().getPathToAttach(sender.photo.photo_small, true);
}
}
loadRoundAvatar(avatar, personBuilder);
@ -4361,7 +4358,7 @@ public class NotificationsController extends BaseController {
boolean setPhoto = false;
if (preview[0] && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && !((ActivityManager) ApplicationLoader.applicationContext.getSystemService(Context.ACTIVITY_SERVICE)).isLowRamDevice()) {
if (!waitingForPasscode && !messageObject.isSecretMedia() && (messageObject.type == 1 || messageObject.isSticker())) {
File attach = FileLoader.getPathToMessage(messageObject.messageOwner);
File attach = getFileLoader().getPathToMessage(messageObject.messageOwner);
NotificationCompat.MessagingStyle.Message msg = new NotificationCompat.MessagingStyle.Message(message, ((long) messageObject.messageOwner.date) * 1000L, person);
String mimeType = messageObject.isSticker() ? "image/webp" : "image/jpeg";
Uri uri;
@ -4404,7 +4401,7 @@ public class NotificationsController extends BaseController {
if (preview[0] && !waitingForPasscode && messageObject.isVoice()) {
List<NotificationCompat.MessagingStyle.Message> messages = messagingStyle.getMessages();
if (!messages.isEmpty()) {
File f = FileLoader.getPathToMessage(messageObject.messageOwner);
File f = getFileLoader().getPathToMessage(messageObject.messageOwner);
Uri uri;
if (Build.VERSION.SDK_INT >= 24) {
try {

View File

@ -532,7 +532,7 @@ public class SecretChatHelper extends BaseController {
size.location.local_id = file.key_fingerprint;
String fileName2 = size.location.volume_id + "_" + size.location.local_id;
File cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName + ".jpg");
File cacheFile2 = FileLoader.getPathToAttach(size);
File cacheFile2 = getFileLoader().getPathToAttach(size);
cacheFile.renameTo(cacheFile2);
ImageLoader.getInstance().replaceImageInCache(fileName, fileName2, ImageLocation.getForPhoto(size, newMsg.media.photo), true);
ArrayList<TLRPC.Message> arr = new ArrayList<>();
@ -561,7 +561,7 @@ public class SecretChatHelper extends BaseController {
if (newMsg.attachPath != null && newMsg.attachPath.startsWith(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE).getAbsolutePath())) {
File cacheFile = new File(newMsg.attachPath);
File cacheFile2 = FileLoader.getPathToAttach(newMsg.media.document);
File cacheFile2 = getFileLoader().getPathToAttach(newMsg.media.document);
if (cacheFile.renameTo(cacheFile2)) {
newMsgObj.mediaExists = newMsgObj.attachPathExists;
newMsgObj.attachPathExists = false;
@ -923,7 +923,7 @@ public class SecretChatHelper extends BaseController {
big.w = decryptedMessage.media.w;
big.h = decryptedMessage.media.h;
big.type = "x";
big.size = file.size;
big.size = (int) file.size;
big.location = new TLRPC.TL_fileEncryptedLocation();
big.location.key = decryptedMessage.media.key;
big.location.iv = decryptedMessage.media.iv;

View File

@ -11,6 +11,8 @@ package org.telegram.messenger;
import android.content.ClipDescription;
import android.content.Context;
import android.content.Intent;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PorterDuff;
@ -27,6 +29,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.SystemClock;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.text.TextUtils;
import android.util.Base64;
import android.util.SparseArray;
@ -61,11 +64,13 @@ import org.telegram.ui.Components.AnimatedFileDrawable;
import org.telegram.ui.Components.Bulletin;
import org.telegram.ui.Components.LayoutHelper;
import org.telegram.ui.Components.Point;
import org.telegram.ui.Components.Premium.LimitReachedBottomSheet;
import org.telegram.ui.TwoStepVerificationActivity;
import org.telegram.ui.TwoStepVerificationSetupActivity;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.RandomAccessFile;
@ -1552,7 +1557,7 @@ public boolean retriedToSend;
}
TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 90);
if (thumb instanceof TLRPC.TL_photoSize || thumb instanceof TLRPC.TL_photoSizeProgressive) {
File file = FileLoader.getPathToAttach(thumb, true);
File file = FileLoader.getInstance(currentAccount).getPathToAttach(thumb, true);
if (file.exists()) {
try {
int len = (int) file.length();
@ -1608,8 +1613,8 @@ public boolean retriedToSend;
docFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_VIDEO), mediaLocationKey + docExt);
}
ensureMediaThumbExists(false, finalDocument, docFile.getAbsolutePath(), null, 0);
keyFinal[0] = getKeyForPhotoSize(FileLoader.getClosestPhotoSizeWithSize(finalDocument.thumbs, 320), bitmapFinal, true, true);
ensureMediaThumbExists(getAccountInstance(), false, finalDocument, docFile.getAbsolutePath(), null, 0);
keyFinal[0] = getKeyForPhotoSize(getAccountInstance(), FileLoader.getClosestPhotoSizeWithSize(finalDocument.thumbs, 320), bitmapFinal, true, true);
AndroidUtilities.runOnUIThread(() -> {
if (bitmapFinal[0] != null && keyFinal[0] != null) {
@ -1694,12 +1699,13 @@ public boolean retriedToSend;
}
continue;
}
if (!canSendStickers && (msgObj.isSticker() || msgObj.isAnimatedSticker() || msgObj.isGif() || msgObj.isGame())) {
boolean mediaIsSticker = (msgObj.isSticker() || msgObj.isAnimatedSticker() || msgObj.isGif() || msgObj.isGame());
if (!canSendStickers && mediaIsSticker) {
if (sendResult == 0) {
sendResult = ChatObject.isActionBannedByDefault(chat, ChatObject.ACTION_SEND_STICKERS) ? 4 : 1;
}
continue;
} else if (!canSendMedia && (msgObj.messageOwner.media instanceof TLRPC.TL_messageMediaPhoto || msgObj.messageOwner.media instanceof TLRPC.TL_messageMediaDocument)) {
} else if (!canSendMedia && (msgObj.messageOwner.media instanceof TLRPC.TL_messageMediaPhoto || msgObj.messageOwner.media instanceof TLRPC.TL_messageMediaDocument) && !mediaIsSticker) {
if (sendResult == 0) {
sendResult = ChatObject.isActionBannedByDefault(chat, ChatObject.ACTION_SEND_MEDIA) ? 5 : 2;
}
@ -2221,7 +2227,7 @@ public boolean retriedToSend;
newMsg.attachPath = path;
} else {
TLRPC.FileLocation location1 = photo.sizes.get(photo.sizes.size() - 1).location;
newMsg.attachPath = FileLoader.getPathToAttach(location1, true).toString();
newMsg.attachPath = FileLoader.getInstance(currentAccount).getPathToAttach(location1, true).toString();
}
} else if (document != null) {
newMsg.media = new TLRPC.TL_messageMediaDocument();
@ -2951,8 +2957,10 @@ public boolean retriedToSend;
} else if (button instanceof TLRPC.TL_keyboardButtonBuy) {
if ((messageObject.messageOwner.media.flags & 4) == 0) {
TLRPC.TL_payments_getPaymentForm req = new TLRPC.TL_payments_getPaymentForm();
req.msg_id = messageObject.getId();
req.peer = getMessagesController().getInputPeer(messageObject.messageOwner.peer_id);
TLRPC.TL_inputInvoiceMessage inputInvoice = new TLRPC.TL_inputInvoiceMessage();
inputInvoice.msg_id = messageObject.getId();
inputInvoice.peer = getMessagesController().getInputPeer(messageObject.messageOwner.peer_id);
req.invoice = inputInvoice;
try {
JSONObject jsonObject = new JSONObject();
jsonObject.put("bg_color", Theme.getColor(Theme.key_windowBackgroundWhite));
@ -3298,7 +3306,7 @@ public boolean retriedToSend;
newMsg.attachPath = path;
} else {
TLRPC.FileLocation location1 = photo.sizes.get(photo.sizes.size() - 1).location;
newMsg.attachPath = FileLoader.getPathToAttach(location1, true).toString();
newMsg.attachPath = FileLoader.getInstance(currentAccount).getPathToAttach(location1, true).toString();
}
} else if (game != null) {
newMsg = new TLRPC.TL_message();
@ -3381,7 +3389,7 @@ public boolean retriedToSend;
params.put("ve", ve);
}
if (encryptedChat != null && document.dc_id > 0 && !MessageObject.isStickerDocument(document) && !MessageObject.isAnimatedStickerDocument(document, true)) {
newMsg.attachPath = FileLoader.getPathToAttach(document).toString();
newMsg.attachPath = FileLoader.getInstance(currentAccount).getPathToAttach(document).toString();
} else {
newMsg.attachPath = path;
}
@ -4460,16 +4468,16 @@ public boolean retriedToSend;
ImageLoader.getInstance().loadHttpFile(message.httpLocation, "file", currentAccount);
} else {
if (message.sendRequest != null) {
String location = FileLoader.getPathToAttach(message.photoSize).toString();
String location = FileLoader.getInstance(currentAccount).getPathToAttach(message.photoSize).toString();
putToDelayedMessages(location, message);
getFileLoader().uploadFile(location, false, true, ConnectionsManager.FileTypePhoto);
putToUploadingMessages(message.obj);
} else {
String location = FileLoader.getPathToAttach(message.photoSize).toString();
String location = FileLoader.getInstance(currentAccount).getPathToAttach(message.photoSize).toString();
if (message.sendEncryptedRequest != null && message.photoSize.location.dc_id != 0) {
File file = new File(location);
if (!file.exists()) {
location = FileLoader.getPathToAttach(message.photoSize, true).toString();
location = FileLoader.getInstance(currentAccount).getPathToAttach(message.photoSize, true).toString();
file = new File(location);
}
if (!file.exists()) {
@ -4697,7 +4705,7 @@ public boolean retriedToSend;
TLRPC.TL_messages_sendEncryptedMultiMedia request = (TLRPC.TL_messages_sendEncryptedMultiMedia) message.sendEncryptedRequest;
inputMedia = request.files.get(index);
}
String location = FileLoader.getPathToAttach(message.photoSize).toString();
String location = FileLoader.getInstance(currentAccount).getPathToAttach(message.photoSize).toString();
putToDelayedMessages(location, message);
message.extraHashMap.put(location, inputMedia);
message.extraHashMap.put(messageObject, location);
@ -5608,7 +5616,7 @@ public boolean retriedToSend;
File cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName + ".jpg");
File cacheFile2;
if (sentMessage.media.ttl_seconds == 0 && (sentMessage.media.photo.sizes.size() == 1 || size.w > 90 || size.h > 90)) {
cacheFile2 = FileLoader.getPathToAttach(size);
cacheFile2 = FileLoader.getInstance(currentAccount).getPathToAttach(size);
} else {
cacheFile2 = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName2 + ".jpg");
}
@ -5703,7 +5711,7 @@ public boolean retriedToSend;
save = true;
}
if (save) {
getMediaDataController().addRecentGif(sentMessage.media.document, sentMessage.date);
getMediaDataController().addRecentGif(sentMessage.media.document, sentMessage.date, true);
}
} else if (MessageObject.isStickerDocument(sentMessage.media.document) || MessageObject.isAnimatedStickerDocument(sentMessage.media.document, true)) {
getMediaDataController().addRecentSticker(MediaDataController.TYPE_IMAGE, sentMessage, sentMessage.media.document, sentMessage.date, false);
@ -5712,7 +5720,7 @@ public boolean retriedToSend;
if (newMsg.attachPath != null && newMsg.attachPath.startsWith(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE).getAbsolutePath())) {
File cacheFile = new File(newMsg.attachPath);
File cacheFile2 = FileLoader.getPathToAttach(sentMessage.media.document, sentMessage.media.ttl_seconds != 0);
File cacheFile2 = FileLoader.getInstance(currentAccount).getPathToAttach(sentMessage.media.document, sentMessage.media.ttl_seconds != 0);
if (!cacheFile.renameTo(cacheFile2)) {
if (cacheFile.exists()) {
sentMessage.attachPath = newMsg.attachPath;
@ -6001,14 +6009,26 @@ public boolean retriedToSend;
}
}
private static boolean prepareSendingDocumentInternal(AccountInstance accountInstance, String path, String originalPath, Uri uri, String mime, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, CharSequence caption, final ArrayList<TLRPC.MessageEntity> entities, final MessageObject editingMessageObject, long[] groupId, boolean isGroupFinal, boolean forceDocument, boolean notify, int scheduleDate, Integer[] docType) {
private final static int ERROR_TYPE_UNSUPPORTED = 1;
private final static int ERROR_TYPE_FILE_TOO_LARGE = 2;
private static int prepareSendingDocumentInternal(AccountInstance accountInstance, String path, String originalPath, Uri uri, String mime, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, CharSequence caption, final ArrayList<TLRPC.MessageEntity> entities, final MessageObject editingMessageObject, long[] groupId, boolean isGroupFinal, boolean forceDocument, boolean notify, int scheduleDate, Integer[] docType) {
if ((path == null || path.length() == 0) && uri == null) {
return false;
return ERROR_TYPE_UNSUPPORTED;
}
if (uri != null && AndroidUtilities.isInternalUri(uri)) {
return ERROR_TYPE_UNSUPPORTED;
}
if (path != null && AndroidUtilities.isInternalUri(Uri.fromFile(new File(path)))) {
return ERROR_TYPE_UNSUPPORTED;
}
MimeTypeMap myMime = MimeTypeMap.getSingleton();
TLRPC.TL_documentAttributeAudio attributeAudio = null;
String extension = null;
if (uri != null && path == null) {
if (checkFileSize(accountInstance, uri)) {
return ERROR_TYPE_FILE_TOO_LARGE;
}
boolean hasExt = false;
if (mime != null) {
extension = myMime.getExtensionFromMimeType(mime);
@ -6020,7 +6040,7 @@ public boolean retriedToSend;
}
path = MediaController.copyFileToCache(uri, extension);
if (path == null) {
return false;
return ERROR_TYPE_UNSUPPORTED;
}
if (!hasExt) {
extension = null;
@ -6028,7 +6048,11 @@ public boolean retriedToSend;
}
final File f = new File(path);
if (!f.exists() || f.length() == 0) {
return false;
return ERROR_TYPE_UNSUPPORTED;
}
if (!FileLoader.checkUploadFileSize(accountInstance.getCurrentAccount(), f.length())) {
return ERROR_TYPE_FILE_TOO_LARGE;
}
boolean isEncrypted = DialogObject.isEncryptedDialog(dialogId);
@ -6127,7 +6151,7 @@ public boolean retriedToSend;
parentObject = (String) sentData[1];
}
}
ensureMediaThumbExists(isEncrypted, document, path, null, 0);
ensureMediaThumbExists(accountInstance, isEncrypted, document, path, null, 0);
}
if (document == null) {
document = new TLRPC.TL_document();
@ -6137,7 +6161,7 @@ public boolean retriedToSend;
fileName.file_name = name;
document.file_reference = new byte[0];
document.attributes.add(fileName);
document.size = (int) f.length();
document.size = f.length();
document.dc_id = 0;
if (attributeAudio != null) {
document.attributes.add(attributeAudio);
@ -6264,7 +6288,31 @@ public boolean retriedToSend;
accountInstance.getSendMessagesHelper().sendMessage(documentFinal, null, pathFinal, dialogId, replyToMsg, replyToTopMsg, captionFinal, entities, null, params, notify, scheduleDate, 0, parentFinal, null);
}
});
return true;
return 0;
}
private static boolean checkFileSize(AccountInstance accountInstance, Uri uri) {
long len = 0;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
try {
AssetFileDescriptor assetFileDescriptor = ApplicationLoader.applicationContext.getContentResolver().openAssetFileDescriptor(uri, "r", null);
if (assetFileDescriptor != null ) {
len = assetFileDescriptor.getLength();
}
Cursor cursor = ApplicationLoader.applicationContext.getContentResolver().query(uri, new String[]{OpenableColumns.SIZE}, null, null, null);
int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
cursor.moveToFirst();
len = cursor.getLong(sizeIndex);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
if (!FileLoader.checkUploadFileSize(accountInstance.getCurrentAccount(), len)) {
return true;
}
return false;
}
@UiThread
@ -6314,7 +6362,7 @@ public boolean retriedToSend;
if (sentData != null && sentData[0] instanceof TLRPC.TL_document) {
document = (TLRPC.TL_document) sentData[0];
parentObject = (String) sentData[1];
ensureMediaThumbExists(isEncrypted, document, originalPath, null, 0);
ensureMediaThumbExists(accountInstance, isEncrypted, document, originalPath, null, 0);
}
}
if (document == null) {
@ -6380,8 +6428,8 @@ public boolean retriedToSend;
if (paths == null && originalPaths == null && uris == null || paths != null && originalPaths != null && paths.size() != originalPaths.size()) {
return;
}
new Thread(() -> {
boolean error = false;
Utilities.globalQueue.postRunnable(() -> {
int error = 0;
long[] groupId = new long[1];
int mediaCount = 0;
Integer[] docType = new Integer[1];
@ -6401,9 +6449,7 @@ public boolean retriedToSend;
}
mediaCount++;
long prevGroupId = groupId[0];
if (!prepareSendingDocumentInternal(accountInstance, paths.get(a), originalPaths.get(a), null, mime, dialogId, replyToMsg, replyToTopMsg, captionFinal, null, editingMessageObject, groupId, mediaCount == 10 || a == count - 1, inputContent == null, notify, scheduleDate, docType)) {
error = true;
}
error = prepareSendingDocumentInternal(accountInstance, paths.get(a), originalPaths.get(a), null, mime, dialogId, replyToMsg, replyToTopMsg, captionFinal, null, editingMessageObject, groupId, mediaCount == 10 || a == count - 1, inputContent == null, notify, scheduleDate, docType);
if (prevGroupId != groupId[0] || groupId[0] == -1) {
mediaCount = 1;
}
@ -6424,9 +6470,7 @@ public boolean retriedToSend;
}
mediaCount++;
long prevGroupId = groupId[0];
if (!prepareSendingDocumentInternal(accountInstance, null, null, uris.get(a), mime, dialogId, replyToMsg, replyToTopMsg, captionFinal, null, editingMessageObject, groupId, mediaCount == 10 || a == count - 1, inputContent == null, notify, scheduleDate, docType)) {
error = true;
}
error = prepareSendingDocumentInternal(accountInstance, null, null, uris.get(a), mime, dialogId, replyToMsg, replyToTopMsg, captionFinal, null, editingMessageObject, groupId, mediaCount == 10 || a == count - 1, inputContent == null, notify, scheduleDate, docType);
if (prevGroupId != groupId[0] || groupId[0] == -1) {
mediaCount = 1;
}
@ -6435,16 +6479,25 @@ public boolean retriedToSend;
if (inputContent != null) {
inputContent.releasePermission();
}
if (error) {
AndroidUtilities.runOnUIThread(() -> {
try {
handleError(error, accountInstance);
});
}
private static void handleError(int error, AccountInstance accountInstance) {
if (error != 0) {
int finalError = error;
AndroidUtilities.runOnUIThread(() -> {
try {
if (finalError == ERROR_TYPE_UNSUPPORTED) {
NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.showBulletin, Bulletin.TYPE_ERROR, LocaleController.getString("UnsupportedAttachment", R.string.UnsupportedAttachment));
} catch (Exception e) {
FileLog.e(e);
} else if (finalError == ERROR_TYPE_FILE_TOO_LARGE) {
NotificationCenter.getInstance(accountInstance.getCurrentAccount()).postNotificationName(NotificationCenter.currentUserShowLimitReachedDialog, LimitReachedBottomSheet.TYPE_LARGE_FILE);
}
});
}
}).start();
} catch (Exception e) {
FileLog.e(e);
}
});
}
}
@UiThread
@ -6745,12 +6798,12 @@ public boolean retriedToSend;
if (MessageObject.isGifDocument(document)) {
TLRPC.PhotoSize photoSizeThumb = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 320);
File gifFile = FileLoader.getPathToAttach(document);
File gifFile = FileLoader.getInstance(accountInstance.getCurrentAccount()).getPathToAttach(document);
if (!gifFile.exists()) {
gifFile = FileLoader.getPathToAttach(document, true);
gifFile = FileLoader.getInstance(accountInstance.getCurrentAccount()).getPathToAttach(document, true);
}
ensureMediaThumbExists(isEncrypted, document, gifFile.getAbsolutePath(), null, 0);
precachedKey[0] = getKeyForPhotoSize(photoSizeThumb, precahcedThumb, true, true);
ensureMediaThumbExists(accountInstance, isEncrypted, document, gifFile.getAbsolutePath(), null, 0);
precachedKey[0] = getKeyForPhotoSize(accountInstance, photoSizeThumb, precahcedThumb, true, true);
}
AndroidUtilities.runOnUIThread(() -> {
if (finalDocument != null) {
@ -6864,7 +6917,7 @@ public boolean retriedToSend;
})));
}
public static void ensureMediaThumbExists(boolean isEncrypted, TLObject object, String path, Uri uri, long startTime) {
public static void ensureMediaThumbExists(AccountInstance accountInstance, boolean isEncrypted, TLObject object, String path, Uri uri, long startTime) {
if (object instanceof TLRPC.TL_photo) {
TLRPC.TL_photo photo = (TLRPC.TL_photo) object;
boolean smallExists;
@ -6872,11 +6925,11 @@ public boolean retriedToSend;
if (smallSize instanceof TLRPC.TL_photoStrippedSize || smallSize instanceof TLRPC.TL_photoPathSize) {
smallExists = true;
} else {
File smallFile = FileLoader.getPathToAttach(smallSize, true);
File smallFile = FileLoader.getInstance(accountInstance.getCurrentAccount()).getPathToAttach(smallSize, true);
smallExists = smallFile.exists();
}
TLRPC.PhotoSize bigSize = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, AndroidUtilities.getPhotoSize());
File bigFile = FileLoader.getPathToAttach(bigSize, false);
File bigFile = FileLoader.getInstance(accountInstance.getCurrentAccount()).getPathToAttach(bigSize, false);
boolean bigExists = bigFile.exists();
if (!smallExists || !bigExists) {
Bitmap bitmap = ImageLoader.loadBitmap(path, uri, AndroidUtilities.getPhotoSize(), AndroidUtilities.getPhotoSize(), true);
@ -6906,7 +6959,7 @@ public boolean retriedToSend;
if (photoSize instanceof TLRPC.TL_photoStrippedSize || photoSize instanceof TLRPC.TL_photoPathSize) {
return;
}
File smallFile = FileLoader.getPathToAttach(photoSize, true);
File smallFile = FileLoader.getInstance(accountInstance.getCurrentAccount()).getPathToAttach(photoSize, true);
if (!smallFile.exists()) {
Bitmap thumb = createVideoThumbnailAtTime(path, startTime);
if (thumb == null) {
@ -6919,7 +6972,7 @@ public boolean retriedToSend;
}
}
public static String getKeyForPhotoSize(TLRPC.PhotoSize photoSize, Bitmap[] bitmap, boolean blur, boolean forceCache) {
public static String getKeyForPhotoSize(AccountInstance accountInstance, TLRPC.PhotoSize photoSize, Bitmap[] bitmap, boolean blur, boolean forceCache) {
if (photoSize == null || photoSize.location == null) {
return null;
}
@ -6929,7 +6982,7 @@ public boolean retriedToSend;
try {
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
File file = FileLoader.getPathToAttach(photoSize, forceCache);
File file = FileLoader.getInstance(accountInstance.getCurrentAccount()).getPathToAttach(photoSize, forceCache);
FileInputStream is = new FileInputStream(file);
BitmapFactory.decodeStream(is, null, opts);
@ -7060,7 +7113,7 @@ public boolean retriedToSend;
parentObject = (String) sentData[1];
}
}
ensureMediaThumbExists(isEncrypted, photo, info.path, info.uri, 0);
ensureMediaThumbExists(accountInstance, isEncrypted, photo, info.path, info.uri, 0);
}
final MediaSendPrepareWorker worker = new MediaSendPrepareWorker();
workers.put(info, worker);
@ -7108,7 +7161,7 @@ public boolean retriedToSend;
File cacheFile;
if (info.searchImage.document instanceof TLRPC.TL_document) {
document = (TLRPC.TL_document) info.searchImage.document;
cacheFile = FileLoader.getPathToAttach(document, true);
cacheFile = FileLoader.getInstance(accountInstance.getCurrentAccount()).getPathToAttach(document, true);
} else {
/*if (!isEncrypted) {
Object[] sentData = getMessagesStorage().getSentFile(info.searchImage.imageUrl, !isEncrypted ? 1 : 4);
@ -7286,7 +7339,7 @@ public boolean retriedToSend;
if (!forceDocument && (videoEditedInfo != null || info.path.endsWith("mp4"))) {
if (info.path == null && info.searchImage != null) {
if (info.searchImage.photo instanceof TLRPC.TL_photo) {
info.path = FileLoader.getPathToAttach(info.searchImage.photo, true).getAbsolutePath();
info.path = FileLoader.getInstance(accountInstance.getCurrentAccount()).getPathToAttach(info.searchImage.photo, true).getAbsolutePath();
} else {
String md5 = Utilities.MD5(info.searchImage.imageUrl) + "." + ImageLoader.getHttpUrlExtension(info.searchImage.imageUrl, "jpg");
info.path = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), md5).getAbsolutePath();
@ -7314,7 +7367,7 @@ public boolean retriedToSend;
if (sentData != null && sentData[0] instanceof TLRPC.TL_document) {
document = (TLRPC.TL_document) sentData[0];
parentObject = (String) sentData[1];
ensureMediaThumbExists(isEncrypted, document, info.path, null, startTime);
ensureMediaThumbExists(accountInstance, isEncrypted, document, info.path, null, startTime);
}
}
if (document == null) {
@ -7332,7 +7385,7 @@ public boolean retriedToSend;
if (thumb != null) {
int side = isEncrypted || info.ttl != 0 ? 90 : Math.max(thumb.getWidth(), thumb.getHeight());
size = ImageLoader.scaleAndSaveImage(thumb, side, side, side > 90 ? 80 : 55, isEncrypted);
thumbKey = getKeyForPhotoSize(size, null, true, false);
thumbKey = getKeyForPhotoSize(accountInstance, size, null, true, false);
}
document = new TLRPC.TL_document();
document.file_reference = new byte[0];
@ -7578,7 +7631,7 @@ public boolean retriedToSend;
parentObject = (String) sentData[1];
}
}
ensureMediaThumbExists(isEncrypted, photo, info.path, info.uri, 0);
ensureMediaThumbExists(accountInstance, isEncrypted, photo, info.path, info.uri, 0);
}
if (photo == null) {
photo = accountInstance.getSendMessagesHelper().generatePhotoSizes(info.path, info.uri);
@ -7613,7 +7666,7 @@ public boolean retriedToSend;
if (!groupMediaFinal || media.size() == 1) {
TLRPC.PhotoSize currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(photoFinal.sizes, AndroidUtilities.getPhotoSize());
if (currentPhotoObject != null) {
keyFinal[0] = getKeyForPhotoSize(currentPhotoObject, bitmapFinal, false, false);
keyFinal[0] = getKeyForPhotoSize(accountInstance, currentPhotoObject, bitmapFinal, false, false);
}
}
} catch (Exception e) {
@ -7671,7 +7724,8 @@ public boolean retriedToSend;
mediaCount = 0;
}
mediaCount++;
prepareSendingDocumentInternal(accountInstance, sendAsDocuments.get(a), sendAsDocumentsOriginal.get(a), sendAsDocumentsUri.get(a), extension, dialogId, replyToMsg, replyToTopMsg, sendAsDocumentsCaptions.get(a), sendAsDocumentsEntities.get(a), editingMessageObject, groupId2, mediaCount == 10 || a == documentsCount - 1, forceDocument, notify, scheduleDate, null);
int error = prepareSendingDocumentInternal(accountInstance, sendAsDocuments.get(a), sendAsDocumentsOriginal.get(a), sendAsDocumentsUri.get(a), extension, dialogId, replyToMsg, replyToTopMsg, sendAsDocumentsCaptions.get(a), sendAsDocumentsEntities.get(a), editingMessageObject, groupId2, mediaCount == 10 || a == documentsCount - 1, forceDocument, notify, scheduleDate, null);
handleError(error, accountInstance);
}
}
if (BuildVars.LOGS_ENABLED) {
@ -7967,7 +8021,7 @@ public boolean retriedToSend;
if (sentData != null && sentData[0] instanceof TLRPC.TL_document) {
document = (TLRPC.TL_document) sentData[0];
parentObject = (String) sentData[1];
ensureMediaThumbExists(isEncrypted, document, videoPath, null, startTime);
ensureMediaThumbExists(accountInstance, isEncrypted, document, videoPath, null, startTime);
}
}
if (document == null) {

View File

@ -19,6 +19,7 @@ import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Base64;
import android.util.SparseArray;
import android.webkit.WebView;
import androidx.annotation.Nullable;
import androidx.core.content.pm.ShortcutManagerCompat;
@ -84,6 +85,10 @@ public class SharedConfig {
})
public @interface PasscodeType {}
public final static int SAVE_TO_GALLERY_FLAG_PEER = 1;
public final static int SAVE_TO_GALLERY_FLAG_GROUP = 2;
public final static int SAVE_TO_GALLERY_FLAG_CHANNELS = 4;
public static String pushString = "";
public static String pushStringStatus = "";
public static long pushStringGetTimeStart;
@ -135,7 +140,7 @@ public class SharedConfig {
private static final Object sync = new Object();
private static final Object localIdSync = new Object();
public static boolean saveToGallery;
public static int saveToGalleryFlags;
public static int mapPreviewType = 2;
public static boolean chatBubbles = Build.VERSION.SDK_INT >= 30;
public static boolean autoplayGifs = true;
@ -156,6 +161,7 @@ public class SharedConfig {
public static boolean noiseSupression;
public static boolean noStatusBar = true;
public static boolean forceRtmpStream;
public static boolean debugWebView;
public static boolean sortContactsByName;
public static boolean sortFilesByName;
public static boolean shuffleMusic;
@ -1206,7 +1212,13 @@ public class SharedConfig {
}
preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
saveToGallery = preferences.getBoolean("save_gallery", false);
boolean saveToGalleryLegacy = preferences.getBoolean("save_gallery", false);
if (saveToGalleryLegacy) {
saveToGalleryFlags = SAVE_TO_GALLERY_FLAG_PEER + SAVE_TO_GALLERY_FLAG_CHANNELS + SAVE_TO_GALLERY_FLAG_GROUP;
preferences.edit().remove("save_gallery").putInt("save_gallery_flags", saveToGalleryFlags).apply();
} else {
saveToGalleryFlags = preferences.getInt("save_gallery_flags", 0);
}
autoplayGifs = preferences.getBoolean("autoplay_gif", true);
autoplayVideo = preferences.getBoolean("autoplay_video", true);
mapPreviewType = preferences.getInt("mapPreviewType", 2);
@ -1244,6 +1256,7 @@ public class SharedConfig {
keepMedia = preferences.getInt("keep_media", 2);
noStatusBar = NekoConfig.transparentStatusBar.Bool();
forceRtmpStream = preferences.getBoolean("forceRtmpStream", false);
debugWebView = preferences.getBoolean("debugWebView", false);
lastKeepMediaCheckTime = preferences.getInt("lastKeepMediaCheckTime", 0);
lastLogsCheckTime = preferences.getInt("lastLogsCheckTime", 0);
searchMessagesAsListHintShows = preferences.getInt("searchMessagesAsListHintShows", 0);
@ -1300,6 +1313,13 @@ public class SharedConfig {
configLoaded = true;
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && debugWebView) {
WebView.setWebContentsDebuggingEnabled(true);
}
} catch (Exception e) {
FileLog.e(e);
}
}
}
@ -1494,7 +1514,7 @@ public class SharedConfig {
editor.commit();
}
public static void removeScheduledOrNoSuoundHint() {
public static void removeScheduledOrNoSoundHint() {
SharedPreferences preferences = MessagesController.getGlobalMainSettings();
SharedPreferences.Editor editor = preferences.edit();
editor.putInt("scheduledOrNoSoundHintShows", 3);
@ -1627,6 +1647,17 @@ public class SharedConfig {
editor.apply();
}
public static void toggleDebugWebView() {
debugWebView = !debugWebView;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(debugWebView);
}
SharedPreferences preferences = MessagesController.getGlobalMainSettings();
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean("debugWebView", debugWebView);
editor.apply();
}
// public static void toggleNoStatusBar() {
// noStatusBar = !noStatusBar;
// noStatusBar |= NekoConfig.transparentStatusBar.Bool();
@ -1682,12 +1713,14 @@ public class SharedConfig {
editor.commit();
}
public static void toggleSaveToGallery() {
saveToGallery = !saveToGallery;
public static void toggleSaveToGalleryFlag(int flag) {
if ((saveToGalleryFlags & flag) != 0) {
saveToGalleryFlags &= ~flag;
} else {
saveToGalleryFlags |= flag;
}
SharedPreferences preferences = MessagesController.getGlobalMainSettings();
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean("save_gallery", saveToGallery);
editor.commit();
preferences.edit().putInt("save_gallery_flags", saveToGalleryFlags).apply();
ImageLoader.getInstance().checkMediaPaths();
ImageLoader.getInstance().getCacheOutQueue().postRunnable(() -> {
checkSaveToGalleryFiles();
@ -2209,7 +2242,7 @@ public class SharedConfig {
File videoPath = new File(telegramPath, "videos");
videoPath.mkdirs();
if (saveToGallery) {
if (saveToGalleryFlags != 0 || !BuildVars.NO_SCOPED_STORAGE) {
if (imagePath.isDirectory()) {
new File(imagePath, ".nomedia").delete();
}
@ -2338,6 +2371,7 @@ public class SharedConfig {
public static boolean canBlurChat() {
return getDevicePerformanceClass() == PERFORMANCE_CLASS_HIGH || NekoConfig.forceBlurInChat.Bool();
}
public static boolean chatBlurEnabled() {
return (canBlurChat() && chatBlur) || NekoConfig.forceBlurInChat.Bool();
}

View File

@ -102,6 +102,7 @@ public class SvgHelper {
protected ArrayList<Object> commands = new ArrayList<>();
protected HashMap<Object, Paint> paints = new HashMap<>();
private Paint overridePaint;
protected int width;
protected int height;
private static int[] parentPosition = new int[2];
@ -148,9 +149,7 @@ public class SvgHelper {
setupGradient(currentColorKey, colorAlpha);
}
Rect bounds = getBounds();
float scaleX = bounds.width() / (float) width;
float scaleY = bounds.height() / (float) height;
float scale = aspectFill ? Math.max(scaleX, scaleY) : Math.min(scaleX, scaleY);
float scale = getScale();
canvas.save();
canvas.translate(bounds.left, bounds.top);
if (!aspectFill) {
@ -165,7 +164,12 @@ public class SvgHelper {
} else if (object == null) {
canvas.restore();
} else {
Paint paint = paints.get(object);
Paint paint;
if (overridePaint != null) {
paint = overridePaint;
} else {
paint = paints.get(object);
}
int originalAlpha = paint.getAlpha();
paint.setAlpha((int) (crossfadeAlpha * originalAlpha));
if (object instanceof Path) {
@ -222,6 +226,13 @@ public class SvgHelper {
}
}
public float getScale() {
Rect bounds = getBounds();
float scaleX = bounds.width() / (float) width;
float scaleY = bounds.height() / (float) height;
return aspectFill ? Math.max(scaleX, scaleY) : Math.min(scaleX, scaleY);
}
@Override
public void setAlpha(int alpha) {
crossfadeAlpha = alpha / 255.0f;
@ -283,6 +294,10 @@ public class SvgHelper {
}
}
}
public void setPaint(Paint paint) {
overridePaint = paint;
}
}
public static Bitmap getBitmap(int res, int width, int height, int color) {

View File

@ -22,7 +22,8 @@ import java.util.Arrays;
public class UserConfig extends BaseController {
public static int selectedAccount;
//public final static int MAX_ACCOUNT_COUNT = 16;
//public final static int MAX_ACCOUNT_DEFAULT_COUNT = 16;
public final static int MAX_ACCOUNT_COUNT = 4;
private final Object sync = new Object();
private boolean configLoaded;
@ -96,6 +97,19 @@ public class UserConfig extends BaseController {
super(instance);
}
public static boolean hasPremiumOnAccounts() {
for (int a = 0; a < MAX_ACCOUNT_COUNT; a++) {
if (AccountInstance.getInstance(a).getUserConfig().isClientActivated() && AccountInstance.getInstance(a).getUserConfig().getUserConfig().isPremium()) {
return true;
}
}
return false;
}
public static int getMaxAccountCount() {
return hasPremiumOnAccounts() ? 5 : 3;
}
public int getNewMessageId() {
int id;
synchronized (sync) {
@ -223,8 +237,22 @@ public class UserConfig extends BaseController {
public void setCurrentUser(TLRPC.User user) {
synchronized (sync) {
TLRPC.User oldUser = currentUser;
currentUser = user;
clientUserId = user.id;
checkPremium(oldUser, user);
}
}
private void checkPremium(TLRPC.User oldUser, TLRPC.User newUser) {
if (oldUser == null || (newUser != null && oldUser.premium != newUser.premium)) {
AndroidUtilities.runOnUIThread(() -> {
getMessagesController().updatePremium(newUser.premium);
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.currentUserPremiumStatusChanged);
NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.premiumStatusChangedGlobal);
getMediaDataController().loadPremiumPromo(false);
});
}
}
@ -306,6 +334,7 @@ public class UserConfig extends BaseController {
}
}
if (currentUser != null) {
checkPremium(null, currentUser);
clientUserId = currentUser.id;
}
configLoaded = true;
@ -441,4 +470,11 @@ public class UserConfig extends BaseController {
editor.putBoolean("hasValidDialogLoadIds", true);
editor.commit();
}
public boolean isPremium() {
if (currentUser == null) {
return false;
}
return currentUser.premium;
}
}

View File

@ -65,7 +65,7 @@ public class Utilities {
private native static void aesIgeEncryption(ByteBuffer buffer, byte[] key, byte[] iv, boolean encrypt, int offset, int length);
private native static void aesIgeEncryptionByteArray(byte[] buffer, byte[] key, byte[] iv, boolean encrypt, int offset, int length);
public native static void aesCtrDecryption(ByteBuffer buffer, byte[] key, byte[] iv, int offset, int length);
public native static void aesCtrDecryptionByteArray(byte[] buffer, byte[] key, byte[] iv, int offset, int length, int n);
public native static void aesCtrDecryptionByteArray(byte[] buffer, byte[] key, byte[] iv, int offset, long length, int n);
private native static void aesCbcEncryptionByteArray(byte[] buffer, byte[] key, byte[] iv, int offset, int length, int n, int encrypt);
public native static void aesCbcEncryption(ByteBuffer buffer, byte[] key, byte[] iv, int offset, int length, int encrypt);
public native static String readlink(String path);
@ -113,16 +113,47 @@ public class Utilities {
}
int val = 0;
try {
Matcher matcher = pattern.matcher(value);
if (matcher.find()) {
String num = matcher.group(0);
val = Integer.parseInt(num);
int start = -1, end;
for (end = 0; end < value.length(); ++end) {
char character = value.charAt(end);
boolean allowedChar = character == '-' || character >= '0' && character <= '9';
if (allowedChar && start < 0) {
start = end;
} else if (!allowedChar && start >= 0) {
end++;
break;
}
}
} catch (Exception ignore) {
}
if (start >= 0) {
String str = value.subSequence(start, end).toString();
// val = parseInt(str);
val = Integer.parseInt(str);
}
// Matcher matcher = pattern.matcher(value);
// if (matcher.find()) {
// String num = matcher.group(0);
// val = Integer.parseInt(num);
// }
} catch (Exception ignore) {}
return val;
}
private static int parseInt(final String s) {
int num = 0;
boolean negative = true;
final int len = s.length();
final char ch = s.charAt(0);
if (ch == '-') {
negative = false;
} else {
num = '0' - ch;
}
int i = 1;
while (i < len) {
num = num * 10 + '0' - s.charAt(i++);
}
return negative ? -num : num;
}
public static Long parseLong(String value) {
if (value == null) {
@ -283,10 +314,10 @@ public class Utilities {
return computeSHA256(convertme, 0, convertme.length);
}
public static byte[] computeSHA256(byte[] convertme, int offset, int len) {
public static byte[] computeSHA256(byte[] convertme, int offset, long len) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(convertme, offset, len);
md.update(convertme, offset, (int) len);
return md.digest();
} catch (Exception e) {
FileLog.e(e);
@ -403,8 +434,14 @@ public class Utilities {
return null;
}
public static float clamp(float value, float top, float bottom) {
return Math.max(Math.min(value, top), bottom);
public static float clamp(float value, float maxValue, float minValue) {
if (Float.isNaN(value)) {
return minValue;
}
if (Float.isInfinite(value)) {
return maxValue;
}
return Math.max(Math.min(value, maxValue), minValue);
}
public static String generateRandomString() {

View File

@ -54,6 +54,7 @@ public class VideoEditedInfo {
public boolean canceled;
public boolean videoConvertFirstWrite;
public boolean needUpdateProgress = false;
public boolean shouldLimitFps = true;
public static class MediaEntity {
public byte type;

View File

@ -340,7 +340,7 @@ public class Browser {
.build();
builder.setDefaultColorSchemeParams(params);
builder.setShowTitle(true);
builder.setActionButton(BitmapFactory.decodeResource(context.getResources(), R.drawable.abc_ic_menu_share_mtrl_alpha), LocaleController.getString("ShareFile", R.string.ShareFile), PendingIntent.getBroadcast(ApplicationLoader.applicationContext, 0, share, 0), true);
builder.setActionButton(BitmapFactory.decodeResource(context.getResources(), R.drawable.msg_filled_shareout), LocaleController.getString("ShareFile", R.string.ShareFile), PendingIntent.getBroadcast(ApplicationLoader.applicationContext, 0, share, 0), true);
CustomTabsIntent intent = builder.build();
intent.intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.launchUrl(context, uri);

View File

@ -239,7 +239,7 @@ public class CameraController implements MediaRecorder.OnInfoListener {
NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.cameraInitied);
});
} catch (Exception e) {
FileLog.e(e);
FileLog.e(e, !"APP_PAUSED".equals(e.getMessage()));
AndroidUtilities.runOnUIThread(() -> {
onFinishCameraInitRunnables.clear();
loadingCameras = false;

View File

@ -24,7 +24,6 @@ import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.Camera;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaCodec;
@ -42,7 +41,6 @@ import android.os.Looper;
import android.os.Message;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.util.Log;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.Surface;
@ -151,6 +149,8 @@ public class CameraView extends FrameLayout implements TextureView.SurfaceTextur
private FloatBuffer vertexBuffer;
private FloatBuffer textureBuffer;
private final static int audioSampleRate = 44100;
public void setRecordFile(File generateVideoPath) {
recordFile = generateVideoPath;
}
@ -415,8 +415,8 @@ public class CameraView extends FrameLayout implements TextureView.SurfaceTextur
int photoMaxHeight;
if (initialFrontface) {
aspectRatio = new Size(16, 9);
photoMaxWidth = wantedWidth = 480;
photoMaxHeight = wantedHeight = 270;
photoMaxWidth = wantedWidth = 1280;
photoMaxHeight = wantedHeight = 720;
} else {
if (Math.abs(screenSize - size4to3) < 0.1f) {
aspectRatio = new Size(4, 3);
@ -1298,7 +1298,7 @@ public class CameraView extends FrameLayout implements TextureView.SurfaceTextur
}
buffer.offset[a] = audioPresentationTimeUs;
buffer.read[a] = readResult;
int bufferDurationUs = 1000000 * readResult / 44100 / 2;
int bufferDurationUs = 1000000 * readResult / audioSampleRate / 2;
audioPresentationTimeUs += bufferDurationUs;
}
if (buffer.results >= 0 || buffer.last) {
@ -1676,7 +1676,7 @@ public class CameraView extends FrameLayout implements TextureView.SurfaceTextur
private void prepareEncoder() {
try {
int recordBufferSize = AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
int recordBufferSize = AudioRecord.getMinBufferSize(audioSampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
if (recordBufferSize <= 0) {
recordBufferSize = 3584;
}
@ -1687,7 +1687,7 @@ public class CameraView extends FrameLayout implements TextureView.SurfaceTextur
for (int a = 0; a < 3; a++) {
buffers.add(new InstantCameraView.AudioBufferInfo());
}
audioRecorder = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, 44100, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
audioRecorder = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, audioSampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
audioRecorder.startRecording();
if (BuildVars.LOGS_ENABLED) {
FileLog.d("CameraView " + "initied audio record with channels " + audioRecorder.getChannelCount() + " sample rate = " + audioRecorder.getSampleRate() + " bufferSize = " + bufferSize);
@ -1701,7 +1701,7 @@ public class CameraView extends FrameLayout implements TextureView.SurfaceTextur
MediaFormat audioFormat = new MediaFormat();
audioFormat.setString(MediaFormat.KEY_MIME, AUDIO_MIME_TYPE);
audioFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100);
audioFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, audioSampleRate);
audioFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, 32000);
audioFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 2048 * InstantCameraView.AudioBufferInfo.MAX_SAMPLES);

View File

@ -106,7 +106,9 @@ public class RingtoneDataStore {
}
}
if (notify) {
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.onUserRingtonesUpdated);
AndroidUtilities.runOnUIThread(() -> {
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.onUserRingtonesUpdated);
});
}
}
@ -226,7 +228,7 @@ public class RingtoneDataStore {
if (!TextUtils.isEmpty(userRingtones.get(i).localUri)) {
return userRingtones.get(i).localUri;
}
return FileLoader.getPathToAttach(userRingtones.get(i).document).toString();
return FileLoader.getInstance(currentAccount).getPathToAttach(userRingtones.get(i).document).toString();
}
}
return "NoSound";
@ -241,6 +243,9 @@ public class RingtoneDataStore {
Utilities.globalQueue.postRunnable(() -> {
for (int i = 0; i < cachedTones.size(); i++) {
CachedTone tone = cachedTones.get(i);
if (tone == null) {
continue;
}
if (!TextUtils.isEmpty(tone.localUri)) {
File file = new File(tone.localUri);
if (file.exists()) {
@ -250,7 +255,7 @@ public class RingtoneDataStore {
if (tone.document != null) {
TLRPC.Document document = tone.document;
File file = FileLoader.getPathToAttach(document);
File file = FileLoader.getInstance(currentAccount).getPathToAttach(document);
if (file == null || !file.exists()) {
AndroidUtilities.runOnUIThread(() -> {
FileLoader.getInstance(currentAccount).loadFile(document, document, 0, 0);
@ -303,10 +308,14 @@ public class RingtoneDataStore {
loadFromPrefs(true);
loaded = true;
}
for (int i = 0; i < userRingtones.size(); i++) {
if (userRingtones.get(i).document != null && userRingtones.get(i).document.id == id) {
return userRingtones.get(i).document;
try {
for (int i = 0; i < userRingtones.size(); i++) {
if (userRingtones.get(i) != null && userRingtones.get(i).document != null && userRingtones.get(i).document.id == id) {
return userRingtones.get(i).document;
}
}
} catch (Exception e) {
FileLog.e(e);
}
return null;
}

View File

@ -204,8 +204,9 @@ public final class Instance {
public final boolean stun;
public final String username;
public final String password;
public final boolean tcp;
public Endpoint(boolean isRtc, long id, String ipv4, String ipv6, int port, int type, byte[] peerTag, boolean turn, boolean stun, String username, String password) {
public Endpoint(boolean isRtc, long id, String ipv4, String ipv6, int port, int type, byte[] peerTag, boolean turn, boolean stun, String username, String password, boolean tcp) {
this.isRtc = isRtc;
this.id = id;
this.ipv4 = ipv4;
@ -217,6 +218,7 @@ public final class Instance {
this.stun = stun;
this.username = username;
this.password = password;
this.tcp = tcp;
}
@Override
@ -232,6 +234,7 @@ public final class Instance {
", stun=" + stun +
", username=" + username +
", password=" + password +
", tcp=" + tcp +
'}';
}
}

View File

@ -89,7 +89,6 @@ import org.telegram.messenger.BuildConfig;
import org.telegram.messenger.BuildVars;
import org.telegram.messenger.ChatObject;
import org.telegram.messenger.ContactsController;
import org.telegram.messenger.DownloadController;
import org.telegram.messenger.FileLoader;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.ImageLoader;
@ -2351,7 +2350,7 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
final Instance.Endpoint[] endpoints = new Instance.Endpoint[privateCall.connections.size()];
for (int i = 0; i < endpoints.length; i++) {
final TLRPC.PhoneConnection connection = privateCall.connections.get(i);
endpoints[i] = new Instance.Endpoint(connection instanceof TLRPC.TL_phoneConnectionWebrtc, connection.id, connection.ip, connection.ipv6, connection.port, endpointType, connection.peer_tag, connection.turn, connection.stun, connection.username, connection.password);
endpoints[i] = new Instance.Endpoint(connection instanceof TLRPC.TL_phoneConnectionWebrtc, connection.id, connection.ip, connection.ipv6, connection.port, endpointType, connection.peer_tag, connection.turn, connection.stun, connection.username, connection.password, connection.tcp);
}
if (forceTcp) {
AndroidUtilities.runOnUIThread(() -> Toast.makeText(VoIPService.this, "This call uses TCP which will degrade its quality.", Toast.LENGTH_SHORT).show());
@ -3411,62 +3410,15 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
req.peer = new TLRPC.TL_inputPhoneCall();
req.peer.access_hash = privateCall.access_hash;
req.peer.id = privateCall.id;
File file = new File(VoIPHelper.getLogFilePath(privateCall.id, true));
String cachedFile = MediaController.copyFileToCache(Uri.fromFile(file), "log");
ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> {
if (BuildVars.LOGS_ENABLED) {
FileLog.d("Sent debug logs, response = " + response);
}
try {
if (response instanceof TLRPC.TL_boolFalse) {
AndroidUtilities.runOnUIThread(() -> {
uploadLogFile(cachedFile);
});
} else {
File cacheFile = new File(cachedFile);
cacheFile.delete();
}
} catch (Exception e) {
FileLog.e(e);
}
});
needSendDebugLog = false;
}
}
private void uploadLogFile(String filePath) {
NotificationCenter.NotificationCenterDelegate uploadDelegate = new NotificationCenter.NotificationCenterDelegate() {
@Override
public void didReceivedNotification(int id, int account, Object... args) {
if (id == NotificationCenter.fileUploaded || id == NotificationCenter.fileUploadFailed) {
final String location = (String) args[0];
if (location.equals(filePath)) {
if (id == NotificationCenter.fileUploaded) {
TLRPC.TL_phone_saveCallLog req = new TLRPC.TL_phone_saveCallLog();
final TLRPC.InputFile file = (TLRPC.InputFile) args[1];
req.file = file;
req.peer = new TLRPC.TL_inputPhoneCall();
req.peer.access_hash = privateCall.access_hash;
req.peer.id = privateCall.id;
ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> {
if (BuildVars.LOGS_ENABLED) {
FileLog.d("Sent debug file log, response = " + response);
}
});
}
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.fileUploaded);
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.fileUploadFailed);
}
}
}
};
NotificationCenter.getInstance(currentAccount).addObserver(uploadDelegate, NotificationCenter.fileUploaded);
NotificationCenter.getInstance(currentAccount).addObserver(uploadDelegate, NotificationCenter.fileUploadFailed);
FileLoader.getInstance(currentAccount).uploadFile(filePath, false, true, ConnectionsManager.FileTypeFile);
}
private void initializeAccountRelatedThings() {
updateServerConfig();
NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.appDidLogout);
@ -3911,7 +3863,7 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
try {
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inMutable = true;
bitmap = BitmapFactory.decodeFile(FileLoader.getPathToAttach(user.photo.photo_small, true).toString(), opts);
bitmap = BitmapFactory.decodeFile(FileLoader.getInstance(currentAccount).getPathToAttach(user.photo.photo_small, true).toString(), opts);
} catch (Throwable e) {
FileLog.e(e);
}
@ -3927,7 +3879,7 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
try {
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inMutable = true;
bitmap = BitmapFactory.decodeFile(FileLoader.getPathToAttach(chat.photo.photo_small, true).toString(), opts);
bitmap = BitmapFactory.decodeFile(FileLoader.getInstance(currentAccount).getPathToAttach(chat.photo.photo_small, true).toString(), opts);
} catch (Throwable e) {
FileLog.e(e);
}

View File

@ -10,7 +10,6 @@ import android.util.Base64;
import android.util.SparseArray;
import com.v2ray.ang.util.Utils;
import org.telegram.messenger.AccountInstance;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.ApplicationLoader;
@ -114,12 +113,21 @@ public class ConnectionsManager extends BaseController {
}
};
private boolean forceTryIpV6;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
DNS_THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
public void setForceTryIpV6(boolean forceTryIpV6) {
if (this.forceTryIpV6 != forceTryIpV6) {
this.forceTryIpV6 = forceTryIpV6;
checkConnection();
}
}
private static class ResolvedDomain {
public InetAddress[] addresses;
@ -216,6 +224,13 @@ public class ConnectionsManager extends BaseController {
String pushString = getRegId();
int timezoneOffset = (TimeZone.getDefault().getRawOffset() + TimeZone.getDefault().getDSTSavings()) / 1000;
SharedPreferences mainPreferences;
if (currentAccount == 0) {
mainPreferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
} else {
mainPreferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig" + currentAccount, Activity.MODE_PRIVATE);
}
forceTryIpV6 = mainPreferences.getBoolean("forceTryIpV6", false);
init(version, TLRPC.LAYER, appId, deviceModel, systemVersion, appVersion, langCode, systemLangCode, configPath, FileLog.getNetworkLogPath(), pushString, fingerprint, timezoneOffset, getUserConfig().getClientUserId(), enablePushConnection);
}

File diff suppressed because it is too large Load Diff

View File

@ -74,12 +74,14 @@ public class ActionBar extends FrameLayout {
}
private UnreadImageView backButtonImageView;
private Drawable backButtonDrawable;
private SimpleTextView[] titleTextView = new SimpleTextView[2];
private SimpleTextView subtitleTextView;
private SimpleTextView additionalSubtitleTextView;
private View actionModeTop;
private int actionModeColor;
private int actionBarColor;
private boolean isMenuOffsetSuppressed;
private ActionBarMenu menu;
private ActionBarMenu actionMode;
private String actionModeTag;
@ -89,6 +91,7 @@ public class ActionBar extends FrameLayout {
private boolean addToContainer = true;
private boolean clipContent;
private boolean interceptTouches = true;
private boolean forceSkipTouches;
private int extraHeight;
private AnimatorSet actionModeAnimation;
private View actionModeExtraView;
@ -132,6 +135,11 @@ public class ActionBar extends FrameLayout {
private View.OnTouchListener interceptTouchEventListener;
private final Theme.ResourcesProvider resourcesProvider;
SizeNotifierFrameLayout contentView;
boolean blurredBackground;
public Paint blurScrimPaint = new Paint();
Rect rectTmp = new Rect();
EllipsizeSpanAnimator ellipsizeSpanAnimator = new EllipsizeSpanAnimator(this);
public ActionBar(Context context) {
@ -176,12 +184,16 @@ public class ActionBar extends FrameLayout {
backButtonImageView.setContentDescription(LocaleController.getString("AccDescrGoBack", R.string.AccDescrGoBack));
}
public Drawable getBackButtonDrawable() {
return backButtonDrawable;
}
public void setBackButtonDrawable(Drawable drawable) {
if (backButtonImageView == null) {
createBackButtonImage();
}
backButtonImageView.setVisibility(drawable == null ? GONE : VISIBLE);
backButtonImageView.setImageDrawable(drawable);
backButtonImageView.setImageDrawable(backButtonDrawable = drawable);
if (drawable instanceof BackDrawable) {
BackDrawable backDrawable = (BackDrawable) drawable;
backDrawable.setRotation(isActionModeShowed() ? 1 : 0, false);
@ -1054,13 +1066,19 @@ public class ActionBar extends FrameLayout {
menu.measure(menuWidth, actionBarHeightSpec);
int itemsWidth = menu.getItemsMeasuredWidth();
menuWidth = MeasureSpec.makeMeasureSpec(width - AndroidUtilities.dp(AndroidUtilities.isTablet() ? 74 : 66) + menu.getItemsMeasuredWidth(), MeasureSpec.EXACTLY);
menu.translateXItems(-itemsWidth);
if (!isMenuOffsetSuppressed) {
menu.translateXItems(-itemsWidth);
}
} else if (isSearchFieldVisible) {
menuWidth = MeasureSpec.makeMeasureSpec(width - AndroidUtilities.dp(AndroidUtilities.isTablet() ? 74 : 66), MeasureSpec.EXACTLY);
menu.translateXItems(0);
if (!isMenuOffsetSuppressed) {
menu.translateXItems(0);
}
} else {
menuWidth = MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST);
menu.translateXItems(0);
if (!isMenuOffsetSuppressed) {
menu.translateXItems(0);
}
}
menu.measure(menuWidth, actionBarHeightSpec);
@ -1124,6 +1142,10 @@ public class ActionBar extends FrameLayout {
}
}
public void setMenuOffsetSuppressed(boolean menuOffsetSuppressed) {
isMenuOffsetSuppressed = menuOffsetSuppressed;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int additionalTop = occupyStatusBar ? AndroidUtilities.statusBarHeight : 0;
@ -1404,6 +1426,9 @@ public class ActionBar extends FrameLayout {
@Override
public boolean onTouchEvent(MotionEvent event) {
if (forceSkipTouches) {
return false;
}
return super.onTouchEvent(event) || interceptTouches;
}
@ -1589,9 +1614,6 @@ public class ActionBar extends FrameLayout {
return color != null ? color : Theme.getColor(key);
}
SizeNotifierFrameLayout contentView;
boolean blurredBackground;
public void setDrawBlurBackground(SizeNotifierFrameLayout contentView) {
blurredBackground = true;
this.contentView = contentView;
@ -1599,8 +1621,6 @@ public class ActionBar extends FrameLayout {
setBackground(null);
}
public Paint blurScrimPaint = new Paint();
Rect rectTmp = new Rect();
@Override
protected void dispatchDraw(Canvas canvas) {
if (blurredBackground && actionBarColor != Color.TRANSPARENT) {
@ -1611,6 +1631,10 @@ public class ActionBar extends FrameLayout {
super.dispatchDraw(canvas);
}
public void setForceSkipTouches(boolean forceSkipTouches) {
this.forceSkipTouches = forceSkipTouches;
}
// NekoX Changes
private StaticLayout countLayout;

View File

@ -12,6 +12,7 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
@ -36,6 +37,7 @@ import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.FrameLayout;
import androidx.annotation.Keep;
@ -45,6 +47,7 @@ import androidx.core.math.MathUtils;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.ImageLoader;
import org.telegram.messenger.MessagesController;
import org.telegram.messenger.R;
import org.telegram.messenger.SharedConfig;
@ -74,6 +77,8 @@ public class ActionBarLayout extends FrameLayout {
void onRebuildAllFragments(ActionBarLayout layout, boolean last);
}
public boolean highlightActionButtons = false;
public class LayoutContainer extends FrameLayout {
private Rect rect = new Rect();
@ -83,6 +88,8 @@ public class ActionBarLayout extends FrameLayout {
private Paint backgroundPaint = new Paint();
private int backgroundColor;
private boolean wasPortrait;
public LayoutContainer(Context context) {
super(context);
setWillNotDraw(false);
@ -130,6 +137,12 @@ public class ActionBarLayout extends FrameLayout {
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
boolean isPortrait = height > width;
if (wasPortrait != isPortrait && isInPreviewMode()) {
finishPreviewFragment();
}
wasPortrait = isPortrait;
int count = getChildCount();
int actionBarHeight = 0;
for (int a = 0; a < count; a++) {
@ -190,11 +203,11 @@ public class ActionBarLayout extends FrameLayout {
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
processMenuButtonsTouch(ev);
boolean passivePreview = inPreviewMode && previewMenu == null;
if ((passivePreview || transitionAnimationPreviewMode) && (ev.getActionMasked() == MotionEvent.ACTION_DOWN || ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN)) {
return false;
}
//
try {
return (!passivePreview || this != containerView) && super.dispatchTouchEvent(ev);
} catch (Throwable e) {
@ -219,6 +232,76 @@ public class ActionBarLayout extends FrameLayout {
this.fragmentPanTranslationOffset = fragmentPanTranslationOffset;
invalidate();
}
// for menu buttons to be clicked by hover:
private float pressX, pressY;
private boolean allowToPressByHover;
public void processMenuButtonsTouch(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
pressX = event.getX();
pressY = event.getY();
allowToPressByHover = false;
} else if (event.getAction() == MotionEvent.ACTION_MOVE || event.getAction() == MotionEvent.ACTION_UP) {
if (previewMenu != null && highlightActionButtons) {
// movePreviewFragment(Math.min(pressY, AndroidUtilities.displaySize.y * .4f) - event.getY());
if (!allowToPressByHover && Math.sqrt(Math.pow(pressX - event.getX(), 2) + Math.pow(pressY - event.getY(), 2)) > AndroidUtilities.dp(30)) {
allowToPressByHover = true;
}
if (allowToPressByHover && (previewMenu.getSwipeBack() == null || !previewMenu.getSwipeBack().isForegroundOpen())) {
for (int i = 0; i < previewMenu.getItemsCount(); ++i) {
ActionBarMenuSubItem button = (ActionBarMenuSubItem) previewMenu.getItemAt(i);
if (button != null) {
Drawable ripple = button.getBackground();
button.getGlobalVisibleRect(AndroidUtilities.rectTmp2);
boolean shouldBeEnabled = AndroidUtilities.rectTmp2.contains((int) event.getX(), (int) event.getY()),
enabled = ripple.getState().length == 2;
if (event.getAction() == MotionEvent.ACTION_MOVE) {
if (shouldBeEnabled != enabled) {
ripple.setState(shouldBeEnabled ? new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled} : new int[]{});
if (shouldBeEnabled) {
try {
button.performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
} catch (Exception ignore) {}
}
}
} else if (event.getAction() == MotionEvent.ACTION_UP) {
if (shouldBeEnabled) {
button.performClick();
}
}
}
}
}
}
}
if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) {
if (previewMenu != null && highlightActionButtons) {
int alpha = 255;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alpha = Theme.moveUpDrawable.getAlpha();
}
ValueAnimator arrowAlphaUpdate = ValueAnimator.ofFloat(alpha, 0);
arrowAlphaUpdate.addUpdateListener(a -> {
Theme.moveUpDrawable.setAlpha(((Float) a.getAnimatedValue()).intValue());
if (drawerLayoutContainer != null) {
drawerLayoutContainer.invalidate();
}
if (containerView != null) {
containerView.invalidate();
}
ActionBarLayout.this.invalidate();
});
arrowAlphaUpdate.setDuration(150);
arrowAlphaUpdate.setInterpolator(CubicBezierInterpolator.DEFAULT);
arrowAlphaUpdate.start();
ObjectAnimator containerTranslationUpdate = ObjectAnimator.ofFloat(containerView, View.TRANSLATION_Y, 0);
containerTranslationUpdate.setDuration(150);
containerTranslationUpdate.setInterpolator(CubicBezierInterpolator.DEFAULT);
containerTranslationUpdate.start();
}
highlightActionButtons = false;
}
}
}
public static class ThemeAnimationSettings {
@ -270,10 +353,11 @@ public class ActionBarLayout extends FrameLayout {
private BaseFragment oldFragment;
/* Contest */
private View previewMenu;
private ActionBarPopupWindow.ActionBarPopupWindowLayout previewMenu;
private AnimatorSet currentAnimation;
private DecelerateInterpolator decelerateInterpolator = new DecelerateInterpolator(1.5f);
private OvershootInterpolator overshootInterpolator = new OvershootInterpolator(1.02f);
private AccelerateDecelerateInterpolator accelerateDecelerateInterpolator = new AccelerateDecelerateInterpolator();
public float innerTranslationX;
@ -369,6 +453,7 @@ public class ActionBarLayout extends FrameLayout {
fragment.setParentLayout(this);
}
}
@Override
public void onConfigurationChanged(android.content.res.Configuration newConfig) {
super.onConfigurationChanged(newConfig);
@ -474,6 +559,13 @@ public class ActionBarLayout extends FrameLayout {
}
}
public void onUserLeaveHint() {
if (!fragmentsStack.isEmpty()) {
BaseFragment lastFragment = fragmentsStack.get(fragmentsStack.size() - 1);
lastFragment.onUserLeaveHint();
}
}
public void onPause() {
if (!fragmentsStack.isEmpty()) {
BaseFragment lastFragment = fragmentsStack.get(fragmentsStack.size() - 1);
@ -600,9 +692,10 @@ public class ActionBarLayout extends FrameLayout {
previewBackgroundDrawable.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
previewBackgroundDrawable.draw(canvas);
if (previewMenu == null) {
int x = (getMeasuredWidth() - AndroidUtilities.dp(24)) / 2;
int width = AndroidUtilities.dp(32), height = width / 2;
int x = (getMeasuredWidth() - width) / 2;
int y = (int) (view.getTop() + containerView.getTranslationY() - AndroidUtilities.dp(12 + (Build.VERSION.SDK_INT < 21 ? 20 : 0)));
Theme.moveUpDrawable.setBounds(x, y, x + AndroidUtilities.dp(24), y + AndroidUtilities.dp(24));
Theme.moveUpDrawable.setBounds(x, y, x + width, y + height);
Theme.moveUpDrawable.draw(canvas);
}
}
@ -976,7 +1069,7 @@ public class ActionBarLayout extends FrameLayout {
return presentFragment(fragment, false, false, true, true, null);
}
public boolean presentFragmentAsPreviewWithMenu(BaseFragment fragment, View menu) {
public boolean presentFragmentAsPreviewWithMenu(BaseFragment fragment, ActionBarPopupWindow.ActionBarPopupWindowLayout menu) {
return presentFragment(fragment, false, false, true, true, menu);
}
@ -1009,7 +1102,8 @@ public class ActionBarLayout extends FrameLayout {
dt = 18;
}
lastFrameTime = newTime;
animationProgress += dt / 150.0f;
float duration = preview && open ? 190.0f : 150.0f;
animationProgress += dt / duration;
if (animationProgress > 1.0f) {
animationProgress = 1.0f;
}
@ -1021,30 +1115,49 @@ public class ActionBarLayout extends FrameLayout {
}
Integer oldNavigationBarColor = oldFragment != null ? oldFragment.getNavigationBarColor() : null;
Integer newNavigationBarColor = newFragment != null ? newFragment.getNavigationBarColor() : null;
if (newFragment != null && !newFragment.inPreviewMode && oldNavigationBarColor != null) {
if (newFragment != null && oldNavigationBarColor != null) {
float ratio = MathUtils.clamp(2f * animationProgress - (open ? 1f : 0f), 0f, 1f);
newFragment.setNavigationBarColor(ColorUtils.blendARGB(oldNavigationBarColor, newNavigationBarColor, ratio));
}
float interpolated = decelerateInterpolator.getInterpolation(animationProgress);
float interpolated;
if (preview) {
if (open) {
interpolated = overshootInterpolator.getInterpolation(animationProgress);
} else {
interpolated = CubicBezierInterpolator.EASE_OUT_QUINT.getInterpolation(animationProgress);
}
} else {
interpolated = decelerateInterpolator.getInterpolation(animationProgress);
}
if (open) {
containerView.setAlpha(interpolated);
float clampedInterpolated = MathUtils.clamp(interpolated, 0, 1);
containerView.setAlpha(clampedInterpolated);
if (preview) {
containerView.setScaleX(0.9f + 0.1f * interpolated);
containerView.setScaleY(0.9f + 0.1f * interpolated);
previewBackgroundDrawable.setAlpha((int) (0x2e * interpolated));
Theme.moveUpDrawable.setAlpha((int) (255 * interpolated));
containerView.setScaleX(0.7f + 0.3f * interpolated);
containerView.setScaleY(0.7f + 0.3f * interpolated);
if (previewMenu != null) {
containerView.setTranslationY(AndroidUtilities.dp(40) * (1f - interpolated));
previewMenu.setTranslationY(-AndroidUtilities.dp(40 + 30) * (1f - interpolated));
previewMenu.setScaleX(0.95f + 0.05f * interpolated);
previewMenu.setScaleY(0.95f + 0.05f * interpolated);
}
previewBackgroundDrawable.setAlpha((int) (0x2e * clampedInterpolated));
Theme.moveUpDrawable.setAlpha((int) (255 * clampedInterpolated));
containerView.invalidate();
invalidate();
} else {
containerView.setTranslationX(AndroidUtilities.dp(48) * (1.0f - interpolated));
}
} else {
containerViewBack.setAlpha(1.0f - interpolated);
float clampedReverseInterpolated = MathUtils.clamp(1f - interpolated, 0, 1);
containerViewBack.setAlpha(clampedReverseInterpolated);
if (preview) {
containerViewBack.setScaleX(0.9f + 0.1f * (1.0f - interpolated));
containerViewBack.setScaleY(0.9f + 0.1f * (1.0f - interpolated));
previewBackgroundDrawable.setAlpha((int) (0x2e * (1.0f - interpolated)));
Theme.moveUpDrawable.setAlpha((int) (255 * (1.0f - interpolated)));
previewBackgroundDrawable.setAlpha((int) (0x2e * clampedReverseInterpolated));
if (previewMenu == null) {
Theme.moveUpDrawable.setAlpha((int) (255 * clampedReverseInterpolated));
}
containerView.invalidate();
invalidate();
} else {
@ -1087,13 +1200,27 @@ public class ActionBarLayout extends FrameLayout {
return presentFragment(fragment, removeLast, forceWithoutAnimation, check, preview, null);
}
public boolean presentFragment(final BaseFragment fragment, final boolean removeLast, boolean forceWithoutAnimation, boolean check, final boolean preview, View menu) {
public boolean presentFragment(final BaseFragment fragment, final boolean removeLast, boolean forceWithoutAnimation, boolean check, final boolean preview, ActionBarPopupWindow.ActionBarPopupWindowLayout menu) {
Log.i("UI", "presenting Fragment " + fragment);
if (fragment == null || checkTransitionAnimation() || delegate != null && check && !delegate.needPresentFragment(fragment, removeLast, forceWithoutAnimation, this) || !fragment.onFragmentCreate()) {
return false;
}
if (inPreviewMode && transitionAnimationPreviewMode) {
if (delayedOpenAnimationRunnable != null) {
AndroidUtilities.cancelRunOnUIThread(delayedOpenAnimationRunnable);
delayedOpenAnimationRunnable = null;
}
closeLastFragment(false, true);
}
fragment.setInPreviewMode(preview);
fragment.setInMenuMode(menu != null);
if (previewMenu != null) {
if (previewMenu.getParent() != null) {
((ViewGroup) previewMenu.getParent()).removeView(previewMenu);
}
previewMenu = null;
}
previewMenu = menu;
fragment.setInMenuMode(previewMenu != null);
if (parentActivity.getCurrentFocus() != null && fragment.hideKeyboardOnShow() && !preview) {
AndroidUtilities.hideKeyboard(parentActivity.getCurrentFocus());
}
@ -1138,11 +1265,12 @@ public class ActionBarLayout extends FrameLayout {
layoutParams.height = height;
layoutParams.topMargin = statusBarHeight + (getMeasuredHeight() - statusBarHeight - height) / 2;
} else {
layoutParams.topMargin = layoutParams.bottomMargin = AndroidUtilities.dp(menu != null ? 0 : 46);
layoutParams.topMargin = layoutParams.bottomMargin = AndroidUtilities.dp(menu != null ? 0 : 24);
layoutParams.topMargin += AndroidUtilities.statusBarHeight;
}
if (menu != null) {
layoutParams.bottomMargin += menuHeight + AndroidUtilities.dp(8);
// layoutParams.topMargin += AndroidUtilities.dp(32);
}
layoutParams.rightMargin = layoutParams.leftMargin = AndroidUtilities.dp(8);
} else {
@ -1380,6 +1508,7 @@ public class ActionBarLayout extends FrameLayout {
if (onFragmentStackChangedListener != null) {
onFragmentStackChangedListener.run();
}
ImageLoader.getInstance().onFragmentStackChanged();
}
public boolean addFragmentToStack(BaseFragment fragment) {
@ -1431,7 +1560,7 @@ public class ActionBarLayout extends FrameLayout {
}
public void movePreviewFragment(float dy) {
if (!inPreviewMode || transitionAnimationPreviewMode || previewMenu != null) {
if (!inPreviewMode || previewMenu != null || transitionAnimationPreviewMode) {
return;
}
float currentTranslation = containerView.getTranslationY();
@ -1439,43 +1568,8 @@ public class ActionBarLayout extends FrameLayout {
if (nextTranslation > 0) {
nextTranslation = 0;
} else if (nextTranslation < -AndroidUtilities.dp(60)) {
previewOpenAnimationInProgress = true;
inPreviewMode = false;
nextTranslation = 0;
BaseFragment prevFragment = fragmentsStack.get(fragmentsStack.size() - 2);
BaseFragment fragment = fragmentsStack.get(fragmentsStack.size() - 1);
if (Build.VERSION.SDK_INT >= 21) {
fragment.fragmentView.setOutlineProvider(null);
fragment.fragmentView.setClipToOutline(false);
}
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) fragment.fragmentView.getLayoutParams();
layoutParams.topMargin = layoutParams.bottomMargin = layoutParams.rightMargin = layoutParams.leftMargin = 0;
layoutParams.height = LayoutHelper.MATCH_PARENT;
fragment.fragmentView.setLayoutParams(layoutParams);
presentFragmentInternalRemoveOld(false, prevFragment);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(
ObjectAnimator.ofFloat(fragment.fragmentView, View.SCALE_X, 1.0f, 1.05f, 1.0f),
ObjectAnimator.ofFloat(fragment.fragmentView, View.SCALE_Y, 1.0f, 1.05f, 1.0f));
animatorSet.setDuration(200);
animatorSet.setInterpolator(new CubicBezierInterpolator(0.42, 0.0, 0.58, 1.0));
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
previewOpenAnimationInProgress = false;
fragment.onPreviewOpenAnimationEnd();
}
});
animatorSet.start();
if (!NekoConfig.disableVibration.Bool()) {
performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
}
fragment.setInPreviewMode(false);
expandPreviewFragment();
}
if (currentTranslation != nextTranslation) {
containerView.setTranslationY(nextTranslation);
@ -1483,6 +1577,46 @@ public class ActionBarLayout extends FrameLayout {
}
}
public void expandPreviewFragment() {
previewOpenAnimationInProgress = true;
inPreviewMode = false;
BaseFragment prevFragment = fragmentsStack.get(fragmentsStack.size() - 2);
BaseFragment fragment = fragmentsStack.get(fragmentsStack.size() - 1);
if (Build.VERSION.SDK_INT >= 21) {
fragment.fragmentView.setOutlineProvider(null);
fragment.fragmentView.setClipToOutline(false);
}
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) fragment.fragmentView.getLayoutParams();
layoutParams.topMargin = layoutParams.bottomMargin = layoutParams.rightMargin = layoutParams.leftMargin = 0;
layoutParams.height = LayoutHelper.MATCH_PARENT;
fragment.fragmentView.setLayoutParams(layoutParams);
presentFragmentInternalRemoveOld(false, prevFragment);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(
ObjectAnimator.ofFloat(fragment.fragmentView, View.SCALE_X, 1.0f, 1.05f, 1.0f),
ObjectAnimator.ofFloat(fragment.fragmentView, View.SCALE_Y, 1.0f, 1.05f, 1.0f));
animatorSet.setDuration(200);
animatorSet.setInterpolator(new CubicBezierInterpolator(0.42, 0.0, 0.58, 1.0));
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
previewOpenAnimationInProgress = false;
fragment.onPreviewOpenAnimationEnd();
}
});
animatorSet.start();
if (!NekoConfig.disableVibration.Bool()) {
performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
}
fragment.setInPreviewMode(false);
fragment.setInMenuMode(false);
}
public void finishPreviewFragment() {
if (!inPreviewMode && !transitionAnimationPreviewMode) {
return;
@ -1495,6 +1629,10 @@ public class ActionBarLayout extends FrameLayout {
}
public void closeLastFragment(boolean animated) {
closeLastFragment(animated, false);
}
public void closeLastFragment(boolean animated, boolean forceNoAnimation) {
if (delegate != null && !delegate.needCloseLastFragment(this) || checkTransitionAnimation() || fragmentsStack.isEmpty()) {
return;
}
@ -1502,7 +1640,7 @@ public class ActionBarLayout extends FrameLayout {
AndroidUtilities.hideKeyboard(parentActivity.getCurrentFocus());
}
setInnerTranslationX(0);
boolean needAnimation = inPreviewMode || transitionAnimationPreviewMode || animated && MessagesController.getGlobalMainSettings().getBoolean("view_animations", true);
boolean needAnimation = !forceNoAnimation && (inPreviewMode || transitionAnimationPreviewMode || animated && MessagesController.getGlobalMainSettings().getBoolean("view_animations", true));
final BaseFragment currentFragment = fragmentsStack.get(fragmentsStack.size() - 1);
BaseFragment previousFragment = null;
if (fragmentsStack.size() > 1) {
@ -1567,10 +1705,6 @@ public class ActionBarLayout extends FrameLayout {
fragmentView.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite));
}
if (!needAnimation) {
closeLastFragmentInternalRemoveOld(currentFragment);
}
if (needAnimation) {
transitionAnimationStartTime = System.currentTimeMillis();
transitionAnimationInProgress = true;
@ -1631,12 +1765,13 @@ public class ActionBarLayout extends FrameLayout {
}
onFragmentStackChanged();
} else {
closeLastFragmentInternalRemoveOld(currentFragment);
currentFragment.onTransitionAnimationEnd(false, true);
previousFragment.onTransitionAnimationEnd(true, true);
previousFragment.onBecomeFullyVisible();
}
} else {
if (useAlphaAnimations) {
if (useAlphaAnimations && !forceNoAnimation) {
transitionAnimationStartTime = System.currentTimeMillis();
transitionAnimationInProgress = true;
layoutToIgnore = containerView;

View File

@ -82,6 +82,10 @@ public class ActionBarMenu extends LinearLayout {
return addItem(id, icon, null, isActionMode ? parentActionBar.itemsActionModeBackgroundColor : parentActionBar.itemsBackgroundColor, null, width, null);
}
public ActionBarMenuItem addItemWithWidth(int id, Drawable drawable, int width, CharSequence title) {
return addItem(id, 0, null, isActionMode ? parentActionBar.itemsActionModeBackgroundColor : parentActionBar.itemsBackgroundColor, drawable, width, title);
}
public ActionBarMenuItem addItemWithWidth(int id, int icon, int width, CharSequence title) {
return addItem(id, icon, null, isActionMode ? parentActionBar.itemsActionModeBackgroundColor : parentActionBar.itemsBackgroundColor, null, width, title);
}
@ -355,7 +359,7 @@ public class ActionBarMenu extends LinearLayout {
return false;
}
public void translateXItems(int offset) {
public void translateXItems(float offset) {
int count = getChildCount();
for (int a = 0; a < count; a++) {
View view = getChildAt(a);

View File

@ -29,6 +29,7 @@ import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.transition.TransitionValues;
import android.transition.Visibility;
import android.util.Log;
import android.util.TypedValue;
import android.view.ActionMode;
import android.view.Gravity;
@ -59,6 +60,7 @@ import org.telegram.messenger.LocaleController;
import org.telegram.messenger.NotificationCenter;
import org.telegram.messenger.R;
import org.telegram.messenger.UserConfig;
import org.telegram.messenger.XiaomiUtilities;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.Adapters.FiltersView;
import org.telegram.ui.Components.BackupImageView;
@ -379,7 +381,7 @@ public class ActionBarMenuItem extends FrameLayout {
if (popupLayout == null) {
return;
}
popupLayout.setShownFromBotton(value);
popupLayout.setShownFromBottom(value);
}
public void addSubItem(View view, int width, int height) {
@ -791,7 +793,7 @@ public class ActionBarMenuItem extends FrameLayout {
}
}
}
clearSearchFilters();
// clearSearchFilters();
}
if (listener != null) {
listener.onSearchCollapse();
@ -1333,6 +1335,8 @@ public class ActionBarMenuItem extends FrameLayout {
searchField.setImeOptions(EditorInfo.IME_FLAG_NO_FULLSCREEN | EditorInfo.IME_ACTION_SEARCH);
searchField.setTextIsSelectable(false);
searchField.setHighlightColor(getThemedColor(Theme.key_chat_inTextSelectionHighlight));
searchField.setHandlesColor(getThemedColor(Theme.key_chat_TextSelectionCursor));
searchFilterLayout = new LinearLayout(getContext());
searchFilterLayout.setOrientation(LinearLayout.HORIZONTAL);
@ -1720,11 +1724,17 @@ public class ActionBarMenuItem extends FrameLayout {
}
public void showSubItem(int id) {
showSubItem(id, false);
}
public void showSubItem(int id, boolean animated) {
if (popupLayout == null) {
return;
}
View view = popupLayout.findViewWithTag(id);
if (view != null && view.getVisibility() != VISIBLE) {
view.setAlpha(0);
view.animate().alpha(1f).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(150).start();
view.setVisibility(VISIBLE);
measurePopup = true;
}
@ -1774,6 +1784,13 @@ public class ActionBarMenuItem extends FrameLayout {
}
}
}
if (searchField != null) {
searchField.setCursorColor(getThemedColor(Theme.key_actionBarDefaultSearch));
searchField.setHintTextColor(getThemedColor(Theme.key_actionBarDefaultSearchPlaceholder));
searchField.setTextColor(getThemedColor(Theme.key_actionBarDefaultSearch));
searchField.setHighlightColor(getThemedColor(Theme.key_chat_inTextSelectionHighlight));
searchField.setHandlesColor(getThemedColor(Theme.key_chat_TextSelectionCursor));
}
}
public void collapseSearchFilters() {
@ -1781,7 +1798,7 @@ public class ActionBarMenuItem extends FrameLayout {
onFiltersChanged();
}
public void setTransitionOffset(int offset) {
public void setTransitionOffset(float offset) {
this.transitionOffset = offset;
setTranslationX(0);
}
@ -1942,13 +1959,13 @@ public class ActionBarMenuItem extends FrameLayout {
public ActionBarPopupWindow.GapView addColoredGap() {
createPopupLayout();
ActionBarPopupWindow.GapView gap = new ActionBarPopupWindow.GapView(getContext(), Theme.key_graySection);
ActionBarPopupWindow.GapView gap = new ActionBarPopupWindow.GapView(getContext(), resourcesProvider, Theme.key_actionBarDefaultSubmenuSeparator);
gap.setTag(R.id.fit_width_tag, 1);
popupLayout.addView(gap, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 8));
return gap;
}
public static ActionBarMenuSubItem addItem(ActionBarPopupWindow.ActionBarPopupWindowLayout windowLayout, int icon, String text, boolean needCheck, Theme.ResourcesProvider resourcesProvider) {
public static ActionBarMenuSubItem addItem(ActionBarPopupWindow.ActionBarPopupWindowLayout windowLayout, int icon, CharSequence text, boolean needCheck, Theme.ResourcesProvider resourcesProvider) {
ActionBarMenuSubItem cell = new ActionBarMenuSubItem(windowLayout.getContext(), needCheck, false, false, resourcesProvider);
cell.setTextAndIcon(text, icon);
cell.setMinimumWidth(AndroidUtilities.dp(196));

View File

@ -4,16 +4,21 @@ import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
import android.os.Build;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.R;
import org.telegram.ui.Components.CheckBox2;
import org.telegram.ui.Components.LayoutHelper;
@ -101,6 +106,17 @@ public class ActionBarMenuSubItem extends FrameLayout {
checkView.setChecked(checked, true);
}
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setEnabled(isEnabled());
if (checkView != null && checkView.isChecked()) {
info.setCheckable(true);
info.setChecked(checkView.isChecked());
info.setClassName("android.widget.CheckBox");
}
}
public void setCheckColor(String colorKey) {
checkView.setColor(null, null, colorKey);
}

View File

@ -17,16 +17,17 @@ import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.Log;
import android.view.Gravity;
import androidx.annotation.Keep;
import androidx.core.view.ViewCompat;
import androidx.core.widget.ScrollerCompat;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
@ -67,6 +68,7 @@ public class ActionBarPopupWindow extends PopupWindow {
private int currentAccount = UserConfig.selectedAccount;
private boolean pauseNotifications;
private long outEmptyTime = -1;
private boolean scaleOut;
static {
Field f = null;
@ -87,21 +89,27 @@ public class ActionBarPopupWindow extends PopupWindow {
private ViewTreeObserver mViewTreeObserver;
private int popupAnimationIndex = -1;
public void setScaleOut(boolean b) {
scaleOut = b;
}
public interface OnDispatchKeyEventListener {
void onDispatchKeyEvent(KeyEvent keyEvent);
}
public static class ActionBarPopupWindowLayout extends FrameLayout {
public final static int FLAG_USE_SWIPEBACK = 1;
public final static int FLAG_SHOWN_FROM_BOTTOM = 2;
public boolean updateAnimation;
public boolean swipeBackGravityRight;
private OnDispatchKeyEventListener mOnDispatchKeyEventListener;
private float backScaleX = 1;
private float backScaleY = 1;
private boolean startAnimationPending = false;
private int backAlpha = 255;
private int lastStartedChild = 0;
private boolean shownFromBotton;
private boolean shownFromBottom;
private boolean animationEnabled = allowAnimation;
private ArrayList<AnimatorSet> itemAnimators;
private HashMap<View, Integer> positions = new HashMap<>();
@ -121,6 +129,8 @@ public class ActionBarPopupWindow extends PopupWindow {
private final Theme.ResourcesProvider resourcesProvider;
private View topView;
public int subtractBackgroundHeight;
public ActionBarPopupWindowLayout(Context context) {
this(context, null);
}
@ -148,6 +158,10 @@ public class ActionBarPopupWindow extends PopupWindow {
setWillNotDraw(false);
if ((flags & FLAG_SHOWN_FROM_BOTTOM) > 0) {
shownFromBottom = true;
}
if ((flags & FLAG_USE_SWIPEBACK) > 0) {
swipeBackLayout = new PopupSwipeBackLayout(context, resourcesProvider);
addView(swipeBackLayout, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT));
@ -157,8 +171,10 @@ public class ActionBarPopupWindow extends PopupWindow {
scrollView = new ScrollView(context);
// scrollView.setVerticalScrollBarEnabled(verticalScrollBarEnabled);
if (swipeBackLayout != null) {
swipeBackLayout.addView(scrollView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT));
} else addView(scrollView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT));
swipeBackLayout.addView(scrollView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, shownFromBottom ? Gravity.BOTTOM : Gravity.TOP));
} else {
addView(scrollView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT));
}
} catch (Throwable e) {
FileLog.e(e);
}
@ -207,12 +223,20 @@ public class ActionBarPopupWindow extends PopupWindow {
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
if (child instanceof GapView) {
return false;
}
return super.drawChild(canvas, child, drawingTime);
}
};
linearLayout.setOrientation(LinearLayout.VERTICAL);
if (scrollView != null) {
scrollView.addView(linearLayout, new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
} else if (swipeBackLayout != null) {
swipeBackLayout.addView(linearLayout, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT));
swipeBackLayout.addView(linearLayout, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, shownFromBottom ? Gravity.BOTTOM : Gravity.TOP));
} else {
addView(linearLayout, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT));
}
@ -224,7 +248,7 @@ public class ActionBarPopupWindow extends PopupWindow {
}
public int addViewToSwipeBack(View v) {
swipeBackLayout.addView(v, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT));
swipeBackLayout.addView(v, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, shownFromBottom ? Gravity.BOTTOM : Gravity.TOP));
return swipeBackLayout.getChildCount() - 1;
}
@ -232,8 +256,8 @@ public class ActionBarPopupWindow extends PopupWindow {
fitItems = value;
}
public void setShownFromBotton(boolean value) {
shownFromBotton = value;
public void setShownFromBottom(boolean value) {
shownFromBottom = value;
}
public void setDispatchKeyEventListener(OnDispatchKeyEventListener listener) {
@ -262,8 +286,7 @@ public class ActionBarPopupWindow extends PopupWindow {
@Keep
public void setBackScaleX(float value) {
if (backScaleY != value) {
backScaleY = value;
if (backScaleX != value) {
backScaleX = value;
invalidate();
if (onSizeChangedListener != null) {
@ -272,13 +295,23 @@ public class ActionBarPopupWindow extends PopupWindow {
}
}
public void translateChildrenAfter(int index, float ty) {
subtractBackgroundHeight = (int) -ty;
for (int i = index + 1; i < linearLayout.getChildCount(); ++i) {
View child = linearLayout.getChildAt(i);
if (child != null) {
child.setTranslationY(ty);
}
}
}
@Keep
public void setBackScaleY(float value) {
if (backScaleY != value) {
backScaleY = value;
if (animationEnabled && updateAnimation) {
int height = getMeasuredHeight() - AndroidUtilities.dp(16);
if (shownFromBotton) {
if (shownFromBottom) {
for (int a = lastStartedChild; a >= 0; a--) {
View child = getItemAt(a);
if (child.getVisibility() != VISIBLE || child instanceof GapView) {
@ -332,7 +365,7 @@ public class ActionBarPopupWindow extends PopupWindow {
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(
ObjectAnimator.ofFloat(child, View.ALPHA, 0f, child.isEnabled() ? 1f : 0.5f),
ObjectAnimator.ofFloat(child, View.TRANSLATION_Y, AndroidUtilities.dp(shownFromBotton ? 6 : -6), 0));
ObjectAnimator.ofFloat(child, View.TRANSLATION_Y, AndroidUtilities.dp(shownFromBottom ? 6 : -6), 0));
animatorSet.setDuration(180);
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
@ -362,6 +395,16 @@ public class ActionBarPopupWindow extends PopupWindow {
linearLayout.addView(child, layoutParams);
}
public int getViewsCount() {
return linearLayout.getChildCount();
}
public int precalculateHeight() {
int MOST_SPEC = View.MeasureSpec.makeMeasureSpec(999999, View.MeasureSpec.AT_MOST);
linearLayout.measure(MOST_SPEC, MOST_SPEC);
return linearLayout.getMeasuredHeight();
}
public void removeInnerViews() {
linearLayout.removeAllViews();
}
@ -405,7 +448,7 @@ public class ActionBarPopupWindow extends PopupWindow {
int end = gapEndY - scrollView.getScrollY();
boolean hasGap = false;
for (int i = 0; i < linearLayout.getChildCount(); i++) {
if (linearLayout.getChildAt(i) instanceof GapView) {
if (linearLayout.getChildAt(i) instanceof GapView && linearLayout.getChildAt(i).getVisibility() == View.VISIBLE) {
hasGap = true;
break;
}
@ -426,14 +469,14 @@ public class ActionBarPopupWindow extends PopupWindow {
canvas.clipRect(0, bgPaddings.top, getMeasuredWidth(), getMeasuredHeight());
}
backgroundDrawable.setAlpha(applyAlpha ? backAlpha : 255);
if (shownFromBotton) {
if (shownFromBottom) {
final int height = getMeasuredHeight();
backgroundDrawable.setBounds(0, (int) (height * (1.0f - backScaleY)), (int) (getMeasuredWidth() * backScaleX), height);
} else {
if (start > -AndroidUtilities.dp(16)) {
int h = (int) (getMeasuredHeight() * backScaleY);
if (a == 0) {
backgroundDrawable.setBounds(0, -scrollView.getScrollY() + (gapStartY != -1000000 ? AndroidUtilities.dp(1) : 0), (int) (getMeasuredWidth() * backScaleX), (gapStartY != -1000000 ? Math.min(h, start + AndroidUtilities.dp(16)) : h));
backgroundDrawable.setBounds(0, -scrollView.getScrollY() + (gapStartY != -1000000 ? AndroidUtilities.dp(1) : 0), (int) (getMeasuredWidth() * backScaleX), (gapStartY != -1000000 ? Math.min(h, start + AndroidUtilities.dp(16)) : h) - subtractBackgroundHeight);
} else {
if (h < end) {
if (gapStartY != -1000000) {
@ -441,34 +484,35 @@ public class ActionBarPopupWindow extends PopupWindow {
}
continue;
}
backgroundDrawable.setBounds(0, end, (int) (getMeasuredWidth() * backScaleX), h);
backgroundDrawable.setBounds(0, end, (int) (getMeasuredWidth() * backScaleX), h - subtractBackgroundHeight);
}
} else {
backgroundDrawable.setBounds(0, gapStartY < 0 ? 0 : -AndroidUtilities.dp(16), (int) (getMeasuredWidth() * backScaleX), (int) (getMeasuredHeight() * backScaleY));
backgroundDrawable.setBounds(0, (gapStartY < 0 ? 0 : -AndroidUtilities.dp(16)), (int) (getMeasuredWidth() * backScaleX), (int) (getMeasuredHeight() * backScaleY) - subtractBackgroundHeight);
}
}
backgroundDrawable.draw(canvas);
backgroundDrawable.draw(canvas);
if (hasGap) {
canvas.save();
AndroidUtilities.rectTmp2.set(backgroundDrawable.getBounds());
AndroidUtilities.rectTmp2.inset(AndroidUtilities.dp(8), AndroidUtilities.dp(8));
canvas.clipRect(AndroidUtilities.rectTmp2);
for (int i = 0; i < linearLayout.getChildCount(); i++) {
if (linearLayout.getChildAt(i) instanceof GapView) {
if (linearLayout.getChildAt(i) instanceof GapView && linearLayout.getChildAt(i).getVisibility() == View.VISIBLE) {
canvas.save();
float x = 0, y = 0;
View view = linearLayout.getChildAt(i) ;
GapView child = (GapView) linearLayout.getChildAt(i);
View view = child;
while (view != this) {
x += view.getX();
y += view.getY();
view = (View) view.getParent();
if (view == null) {
return;
break;
}
}
canvas.translate(x, y);
linearLayout.getChildAt(i).draw(canvas);
canvas.translate(x, y * scrollView.getScaleY());
child.draw(canvas);
canvas.restore();
}
}
@ -560,6 +604,14 @@ public class ActionBarPopupWindow extends PopupWindow {
public void setSwipeBackForegroundColor(int color) {
getSwipeBack().setForegroundColor(color);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (swipeBackLayout != null) {
swipeBackLayout.invalidateTransforms(!startAnimationPending);
}
}
}
public ActionBarPopupWindow() {
@ -697,10 +749,12 @@ public class ActionBarPopupWindow extends PopupWindow {
ActionBarPopupWindowLayout content = null;
if (viewGroup instanceof ActionBarPopupWindowLayout) {
content = (ActionBarPopupWindowLayout) viewGroup;
content.startAnimationPending = true;
} else {
for (int i = 0; i < viewGroup.getChildCount(); i++) {
if (viewGroup.getChildAt(i) instanceof ActionBarPopupWindowLayout) {
content = (ActionBarPopupWindowLayout) viewGroup.getChildAt(i);
content.startAnimationPending = true;
}
}
}
@ -726,19 +780,19 @@ public class ActionBarPopupWindow extends PopupWindow {
content.positions.put(child, visibleCount);
visibleCount++;
}
if (content.shownFromBotton) {
if (content.shownFromBottom) {
content.lastStartedChild = count - 1;
} else {
content.lastStartedChild = 0;
}
float finalsScaleY = 1f;
float finalScaleY = 1f;
if (content.getSwipeBack() != null) {
content.getSwipeBack().invalidateTransforms();
finalsScaleY = content.backScaleY;
finalScaleY = content.backScaleY;
}
windowAnimatorSet = new AnimatorSet();
windowAnimatorSet.playTogether(
ObjectAnimator.ofFloat(content, "backScaleY", 0.0f, finalsScaleY),
ObjectAnimator.ofFloat(content, "backScaleY", 0.0f, finalScaleY),
ObjectAnimator.ofInt(content, "backAlpha", 0, 255));
windowAnimatorSet.setDuration(150 + 16 * visibleCount);
int finalCount = count;
@ -750,10 +804,12 @@ public class ActionBarPopupWindow extends PopupWindow {
ActionBarPopupWindowLayout content = null;
if (viewGroup instanceof ActionBarPopupWindowLayout) {
content = (ActionBarPopupWindowLayout) viewGroup;
content.startAnimationPending = false;
} else {
for (int i = 0; i < viewGroup.getChildCount(); i++) {
if (viewGroup.getChildAt(i) instanceof ActionBarPopupWindowLayout) {
content = (ActionBarPopupWindowLayout) viewGroup.getChildAt(i);
content.startAnimationPending = false;
}
}
}
@ -832,9 +888,15 @@ public class ActionBarPopupWindow extends PopupWindow {
if (outEmptyTime > 0) {
windowAnimatorSet.playTogether(ValueAnimator.ofFloat(0, 1f));
windowAnimatorSet.setDuration(outEmptyTime);
} else if (scaleOut) {
windowAnimatorSet.playTogether(
ObjectAnimator.ofFloat(viewGroup, View.SCALE_Y, 0.8f),
ObjectAnimator.ofFloat(viewGroup, View.SCALE_X, 0.8f),
ObjectAnimator.ofFloat(viewGroup, View.ALPHA, 0.0f));
windowAnimatorSet.setDuration(dismissAnimationDuration);
} else {
windowAnimatorSet.playTogether(
ObjectAnimator.ofFloat(viewGroup, View.TRANSLATION_Y, AndroidUtilities.dp((content != null && content.shownFromBotton) ? 5 : -5)),
ObjectAnimator.ofFloat(viewGroup, View.TRANSLATION_Y, AndroidUtilities.dp((content != null && content.shownFromBottom) ? 5 : -5)),
ObjectAnimator.ofFloat(viewGroup, View.ALPHA, 0.0f));
windowAnimatorSet.setDuration(dismissAnimationDuration);
}
@ -880,26 +942,24 @@ public class ActionBarPopupWindow extends PopupWindow {
public static class GapView extends FrameLayout {
Paint paint = new Paint();
Theme.ResourcesProvider resourcesProvider;
String colorKey;
int color = 0;
public GapView(Context context, String colorKey) {
public GapView(Context context, Theme.ResourcesProvider resourcesProvider, String colorKey) {
super(context);
this.resourcesProvider = resourcesProvider;
this.colorKey = colorKey;
setBackgroundColor(getThemedColor(colorKey));
}
@Override
protected void onDraw(Canvas canvas) {
if (color == 0) {
paint.setColor(Theme.getColor(colorKey));
} else {
paint.setColor(color);
}
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint);
private int getThemedColor(String key) {
Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null;
return color != null ? color : Theme.getColor(key);
}
public void setColor(int color) {
this.color = color;
setBackgroundColor(color);
}
}
}

View File

@ -5,15 +5,22 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.os.SystemClock;
import android.view.ContextThemeWrapper;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.core.view.WindowInsetsCompat;
import androidx.recyclerview.widget.ChatListItemAnimator;
import org.telegram.messenger.AndroidUtilities;
@ -22,9 +29,12 @@ import org.telegram.messenger.SharedConfig;
import org.telegram.messenger.UserConfig;
import java.util.ArrayList;
import java.util.List;
public class AdjustPanLayoutHelper {
public static boolean USE_ANDROID11_INSET_ANIMATOR = false;
public final static Interpolator keyboardInterpolator = ChatListItemAnimator.DEFAULT_INTERPOLATOR;
public final static long keyboardDuration = 250;
@ -33,6 +43,7 @@ public class AdjustPanLayoutHelper {
private ViewGroup contentView;
private View resizableView;
private boolean usingInsetAnimator = false;
private boolean animationInProgress;
private boolean needDelay;
private Runnable delayedAnimationRunnable = new Runnable() {
@ -44,6 +55,14 @@ public class AdjustPanLayoutHelper {
}
};
public View getAdjustingParent() {
return parent;
}
public View getAdjustingContentView() {
return contentView;
}
int previousHeight = -1;
int previousContentHeight = -1;
int previousStartOffset = -1;
@ -58,6 +77,11 @@ public class AdjustPanLayoutHelper {
boolean checkHierarchyHeight;
float from, to;
boolean inverse;
boolean isKeyboardVisible;
long startAfter;
ViewTreeObserver.OnPreDrawListener onPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
@ -71,6 +95,7 @@ public class AdjustPanLayoutHelper {
previousHeight = contentHeight;
previousContentHeight = contentView.getHeight();
previousStartOffset = startOffset();
usingInsetAnimator = false;
}
return true;
}
@ -79,11 +104,12 @@ public class AdjustPanLayoutHelper {
previousHeight = contentHeight;
previousContentHeight = contentView.getHeight();
previousStartOffset = startOffset();
usingInsetAnimator = false;
return true;
}
if (previousHeight != -1 && previousContentHeight == contentView.getHeight()) {
boolean isKeyboardVisible = contentHeight < contentView.getBottom();
isKeyboardVisible = contentHeight < contentView.getBottom();
animateHeight(previousHeight, contentHeight, isKeyboardVisible);
previousHeight = contentHeight;
previousContentHeight = contentView.getHeight();
@ -99,6 +125,43 @@ public class AdjustPanLayoutHelper {
};
private void animateHeight(int previousHeight, int contentHeight, boolean isKeyboardVisible) {
if (ignoreOnce) {
ignoreOnce = false;
return;
}
if (!enabled) {
return;
}
startTransition(previousHeight, contentHeight, isKeyboardVisible);
animator.addUpdateListener(animation -> {
if (!usingInsetAnimator) {
updateTransition((float) animation.getAnimatedValue());
}
});
int selectedAccount = UserConfig.selectedAccount;
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (!usingInsetAnimator) {
stopTransition();
}
}
});
animator.setDuration(keyboardDuration);
animator.setInterpolator(keyboardInterpolator);
notificationsIndex = NotificationCenter.getInstance(selectedAccount).setAnimationInProgress(notificationsIndex, null);
if (needDelay) {
needDelay = false;
startAfter = SystemClock.elapsedRealtime() + 100;
AndroidUtilities.runOnUIThread(delayedAnimationRunnable, 100);
} else {
animator.start();
startAfter = -1;
}
}
public void startTransition(int previousHeight, int contentHeight, boolean isKeyboardVisible) {
if (animator != null) {
animator.cancel();
}
@ -118,59 +181,67 @@ public class AdjustPanLayoutHelper {
onTransitionStart(isKeyboardVisible, contentHeight);
float dy = contentHeight - previousHeight;
float from;
float to;
keyboardSize = Math.abs(dy);
animationInProgress = true;
if (contentHeight > previousHeight) {
dy -= startOffset;
parent.setTranslationY(-dy);
onPanTranslationUpdate(dy, 1f, isKeyboardVisible);
from = -dy;
to = 0;
animator = ValueAnimator.ofFloat(1f, 0);
inverse = true;
} else {
parent.setTranslationY(previousStartOffset);
onPanTranslationUpdate(-previousStartOffset, 0f, isKeyboardVisible);
to = -previousStartOffset;
from = dy;
animator = ValueAnimator.ofFloat(0, 1f);
}
animator.addUpdateListener(animation -> {
float v = (float) animation.getAnimatedValue();
float y = (int) (from * v + to * (1f - v));
parent.setTranslationY(y);
onPanTranslationUpdate(-y, v, isKeyboardVisible);
});
animationInProgress = true;
int selectedAccount = UserConfig.selectedAccount;
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
animationInProgress = false;
NotificationCenter.getInstance(selectedAccount).onAnimationFinish(notificationsIndex);
animator = null;
setViewHeight(ViewGroup.LayoutParams.MATCH_PARENT);
viewsToHeightSet.clear();
resizableView.requestLayout();
onPanTranslationUpdate(0, isKeyboardVisible ? 1f : 0f, isKeyboardVisible);
parent.setTranslationY(0);
onTransitionEnd();
}
});
animator.setDuration(keyboardDuration);
animator.setInterpolator(keyboardInterpolator);
notificationsIndex = NotificationCenter.getInstance(selectedAccount).setAnimationInProgress(notificationsIndex, null);
if (needDelay) {
needDelay = false;
AndroidUtilities.runOnUIThread(delayedAnimationRunnable, 100);
} else {
animator.start();
inverse = false;
}
animator = ValueAnimator.ofFloat(0, 1);
usingInsetAnimator = false;
}
private void setViewHeight(int height) {
public void updateTransition(float t) {
if (inverse) {
t = 1f - t;
}
float y = (int) (from * t + to * (1f - t));
parent.setTranslationY(y);
onPanTranslationUpdate(-y, t, isKeyboardVisible);
}
public void stopTransition() {
if (animator != null) {
animator.cancel();
}
animationInProgress = false;
usingInsetAnimator = false;
NotificationCenter.getInstance(UserConfig.selectedAccount).onAnimationFinish(notificationsIndex);
animator = null;
setViewHeight(ViewGroup.LayoutParams.MATCH_PARENT);
viewsToHeightSet.clear();
resizableView.requestLayout();
onPanTranslationUpdate(0, isKeyboardVisible ? 1f : 0f, isKeyboardVisible);
parent.setTranslationY(0);
onTransitionEnd();
}
public void stopTransition(float t, boolean isKeyboardVisible) {
if (animator != null) {
animator.cancel();
}
animationInProgress = false;
NotificationCenter.getInstance(UserConfig.selectedAccount).onAnimationFinish(notificationsIndex);
animator = null;
setViewHeight(ViewGroup.LayoutParams.MATCH_PARENT);
viewsToHeightSet.clear();
resizableView.requestLayout();
onPanTranslationUpdate(0, t, this.isKeyboardVisible = isKeyboardVisible);
parent.setTranslationY(0);
onTransitionEnd();
}
public void setViewHeight(int height) {
for (int i = 0; i < viewsToHeightSet.size(); i++) {
viewsToHeightSet.get(i).getLayoutParams().height = height;
viewsToHeightSet.get(i).requestLayout();
@ -181,7 +252,7 @@ public class AdjustPanLayoutHelper {
return 0;
}
private void getViewsToSetHeight(View parent) {
public void getViewsToSetHeight(View parent) {
viewsToHeightSet.clear();
View v = parent;
while (v != null) {
@ -202,6 +273,12 @@ public class AdjustPanLayoutHelper {
onAttach();
}
public AdjustPanLayoutHelper(View parent, boolean useInsetsAnimator) {
USE_ANDROID11_INSET_ANIMATOR = USE_ANDROID11_INSET_ANIMATOR && useInsetsAnimator;
this.parent = parent;
onAttach();
}
public void onAttach() {
if (!SharedConfig.smoothKeyboard) {
return;
@ -218,6 +295,9 @@ public class AdjustPanLayoutHelper {
parentForListener = resizableView;
resizableView.getViewTreeObserver().addOnPreDrawListener(onPreDrawListener);
}
if (USE_ANDROID11_INSET_ANIMATOR && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
setupNewCallback();
}
}
private Activity getActivity(Context context) {
@ -255,12 +335,35 @@ public class AdjustPanLayoutHelper {
parentForListener.getViewTreeObserver().removeOnPreDrawListener(onPreDrawListener);
parentForListener = null;
}
if (parent != null && USE_ANDROID11_INSET_ANIMATOR && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
parent.setWindowInsetsAnimationCallback(null);
}
}
private boolean enabled = true;
public void setEnabled(boolean value) {
this.enabled = value;
}
private boolean ignoreOnce;
public void ignoreOnce() {
ignoreOnce = true;
}
protected boolean heightAnimationEnabled() {
return true;
}
public void OnPanTranslationUpdate(float y, float progress, boolean keyboardVisible) {
onPanTranslationUpdate(y, progress, keyboardVisible);
}
public void OnTransitionStart(boolean keyboardVisible, int contentHeight) {
onTransitionStart(keyboardVisible, contentHeight);
}
public void OnTransitionEnd() {
onTransitionEnd();
}
protected void onPanTranslationUpdate(float y, float progress, boolean keyboardVisible) {
}
@ -293,4 +396,44 @@ public class AdjustPanLayoutHelper {
AndroidUtilities.cancelRunOnUIThread(delayedAnimationRunnable);
delayedAnimationRunnable.run();
}
@RequiresApi(api = Build.VERSION_CODES.R)
private void setupNewCallback() {
if (resizableView == null) {
return;
}
resizableView.setWindowInsetsAnimationCallback(
new WindowInsetsAnimation.Callback(WindowInsetsAnimation.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE) {
@NonNull
@Override
public WindowInsets onProgress(@NonNull WindowInsets insets, @NonNull List<WindowInsetsAnimation> runningAnimations) {
if (!animationInProgress || AndroidUtilities.screenRefreshRate < 90) {
return insets;
}
WindowInsetsAnimation imeAnimation = null;
for (WindowInsetsAnimation animation : runningAnimations) {
if ((animation.getTypeMask() & WindowInsetsCompat.Type.ime()) != 0) {
imeAnimation = animation;
break;
}
}
if (imeAnimation != null && SystemClock.elapsedRealtime() >= startAfter) {
usingInsetAnimator = true;
updateTransition((float) imeAnimation.getInterpolatedFraction());
}
return insets;
}
@Override
public void onEnd(@NonNull WindowInsetsAnimation animation) {
if (!animationInProgress || AndroidUtilities.screenRefreshRate < 90) {
return;
}
stopTransition();
}
}
);
}
}

View File

@ -723,10 +723,10 @@ public class AlertDialog extends Dialog implements Drawable.Callback {
}
if (totalWidth > availableWidth) {
View negative = findViewWithTag(BUTTON_NEGATIVE);
View neuntral = findViewWithTag(BUTTON_NEUTRAL);
if (negative != null && neuntral != null) {
if (negative.getMeasuredWidth() < neuntral.getMeasuredWidth()) {
neuntral.measure(MeasureSpec.makeMeasureSpec(neuntral.getMeasuredWidth() - (totalWidth - availableWidth), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(neuntral.getMeasuredHeight(), MeasureSpec.EXACTLY));
View neutral = findViewWithTag(BUTTON_NEUTRAL);
if (negative != null && neutral != null) {
if (negative.getMeasuredWidth() < neutral.getMeasuredWidth()) {
neutral.measure(MeasureSpec.makeMeasureSpec(neutral.getMeasuredWidth() - (totalWidth - availableWidth), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(neutral.getMeasuredHeight(), MeasureSpec.EXACTLY));
} else {
negative.measure(MeasureSpec.makeMeasureSpec(negative.getMeasuredWidth() - (totalWidth - availableWidth), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(negative.getMeasuredHeight(), MeasureSpec.EXACTLY));
}
@ -841,7 +841,7 @@ public class AlertDialog extends Dialog implements Drawable.Callback {
textView.setEllipsize(TextUtils.TruncateAt.END);
textView.setSingleLine(true);
textView.setText(neutralButtonText.toString().toUpperCase());
textView.setBackgroundDrawable(Theme.getRoundRectSelectorDrawable(getThemedColor(dialogButtonColorKey)));
textView.setBackground(Theme.getRoundRectSelectorDrawable(getThemedColor(dialogButtonColorKey)));
textView.setPadding(AndroidUtilities.dp(10), 0, AndroidUtilities.dp(10), 0);
if (verticalButtons) {
buttonsLayout.addView(textView, 1, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 36, LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT));
@ -1210,6 +1210,10 @@ public class AlertDialog extends Dialog implements Drawable.Callback {
return null;
}
public ViewGroup getButtonsLayout() {
return buttonsLayout;
}
public static class Builder {
private AlertDialog alertDialog;

View File

@ -237,13 +237,15 @@ public abstract class BaseFragment {
}
if (parentLayout != null && actionBar == null) {
actionBar = createActionBar(parentLayout.getContext());
actionBar.parentFragment = this;
if (actionBar != null) {
actionBar.parentFragment = this;
}
}
}
}
protected ActionBar createActionBar(Context context) {
ActionBar actionBar = new ActionBar(context);
ActionBar actionBar = new ActionBar(context, getResourceProvider());
actionBar.setBackgroundColor(getThemedColor(Theme.key_actionBarDefault));
actionBar.setItemsBackgroundColor(getThemedColor(Theme.key_actionBarDefaultSelector), false);
actionBar.setItemsBackgroundColor(getThemedColor(Theme.key_actionBarActionModeDefaultSelector), true);
@ -322,6 +324,8 @@ public abstract class BaseFragment {
}
}
public void onUserLeaveHint() {}
@CallSuper
public void onResume() {
isPaused = false;
@ -396,7 +400,7 @@ public abstract class BaseFragment {
return allowPresentFragment() && parentLayout != null && parentLayout.presentFragmentAsPreview(fragment);
}
public boolean presentFragmentAsPreviewWithMenu(BaseFragment fragment, View menu) {
public boolean presentFragmentAsPreviewWithMenu(BaseFragment fragment, ActionBarPopupWindow.ActionBarPopupWindowLayout menu) {
return allowPresentFragment() && parentLayout != null && parentLayout.presentFragmentAsPreviewWithMenu(fragment, menu);
}

View File

@ -38,6 +38,7 @@ import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
@ -74,6 +75,7 @@ public class BottomSheet extends Dialog {
protected boolean keyboardVisible;
private WindowInsets lastInsets;
public boolean drawNavigationBar;
public boolean drawDoubleNavigationBar;
public boolean scrollNavBar;
protected boolean useSmoothKeyboard;
@ -95,6 +97,7 @@ public class BottomSheet extends Dialog {
private View customView;
private CharSequence title;
private boolean bigTitle;
private boolean multipleLinesTitle;
private int bottomInset;
private int leftInset;
private int rightInset;
@ -152,6 +155,8 @@ public class BottomSheet extends Dialog {
protected AnimatorSet currentSheetAnimation;
protected int currentSheetAnimationType;
protected ValueAnimator navigationBarAnimation;
protected float navigationBarAlpha = 0;
protected View nestedScrollChild;
private boolean disableScroll;
@ -162,6 +167,7 @@ public class BottomSheet extends Dialog {
private OnDismissListener onHideListener;
protected Theme.ResourcesProvider resourcesProvider;
protected boolean isPortrait;
public void setDisableScroll(boolean b) {
disableScroll = b;
@ -172,6 +178,7 @@ public class BottomSheet extends Dialog {
private boolean openNoDelay;
private float hideSystemVerticalInsetsProgress;
public boolean useBackgroundTopPadding = true;
protected class ContainerView extends LinearLayout implements NestedScrollingParent {
@ -280,9 +287,18 @@ public class BottomSheet extends Dialog {
allowCustomAnimation = allowOld;
} else {
currentAnimation = new AnimatorSet();
currentAnimation.playTogether(ObjectAnimator.ofFloat(containerView, "translationY", 0));
currentAnimation.setDuration((int) (150 * (Math.max(0, translationY) / AndroidUtilities.getPixelsInCM(0.8f, false))));
currentAnimation.setInterpolator(CubicBezierInterpolator.EASE_OUT);
ValueAnimator invalidateContainer = ValueAnimator.ofFloat(0, 1);
invalidateContainer.addUpdateListener(a -> {
if (container != null) {
container.invalidate();
}
});
currentAnimation.playTogether(
ObjectAnimator.ofFloat(containerView, "translationY", 0),
invalidateContainer
);
currentAnimation.setDuration((int) (250 * (Math.max(0, translationY) / AndroidUtilities.getPixelsInCM(0.8f, false))));
currentAnimation.setInterpolator(CubicBezierInterpolator.DEFAULT);
currentAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@ -421,7 +437,7 @@ public class BottomSheet extends Dialog {
if (lastInsets != null && Build.VERSION.SDK_INT >= 21) {
width -= getRightInset() + getLeftInset();
}
boolean isPortrait = width < height;
isPortrait = width < height;
if (containerView != null) {
if (!fullWidth) {
@ -429,7 +445,7 @@ public class BottomSheet extends Dialog {
if (AndroidUtilities.isTablet()) {
widthSpec = MeasureSpec.makeMeasureSpec((int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.8f) + backgroundPaddingLeft * 2, MeasureSpec.EXACTLY);
} else {
widthSpec = MeasureSpec.makeMeasureSpec(isPortrait ? width + backgroundPaddingLeft * 2 : (int) Math.max(width * 0.8f, Math.min(AndroidUtilities.dp(480), width)) + backgroundPaddingLeft * 2, MeasureSpec.EXACTLY);
widthSpec = MeasureSpec.makeMeasureSpec((getBottomSheetWidth(isPortrait, width, height)) + backgroundPaddingLeft * 2, MeasureSpec.EXACTLY);
}
containerView.measure(widthSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
} else {
@ -588,6 +604,9 @@ public class BottomSheet extends Dialog {
} else {
backgroundPaint.setColor(0xff000000);
}
if (drawDoubleNavigationBar && !shouldOverlayCameraViewOverNavBar()) {
drawNavigationBar(canvas, 1f);
}
if (backgroundPaint.getAlpha() < 255 && drawNavigationBar) {
float translation = 0;
if (scrollNavBar || Build.VERSION.SDK_INT >= 29 && getAdditionalMandatoryOffsets() > 0) {
@ -603,7 +622,7 @@ public class BottomSheet extends Dialog {
super.dispatchDraw(canvas);
}
if (!shouldOverlayCameraViewOverNavBar()) {
drawNavigationBar(canvas);
drawNavigationBar(canvas, (drawDoubleNavigationBar ? 0.7f * navigationBarAlpha : 1f));
}
if (drawNavigationBar && rightInset != 0 && rightInset > leftInset && fullWidth && AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y) {
canvas.drawRect(containerView.getRight() - backgroundPaddingLeft, containerView.getTranslationY(), containerView.getRight() + rightInset, getMeasuredHeight(), backgroundPaint);
@ -623,7 +642,7 @@ public class BottomSheet extends Dialog {
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
if (child instanceof CameraView) {
if (shouldOverlayCameraViewOverNavBar()) {
drawNavigationBar(canvas);
drawNavigationBar(canvas, 1f);
}
return super.drawChild(canvas, child, drawingTime);
}
@ -655,7 +674,7 @@ public class BottomSheet extends Dialog {
}
}
public void drawNavigationBar(Canvas canvas) {
public void drawNavigationBar(Canvas canvas, float alpha) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (navBarColorKey != null) {
backgroundPaint.setColor(getThemedColor(navBarColorKey));
@ -667,21 +686,40 @@ public class BottomSheet extends Dialog {
}
if ((drawNavigationBar && bottomInset != 0) || currentPanTranslationY != 0) {
float translation = 0;
if (scrollNavBar || Build.VERSION.SDK_INT >= 29 && getAdditionalMandatoryOffsets() > 0) {
float dist = containerView.getMeasuredHeight() - containerView.getTranslationY();
translation = Math.max(0, getBottomInset() - dist);
}
int navBarHeight = drawNavigationBar ? getBottomInset() : 0;
if (scrollNavBar || Build.VERSION.SDK_INT >= 29 && getAdditionalMandatoryOffsets() > 0) {
if (drawDoubleNavigationBar) {
translation = Math.max(0, Math.min(navBarHeight - currentPanTranslationY, containerView.getTranslationY()));
} else {
float dist = containerView.getMeasuredHeight() - containerView.getTranslationY();
translation = Math.max(0, getBottomInset() - dist);
}
}
int wasAlpha = backgroundPaint.getAlpha();
if (alpha < 1f) {
backgroundPaint.setAlpha((int) (wasAlpha * alpha));
}
canvas.drawRect(containerView.getLeft() + backgroundPaddingLeft, getMeasuredHeight() - navBarHeight + translation - currentPanTranslationY, containerView.getRight() - backgroundPaddingLeft, getMeasuredHeight() + translation, backgroundPaint);
backgroundPaint.setAlpha(wasAlpha);
if (overlayDrawNavBarColor != 0) {
backgroundPaint.setColor(overlayDrawNavBarColor);
wasAlpha = backgroundPaint.getAlpha();
if (alpha < 1f) {
backgroundPaint.setAlpha((int) (wasAlpha * alpha));
translation = 0;
}
canvas.drawRect(containerView.getLeft() + backgroundPaddingLeft, getMeasuredHeight() - navBarHeight + translation - currentPanTranslationY, containerView.getRight() - backgroundPaddingLeft, getMeasuredHeight() + translation, backgroundPaint);
backgroundPaint.setAlpha(wasAlpha);
}
}
}
}
protected int getBottomSheetWidth(boolean isPortrait, int width,int height) {
return isPortrait ? width : (int) Math.max(width * 0.8f, Math.min(AndroidUtilities.dp(480), width));
}
protected boolean shouldOverlayCameraViewOverNavBar() {
return false;
}
@ -785,7 +823,7 @@ public class BottomSheet extends Dialog {
textView.setTextColor(getThemedColor(Theme.key_featuredStickers_buttonText));
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
textView.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(4), getThemedColor(Theme.key_featuredStickers_addButton), getThemedColor(Theme.key_featuredStickers_addButtonPressed)));
textView.setBackground(Theme.AdaptiveRipple.filledRect(getThemedColor(Theme.key_featuredStickers_addButton), 4));
addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0, 16, 16, 16, 16));
}
}
@ -847,6 +885,16 @@ public class BottomSheet extends Dialog {
Integer color = resourcesProvider != null ? resourcesProvider.getColor(key) : null;
return color != null ? color : Theme.getColor(key);
}
public boolean isSelected = false;
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
if (isSelected) {
info.setSelected(true);
}
}
}
public void setAllowNestedScroll(boolean value) {
@ -892,6 +940,12 @@ public class BottomSheet extends Dialog {
}
return true;
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
mainContainerDispatchDraw(canvas);
}
};
container.setBackgroundDrawable(backDrawable);
focusable = needFocus;
@ -920,6 +974,22 @@ public class BottomSheet extends Dialog {
backDrawable.setAlpha(0);
}
protected void mainContainerDispatchDraw(Canvas canvas) {
}
public void fixNavigationBar() {
fixNavigationBar(getThemedColor(Theme.key_dialogBackground));
}
public void fixNavigationBar(int bgColor) {
drawNavigationBar = true;
drawDoubleNavigationBar = true;
scrollNavBar = true;
navBarColorKey = null;
setOverlayNavBarColor(navBarColor = bgColor);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -963,8 +1033,29 @@ public class BottomSheet extends Dialog {
containerView.setVisibility(View.INVISIBLE);
container.addView(containerView, 0, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM));
int topOffset = 0;
if (title != null) {
titleView = new TextView(getContext());
titleView = new TextView(getContext()) {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (multipleLinesTitle) {
int topOffset = getMeasuredHeight();
if (customView != null) {
((ViewGroup.MarginLayoutParams) customView.getLayoutParams()).topMargin = topOffset;
} else if (containerView != null) {
for (int i = 1; i < containerView.getChildCount(); ++i) {
View child = containerView.getChildAt(i);
if (child instanceof BottomSheetCell) {
((ViewGroup.MarginLayoutParams) child.getLayoutParams()).topMargin = topOffset;
topOffset += AndroidUtilities.dp(48);
}
}
}
}
}
};
int height = 48;
titleView.setText(title);
titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
titleView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL);
@ -975,34 +1066,52 @@ public class BottomSheet extends Dialog {
titleView.setEllipsize(TextUtils.TruncateAt.MIDDLE);
titleView.setTextColor(getThemedColor(Theme.key_dialogTextBlue2));
titleView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
titleView.setPadding(AndroidUtilities.dp(21), AndroidUtilities.dp(multipleLinesTitle ? 14 : 6), AndroidUtilities.dp(21), AndroidUtilities.dp(8));
} else {
titleView.setTextColor(getThemedColor(Theme.key_dialogTextGray2));
titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
titleView.setPadding(AndroidUtilities.dp(16), AndroidUtilities.dp(multipleLinesTitle ? 8 : 0), AndroidUtilities.dp(16), AndroidUtilities.dp(8));
}
if (multipleLinesTitle) {
titleView.setSingleLine(false);
titleView.setMaxLines(5);
titleView.setEllipsize(TextUtils.TruncateAt.END);
} else {
titleView.setLines(1);
titleView.setSingleLine(true);
titleView.setEllipsize(TextUtils.TruncateAt.MIDDLE);
}
titleView.setGravity(Gravity.CENTER_VERTICAL);
containerView.addView(titleView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 21, 15, 21, 0));
containerView.addView(titleView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, multipleLinesTitle ? ViewGroup.LayoutParams.WRAP_CONTENT : height));
titleView.setOnTouchListener((v, event) -> true);
topOffset += height;
}
if (customView != null) {
if (customView.getParent() != null) {
ViewGroup viewGroup = (ViewGroup) customView.getParent();
viewGroup.removeView(customView);
}
containerView.addView(customView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 0));
if (!useBackgroundTopPadding) {
containerView.setClipToPadding(false);
containerView.setClipChildren(false);
container.setClipToPadding(false);
container.setClipChildren(false);
containerView.addView(customView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 0, -backgroundPaddingTop + topOffset, 0, 0));
} else {
containerView.addView(customView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 0, topOffset, 0, 0));
}
} else {
if (items != null) {
FrameLayout rootView = new ScrollView(getContext());
LinearLayout rowView = new LinearLayout(getContext());
rowView.setOrientation(LinearLayout.VERTICAL);
rootView.addView(rowView, new ScrollView.LayoutParams(-1, -1));
containerView.addView(rootView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 0));
FrameLayout rowLayout = null;
int lastRowLayoutNum = 0;
for (int a = 0; a < items.length; a++) {
if (items[a] == null) {
continue;
}
BottomSheetCell cell = new BottomSheetCell(getContext(), 0, resourcesProvider);
cell.setTextAndIcon(items[a], itemIcons != null ? itemIcons[a] : 0, null, bigTitle);
rowView.addView(cell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 56, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 0));
containerView.addView(cell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.TOP, 0, topOffset, 0, 0));
topOffset += 48;
cell.setTag(a);
cell.setOnClickListener(v -> dismissWithButtonClick((Integer) v.getTag()));
itemViews.add(cell);
@ -1197,8 +1306,8 @@ public class BottomSheet extends Dialog {
if (currentSheetAnimation != null) {
currentSheetAnimation.cancel();
currentSheetAnimation = null;
currentSheetAnimationType = 0;
}
currentSheetAnimationType = 0;
}
public void setOnHideListener(OnDismissListener listener) {
@ -1219,12 +1328,24 @@ public class BottomSheet extends Dialog {
if (Build.VERSION.SDK_INT >= 20 && useHardwareLayer) {
container.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
containerView.setTranslationY(containerView.getMeasuredHeight() + (scrollNavBar ? getBottomInset() : 0));
containerView.setTranslationY(getContainerViewHeight() + container.keyboardHeight + AndroidUtilities.dp(10) + (scrollNavBar ? getBottomInset() : 0));
currentSheetAnimationType = 1;
if (navigationBarAnimation != null) {
navigationBarAnimation.cancel();
}
navigationBarAnimation = ValueAnimator.ofFloat(navigationBarAlpha, 1f);
navigationBarAnimation.addUpdateListener(a -> {
navigationBarAlpha = (float) a.getAnimatedValue();
if (container != null) {
container.invalidate();
}
});
currentSheetAnimation = new AnimatorSet();
currentSheetAnimation.playTogether(
ObjectAnimator.ofFloat(containerView, View.TRANSLATION_Y, 0),
ObjectAnimator.ofInt(backDrawable, AnimationProperties.COLOR_DRAWABLE_ALPHA, dimBehind ? dimBehindAlpha : 0));
ObjectAnimator.ofInt(backDrawable, AnimationProperties.COLOR_DRAWABLE_ALPHA, dimBehind ? dimBehindAlpha : 0),
navigationBarAnimation
);
currentSheetAnimation.setDuration(400);
currentSheetAnimation.setInterpolator(openInterpolator);
currentSheetAnimation.addListener(new AnimatorListenerAdapter() {
@ -1333,7 +1454,7 @@ public class BottomSheet extends Dialog {
currentSheetAnimationType = 2;
currentSheetAnimation = new AnimatorSet();
currentSheetAnimation.playTogether(
ObjectAnimator.ofFloat(containerView, View.TRANSLATION_Y, containerView.getMeasuredHeight() + AndroidUtilities.dp(10) + (scrollNavBar ? getBottomInset() : 0)),
ObjectAnimator.ofFloat(containerView, View.TRANSLATION_Y, getContainerViewHeight() + container.keyboardHeight + AndroidUtilities.dp(10) + (scrollNavBar ? getBottomInset() : 0)),
ObjectAnimator.ofInt(backDrawable, AnimationProperties.COLOR_DRAWABLE_ALPHA, 0)
);
currentSheetAnimation.setDuration(180);
@ -1381,6 +1502,15 @@ public class BottomSheet extends Dialog {
return super.dispatchTouchEvent(ev);
}
public void onDismissAnimationStart() {}
public int getContainerViewHeight() {
if (containerView == null) {
return 0;
}
return containerView.getMeasuredHeight();
}
@Override
public void dismiss() {
if (delegate != null && !delegate.canDismiss()) {
@ -1395,22 +1525,34 @@ public class BottomSheet extends Dialog {
}
cancelSheetAnimation();
long duration = 0;
onDismissAnimationStart();
if (!allowCustomAnimation || !onCustomCloseAnimation()) {
currentSheetAnimationType = 2;
currentSheetAnimation = new AnimatorSet();
currentSheetAnimation.playTogether(
ObjectAnimator.ofFloat(containerView, View.TRANSLATION_Y, containerView.getMeasuredHeight() + container.keyboardHeight + AndroidUtilities.dp(10) + (scrollNavBar ? getBottomInset() : 0)),
ObjectAnimator.ofInt(backDrawable, AnimationProperties.COLOR_DRAWABLE_ALPHA, 0)
);
if (useFastDismiss) {
int height = containerView.getMeasuredHeight();
duration = Math.max(60, (int) (250 * (height - containerView.getTranslationY()) / (float) height));
currentSheetAnimation.setDuration(duration);
useFastDismiss = false;
} else {
currentSheetAnimation.setDuration(duration = 250);
if (navigationBarAnimation != null) {
navigationBarAnimation.cancel();
}
currentSheetAnimation.setInterpolator(CubicBezierInterpolator.DEFAULT);
navigationBarAnimation = ValueAnimator.ofFloat(navigationBarAlpha, 0f);
navigationBarAnimation.addUpdateListener(a -> {
navigationBarAlpha = (float) a.getAnimatedValue();
if (container != null) {
container.invalidate();
}
});
currentSheetAnimation.playTogether(
ObjectAnimator.ofFloat(containerView, View.TRANSLATION_Y, getContainerViewHeight() + container.keyboardHeight + AndroidUtilities.dp(10) + (scrollNavBar ? getBottomInset() : 0)),
ObjectAnimator.ofInt(backDrawable, AnimationProperties.COLOR_DRAWABLE_ALPHA, 0),
navigationBarAnimation
);
// if (useFastDismiss) {
// int height = containerView.getMeasuredHeight();
// duration = Math.max(60, (int) (250 * (height - containerView.getTranslationY()) / (float) height));
// currentSheetAnimation.setDuration(duration);
// useFastDismiss = false;
// } else {
currentSheetAnimation.setDuration(duration = 250);
// }
currentSheetAnimation.setInterpolator(CubicBezierInterpolator.EASE_OUT);
currentSheetAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@ -1478,12 +1620,27 @@ public class BottomSheet extends Dialog {
this(context, false);
}
public Builder(Context context, int bgColor) {
this(context, false, null, bgColor);
}
public Builder(Context context, boolean needFocus) {
this(context, needFocus, null);
}
public Builder(Context context, boolean needFocus, int bgColor) {
this(context, needFocus, null, bgColor);
}
public Builder(Context context, boolean needFocus, Theme.ResourcesProvider resourcesProvider) {
bottomSheet = new BottomSheet(context, needFocus, resourcesProvider);
bottomSheet.fixNavigationBar();
}
public Builder(Context context, boolean needFocus, Theme.ResourcesProvider resourcesProvider, int bgColor) {
bottomSheet = new BottomSheet(context, needFocus, resourcesProvider);
bottomSheet.setBackgroundColor(bgColor);
bottomSheet.fixNavigationBar(bgColor);
}
public Builder setItems(CharSequence[] items, final OnClickListener onClickListener) {
@ -1518,6 +1675,11 @@ public class BottomSheet extends Dialog {
return this;
}
public Builder setTitleMultipleLines(boolean allowMultipleLines) {
bottomSheet.multipleLinesTitle = allowMultipleLines;
return this;
}
public BottomSheet create() {
return bottomSheet;
}
@ -1575,13 +1737,6 @@ public class BottomSheet extends Dialog {
bottomSheet.setOnHideListener(onDismissListener);
return this;
}
public Builder fixNavigationBar() {
bottomSheet.drawNavigationBar = true;
bottomSheet.scrollNavBar = true;
bottomSheet.setOverlayNavBarColor(bottomSheet.getThemedColor(Theme.key_dialogBackground));
return this;
}
}
public int getLeftInset() {

View File

@ -15,7 +15,6 @@ import org.telegram.messenger.FileLog;
import org.telegram.messenger.ImageLoader;
import org.telegram.messenger.ImageLocation;
import org.telegram.messenger.ImageReceiver;
import org.telegram.messenger.SharedConfig;
import org.telegram.messenger.Utilities;
import org.telegram.tgnet.ResultCallback;
import org.telegram.tgnet.TLRPC;
@ -31,7 +30,7 @@ public class EmojiThemes {
public boolean showAsDefaultStub;
public String emoji;
int currentIndex = 0;
ArrayList<ThemeItem> items = new ArrayList<>();
public ArrayList<ThemeItem> items = new ArrayList<>();
private static final String[] previewColorKeys = new String[]{
Theme.key_chat_inBubble,
@ -364,14 +363,11 @@ public class EmojiThemes {
}
ImageLocation imageLocation = ImageLocation.getForDocument(wallPaper.document);
ImageReceiver imageReceiver = new ImageReceiver();
String imageFilter;
if (SharedConfig.getDevicePerformanceClass() == SharedConfig.PERFORMANCE_CLASS_LOW) {
int w = Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y);
int h = Math.max(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y);
imageFilter = (int) (w / AndroidUtilities.density) + "_" + (int) (h / AndroidUtilities.density) + "_f";
} else {
imageFilter = (int) (1080 / AndroidUtilities.density) + "_" + (int) (1920 / AndroidUtilities.density) + "_f";
}
int w = Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y);
int h = Math.max(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y);
imageFilter = (int) (w / AndroidUtilities.density) + "_" + (int) (h / AndroidUtilities.density) + "_f";
imageReceiver.setImage(imageLocation, imageFilter, null, ".jpg", wallPaper, 1);
imageReceiver.setDelegate((receiver, set, thumb, memCache) -> {
@ -424,13 +420,13 @@ public class EmojiThemes {
}
return;
}
final TLRPC.PhotoSize thumbSize = FileLoader.getClosestPhotoSizeWithSize(wallpaper.document.thumbs, 120);
final TLRPC.PhotoSize thumbSize = FileLoader.getClosestPhotoSizeWithSize(wallpaper.document.thumbs, 140);
ImageLocation imageLocation = ImageLocation.getForDocument(thumbSize, wallpaper.document);
ImageReceiver imageReceiver = new ImageReceiver();
imageReceiver.setImage(imageLocation, "120_80", null, null, null, 1);
imageReceiver.setImage(imageLocation, "120_140", null, null, null, 1);
imageReceiver.setDelegate((receiver, set, thumb, memCache) -> {
ImageReceiver.BitmapHolder holder = receiver.getBitmapSafe();
if (!set || holder == null) {
if (!set || holder == null || holder.bitmap.isRecycled()) {
return;
}
Bitmap resultBitmap = holder.bitmap;

View File

@ -27,7 +27,9 @@ import android.text.SpannableStringBuilder;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
@ -84,6 +86,8 @@ public class SimpleTextView extends View implements Drawable.Callback {
private int textWidth;
private int totalWidth;
private int textHeight;
public int rightDrawableX;
public int rightDrawableY;
private boolean wasLayout;
private int minWidth;
@ -104,6 +108,11 @@ public class SimpleTextView extends View implements Drawable.Callback {
private Stack<SpoilerEffect> spoilersPool = new Stack<>();
private Path path = new Path();
private boolean usaAlphaForEmoji;
private boolean canHideRightDrawable;
private boolean rightDrawableHidden;
private OnClickListener rightDrawableOnClickListener;
private boolean maybeClick;
private float touchDownX, touchDownY;
public SimpleTextView(Context context) {
super(context);
@ -244,6 +253,7 @@ public class SimpleTextView extends View implements Drawable.Callback {
protected boolean createLayout(int width) {
CharSequence text = this.text;
replacingDrawableTextIndex = -1;
rightDrawableHidden = false;
if (text != null) {
text = Emoji.replaceEmoji(text, textPaint.getFontMetricsInt(), (int) textPaint.getTextSize(), false);
try {
@ -251,9 +261,10 @@ public class SimpleTextView extends View implements Drawable.Callback {
width -= leftDrawable.getIntrinsicWidth();
width -= drawablePadding;
}
int rightDrawableWidth = 0;
if (rightDrawable != null) {
int dw = (int) (rightDrawable.getIntrinsicWidth() * rightDrawableScale);
width -= dw;
rightDrawableWidth = (int) (rightDrawable.getIntrinsicWidth() * rightDrawableScale);
width -= rightDrawableWidth;
width -= drawablePadding;
}
if (replacedText != null && replacedDrawable != null) {
@ -267,6 +278,14 @@ public class SimpleTextView extends View implements Drawable.Callback {
width -= drawablePadding;
}
}
if (canHideRightDrawable && rightDrawableWidth != 0) {
CharSequence string = TextUtils.ellipsize(text, textPaint, width, TextUtils.TruncateAt.END);
if (!text.equals(string)) {
rightDrawableHidden = true;
width += rightDrawableWidth;
width += drawablePadding;
}
}
if (buildFullLayout) {
CharSequence string = TextUtils.ellipsize(text, textPaint, width, TextUtils.TruncateAt.END);
if (!string.equals(text)) {
@ -621,7 +640,7 @@ public class SimpleTextView extends View implements Drawable.Callback {
totalWidth += drawablePadding + replacedDrawable.getIntrinsicWidth();
}
}
if (rightDrawable != null) {
if (rightDrawable != null && !rightDrawableHidden && rightDrawableScale > 0) {
int x = textOffsetX + textWidth + drawablePadding + (int) -scrollingOffset;
if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.CENTER_HORIZONTAL) {
x += offsetX;
@ -632,6 +651,8 @@ public class SimpleTextView extends View implements Drawable.Callback {
int dh = (int) (rightDrawable.getIntrinsicHeight() * rightDrawableScale);
int y = (textHeight - dh) / 2 + rightDrawableTopPadding;
rightDrawable.setBounds(x, y, x + dw, y + dh);
rightDrawableX = x + (dw >> 1);
rightDrawableY = y + (dh >> 1);
rightDrawable.draw(canvas);
totalWidth += drawablePadding + dw;
}
@ -832,4 +853,37 @@ public class SimpleTextView extends View implements Drawable.Callback {
public int getTextColor() {
return textPaint.getColor();
}
public void setCanHideRightDrawable(boolean b) {
canHideRightDrawable = b;
}
public void setRightDrawableOnClick(OnClickListener onClickListener) {
rightDrawableOnClickListener = onClickListener;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (rightDrawableOnClickListener != null && rightDrawable != null) {
AndroidUtilities.rectTmp.set(rightDrawableX - AndroidUtilities.dp(16), rightDrawableY - AndroidUtilities.dp(16), rightDrawableX + AndroidUtilities.dp(16), rightDrawableY + AndroidUtilities.dp(16));
if (event.getAction() == MotionEvent.ACTION_DOWN && AndroidUtilities.rectTmp.contains((int) event.getX(), (int) event.getY())) {
maybeClick = true;
touchDownX = event.getX();
touchDownY = event.getY();
getParent().requestDisallowInterceptTouchEvent(true);
} else if (event.getAction() == MotionEvent.ACTION_MOVE && maybeClick) {
if (Math.abs(event.getX() - touchDownX) >= AndroidUtilities.touchSlop || Math.abs(event.getY() - touchDownY) >= AndroidUtilities.touchSlop) {
maybeClick = false;
getParent().requestDisallowInterceptTouchEvent(false);
}
} else if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) {
if (maybeClick && event.getAction() == MotionEvent.ACTION_UP) {
rightDrawableOnClickListener.onClick(this);
}
maybeClick = false;
getParent().requestDisallowInterceptTouchEvent(false);
}
}
return super.onTouchEvent(event) || maybeClick;
}
}

View File

@ -594,7 +594,7 @@ public class ActionIntroActivity extends BaseFragment implements LocationControl
}
cellFlickerDrawable.setParentWidth(getMeasuredWidth());
AndroidUtilities.rectTmp.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
cellFlickerDrawable.draw(canvas, AndroidUtilities.rectTmp, AndroidUtilities.dp(4));
cellFlickerDrawable.draw(canvas, AndroidUtilities.rectTmp, AndroidUtilities.dp(4), null);
invalidate();
}
}
@ -606,7 +606,7 @@ public class ActionIntroActivity extends BaseFragment implements LocationControl
buttonTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
buttonTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
int buttonRadiusDp = currentType == ACTION_TYPE_SET_PASSCODE || currentType == ACTION_TYPE_CHANGE_PHONE_NUMBER ? 6 : 4;
buttonTextView.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(buttonRadiusDp), Theme.getColor(Theme.key_featuredStickers_addButton), Theme.getColor(Theme.key_featuredStickers_addButtonPressed)));
buttonTextView.setBackground(Theme.AdaptiveRipple.filledRect(Theme.key_featuredStickers_addButton, buttonRadiusDp));
viewGroup.addView(buttonTextView);
buttonTextView.setOnClickListener(v -> {
if (getParentActivity() == null) {
@ -663,7 +663,7 @@ public class ActionIntroActivity extends BaseFragment implements LocationControl
BottomBuilder builder = new BottomBuilder(getParentActivity());
builder.addTitle(LocaleController.getString("PhoneNumberAlert", R.string.PhoneNumberAlert));
builder.addItem(LocaleController.getString("Change", R.string.Change),R.drawable.baseline_check_circle_24, i -> {
presentFragment(new ChangePhoneActivity(), true);
presentFragment(new LoginActivity().changePhoneNumber(), true);
return Unit.INSTANCE;
});
builder.addCancelItem();
@ -687,6 +687,7 @@ public class ActionIntroActivity extends BaseFragment implements LocationControl
case ACTION_TYPE_SET_PASSCODE: {
imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
imageView.setAnimation(R.raw.utyan_passcode, 200, 200);
imageView.setFocusable(false);
imageView.setOnClickListener(v -> {
if (!imageView.getAnimatedDrawable().isRunning()) {
imageView.getAnimatedDrawable().setCurrentFrame(0, false);

View File

@ -19,6 +19,9 @@ import android.widget.FrameLayout;
import androidx.recyclerview.widget.RecyclerView;
import androidx.collection.LongSparseArray;
import androidx.recyclerview.widget.RecyclerView;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.ContactsController;
import org.telegram.messenger.FileLog;
@ -45,9 +48,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import androidx.collection.LongSparseArray;
import androidx.recyclerview.widget.RecyclerView;
public class ContactsAdapter extends RecyclerListView.SectionsAdapter {
private int currentAccount = UserConfig.selectedAccount;
@ -447,9 +447,9 @@ public class ContactsAdapter extends RecyclerListView.SectionsAdapter {
}
} else if (isAdmin) {
if (isChannel) {
textCell.setTextAndIcon(LocaleController.getString("ChannelInviteViaLink", R.string.ChannelInviteViaLink), R.drawable.profile_link, false);
textCell.setTextAndIcon(LocaleController.getString("ChannelInviteViaLink", R.string.ChannelInviteViaLink), R.drawable.msg_link2, false);
} else {
textCell.setTextAndIcon(LocaleController.getString("InviteToGroupByLink", R.string.InviteToGroupByLink), R.drawable.profile_link, false);
textCell.setTextAndIcon(LocaleController.getString("InviteToGroupByLink", R.string.InviteToGroupByLink), R.drawable.msg_link2, false);
}
} else {
if (position == 0) {

View File

@ -28,7 +28,6 @@ import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager.widget.ViewPager;
import org.telegram.messenger.AccountInstance;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.BuildVars;
import org.telegram.messenger.ChatObject;
@ -186,6 +185,9 @@ public class DialogsAdapter extends RecyclerListView.SelectionAdapter {
}
return (currentCount = 0);
}
if (dialogsCount == 0 && messagesController.isLoadingDialogs(folderId)) {
return (currentCount = 0);
}
int count = dialogsCount;
if (dialogsType == 7 || dialogsType == 8) {
if (dialogsCount == 0) {
@ -418,7 +420,11 @@ public class DialogsAdapter extends RecyclerListView.SelectionAdapter {
case VIEW_TYPE_CONTACTS_FLICKER:
FlickerLoadingView flickerLoadingView = new FlickerLoadingView(mContext);
flickerLoadingView.setIsSingleCell(true);
flickerLoadingView.setViewType(viewType == VIEW_TYPE_CONTACTS_FLICKER ? FlickerLoadingView.CONTACT_TYPE : FlickerLoadingView.DIALOG_CELL_TYPE);
int flickerType = viewType == VIEW_TYPE_CONTACTS_FLICKER ? FlickerLoadingView.CONTACT_TYPE : FlickerLoadingView.DIALOG_CELL_TYPE;
flickerLoadingView.setViewType(flickerType);
if (flickerType == FlickerLoadingView.CONTACT_TYPE) {
flickerLoadingView.setIgnoreHeightCheck(true);
}
if (viewType == VIEW_TYPE_CONTACTS_FLICKER) {
flickerLoadingView.setItemsCount((int) (AndroidUtilities.displaySize.y * 0.5f / AndroidUtilities.dp(64)));
}
@ -703,25 +709,17 @@ public class DialogsAdapter extends RecyclerListView.SelectionAdapter {
cell.setTextSize(14);
cell.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText));
cell.setBackgroundColor(Theme.getColor(Theme.key_graySection));
try {
MessagesController messagesController = AccountInstance.getInstance(currentAccount).getMessagesController();
int j = 0;
if (messagesController.dialogsMyChannels.size() > 0) {
if (i == j) {
cell.setText(LocaleController.getString("MyChannels", R.string.MyChannels));
}
j += 1 + messagesController.dialogsMyChannels.size();
}
if (messagesController.dialogsMyGroups.size() > 0) {
if (i == j) {
cell.setText(LocaleController.getString("MyGroups", R.string.MyGroups));
}
j += 1 + messagesController.dialogsMyGroups.size();
}
if (messagesController.dialogsCanAddUsers.size() > 0 && i == j) {
switch (((DialogsActivity.DialogsHeader) getItem(i)).headerType) {
case DialogsActivity.DialogsHeader.HEADER_TYPE_MY_CHANNELS:
cell.setText(LocaleController.getString("MyChannels", R.string.MyChannels));
break;
case DialogsActivity.DialogsHeader.HEADER_TYPE_MY_GROUPS:
cell.setText(LocaleController.getString("MyGroups", R.string.MyGroups));
break;
case DialogsActivity.DialogsHeader.HEADER_TYPE_GROUPS:
cell.setText(LocaleController.getString("FilterGroups", R.string.FilterGroups));
}
} catch (Exception ignore) {}
break;
}
break;
}
case VIEW_TYPE_NEW_CHAT_HINT: {
@ -740,7 +738,7 @@ public class DialogsAdapter extends RecyclerListView.SelectionAdapter {
case VIEW_TYPE_TEXT: {
TextCell cell = (TextCell) holder.itemView;
cell.setColors(Theme.key_windowBackgroundWhiteBlueText4, Theme.key_windowBackgroundWhiteBlueText4);
cell.setTextAndIcon(LocaleController.getString("CreateGroupForImport", R.string.CreateGroupForImport), R.drawable.groups_create, dialogsCount != 0);
cell.setTextAndIcon(LocaleController.getString("CreateGroupForImport", R.string.CreateGroupForImport), R.drawable.msg_groups_create, dialogsCount != 0);
cell.setIsInDialogs();
cell.setOffsetFromImage(75);
break;
@ -839,7 +837,7 @@ public class DialogsAdapter extends RecyclerListView.SelectionAdapter {
} else if (i > size) {
return VIEW_TYPE_LAST_EMPTY;
}
if (dialogsType == 2 && getItem(i) == null) {
if (dialogsType == 2 && getItem(i) instanceof DialogsActivity.DialogsHeader) {
return VIEW_TYPE_HEADER_2;
}
return VIEW_TYPE_DIALOG;

View File

@ -9,18 +9,17 @@
package org.telegram.ui.Adapters;
import android.content.Context;
import android.os.SystemClock;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.exoplayer2.util.Log;
import org.telegram.PhoneFormat.PhoneFormat;
import org.telegram.SQLite.SQLiteCursor;
import org.telegram.SQLite.SQLitePreparedStatement;
@ -43,6 +42,7 @@ import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.ActionBar.ThemeDescription;
import org.telegram.ui.Cells.DialogCell;
import org.telegram.ui.Cells.GraySectionCell;
import org.telegram.ui.Cells.HashtagSearchCell;
@ -60,6 +60,7 @@ import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import androidx.collection.LongSparseArray;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@ -95,13 +96,21 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
private int lastLocalSearchId;
private int lastMessagesSearchId;
private int dialogsType;
private DefaultItemAnimator itemAnimator;
private SearchAdapterHelper searchAdapterHelper;
private RecyclerListView innerListView;
private long selfUserId;
public int showMoreLastItem;
public boolean showMoreAnimation = false;
private long lastShowMoreUpdate;
public View showMoreHeader;
private Runnable cancelShowMoreAnimation;
private int currentAccount = UserConfig.selectedAccount;
private ArrayList<RecentSearchObject> recentSearchObjects = new ArrayList<>();
private ArrayList<RecentSearchObject> filteredRecentSearchObjects = new ArrayList<>();
private String filteredRecentQuery = null;
private LongSparseArray<RecentSearchObject> recentSearchObjectsById = new LongSparseArray<>();
private ArrayList<FiltersView.DateData> localTipDates = new ArrayList<>();
private boolean localTipArchive;
@ -139,6 +148,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
private final Context mContext;
private final int currentAccount;
private boolean drawChecked;
private boolean forceDarkTheme;
public CategoryAdapterRecycler(Context context, int account, boolean drawChecked) {
this.drawChecked = drawChecked;
@ -152,9 +162,9 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = new HintDialogCell(mContext, drawChecked);
view.setLayoutParams(new RecyclerView.LayoutParams(AndroidUtilities.dp(80), AndroidUtilities.dp(86)));
return new RecyclerListView.Holder(view);
HintDialogCell cell = new HintDialogCell(mContext, drawChecked);
cell.setLayoutParams(new RecyclerView.LayoutParams(AndroidUtilities.dp(80), AndroidUtilities.dp(86)));
return new RecyclerListView.Holder(cell);
}
@Override
@ -197,7 +207,8 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
}
}
public DialogsSearchAdapter(Context context, int messagesSearch, int type) {
public DialogsSearchAdapter(Context context, int messagesSearch, int type, DefaultItemAnimator itemAnimator) {
this.itemAnimator = itemAnimator;
searchAdapterHelper = new SearchAdapterHelper(false);
searchAdapterHelper.setDelegate(new SearchAdapterHelper.SearchAdapterHelperDelegate() {
@Override
@ -276,12 +287,16 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
reqId = 0;
}
if (TextUtils.isEmpty(query)) {
filteredRecentQuery = null;
searchResultMessages.clear();
lastReqId = 0;
lastMessagesSearchString = null;
searchWas = false;
notifyDataSetChanged();
return;
} else {
filterRecent(query);
searchAdapterHelper.mergeResults(searchResult, filteredRecentSearchObjects);
}
final TLRPC.TL_messages_searchGlobal req = new TLRPC.TL_messages_searchGlobal();
@ -365,6 +380,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
searchAdapterHelper.clear();
}
}
searchAdapterHelper.mergeResults(searchResult, filteredRecentSearchObjects);
if (delegate != null) {
delegate.searchStateChanged(waitingResponseCount > 0, true);
delegate.runResultsEnterAnimation();
@ -380,11 +396,15 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
}
public boolean hasRecentSearch() {
return dialogsType != 2 && dialogsType != 4 && dialogsType != 5 && dialogsType != 6 && dialogsType != 11 && (!recentSearchObjects.isEmpty() || !MediaDataController.getInstance(currentAccount).hints.isEmpty());
return dialogsType != 2 && dialogsType != 4 && dialogsType != 5 && dialogsType != 6 && dialogsType != 11 && getRecentItemsCount() > 0;
}
public boolean isSearchWas() {
return searchWas;
}
public boolean isRecentSearchDisplayed() {
return needMessagesSearch != 2 && !searchWas && (!recentSearchObjects.isEmpty() || !MediaDataController.getInstance(currentAccount).hints.isEmpty()) && dialogsType != 2 && dialogsType != 4 && dialogsType != 5 && dialogsType != 6 && dialogsType != 11;
return needMessagesSearch != 2 && hasRecentSearch();
}
public void loadRecentSearch() {
@ -528,12 +548,36 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
}
public void clearRecentSearch() {
recentSearchObjectsById = new LongSparseArray<>();
recentSearchObjects = new ArrayList<>();
StringBuilder queryFilter = null;
if (searchWas) {
while (filteredRecentSearchObjects.size() > 0) {
RecentSearchObject obj = filteredRecentSearchObjects.remove(0);
recentSearchObjects.remove(obj);
recentSearchObjectsById.remove(obj.did);
if (queryFilter == null) {
queryFilter = new StringBuilder("did IN (");
queryFilter.append(obj.did);
} else {
queryFilter.append(", ").append(obj.did);
}
}
if (queryFilter == null) {
queryFilter = new StringBuilder("1");
} else {
queryFilter.append(")");
}
} else {
filteredRecentSearchObjects.clear();
recentSearchObjects.clear();
recentSearchObjectsById.clear();
queryFilter = new StringBuilder("1");
}
final StringBuilder finalQueryFilter = queryFilter;
notifyDataSetChanged();
MessagesStorage.getInstance(currentAccount).getStorageQueue().postRunnable(() -> {
try {
MessagesStorage.getInstance(currentAccount).getDatabase().executeFast("DELETE FROM search_recent WHERE 1").stepThis().dispose();
finalQueryFilter.insert(0, "DELETE FROM search_recent WHERE ");
MessagesStorage.getInstance(currentAccount).getDatabase().executeFast(finalQueryFilter.toString()).stepThis().dispose();
} catch (Exception e) {
FileLog.e(e);
}
@ -621,6 +665,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
searchResultMessages.clear();
}
searchWas = true;
final int recentCount = filteredRecentSearchObjects.size();
for (int a = 0; a < result.size(); a++) {
Object obj = result.get(a);
long dialogId = 0;
@ -658,11 +703,25 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
});
}
}
boolean foundInRecent = false;
for (int j = 0; j < recentCount; ++j) {
RecentSearchObject o = filteredRecentSearchObjects.get(j);
if (o != null && o.did == dialogId) {
foundInRecent = true;
break;
}
}
if (foundInRecent) {
result.remove(a);
names.remove(a);
a--;
}
}
MessagesController.getInstance(currentAccount).putUsers(encUsers, true);
searchResult = result;
searchResultNames = names;
searchAdapterHelper.mergeResults(searchResult);
searchAdapterHelper.mergeResults(searchResult, filteredRecentSearchObjects);
notifyDataSetChanged();
if (delegate != null) {
delegate.searchStateChanged(waitingResponseCount > 0, true);
@ -704,11 +763,12 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
query = null;
}
if (TextUtils.isEmpty(query)) {
filteredRecentQuery = null;
searchAdapterHelper.unloadRecentHashtags();
searchResult.clear();
searchResultNames.clear();
searchResultHashtags.clear();
searchAdapterHelper.mergeResults(null);
searchAdapterHelper.mergeResults(null, null);
searchAdapterHelper.queryServerSearch(null, true, true, dialogsType != 11, dialogsType != 11, dialogsType == 2 || dialogsType == 11, 0, dialogsType == 0, 0, 0);
searchWas = false;
lastSearchId = 0;
@ -726,6 +786,8 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
filtersDelegate.updateFiltersView(false, null, localTipDates, localTipArchive);
}
} else {
filterRecent(query);
searchAdapterHelper.mergeResults(searchResult, filteredRecentSearchObjects);
if (needMessagesSearch != 2 && (query.startsWith("#") && query.length() == 1)) {
messagesSearchEndReached = true;
if (searchAdapterHelper.loadRecentHashtags()) {
@ -779,21 +841,36 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
}
}
public int getRecentItemsCount() {
ArrayList<RecentSearchObject> recent = searchWas ? filteredRecentSearchObjects : recentSearchObjects;
return (!recent.isEmpty() ? recent.size() + 1 : 0) + (!searchWas && !MediaDataController.getInstance(currentAccount).hints.isEmpty() ? 1 : 0);
}
public int getRecentResultsCount() {
ArrayList<RecentSearchObject> recent = searchWas ? filteredRecentSearchObjects : recentSearchObjects;
return recent != null ? recent.size() : 0;
}
@Override
public int getItemCount() {
if (waitingResponseCount == 3) {
return 0;
}
if (isRecentSearchDisplayed()) {
return (!recentSearchObjects.isEmpty() ? recentSearchObjects.size() + 1 : 0) + (!MediaDataController.getInstance(currentAccount).hints.isEmpty() ? 1 : 0);
}
int count = 0;
if (!searchResultHashtags.isEmpty()) {
count += searchResultHashtags.size() + 1;
return count;
}
count += searchResult.size();
if (isRecentSearchDisplayed()) {
count += getRecentItemsCount();
if (!searchWas) {
return count;
}
}
int resultsCount = searchResult.size();
count += resultsCount;
int localServerCount = searchAdapterHelper.getLocalServerSearch().size();
count += localServerCount;
int globalCount = searchAdapterHelper.getGlobalSearch().size();
if (globalCount > 3 && globalSearchCollapsed) {
globalCount = 3;
@ -803,7 +880,9 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
phoneCount = 3;
}
int messagesCount = searchResultMessages.size();
count += localServerCount;
if (resultsCount + localServerCount > 0 && getRecentItemsCount() > 0) {
count++;
}
if (globalCount != 0) {
count += globalCount + 1;
}
@ -817,10 +896,18 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
}
public Object getItem(int i) {
if (!searchResultHashtags.isEmpty()) {
if (i > 0) {
return searchResultHashtags.get(i - 1);
} else {
return null;
}
}
if (isRecentSearchDisplayed()) {
int offset = (!MediaDataController.getInstance(currentAccount).hints.isEmpty() ? 1 : 0);
if (i > offset && i - 1 - offset < recentSearchObjects.size()) {
TLObject object = recentSearchObjects.get(i - 1 - offset).object;
int offset = (!searchWas && !MediaDataController.getInstance(currentAccount).hints.isEmpty() ? 1 : 0);
ArrayList<RecentSearchObject> recent = searchWas ? filteredRecentSearchObjects : recentSearchObjects;
if (i > offset && i - 1 - offset < recent.size()) {
TLObject object = recent.get(i - 1 - offset).object;
if (object instanceof TLRPC.User) {
TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(((TLRPC.User) object).id);
if (user != null) {
@ -834,14 +921,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
}
return object;
} else {
return null;
}
}
if (!searchResultHashtags.isEmpty()) {
if (i > 0) {
return searchResultHashtags.get(i - 1);
} else {
return null;
i -= getRecentItemsCount();
}
}
ArrayList<TLObject> globalSearch = searchAdapterHelper.getGlobalSearch();
@ -849,6 +929,12 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
ArrayList<Object> phoneSearch = searchAdapterHelper.getPhoneSearch();
int localCount = searchResult.size();
int localServerCount = localServerSearch.size();
if (localCount + localServerCount > 0 && getRecentItemsCount() > 0) {
if (i == 0) {
return null;
}
i--;
}
int phoneCount = phoneSearch.size();
if (phoneCount > 3 && phoneCollapsed) {
phoneCount = 3;
@ -860,37 +946,42 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
int messagesCount = searchResultMessages.isEmpty() ? 0 : searchResultMessages.size() + 1;
if (i >= 0 && i < localCount) {
return searchResult.get(i);
} else {
i -= localCount;
if (i >= 0 && i < localServerCount) {
return localServerSearch.get(i);
} else {
i -= localServerCount;
if (i >= 0 && i < phoneCount) {
return phoneSearch.get(i);
} else {
i -= phoneCount;
if (i > 0 && i < globalCount) {
return globalSearch.get(i - 1);
} else {
i -= globalCount;
if (i > 0 && i < messagesCount) {
return searchResultMessages.get(i - 1);
}
}
}
}
}
i -= localCount;
if (i >= 0 && i < localServerCount) {
return localServerSearch.get(i);
}
i -= localServerCount;
if (i >= 0 && i < phoneCount) {
return phoneSearch.get(i);
}
i -= phoneCount;
if (i > 0 && i < globalCount) {
return globalSearch.get(i - 1);
}
i -= globalCount;
if (i > 0 && i < messagesCount) {
return searchResultMessages.get(i - 1);
}
return null;
}
public boolean isGlobalSearch(int i) {
if (isRecentSearchDisplayed()) {
if (!searchWas) {
return false;
}
if (!searchResultHashtags.isEmpty()) {
return false;
}
if (isRecentSearchDisplayed()) {
int offset = (!searchWas && !MediaDataController.getInstance(currentAccount).hints.isEmpty() ? 1 : 0);
ArrayList<RecentSearchObject> recent = searchWas ? filteredRecentSearchObjects : recentSearchObjects;
if (i > offset && i - 1 - offset < recent.size()) {
return false;
} else {
i -= getRecentItemsCount();
}
}
ArrayList<TLObject> globalSearch = searchAdapterHelper.getGlobalSearch();
ArrayList<TLObject> localServerSearch = searchAdapterHelper.getLocalServerSearch();
int localCount = searchResult.size();
@ -907,26 +998,22 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
if (i >= 0 && i < localCount) {
return false;
} else {
i -= localCount;
if (i >= 0 && i < localServerCount) {
return false;
} else {
i -= localServerCount;
if (i > 0 && i < phoneCount) {
return false;
} else {
i -= phoneCount;
if (i > 0 && i < globalCount) {
return true;
} else {
i -= globalCount;
if (i > 0 && i < messagesCount) {
return false;
}
}
}
}
}
i -= localCount;
if (i >= 0 && i < localServerCount) {
return false;
}
i -= localServerCount;
if (i > 0 && i < phoneCount) {
return false;
}
i -= phoneCount;
if (i > 0 && i < globalCount) {
return true;
}
i -= globalCount;
if (i > 0 && i < messagesCount) {
return false;
}
return false;
}
@ -1020,6 +1107,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
switch (holder.getItemViewType()) {
case VIEW_TYPE_PROFILE_CELL: {
ProfileSearchCell cell = (ProfileSearchCell) holder.itemView;
cell.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite));
long oldDialogId = cell.getDialogId();
TLRPC.User user = null;
@ -1046,81 +1134,91 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
}
if (isRecentSearchDisplayed()) {
isRecent = true;
cell.useSeparator = position != getItemCount() - 1;
} else {
ArrayList<TLObject> globalSearch = searchAdapterHelper.getGlobalSearch();
ArrayList<Object> phoneSearch = searchAdapterHelper.getPhoneSearch();
int localCount = searchResult.size();
int localServerCount = searchAdapterHelper.getLocalServerSearch().size();
int phoneCount = phoneSearch.size();
if (phoneCount > 3 && phoneCollapsed) {
phoneCount = 3;
if (position < getRecentItemsCount()) {
cell.useSeparator = position != getRecentItemsCount() - 1;
isRecent = true;
}
int phoneCount2 = phoneCount;
if (phoneCount > 0 && phoneSearch.get(phoneCount - 1) instanceof String) {
phoneCount2 -= 2;
}
int globalCount = globalSearch.isEmpty() ? 0 : globalSearch.size() + 1;
if (globalCount > 4 && globalSearchCollapsed) {
globalCount = 4;
}
cell.useSeparator = (position != getItemCount() - 1 && position != localCount + phoneCount2 + localServerCount - 1 && position != localCount + globalCount + phoneCount + localServerCount - 1);
if (position < searchResult.size()) {
name = searchResultNames.get(position);
if (name != null && name.toString().startsWith("ID: ")) {
position -= getRecentItemsCount();
}
ArrayList<TLObject> globalSearch = searchAdapterHelper.getGlobalSearch();
ArrayList<Object> phoneSearch = searchAdapterHelper.getPhoneSearch();
int localCount = searchResult.size();
int localServerCount = searchAdapterHelper.getLocalServerSearch().size();
if (localCount + localServerCount > 0 && getRecentItemsCount() > 0) {
position--;
}
int phoneCount = phoneSearch.size();
if (phoneCount > 3 && phoneCollapsed) {
phoneCount = 3;
}
int phoneCount2 = phoneCount;
if (phoneCount > 0 && phoneSearch.get(phoneCount - 1) instanceof String) {
phoneCount2 -= 2;
}
int globalCount = globalSearch.isEmpty() ? 0 : globalSearch.size() + 1;
if (globalCount > 4 && globalSearchCollapsed) {
globalCount = 4;
}
if (!isRecent) {
cell.useSeparator = (position != getItemCount() - getRecentItemsCount() - 1 && position != localCount + phoneCount2 + localServerCount - 1 && position != localCount + globalCount + phoneCount + localServerCount - 1);
}
if (position >= 0 && position < searchResult.size() && user == null) {
name = searchResultNames.get(position);
if (name != null && name.toString().startsWith("ID: ")) {
username = name;
name = null;
if (username instanceof SpannableStringBuilder) {
username = new SpannableStringBuilder(username);
}
} else if (name != null && user != null && user.username != null && user.username.length() > 0) {
if (name.toString().startsWith("@" + user.username)) {
username = name;
name = null;
}
if (name.toString().startsWith("@" + user.username)) {
username = name;
name = null;
}
} else {
String foundUserName = searchAdapterHelper.getLastFoundUsername();
if (!TextUtils.isEmpty(foundUserName)) {
String nameSearch = null;
int index;
if (user != null) {
nameSearch = ContactsController.formatName(user.first_name, user.last_name);
} else if (chat != null) {
nameSearch = chat.title;
}
}
if (username == null) {
String foundUserName = isRecent ? filteredRecentQuery : searchAdapterHelper.getLastFoundUsername();
if (!TextUtils.isEmpty(foundUserName)) {
String nameSearch = null;
int index;
if (user != null) {
nameSearch = ContactsController.formatName(user.first_name, user.last_name);
} else if (chat != null) {
nameSearch = chat.title;
}
if (nameSearch != null && (index = AndroidUtilities.indexOfIgnoreCase(nameSearch, foundUserName)) != -1) {
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(nameSearch);
spannableStringBuilder.setSpan(new ForegroundColorSpanThemable(Theme.key_windowBackgroundWhiteBlueText4), index, index + foundUserName.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
name = spannableStringBuilder;
}
if (un != null && user == null) {
if (foundUserName.startsWith("@")) {
foundUserName = foundUserName.substring(1);
}
if (nameSearch != null && (index = AndroidUtilities.indexOfIgnoreCase(nameSearch, foundUserName)) != -1) {
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(nameSearch);
spannableStringBuilder.setSpan(new ForegroundColorSpanThemable(Theme.key_windowBackgroundWhiteBlueText4), index, index + foundUserName.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
name = spannableStringBuilder;
} else if (un != null) {
if (foundUserName.startsWith("@")) {
foundUserName = foundUserName.substring(1);
}
try {
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder();
spannableStringBuilder.append("@");
spannableStringBuilder.append(un);
if ((index = AndroidUtilities.indexOfIgnoreCase(un, foundUserName)) != -1) {
int len = foundUserName.length();
if (index == 0) {
len++;
} else {
index++;
}
spannableStringBuilder.setSpan(new ForegroundColorSpanThemable(Theme.key_windowBackgroundWhiteBlueText4), index, index + len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
try {
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder();
spannableStringBuilder.append("@");
spannableStringBuilder.append(un);
boolean hasMatch = (index = AndroidUtilities.indexOfIgnoreCase(un, foundUserName)) != -1;
if (hasMatch) {
int len = foundUserName.length();
if (index == 0) {
len++;
} else {
index++;
}
username = spannableStringBuilder;
} catch (Exception e) {
username = un;
FileLog.e(e);
spannableStringBuilder.setSpan(new ForegroundColorSpanThemable(Theme.key_windowBackgroundWhiteBlueText4), index, index + len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
username = spannableStringBuilder;
} catch (Exception e) {
username = un;
FileLog.e(e);
}
}
}
cell.setChecked(false, false);
}
cell.setChecked(false, false);
boolean savedMessages = false;
if (user != null && user.id == selfUserId) {
name = LocaleController.getString("SavedMessages", R.string.SavedMessages);
@ -1142,30 +1240,47 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
username = membersString;
}
}
cell.setData(user != null ? user : chat, encryptedChat, name, username, isRecent, savedMessages);
cell.setData(user != null ? user : chat, encryptedChat, name, username, true, savedMessages);
cell.setChecked(delegate.isSelected(cell.getDialogId()), oldDialogId == cell.getDialogId());
break;
}
case VIEW_TYPE_GRAY_SECTION: {
GraySectionCell cell = (GraySectionCell) holder.itemView;
if (isRecentSearchDisplayed()) {
int offset = (!MediaDataController.getInstance(currentAccount).hints.isEmpty() ? 1 : 0);
if (position < offset) {
cell.setText(LocaleController.getString("ChatHints", R.string.ChatHints));
} else {
cell.setText(LocaleController.getString("Recent", R.string.Recent), LocaleController.getString("ClearButton", R.string.ClearButton), v -> {
if (delegate != null) {
delegate.needClearList();
}
});
}
} else if (!searchResultHashtags.isEmpty()) {
final GraySectionCell cell = (GraySectionCell) holder.itemView;
if (!searchResultHashtags.isEmpty()) {
cell.setText(LocaleController.getString("Hashtags", R.string.Hashtags), LocaleController.getString("ClearButton", R.string.ClearButton), v -> {
if (delegate != null) {
delegate.needClearList();
}
});
} else {
int rawPosition = position;
if (isRecentSearchDisplayed()) {
int offset = (!searchWas && !MediaDataController.getInstance(currentAccount).hints.isEmpty() ? 1 : 0);
if (position < offset) {
cell.setText(LocaleController.getString("ChatHints", R.string.ChatHints));
return;
} else if (position == offset) {
if (!searchWas) {
cell.setText(LocaleController.getString("Recent", R.string.Recent), LocaleController.getString("ClearButton", R.string.ClearButton), v -> {
if (delegate != null) {
delegate.needClearList();
}
});
} else {
cell.setText(LocaleController.getString("Recent", R.string.Recent), LocaleController.getString("Clear", R.string.Clear), v -> {
if (delegate != null) {
delegate.needClearList();
}
});
}
return;
} else if (position == getRecentItemsCount()) {
cell.setText(LocaleController.getString("SearchAllChatsShort", R.string.SearchAllChatsShort));
return;
} else {
position -= getRecentItemsCount();
}
}
ArrayList<TLObject> globalSearch = searchAdapterHelper.getGlobalSearch();
int localCount = searchResult.size();
int localServerCount = searchAdapterHelper.getLocalServerSearch().size();
@ -1188,6 +1303,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
showMore = phoneCollapsed;
onClick = () -> {
phoneCollapsed = !phoneCollapsed;
cell.setRightText(phoneCollapsed ? LocaleController.getString("ShowMore", R.string.ShowMore) : LocaleController.getString("ShowLess", R.string.ShowLess));
notifyDataSetChanged();
};
}
@ -1198,8 +1314,61 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
if (searchAdapterHelper.getGlobalSearch().size() > 3) {
showMore = globalSearchCollapsed;
onClick = () -> {
final long now = SystemClock.elapsedRealtime();
if (now - lastShowMoreUpdate < 300) {
return;
}
lastShowMoreUpdate = now;
int totalGlobalCount = globalSearch.isEmpty() ? 0 : globalSearch.size();
boolean disableRemoveAnimation = getItemCount() > rawPosition + Math.min(totalGlobalCount, globalSearchCollapsed ? 4 : Integer.MAX_VALUE) + 1;
if (itemAnimator != null) {
itemAnimator.setAddDuration(disableRemoveAnimation ? 45 : 200);
itemAnimator.setRemoveDuration(disableRemoveAnimation ? 80 : 200);
itemAnimator.setRemoveDelay(disableRemoveAnimation ? 270 : 0);
}
globalSearchCollapsed = !globalSearchCollapsed;
notifyDataSetChanged();
cell.setRightText(globalSearchCollapsed ? LocaleController.getString("ShowMore", R.string.ShowMore) : LocaleController.getString("ShowLess", R.string.ShowLess), globalSearchCollapsed);
showMoreHeader = null;
View parent = (View) cell.getParent();
if (parent instanceof RecyclerView) {
RecyclerView listView = (RecyclerView) parent;
final int nextGraySectionPosition = !globalSearchCollapsed ? rawPosition + 4 : rawPosition + totalGlobalCount + 1;
for (int i = 0; i < listView.getChildCount(); ++i) {
View child = listView.getChildAt(i);
if (listView.getChildAdapterPosition(child) == nextGraySectionPosition) {
showMoreHeader = child;
break;
}
}
}
if (!globalSearchCollapsed) {
notifyItemChanged(rawPosition + 3);
notifyItemRangeInserted(rawPosition + 4, (totalGlobalCount - 3));
} else {
notifyItemRangeRemoved(rawPosition + 4, (totalGlobalCount - 3));
if (disableRemoveAnimation) {
AndroidUtilities.runOnUIThread(() -> notifyItemChanged(rawPosition + 3), 350);
} else {
notifyItemChanged(rawPosition + 3);
}
}
if (cancelShowMoreAnimation != null) {
AndroidUtilities.cancelRunOnUIThread(cancelShowMoreAnimation);
}
if (disableRemoveAnimation) {
showMoreAnimation = true;
AndroidUtilities.runOnUIThread(cancelShowMoreAnimation = () -> {
showMoreAnimation = false;
showMoreHeader = null;
if (parent != null) {
parent.invalidate();
}
}, 400);
} else {
showMoreAnimation = false;
}
};
}
} else {
@ -1218,6 +1387,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
}
case VIEW_TYPE_DIALOG_CELL: {
DialogCell cell = (DialogCell) holder.itemView;
cell.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite));
cell.useSeparator = (position != getItemCount() - 1);
MessageObject messageObject = (MessageObject) getItem(position);
cell.setDialog(messageObject.getDialogId(), messageObject, messageObject.messageOwner.date, false);
@ -1225,6 +1395,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
}
case VIEW_TYPE_HASHTAG_CELL: {
HashtagSearchCell cell = (HashtagSearchCell) holder.itemView;
cell.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite));
cell.setText(searchResultHashtags.get(position - 1));
cell.setNeedDivider(position != searchResultHashtags.size());
break;
@ -1249,22 +1420,31 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
@Override
public int getItemViewType(int i) {
if (!searchResultHashtags.isEmpty()) {
return i == 0 ? VIEW_TYPE_GRAY_SECTION : VIEW_TYPE_HASHTAG_CELL;
}
if (isRecentSearchDisplayed()) {
int offset = (!MediaDataController.getInstance(currentAccount).hints.isEmpty() ? 1 : 0);
int offset = (!searchWas && !MediaDataController.getInstance(currentAccount).hints.isEmpty() ? 1 : 0);
if (i < offset) {
return VIEW_TYPE_CATEGORY_LIST;
}
if (i == offset) {
return VIEW_TYPE_GRAY_SECTION;
}
return VIEW_TYPE_PROFILE_CELL;
}
if (!searchResultHashtags.isEmpty()) {
return i == 0 ? VIEW_TYPE_GRAY_SECTION : VIEW_TYPE_HASHTAG_CELL;
if (i < getRecentItemsCount()) {
return VIEW_TYPE_PROFILE_CELL;
}
i -= getRecentItemsCount();
}
ArrayList<TLObject> globalSearch = searchAdapterHelper.getGlobalSearch();
int localCount = searchResult.size();
int localServerCount = searchAdapterHelper.getLocalServerSearch().size();
if (localCount + localServerCount > 0 && getRecentItemsCount() > 0) {
if (i == 0) {
return VIEW_TYPE_GRAY_SECTION;
}
i--;
}
int phoneCount = searchAdapterHelper.getPhoneSearch().size();
if (phoneCount > 3 && phoneCollapsed) {
phoneCount = 3;
@ -1277,43 +1457,39 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
if (i >= 0 && i < localCount) {
return VIEW_TYPE_PROFILE_CELL;
} else {
i -= localCount;
if (i >= 0 && i < localServerCount) {
return VIEW_TYPE_PROFILE_CELL;
} else {
i -= localServerCount;
if (i >= 0 && i < phoneCount) {
Object object = getItem(i);
if (object instanceof String) {
String str = (String) object;
if ("section".equals(str)) {
return VIEW_TYPE_GRAY_SECTION;
} else {
return VIEW_TYPE_ADD_BY_PHONE;
}
}
return VIEW_TYPE_PROFILE_CELL;
}
i -= localCount;
if (i >= 0 && i < localServerCount) {
return VIEW_TYPE_PROFILE_CELL;
}
i -= localServerCount;
if (i >= 0 && i < phoneCount) {
Object object = getItem(i);
if (object instanceof String) {
String str = (String) object;
if ("section".equals(str)) {
return VIEW_TYPE_GRAY_SECTION;
} else {
i -= phoneCount;
if (i >= 0 && i < globalCount) {
if (i == 0) {
return VIEW_TYPE_GRAY_SECTION;
} else {
return VIEW_TYPE_PROFILE_CELL;
}
} else {
i -= globalCount;
if (i >= 0 && i < messagesCount) {
if (i == 0) {
return VIEW_TYPE_GRAY_SECTION;
} else {
return VIEW_TYPE_DIALOG_CELL;
}
}
}
return VIEW_TYPE_ADD_BY_PHONE;
}
}
return VIEW_TYPE_PROFILE_CELL;
}
i -= phoneCount;
if (i >= 0 && i < globalCount) {
if (i == 0) {
return VIEW_TYPE_GRAY_SECTION;
} else {
return VIEW_TYPE_PROFILE_CELL;
}
}
i -= globalCount;
if (i >= 0 && i < messagesCount) {
if (i == 0) {
return VIEW_TYPE_GRAY_SECTION;
} else {
return VIEW_TYPE_DIALOG_CELL;
}
}
return VIEW_TYPE_LOADING;
}
@ -1329,6 +1505,54 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
return currentItemCount;
}
public void filterRecent(String query) {
filteredRecentQuery = query;
filteredRecentSearchObjects.clear();
if (TextUtils.isEmpty(query)) {
return;
}
String lowerCasedQuery = query.toLowerCase();
final int count = recentSearchObjects.size();
for (int i = 0; i < count; ++i) {
RecentSearchObject obj = recentSearchObjects.get(i);
if (obj == null || obj.object == null) {
continue;
}
String title = null, username = null;
if (obj.object instanceof TLRPC.Chat) {
title = ((TLRPC.Chat) obj.object).title;
username = ((TLRPC.Chat) obj.object).username;
} else if (obj.object instanceof TLRPC.User) {
title = UserObject.getUserName((TLRPC.User) obj.object);
username = ((TLRPC.User) obj.object).username;
} else if (obj.object instanceof TLRPC.ChatInvite) {
title = ((TLRPC.ChatInvite) obj.object).title;
}
if (title != null && wordStartsWith(title.toLowerCase(), lowerCasedQuery) ||
username != null && wordStartsWith(username.toLowerCase(), lowerCasedQuery)) {
filteredRecentSearchObjects.add(obj);
}
if (filteredRecentSearchObjects.size() >= 5) {
break;
}
}
}
private boolean wordStartsWith(String loweredTitle, String loweredQuery) {
if (loweredQuery == null || loweredTitle == null) {
return false;
}
String[] words = loweredTitle.toLowerCase().split(" ");
boolean found = false;
for (int j = 0; j < words.length; ++j) {
if (words[j] != null && (words[j].startsWith(loweredQuery) || loweredQuery.startsWith(words[j]))) {
found = true;
break;
}
}
return found;
}
public interface OnRecentSearchLoaded {
void setRecentSearch(ArrayList<RecentSearchObject> arrayList, LongSparseArray<RecentSearchObject> hashMap);
}

View File

@ -16,6 +16,8 @@ import android.view.ViewGroup;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.ApplicationLoader;
import org.telegram.messenger.LocaleController;
@ -24,13 +26,14 @@ import org.telegram.messenger.NotificationCenter;
import org.telegram.messenger.R;
import org.telegram.messenger.SharedConfig;
import org.telegram.messenger.UserConfig;
import org.telegram.ui.ActionBar.ActionBarLayout;
import org.telegram.ui.ActionBar.DrawerLayoutContainer;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Cells.DividerCell;
import org.telegram.ui.Cells.DrawerActionCell;
import org.telegram.ui.Cells.DrawerActionCheckCell;
import org.telegram.ui.Cells.DividerCell;
import org.telegram.ui.Cells.DrawerActionCell;
import org.telegram.ui.Cells.DrawerAddCell;
import org.telegram.ui.Cells.DrawerProfileCell;
import org.telegram.ui.Cells.DrawerUserCell;
@ -224,7 +227,7 @@ public class DrawerLayoutAdapter extends RecyclerListView.SelectionAdapter imple
}
i -= getAccountRowsCount();
}
if (items.get(i) == null) {
if (i < 0 || i >= items.size() || items.get(i) == null) {
return 2;
}
return items.get(i) instanceof CheckItem ? 6 : 3;

View File

@ -69,11 +69,11 @@ public class FiltersView extends RecyclerListView {
public final static int FILTER_INDEX_VOICE = 4;
public final static MediaFilterData[] filters = new MediaFilterData[]{
new MediaFilterData(R.drawable.search_media, R.drawable.search_media_filled, LocaleController.getString("SharedMediaTab2", R.string.SharedMediaTab2), new TLRPC.TL_inputMessagesFilterPhotoVideo(), FILTER_TYPE_MEDIA),
new MediaFilterData(R.drawable.search_links, R.drawable.search_links_filled, LocaleController.getString("SharedLinksTab2", R.string.SharedLinksTab2), new TLRPC.TL_inputMessagesFilterUrl(), FILTER_TYPE_LINKS),
new MediaFilterData(R.drawable.search_files, R.drawable.search_files_filled, LocaleController.getString("SharedFilesTab2", R.string.SharedFilesTab2), new TLRPC.TL_inputMessagesFilterDocument(), FILTER_TYPE_FILES),
new MediaFilterData(R.drawable.search_music, R.drawable.search_music_filled, LocaleController.getString("SharedMusicTab2", R.string.SharedMusicTab2), new TLRPC.TL_inputMessagesFilterMusic(), FILTER_TYPE_MUSIC),
new MediaFilterData(R.drawable.search_voice, R.drawable.search_voice_filled, LocaleController.getString("SharedVoiceTab2", R.string.SharedVoiceTab2), new TLRPC.TL_inputMessagesFilterRoundVoice(), FILTER_TYPE_VOICE)
new MediaFilterData(R.drawable.search_media_filled, LocaleController.getString("SharedMediaTab2", R.string.SharedMediaTab2), new TLRPC.TL_inputMessagesFilterPhotoVideo(), FILTER_TYPE_MEDIA),
new MediaFilterData(R.drawable.search_links_filled, LocaleController.getString("SharedLinksTab2", R.string.SharedLinksTab2), new TLRPC.TL_inputMessagesFilterUrl(), FILTER_TYPE_LINKS),
new MediaFilterData(R.drawable.search_files_filled, LocaleController.getString("SharedFilesTab2", R.string.SharedFilesTab2), new TLRPC.TL_inputMessagesFilterDocument(), FILTER_TYPE_FILES),
new MediaFilterData(R.drawable.search_music_filled, LocaleController.getString("SharedMusicTab2", R.string.SharedMusicTab2), new TLRPC.TL_inputMessagesFilterMusic(), FILTER_TYPE_MUSIC),
new MediaFilterData(R.drawable.search_voice_filled, LocaleController.getString("SharedVoiceTab2", R.string.SharedVoiceTab2), new TLRPC.TL_inputMessagesFilterRoundVoice(), FILTER_TYPE_VOICE)
};
private ArrayList<MediaFilterData> usersFilters = new ArrayList<>();
@ -235,7 +235,7 @@ public class FiltersView extends RecyclerListView {
} else {
title = ContactsController.formatName(user.first_name, user.last_name, 10);
}
MediaFilterData data = new MediaFilterData(R.drawable.search_users, R.drawable.search_users_filled, title, null, FILTER_TYPE_CHAT);
MediaFilterData data = new MediaFilterData(R.drawable.search_users_filled, title, null, FILTER_TYPE_CHAT);
data.setUser(user);
usersFilters.add(data);
} else if (object instanceof TLRPC.Chat) {
@ -244,7 +244,7 @@ public class FiltersView extends RecyclerListView {
if (chat.title.length() > 12) {
title = String.format("%s...", title.substring(0, 10));
}
MediaFilterData data = new MediaFilterData(R.drawable.search_users, R.drawable.search_users_filled, title, null, FILTER_TYPE_CHAT);
MediaFilterData data = new MediaFilterData(R.drawable.search_users_filled, title, null, FILTER_TYPE_CHAT);
data.setUser(chat);
usersFilters.add(data);
}
@ -253,13 +253,13 @@ public class FiltersView extends RecyclerListView {
if (dates != null) {
for (int i = 0; i < dates.size(); i++) {
DateData dateData = dates.get(i);
MediaFilterData data = new MediaFilterData(R.drawable.search_date, R.drawable.search_date_filled, dateData.title, null, FILTER_TYPE_DATE);
MediaFilterData data = new MediaFilterData(R.drawable.search_date_filled, dateData.title, null, FILTER_TYPE_DATE);
data.setDate(dateData);
usersFilters.add(data);
}
}
if (archive) {
FiltersView.MediaFilterData filterData = new FiltersView.MediaFilterData(R.drawable.chats_archive, R.drawable.chats_archive, LocaleController.getString("ArchiveSearchFilter", R.string.ArchiveSearchFilter), null, FiltersView.FILTER_TYPE_ARCHIVE);
FiltersView.MediaFilterData filterData = new FiltersView.MediaFilterData(R.drawable.chats_archive, LocaleController.getString("ArchiveSearchFilter", R.string.ArchiveSearchFilter), null, FiltersView.FILTER_TYPE_ARCHIVE);
usersFilters.add(filterData);
}
if (getAdapter() != null) {
@ -787,7 +787,6 @@ public class FiltersView extends RecyclerListView {
public static class MediaFilterData {
public final int iconRes;
public final int iconResFilled;
public final String title;
public final int filterType;
@ -796,8 +795,7 @@ public class FiltersView extends RecyclerListView {
public DateData dateData;
public boolean removable = true;
public MediaFilterData(int iconRes, int iconResFilled, String title, TLRPC.MessagesFilter filter, int filterType) {
this.iconRes = iconRes;
public MediaFilterData(int iconResFilled, String title, TLRPC.MessagesFilter filter, int filterType) {
this.iconResFilled = iconResFilled;
this.title = title;
this.filter = filter;

View File

@ -22,6 +22,9 @@ import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import androidx.collection.LongSparseArray;
import androidx.recyclerview.widget.RecyclerView;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.ChatObject;
import org.telegram.messenger.ContactsController;
@ -29,6 +32,7 @@ import org.telegram.messenger.DialogObject;
import org.telegram.messenger.Emoji;
import org.telegram.messenger.FileLoader;
import org.telegram.messenger.ImageLocation;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.MediaDataController;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.MediaDataController;
@ -61,19 +65,18 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Objects;
import androidx.collection.LongSparseArray;
import androidx.recyclerview.widget.RecyclerView;
public class MentionsAdapter extends RecyclerListView.SelectionAdapter implements NotificationCenter.NotificationCenterDelegate {
public interface MentionsAdapterDelegate {
void needChangePanelVisibility(boolean show);
void onItemCountUpdate(int oldCount, int newCount);
void onContextSearch(boolean searching);
void onContextClick(TLRPC.BotInlineResult result);
}
private final boolean USE_DIVIDERS = false;
private int currentAccount = UserConfig.selectedAccount;
private Context mContext;
private long dialog_id;
@ -170,7 +173,6 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter implement
};
public MentionsAdapter(Context context, boolean darkTheme, long did, int threadMessageId, MentionsAdapterDelegate mentionsAdapterDelegate, Theme.ResourcesProvider resourcesProvider) {
// setHasStableIds(true);
this.resourcesProvider = resourcesProvider;
mContext = context;
delegate = mentionsAdapterDelegate;
@ -204,7 +206,7 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter implement
String fileName = (String) args[0];
stickersToLoad.remove(fileName);
if (stickersToLoad.isEmpty()) {
delegate.needChangePanelVisibility(stickers != null && !stickers.isEmpty());
delegate.needChangePanelVisibility(getItemCountInternal() > 0);
}
}
}
@ -218,10 +220,14 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter implement
if (stickersMap != null && stickersMap.containsKey(key)) {
return;
}
if (!UserConfig.getInstance(currentAccount).isPremium() && MessageObject.isPremiumSticker(document)) {
return;
}
if (stickers == null) {
stickers = new ArrayList<>();
stickersMap = new HashMap<>();
}
stickers.add(new StickerResult(document, parent));
stickersMap.put(key, document);
if (mentionsStickersActionTracker != null) {
@ -239,9 +245,8 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter implement
if (stickersMap != null && stickersMap.containsKey(key)) {
continue;
}
if (stickers == null) {
stickers = new ArrayList<>();
stickersMap = new HashMap<>();
if (!UserConfig.getInstance(currentAccount).isPremium() && MessageObject.isPremiumSticker(document)) {
continue;
}
for (int b = 0, size2 = document.attributes.size(); b < size2; b++) {
TLRPC.DocumentAttribute attribute = document.attributes.get(b);
@ -250,46 +255,15 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter implement
break;
}
}
if (stickers == null) {
stickers = new ArrayList<>();
stickersMap = new HashMap<>();
}
stickers.add(new StickerResult(document, parent));
stickersMap.put(key, document);
}
}
// public long getItemIdInternal(int position) {
// try {
// if (stickers != null) {
// return stickers.get(position).sticker.id;
// } else if (foundContextBot != null && !inlineMediaEnabled) {
// return foundContextBot.id;
// } else if (searchResultBotContext != null) {
// if (position == 0 && searchResultBotContextSwitch != null) {
// return -1;
// }
// return searchResultBotContext.get(position - (searchResultBotContextSwitch != null ? 1 : 0)).query_id;
// } else if (searchResultUsernames != null) {
// TLObject obj = searchResultUsernames.get(position);
// if (obj instanceof TLRPC.User) {
// return ((TLRPC.User) obj).id;
// } else if (obj instanceof TLRPC.Chat) {
// return ((TLRPC.Chat) obj).id;
// }
// return obj.hashCode();
// } else if (searchResultHashtags != null) {
// return searchResultHashtags.get(position).hashCode();
// } else if (searchResultCommands != null) {
// return searchResultCommands.get(position).hashCode();
// } else if (searchResultSuggestions != null) {
// return searchResultSuggestions.get(position).emoji.hashCode();
// }
// } catch (Exception ignore) {}
// return 0;
// }
//
// @Override
// public long getItemId(int position) {
// return Objects.hash(getItemIdInternal(position), getItemCount() < 5 ? getItemCount() - position : position);
// }
private boolean checkStickerFilesExistAndDownload() {
if (stickers == null) {
return false;
@ -300,7 +274,7 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter implement
StickerResult result = stickers.get(a);
TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(result.sticker.thumbs, 90);
if (thumb instanceof TLRPC.TL_photoSize || thumb instanceof TLRPC.TL_photoSizeProgressive) {
File f = FileLoader.getPathToAttach(thumb, "webp", true);
File f = FileLoader.getInstance(currentAccount).getPathToAttach(thumb, "webp", true);
if (!f.exists()) {
stickersToLoad.add(FileLoader.getAttachFileName(thumb, "webp"));
FileLoader.getInstance(currentAccount).loadFile(ImageLocation.getForDocument(thumb, result.sticker), result.parent, "webp", 1, 1);
@ -339,7 +313,7 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter implement
int newCount = stickers != null ? stickers.size() : 0;
if (!visibleByStickersSearch && stickers != null && !stickers.isEmpty()) {
checkStickerFilesExistAndDownload();
delegate.needChangePanelVisibility(stickersToLoad.isEmpty());
delegate.needChangePanelVisibility(getItemCountInternal() > 0);
visibleByStickersSearch = true;
}
if (oldCount != newCount) {
@ -348,6 +322,69 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter implement
}));
}
private Object[] lastData;
@Override
public void notifyDataSetChanged() {
if (lastItemCount == -1 || lastData == null) {
if (delegate != null) {
delegate.onItemCountUpdate(0, getItemCount());
}
super.notifyDataSetChanged();
lastData = new Object[getItemCount()];
for (int i = 0; i < lastData.length; ++i) {
lastData[i] = getItem(i);
}
} else {
int oldCount = lastItemCount, newCount = getItemCount();
boolean hadChanges = oldCount != newCount;
int min = Math.min(oldCount, newCount);
Object[] newData = new Object[newCount];
for (int i = 0; i < newCount; ++i) {
newData[i] = getItem(i);
}
for (int i = 0; i < min; ++i) {
if (i < 0 || i >= lastData.length || i >= newData.length || !itemsEqual(lastData[i], newData[i])) {
notifyItemChanged(i);
hadChanges = true;
} else if ((i == oldCount - 1) != (i == newCount - 1) && USE_DIVIDERS) {
notifyItemChanged(i); // divider update
}
}
notifyItemRangeRemoved(min, oldCount - min);
notifyItemRangeInserted(min, newCount - min);
if (hadChanges && delegate != null) {
delegate.onItemCountUpdate(oldCount, newCount);
}
lastData = newData;
}
}
private boolean itemsEqual(Object a, Object b) {
if (a == b) {
return true;
}
if (a instanceof MentionsAdapter.StickerResult && b instanceof MentionsAdapter.StickerResult && ((StickerResult) a).sticker == ((StickerResult) b).sticker) {
return true;
}
if (a instanceof TLRPC.User && b instanceof TLRPC.User && ((TLRPC.User) a).id == ((TLRPC.User) b).id) {
return true;
}
if (a instanceof TLRPC.Chat && b instanceof TLRPC.Chat && ((TLRPC.Chat) a).id == ((TLRPC.Chat) b).id) {
return true;
}
if (a instanceof String && b instanceof String && a.equals(b)) {
return true;
}
if (a instanceof MediaDataController.KeywordResult && b instanceof MediaDataController.KeywordResult &&
((MediaDataController.KeywordResult) a).keyword != null && ((MediaDataController.KeywordResult) a).keyword.equals(((MediaDataController.KeywordResult) b).keyword) &&
((MediaDataController.KeywordResult) a).emoji != null && ((MediaDataController.KeywordResult) a).emoji.equals(((MediaDataController.KeywordResult) b).emoji)) {
return true;
}
return false;
}
private void clearStickers() {
lastSticker = null;
stickers = null;
@ -518,9 +555,6 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter implement
if (foundContextBot != null && foundContextBot.username != null && foundContextBot.username.equals(username) && searchingContextQuery != null && searchingContextQuery.equals(query)) {
return;
}
searchResultBotContext = null;
searchResultBotContextSwitch = null;
notifyDataSetChanged();
if (foundContextBot != null) {
if (!inlineMediaEnabled && username != null && query != null) {
return;
@ -609,6 +643,7 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter implement
}
}
processFoundUser(user);
contextUsernameReqid = 0;
}));
}
}
@ -992,7 +1027,7 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter implement
lastText = text;
lastPosition = position;
messages = messageObjects;
delegate.needChangePanelVisibility(false);
// delegate.needChangePanelVisibility(false);
return;
}
} else if (a == 0 && botInfo != null && ch == '/') {
@ -1234,7 +1269,7 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter implement
searchResultCommandsUsers = null;
searchResultSuggestions = null;
notifyDataSetChanged();
delegate.needChangePanelVisibility(!newResult.isEmpty());
delegate.needChangePanelVisibility(!searchResultHashtags.isEmpty());
} else if (foundType == 2) {
ArrayList<String> newResult = new ArrayList<>();
ArrayList<String> newResultHelp = new ArrayList<>();
@ -1290,6 +1325,20 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter implement
}
}
private boolean isReversed = false;
public void setIsReversed(boolean isReversed) {
if (this.isReversed != isReversed) {
this.isReversed = isReversed;
int itemCount = getLastItemCount();
if (itemCount > 0) {
notifyItemChanged(0);
}
if (itemCount > 1) {
notifyItemChanged(itemCount - 1);
}
}
}
private void showUsersResult(ArrayList<TLObject> newResult, LongSparseArray<TLObject> newMap, boolean notify) {
searchResultUsernames = newResult;
searchResultUsernamesMap = newMap;
@ -1297,6 +1346,8 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter implement
AndroidUtilities.cancelRunOnUIThread(cancelDelayRunnable);
cancelDelayRunnable = null;
}
searchResultBotContext = null;
stickers = null;
if (notify) {
notifyDataSetChanged();
delegate.needChangePanelVisibility(!searchResultUsernames.isEmpty());
@ -1315,14 +1366,24 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter implement
return searchResultBotContext;
}
private int lastItemCount = -1;
@Override
public int getItemCount() {
return lastItemCount = getItemCountInternal();
}
public int getLastItemCount() {
return lastItemCount;
}
public int getItemCountInternal() {
if (foundContextBot != null && !inlineMediaEnabled) {
return 1;
}
if (stickers != null) {
return stickers.size();
}else if (searchResultBotContext != null) {
} else if (searchResultBotContext != null) {
return searchResultBotContext.size() + (searchResultBotContextSwitch != null ? 1 : 0);
} else if (searchResultUsernames != null) {
return searchResultUsernames.size();
@ -1336,6 +1397,33 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter implement
return 0;
}
public void clear(boolean safe) {
if (safe && (channelReqId != 0 || contextQueryReqid != 0 || contextUsernameReqid != 0 || lastReqId != 0)) {
return;
}
foundContextBot = null;
if (stickers != null) {
stickers.clear();
}
if (searchResultBotContext != null) {
searchResultBotContext.clear();
}
searchResultBotContextSwitch = null;
if (searchResultUsernames != null) {
searchResultUsernames.clear();
}
if (searchResultHashtags != null) {
searchResultHashtags.clear();
}
if (searchResultCommands != null) {
searchResultCommands.clear();
}
if (searchResultSuggestions != null) {
searchResultSuggestions.clear();
}
notifyDataSetChanged();
}
@Override
public int getItemViewType(int position) {
if (stickers != null) {
@ -1447,7 +1535,7 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter implement
View view;
switch (viewType) {
case 0:
view = new MentionCell(mContext);
view = new MentionCell(mContext, resourcesProvider);
((MentionCell) view).setIsDarkTheme(isDarkTheme);
break;
case 1:
@ -1519,6 +1607,7 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter implement
} else if (searchResultCommands != null) {
((MentionCell) holder.itemView).setBotCommand(searchResultCommands.get(position), searchResultCommandsHelp.get(position), searchResultCommandsUsers != null ? searchResultCommandsUsers.get(position) : null);
}
((MentionCell) holder.itemView).setDivider(USE_DIVIDERS && (isReversed ? position > 0 : position < getItemCount() - 1));
}
}

View File

@ -0,0 +1,159 @@
package org.telegram.ui.Adapters;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListAdapter;
import android.widget.WrapperListAdapter;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import org.telegram.ui.Components.RecyclerListView;
/**
* PaddedListAdapter wraps list adapter and adds transparent padding view at the start
*/
public class PaddedListAdapter extends RecyclerListView.SelectionAdapter {
private final int PADDING_VIEW_TYPE = -983904;
private RecyclerListView.SelectionAdapter wrappedAdapter;
private GetPaddingRunnable getPaddingRunnable;
private Integer padding = null;
public View paddingView;
public boolean paddingViewAttached = false;
public PaddedListAdapter(RecyclerListView.SelectionAdapter adapter) {
wrappedAdapter = adapter;
wrappedAdapter.registerAdapterDataObserver(mDataObserver);
}
public PaddedListAdapter(RecyclerListView.SelectionAdapter adapter, GetPaddingRunnable getPaddingRunnable) {
wrappedAdapter = adapter;
wrappedAdapter.registerAdapterDataObserver(mDataObserver);
this.getPaddingRunnable = getPaddingRunnable;
}
@Override
public boolean isEnabled(RecyclerView.ViewHolder holder) {
if (holder.getAdapterPosition() == 0) {
return false;
}
return wrappedAdapter.isEnabled(holder);
}
public interface GetPaddingRunnable {
public int run(int parentHeight);
}
public void setPadding(int padding) {
this.padding = padding;
if (paddingView != null) {
paddingView.requestLayout();
}
}
public void setPadding(GetPaddingRunnable getPaddingRunnable) {
this.getPaddingRunnable = getPaddingRunnable;
if (paddingView != null) {
paddingView.requestLayout();
}
}
private int getPadding(int parentHeight) {
if (padding != null) {
return lastPadding = padding;
} else if (getPaddingRunnable != null) {
return lastPadding = getPaddingRunnable.run(parentHeight);
} else {
return lastPadding = 0;
}
}
private int lastPadding;
public int getPadding() {
return lastPadding;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == PADDING_VIEW_TYPE) {
return new RecyclerListView.Holder(paddingView = new View(parent.getContext()) {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int parentHeight = ((View) getParent()).getMeasuredHeight();
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(getPadding(parentHeight), MeasureSpec.EXACTLY));
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
paddingViewAttached = true;
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
paddingViewAttached = false;
}
});
}
return wrappedAdapter.onCreateViewHolder(parent, viewType);
}
@Override
public int getItemViewType(int position) {
if (position == 0) {
return PADDING_VIEW_TYPE;
}
return wrappedAdapter.getItemViewType(position - 1);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if (position > 0) {
wrappedAdapter.onBindViewHolder(holder, position - 1);
}
}
@Override
public int getItemCount() {
return 1 + wrappedAdapter.getItemCount();
}
private RecyclerView.AdapterDataObserver mDataObserver = new RecyclerView.AdapterDataObserver() {
@Override
public void onChanged() {
super.onChanged();
notifyDataSetChanged();
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
super.onItemRangeChanged(positionStart, itemCount);
notifyItemRangeChanged(1 + positionStart, itemCount);
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
super.onItemRangeInserted(positionStart, itemCount);
notifyItemRangeInserted(1 + positionStart, itemCount);
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
super.onItemRangeRemoved(positionStart, itemCount);
notifyItemRangeRemoved(1 + positionStart, itemCount);
}
@Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
super.onItemRangeMoved(fromPosition, toPosition, itemCount);
notifyItemRangeChanged(1 + fromPosition, 1 + toPosition + itemCount);
}
};
}

View File

@ -77,6 +77,7 @@ public class SearchAdapterHelper {
private LongSparseArray<TLObject> phoneSearchMap = new LongSparseArray<>();
private ArrayList<Object> phonesSearch = new ArrayList<>();
private ArrayList<Object> localSearchResults;
private ArrayList<DialogsSearchAdapter.RecentSearchObject> localRecentResults;
private int currentAccount = UserConfig.selectedAccount;
@ -328,7 +329,7 @@ public class SearchAdapterHelper {
}
removeGroupSearchFromGlobal();
if (localSearchResults != null) {
mergeResults(localSearchResults);
mergeResults(localSearchResults, localRecentResults);
}
mergeExcludeResults();
delegate.onDataSetChanged(searchId);
@ -415,13 +416,23 @@ public class SearchAdapterHelper {
}
public void mergeResults(ArrayList<Object> localResults) {
mergeResults(localResults, null);
}
public void mergeResults(ArrayList<Object> localResults, ArrayList<DialogsSearchAdapter.RecentSearchObject> recentResults) {
localSearchResults = localResults;
if (globalSearchMap.size() == 0 || localResults == null) {
localRecentResults = recentResults;
if (globalSearchMap.size() == 0 || localResults == null && recentResults == null) {
return;
}
int count = localResults.size();
final int localResultsCount = localResults == null ? 0 : localResults.size();
final int recentResultsCount = recentResults == null ? 0 : recentResults.size();
int count = localResultsCount + recentResultsCount;
for (int a = 0; a < count; a++) {
Object obj = localResults.get(a);
Object obj = a < localResultsCount ? localResults.get(a) : recentResults.get(a - localResultsCount);
if (obj instanceof DialogsSearchAdapter.RecentSearchObject) {
obj = ((DialogsSearchAdapter.RecentSearchObject) obj).object;
}
if (obj instanceof ShareAlert.DialogSearchResult) {
ShareAlert.DialogSearchResult searchResult = (ShareAlert.DialogSearchResult) obj;
obj = searchResult.object;

View File

@ -12,13 +12,15 @@ import android.content.Context;
import android.text.TextUtils;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.MediaDataController;
import org.telegram.messenger.Emoji;
import org.telegram.messenger.FileLoader;
import org.telegram.messenger.ImageLocation;
import org.telegram.messenger.MediaDataController;
import org.telegram.messenger.NotificationCenter;
import org.telegram.messenger.UserConfig;
import org.telegram.messenger.FileLoader;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Cells.EmojiReplacementCell;
@ -28,8 +30,6 @@ import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import androidx.recyclerview.widget.RecyclerView;
public class StickersAdapter extends RecyclerListView.SelectionAdapter implements NotificationCenter.NotificationCenterDelegate {
private int currentAccount = UserConfig.selectedAccount;
@ -136,7 +136,7 @@ public class StickersAdapter extends RecyclerListView.SelectionAdapter implement
TLRPC.Document animatedSticker = MediaDataController.getInstance(currentAccount).getEmojiAnimatedSticker(emoji);
if (animatedSticker != null) {
ArrayList<TLRPC.TL_messages_stickerSet> sets = MediaDataController.getInstance(currentAccount).getStickerSets(MediaDataController.TYPE_EMOJI);
File f = FileLoader.getPathToAttach(animatedSticker, true);
File f = FileLoader.getInstance(currentAccount).getPathToAttach(animatedSticker, true);
if (!f.exists()) {
FileLoader.getInstance(currentAccount).loadFile(ImageLocation.getForDocument(animatedSticker), sets.get(0), null, 1, 1);
}

View File

@ -530,6 +530,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg
sizeBar = new SeekBarView(context);
sizeBar.setReportChanges(true);
// sizeBar.setSeparatorsCount(endFontSize - startFontSize);
sizeBar.setDelegate(new SeekBarView.SeekBarViewDelegate() {
@Override
public void onSeekBarDrag(boolean stop, float progress) {
@ -1302,7 +1303,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg
popupWindow.dismiss();
}
});
popupLayout.setShownFromBotton(false);
popupLayout.setShownFromBottom(false);
deleteView = new TextView(parentActivity);
deleteView.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.getColor(Theme.key_listSelector), 2));
@ -4897,13 +4898,13 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg
if (photo != null) {
TLRPC.PhotoSize sizeFull = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, AndroidUtilities.getPhotoSize());
if (sizeFull != null) {
return FileLoader.getPathToAttach(sizeFull, true);
return FileLoader.getInstance(UserConfig.selectedAccount).getPathToAttach(sizeFull, true);
}
}
} else if (block instanceof TLRPC.TL_pageBlockVideo) {
TLRPC.Document document = getDocumentWithId(page, ((TLRPC.TL_pageBlockVideo) block).video_id);
if (document != null) {
return FileLoader.getPathToAttach(document, true);
return FileLoader.getInstance(UserConfig.selectedAccount).getPathToAttach(document, true);
}
}
return null;
@ -6183,7 +6184,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg
if (isGif) {
autoDownload = DownloadController.getInstance(currentAccount).canDownloadMedia(DownloadController.AUTODOWNLOAD_TYPE_VIDEO, currentDocument.size);
File path = FileLoader.getPathToAttach(currentDocument, true);
File path = FileLoader.getInstance(currentAccount).getPathToAttach(currentDocument, true);
if (autoDownload || path.exists()) {
imageView.setStrippedLocation(null);
imageView.setImage(ImageLocation.getForDocument(currentDocument), ImageLoader.AUTOPLAY_FILTER, null, null, ImageLocation.getForDocument(thumb, currentDocument), "80_80_b", null, currentDocument.size, null, parentAdapter.currentPage, 1);
@ -6281,7 +6282,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg
public void updateButtonState(boolean animated) {
String fileName = FileLoader.getAttachFileName(currentDocument);
File path = FileLoader.getPathToAttach(currentDocument, true);
File path = FileLoader.getInstance(currentAccount).getPathToAttach(currentDocument, true);
boolean fileExists = path.exists();
if (TextUtils.isEmpty(fileName)) {
radialProgress.setIcon(MediaActionDrawable.ICON_NONE, false, false);
@ -6689,7 +6690,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg
public void updateButtonState(boolean animated) {
String fileName = FileLoader.getAttachFileName(currentDocument);
File path = FileLoader.getPathToAttach(currentDocument, true);
File path = FileLoader.getInstance(currentAccount).getPathToAttach(currentDocument, true);
boolean fileExists = path.exists();
if (TextUtils.isEmpty(fileName)) {
radialProgress.setIcon(MediaActionDrawable.ICON_NONE, false, false);
@ -9925,7 +9926,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg
isFirst = first;
channelCell.setVisibility(INVISIBLE);
if (!TextUtils.isEmpty(currentBlock.url)) {
linkDrawable = getResources().getDrawable(R.drawable.instant_link);
linkDrawable = getResources().getDrawable(R.drawable.msg_instant_link);
}
if (currentBlock != null) {
TLRPC.Photo photo = parentAdapter.getPhotoWithId(currentBlock.photo_id);
@ -10061,7 +10062,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg
currentThumbFilter = "80_80_b";
autoDownload = (DownloadController.getInstance(currentAccount).getCurrentDownloadMask() & DownloadController.AUTODOWNLOAD_TYPE_PHOTO) != 0;
File path = FileLoader.getPathToAttach(currentPhotoObject, true);
File path = FileLoader.getInstance(currentAccount).getPathToAttach(currentPhotoObject, true);
if (autoDownload || path.exists()) {
imageView.setStrippedLocation(null);
imageView.setImage(ImageLocation.getForPhoto(currentPhotoObject, currentPhoto), currentFilter, ImageLocation.getForPhoto(currentPhotoObjectThumb, currentPhoto), currentThumbFilter, currentPhotoObject.size, null, parentAdapter.currentPage, 1);
@ -10177,7 +10178,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg
public void updateButtonState(boolean animated) {
String fileName = FileLoader.getAttachFileName(currentPhotoObject);
File path = FileLoader.getPathToAttach(currentPhotoObject, true);
File path = FileLoader.getInstance(currentAccount).getPathToAttach(currentPhotoObject, true);
boolean fileExists = path.exists();
if (TextUtils.isEmpty(fileName)) {
radialProgress.setIcon(MediaActionDrawable.ICON_NONE, false, false);

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