diff --git a/TMessagesProj/build.gradle b/TMessagesProj/build.gradle index a61cb7c168..13ce2415ab 100644 --- a/TMessagesProj/build.gradle +++ b/TMessagesProj/build.gradle @@ -118,6 +118,7 @@ android { buildConfigField "int", "OFFICIAL_VERSION_CODE", officialCode + "" buildConfigField "int", "APP_ID", appId buildConfigField "String", "APP_HASH", "\"" + appHash + "\"" + buildConfigField "int", "IS_NO_GCM", "0" externalNativeBuild { cmake { @@ -184,6 +185,8 @@ android { proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" matchingFallbacks = ["release", "debug"] signingConfig signingConfigs.release + + buildConfigField "int", "IS_NO_GCM", "1" } release { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java index 12f911574e..a4af962f41 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java @@ -26,6 +26,7 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo; +import android.net.Uri; import android.os.Build; import android.os.Handler; import android.os.PowerManager; @@ -35,6 +36,7 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; +import androidx.core.content.FileProvider; import androidx.multidex.MultiDex; import androidx.multidex.MultiDex; @@ -43,8 +45,11 @@ import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AlertsCreator; import org.telegram.ui.Components.ForegroundDetector; import org.telegram.ui.Components.Premium.boosts.BoostRepository; +import org.telegram.ui.Components.UpdateAppAlertDialog; +import org.telegram.ui.Components.UpdateLayout; import org.telegram.ui.IUpdateLayout; import org.telegram.ui.LauncherIconController; @@ -658,19 +663,50 @@ protected void logDualCameraInternal(boolean success, boolean vendor) { } public boolean checkApkInstallPermissions(final Context context) { - return false; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !ApplicationLoader.applicationContext.getPackageManager().canRequestPackageInstalls()) { + AlertsCreator.createApkRestrictedDialog(context, null).show(); + return false; + } + return true; } public boolean openApkInstall(Activity activity, TLRPC.Document document) { - return false; + boolean exists = false; + try { + String fileName = FileLoader.getAttachFileName(document); + File f = FileLoader.getInstance(UserConfig.selectedAccount).getPathToAttach(document, true); + if (exists = f.exists()) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + + if (Build.VERSION.SDK_INT >= 24) { + intent.setDataAndType(FileProvider.getUriForFile(activity, ApplicationLoader.getApplicationId() + ".provider", f), "application/vnd.android.package-archive"); + } else { + intent.setDataAndType(Uri.fromFile(f), "application/vnd.android.package-archive"); + } + try { + activity.startActivityForResult(intent, 500); + } catch (Exception e) { + FileLog.e(e); + } + } + } catch (Exception e) { + FileLog.e(e); + } + return exists; } public boolean showUpdateAppPopup(Context context, TLRPC.TL_help_appUpdate update, int account) { - return false; + try { + (new UpdateAppAlertDialog(context, update, account)).show(); + } catch (Exception e) { + FileLog.e(e); + } + return true; } public IUpdateLayout takeUpdateLayout(Activity activity, ViewGroup sideMenu, ViewGroup sideMenuContainer) { - return null; + return new UpdateLayout(activity, sideMenu, sideMenuContainer); } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java b/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java index 0a313c0318..7f6958dc3d 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java @@ -41,6 +41,7 @@ public class BuildVars { public static boolean isFdroid = BuildConfig.BUILD_TYPE.toLowerCase().contains("fdroid"); public static boolean isMini = !BuildConfig.FLAVOR.startsWith("full"); public static boolean isGServicesCompiled = BuildConfig.BUILD_TYPE.equals("debug") || BuildConfig.BUILD_TYPE.equals("release"); + public static boolean isNoGCM = BuildConfig.IS_NO_GCM == 1; static { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java index cef34b9923..671f27f68c 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java @@ -13,6 +13,8 @@ import java.util.Arrays; import java.util.HashMap; +import tw.nekomimi.nekogram.helpers.remote.UpdateHelper; + public class FileRefController extends BaseController { private static class Requester { @@ -466,16 +468,25 @@ private void requestReferenceFromServer(Object parentObject, String locationKey, } favStickersWaiter.add(new Waiter(locationKey, parentKey)); } else if ("update".equals(string)) { - TLRPC.TL_help_getAppUpdate req = new TLRPC.TL_help_getAppUpdate(); - try { - req.source = ApplicationLoader.applicationContext.getPackageManager().getInstallerPackageName(ApplicationLoader.applicationContext.getPackageName()); - } catch (Exception ignore) { - - } - if (req.source == null) { - req.source = ""; - } - getConnectionsManager().sendRequest(req, (response, error) -> onRequestComplete(locationKey, parentKey, response, error, true, false)); +// TLRPC.TL_help_getAppUpdate req = new TLRPC.TL_help_getAppUpdate(); +// try { +// req.source = ApplicationLoader.applicationContext.getPackageManager().getInstallerPackageName(ApplicationLoader.applicationContext.getPackageName()); +// } catch (Exception ignore) { +// +// } +// if (req.source == null) { +// req.source = ""; +// } +// getConnectionsManager().sendRequest(req, (response, error) -> onRequestComplete(locationKey, parentKey, response, error, true, false)); + UpdateHelper.getInstance().checkNewVersionAvailable((response, error) -> { + if (error != null) { + TLRPC.TL_error error1 = new TLRPC.TL_error(); + error1.text = error; + onRequestComplete(locationKey, parentKey, response, error1, true, false); + } else { + onRequestComplete(locationKey, parentKey, response, null, true, false); + } + }); } else if (string.startsWith("avatar_")) { long id = Utilities.parseLong(string); if (id > 0) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java b/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java index 900abde146..7238948662 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java @@ -1667,29 +1667,26 @@ public static boolean isAppUpdateAvailable() { } public static boolean setNewAppVersionAvailable(TLRPC.TL_help_appUpdate update) { - String updateVersionString = null; + //String updateVersionString = null; int versionCode = 0; try { PackageInfo packageInfo = ApplicationLoader.applicationContext.getPackageManager().getPackageInfo(ApplicationLoader.applicationContext.getPackageName(), 0); versionCode = packageInfo.versionCode; - updateVersionString = packageInfo.versionName; + //updateVersionString = packageInfo.versionName; } catch (Exception e) { FileLog.e(e); } if (versionCode == 0) { versionCode = buildVersion(); } - if (updateVersionString == null) { - updateVersionString = BuildVars.BUILD_VERSION_STRING; - } - if (update == null || update.version == null || versionBiggerOrEqual(updateVersionString, update.version)) { - pendingAppUpdate = null; - pendingAppUpdateBuildVersion = 0; - saveConfig(); - return false; - } + //if (updateVersionString == null) { + // updateVersionString = BuildVars.BUILD_VERSION_STRING; + //} + //if (update.version == null || versionBiggerOrEqual(updateVersionString, update.version)) { + // return false; + //} pendingAppUpdate = update; - pendingAppUpdateBuildVersion = BuildConfig.VERSION_CODE; + pendingAppUpdateBuildVersion = versionCode; saveConfig(); return true; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BlockingUpdateView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BlockingUpdateView.java index 947d4d0204..3fe57fff92 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BlockingUpdateView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BlockingUpdateView.java @@ -38,6 +38,8 @@ import java.util.Locale; +import tw.nekomimi.nekogram.helpers.remote.UpdateHelper; + public class BlockingUpdateView extends FrameLayout implements NotificationCenter.NotificationCenterDelegate { private TextView textView; @@ -63,10 +65,8 @@ public BlockingUpdateView(final Context context) { FrameLayout view = new FrameLayout(context); addView(view, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, AndroidUtilities.dp(176) + (Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0))); - RLottieImageView imageView = new RLottieImageView(context); - imageView.setAnimation(R.raw.qr_code_logo, 108, 108); - imageView.playAnimation(); - imageView.getAnimatedDrawable().setAutoRepeat(1); + ImageView imageView = new ImageView(context); + imageView.setImageResource(R.mipmap.ic_launcher); imageView.setScaleType(ImageView.ScaleType.CENTER); imageView.setPadding(0, 0, 0, AndroidUtilities.dp(14)); view.addView(imageView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 0, top, 0, 0)); @@ -94,15 +94,14 @@ public BlockingUpdateView(final Context context) { titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); titleTextView.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP); titleTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - titleTextView.setText(LocaleController.getString("UpdateTelegram", R.string.UpdateTelegram)); + titleTextView.setText(LocaleController.getString("UpdateTelegram", R.string.UpdateTelegram).replace("Telegram", LocaleController.getString("NekoX", R.string.NekoX))); container.addView(titleTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL | Gravity.TOP)); textView = new TextView(context); textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); textView.setLinkTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteLinkText)); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); - textView.setMovementMethod(new AndroidUtilities.LinkMovementMethodMy()); - textView.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP); + textView.setGravity(Gravity.LEFT | Gravity.TOP); textView.setLineSpacing(AndroidUtilities.dp(2), 1.0f); container.addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 0, 44, 0, 0)); @@ -139,24 +138,14 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { acceptButton.setPadding(AndroidUtilities.dp(34), 0, AndroidUtilities.dp(34), 0); addView(acceptButton, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 46, Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM, 0, 0, 0, 45)); acceptButton.setOnClickListener(view1 -> { -// if (ApplicationLoader.isStandaloneBuild() || BuildVars.DEBUG_VERSION) { -// if (!ApplicationLoader.applicationLoaderInstance.checkApkInstallPermissions(getContext())) { -// return; -// } -// if (appUpdate.document instanceof TLRPC.TL_document) { -// if (!ApplicationLoader.applicationLoaderInstance.openApkInstall((Activity) getContext(), appUpdate.document)) { -// FileLoader.getInstance(accountNum).loadFile(appUpdate.document, "update", FileLoader.PRIORITY_HIGH, 1); -// showProgress(true); -// } -// } else if (appUpdate.url != null) { -// Browser.openUrl(getContext(), appUpdate.url); -// } -// } else if (BuildVars.isHuaweiStoreApp()){ -// Browser.openUrl(context, BuildVars.HUAWEI_STORE_URL); -// } else { -// Browser.openUrl(context, BuildVars.PLAYSTORE_APP_URL); -// } - Browser.openUrl(context, BuildVars.GITHUB_RELEASE_URL); + if (appUpdate.document instanceof TLRPC.TL_document) { + if (!ApplicationLoader.applicationLoaderInstance.openApkInstall((Activity) getContext(), appUpdate.document)) { + FileLoader.getInstance(accountNum).loadFile(appUpdate.document, "update", FileLoader.PRIORITY_HIGH, 1); + showProgress(true); + } + } else if (appUpdate.url != null) { + Browser.openUrl(getContext(), appUpdate.url); + } }); acceptTextView = new TextView(context); @@ -292,6 +281,7 @@ public void show(int account, TLRPC.TL_help_appUpdate update, boolean check) { } SpannableStringBuilder builder = new SpannableStringBuilder(update.text); MessageObject.addEntitiesToText(builder, update.entities, false, false, false, false); + MessageObject.replaceAnimatedEmoji(builder, update.entities, textView.getPaint().getFontMetricsInt()); textView.setText(builder); if (update.document instanceof TLRPC.TL_document) { acceptTextView.setText(LocaleController.getString("Update", R.string.Update) + String.format(Locale.US, " (%1$s)", AndroidUtilities.formatFileSize(update.document.size))); @@ -301,20 +291,10 @@ public void show(int account, TLRPC.TL_help_appUpdate update, boolean check) { NotificationCenter.getInstance(accountNum).addObserver(this, NotificationCenter.fileLoaded); NotificationCenter.getInstance(accountNum).addObserver(this, NotificationCenter.fileLoadFailed); NotificationCenter.getInstance(accountNum).addObserver(this, NotificationCenter.fileLoadProgressChanged); - if (check && ApplicationLoader.isStandaloneBuild()) { - TLRPC.TL_help_getAppUpdate req = new TLRPC.TL_help_getAppUpdate(); - try { - req.source = ApplicationLoader.applicationContext.getPackageManager().getInstallerPackageName(ApplicationLoader.applicationContext.getPackageName()); - } catch (Exception ignore) { - - } - if (req.source == null) { - req.source = ""; - } - ConnectionsManager.getInstance(accountNum).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { - if (response instanceof TLRPC.TL_help_appUpdate) { - final TLRPC.TL_help_appUpdate res = (TLRPC.TL_help_appUpdate) response; - if (!res.can_not_skip) { + if (check) { + UpdateHelper.getInstance().checkNewVersionAvailable((response, error) -> AndroidUtilities.runOnUIThread(() -> { + if (response != null) { + if (!response.can_not_skip) { setVisibility(GONE); SharedConfig.pendingAppUpdate = null; SharedConfig.saveConfig(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/UpdateAppAlertDialog.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/UpdateAppAlertDialog.java new file mode 100644 index 0000000000..c947eb8281 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/UpdateAppAlertDialog.java @@ -0,0 +1,364 @@ +package org.telegram.ui.Components; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.Drawable; +import android.text.SpannableStringBuilder; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.core.widget.NestedScrollView; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.DocumentObject; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.ImageLocation; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessageObject; +import org.telegram.messenger.R; +import org.telegram.messenger.SvgHelper; +import org.telegram.messenger.browser.Browser; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.BottomSheet; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Stories.recorder.ButtonWithCounterView; + +public class UpdateAppAlertDialog extends BottomSheet { + + private TLRPC.TL_help_appUpdate appUpdate; + private int accountNum; + private RadialProgress radialProgress; + private FrameLayout radialProgressView; + private AnimatorSet progressAnimation; + + private Drawable shadowDrawable; + private TextView textView; + private TextView messageTextView; + private NestedScrollView scrollView; + + private AnimatorSet shadowAnimation; + + private View shadow; + + private boolean ignoreLayout; + + private LinearLayout linearLayout; + + private int scrollOffsetY; + + private int[] location = new int[2]; + + private boolean animationInProgress; + + public class BottomSheetCell extends FrameLayout { + + private View background; + private TextView[] textView = new TextView[2]; + private boolean hasBackground; + + public BottomSheetCell(Context context, boolean withoutBackground) { + super(context); + + hasBackground = !withoutBackground; + setBackground(null); + + background = new View(context); + if (hasBackground) { + background.setBackground(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 4)); + } + addView(background, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0, 16, withoutBackground ? 0 : 16, 16, 16)); + + for (int a = 0; a < 2; a++) { + textView[a] = new TextView(context); + textView[a].setLines(1); + textView[a].setSingleLine(true); + textView[a].setGravity(Gravity.CENTER_HORIZONTAL); + textView[a].setEllipsize(TextUtils.TruncateAt.END); + textView[a].setGravity(Gravity.CENTER); + if (hasBackground) { + textView[a].setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText)); + textView[a].setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + } else { + textView[a].setTextColor(Theme.getColor(Theme.key_featuredStickers_addButton)); + } + textView[a].setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + textView[a].setPadding(0, 0, 0, hasBackground ? 0 : AndroidUtilities.dp(13)); + addView(textView[a], LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); + if (a == 1) { + textView[a].setAlpha(0.0f); + } + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(hasBackground ? 80 : 50), MeasureSpec.EXACTLY)); + } + + public void setText(CharSequence text, boolean animated) { + if (!animated) { + textView[0].setText(text); + } else { + textView[1].setText(text); + animationInProgress = true; + AnimatorSet animatorSet = new AnimatorSet(); + animatorSet.setDuration(180); + animatorSet.setInterpolator(CubicBezierInterpolator.EASE_OUT); + animatorSet.playTogether( + ObjectAnimator.ofFloat(textView[0], View.ALPHA, 1.0f, 0.0f), + ObjectAnimator.ofFloat(textView[0], View.TRANSLATION_Y, 0, -AndroidUtilities.dp(10)), + ObjectAnimator.ofFloat(textView[1], View.ALPHA, 0.0f, 1.0f), + ObjectAnimator.ofFloat(textView[1], View.TRANSLATION_Y, AndroidUtilities.dp(10), 0) + ); + animatorSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + animationInProgress = false; + TextView temp = textView[0]; + textView[0] = textView[1]; + textView[1] = temp; + } + }); + animatorSet.start(); + } + } + } + + public UpdateAppAlertDialog(Context context, TLRPC.TL_help_appUpdate update, int account) { + super(context, false); + appUpdate = update; + accountNum = account; + setCanceledOnTouchOutside(false); + + setApplyTopPadding(false); + setApplyBottomPadding(false); + + shadowDrawable = context.getResources().getDrawable(R.drawable.sheet_shadow_round).mutate(); + shadowDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogBackground), PorterDuff.Mode.MULTIPLY)); + + FrameLayout container = new FrameLayout(context) { + @Override + public void setTranslationY(float translationY) { + super.setTranslationY(translationY); + updateLayout(); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN && scrollOffsetY != 0 && ev.getY() < scrollOffsetY) { + dismiss(); + return true; + } + return super.onInterceptTouchEvent(ev); + } + + @Override + public boolean onTouchEvent(MotionEvent e) { + return !isDismissed() && super.onTouchEvent(e); + } + + @Override + protected void onDraw(Canvas canvas) { + int top = (int) (scrollOffsetY - backgroundPaddingTop - getTranslationY()); + shadowDrawable.setBounds(0, top, getMeasuredWidth(), getMeasuredHeight()); + shadowDrawable.draw(canvas); + } + }; + container.setWillNotDraw(false); + containerView = container; + + scrollView = new NestedScrollView(context) { + + private boolean ignoreLayout; + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int height = MeasureSpec.getSize(heightMeasureSpec); + measureChildWithMargins(linearLayout, widthMeasureSpec, 0, heightMeasureSpec, 0); + int contentHeight = linearLayout.getMeasuredHeight(); + int padding = (height / 5 * 2); + int visiblePart = height - padding; + if (contentHeight - visiblePart < AndroidUtilities.dp(90) || contentHeight < height / 2 + AndroidUtilities.dp(90)) { + padding = height - contentHeight; + } + if (padding < 0) { + padding = 0; + } + if (getPaddingTop() != padding) { + ignoreLayout = true; + setPadding(0, padding, 0, 0); + ignoreLayout = false; + } + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + updateLayout(); + } + + @Override + public void requestLayout() { + if (ignoreLayout) { + return; + } + super.requestLayout(); + } + + @Override + protected void onScrollChanged(int l, int t, int oldl, int oldt) { + super.onScrollChanged(l, t, oldl, oldt); + updateLayout(); + } + }; + scrollView.setFillViewport(true); + scrollView.setWillNotDraw(false); + scrollView.setClipToPadding(false); + scrollView.setVerticalScrollBarEnabled(false); + container.addView(scrollView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 127)); + + linearLayout = new LinearLayout(context); + linearLayout.setOrientation(LinearLayout.VERTICAL); + scrollView.addView(linearLayout, LayoutHelper.createScroll(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP)); + + if (appUpdate.sticker != null) { + BackupImageView imageView = new BackupImageView(context); + SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(appUpdate.sticker.thumbs, Theme.key_windowBackgroundGray, 1.0f); + TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(appUpdate.sticker.thumbs, 90); + ImageLocation imageLocation = ImageLocation.getForDocument(thumb, appUpdate.sticker); + + if (svgThumb != null) { + imageView.setImage(ImageLocation.getForDocument(appUpdate.sticker), "250_250", svgThumb, 0, "update"); + } else { + imageView.setImage(ImageLocation.getForDocument(appUpdate.sticker), "250_250", imageLocation, null, 0, "update"); + } + linearLayout.addView(imageView, LayoutHelper.createLinear(160, 160, Gravity.CENTER_HORIZONTAL | Gravity.TOP, 17, 8, 17, 0)); + } + + TextView textView = new TextView(context); + textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); + textView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + textView.setSingleLine(true); + textView.setEllipsize(TextUtils.TruncateAt.END); + textView.setText(LocaleController.getString("UpdateTelegram", R.string.UpdateTelegram).replace("Telegram", LocaleController.getString("NekoX", R.string.NekoX))); + linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 23, 16, 23, 0)); + + TextView messageTextView = new TextView(getContext()); + messageTextView.setTextColor(Theme.getColor(Theme.key_dialogTextGray3)); + messageTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + messageTextView.setMovementMethod(new AndroidUtilities.LinkMovementMethodMy()); + messageTextView.setLinkTextColor(Theme.getColor(Theme.key_dialogTextLink)); + messageTextView.setText(LocaleController.formatString("AppUpdateVersionAndSize", R.string.AppUpdateVersionAndSize, appUpdate.version, update.document instanceof TLRPC.TL_document ? AndroidUtilities.formatFileSize(appUpdate.document.size) : "Play Store")); + messageTextView.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP); + linearLayout.addView(messageTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 23, 0, 23, 5)); + + TextView changelogTextView = new TextView(getContext()); + changelogTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + changelogTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + changelogTextView.setLinkTextColor(Theme.getColor(Theme.key_dialogTextLink)); + if (TextUtils.isEmpty(appUpdate.text)) { + changelogTextView.setText(AndroidUtilities.replaceTags(LocaleController.getString("AppUpdateChangelogEmpty", R.string.AppUpdateChangelogEmpty))); + } else { + SpannableStringBuilder builder = new SpannableStringBuilder(appUpdate.text); + MessageObject.addEntitiesToText(builder, update.entities, false, false, false, false); + MessageObject.replaceAnimatedEmoji(builder, update.entities, changelogTextView.getPaint().getFontMetricsInt()); + changelogTextView.setText(builder); + } + changelogTextView.setGravity(Gravity.LEFT | Gravity.TOP); + linearLayout.addView(changelogTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 23, 15, 23, 0)); + + FrameLayout.LayoutParams frameLayoutParams = new FrameLayout.LayoutParams(LayoutHelper.MATCH_PARENT, AndroidUtilities.getShadowHeight(), Gravity.BOTTOM | Gravity.LEFT); + frameLayoutParams.bottomMargin = AndroidUtilities.dp(127); + shadow = new View(context); + shadow.setBackgroundColor(Theme.getColor(Theme.key_dialogShadowLine)); + shadow.setAlpha(0.0f); + shadow.setTag(1); + container.addView(shadow, frameLayoutParams); + + ButtonWithCounterView doneButton = new ButtonWithCounterView(context, true, null); + doneButton.setText(LocaleController.formatString("AppUpdateDownloadNow", R.string.AppUpdateDownloadNow), false); + doneButton.setOnClickListener(v -> { + if (update.document instanceof TLRPC.TL_document) { + FileLoader.getInstance(accountNum).loadFile(appUpdate.document, "update", FileLoader.PRIORITY_NORMAL, 1); + } else { + Browser.openUrl(context, appUpdate.url); + } + dismiss(); + }); + container.addView(doneButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.BOTTOM, 14, 14, 14, 48 + 8 + 8)); + + ButtonWithCounterView scheduleButton = new ButtonWithCounterView(context, false, null); + scheduleButton.setText(LocaleController.getString("AppUpdateRemindMeLater", R.string.AppUpdateRemindMeLater), false); + scheduleButton.setOnClickListener(v -> dismiss()); + container.addView(scheduleButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.BOTTOM, 14, 14, 14, 8)); + } + + private void runShadowAnimation(final int num, final boolean show) { + if (show && shadow.getTag() != null || !show && shadow.getTag() == null) { + shadow.setTag(show ? null : 1); + if (show) { + shadow.setVisibility(View.VISIBLE); + } + if (shadowAnimation != null) { + shadowAnimation.cancel(); + } + shadowAnimation = new AnimatorSet(); + shadowAnimation.playTogether(ObjectAnimator.ofFloat(shadow, View.ALPHA, show ? 1.0f : 0.0f)); + shadowAnimation.setDuration(150); + shadowAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (shadowAnimation != null && shadowAnimation.equals(animation)) { + if (!show) { + shadow.setVisibility(View.INVISIBLE); + } + shadowAnimation = null; + } + } + + @Override + public void onAnimationCancel(Animator animation) { + if (shadowAnimation != null && shadowAnimation.equals(animation)) { + shadowAnimation = null; + } + } + }); + shadowAnimation.start(); + } + } + + private void updateLayout() { + View child = linearLayout.getChildAt(0); + child.getLocationInWindow(location); + int top = location[1] - AndroidUtilities.dp(24); + int newOffset = Math.max(top, 0); + if (location[1] + linearLayout.getMeasuredHeight() <= container.getMeasuredHeight() - AndroidUtilities.dp(110) + containerView.getTranslationY()) { + runShadowAnimation(0, false); + } else { + runShadowAnimation(0, true); + } + if (scrollOffsetY != newOffset) { + scrollOffsetY = newOffset; + scrollView.invalidate(); + } + } + + @Override + protected boolean canDismissWithSwipe() { + return false; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/UpdateLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/UpdateLayout.java new file mode 100644 index 0000000000..aae9a6b112 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/UpdateLayout.java @@ -0,0 +1,234 @@ +package org.telegram.ui.Components; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.app.Activity; +import android.graphics.Canvas; +import android.graphics.LinearGradient; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Shader; +import android.os.Build; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.TextView; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.ImageLoader; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.R; +import org.telegram.messenger.SharedConfig; +import org.telegram.ui.ActionBar.SimpleTextView; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.IUpdateLayout; + +import java.io.File; + +import tw.nekomimi.nekogram.helpers.ApkInstaller; + +public class UpdateLayout extends IUpdateLayout { + + private FrameLayout updateLayout; + private RadialProgress2 updateLayoutIcon; + private SimpleTextView updateTextView; + private TextView updateSizeTextView; + + private Activity activity; + private ViewGroup sideMenu; + private ViewGroup sideMenuContainer; + + public UpdateLayout(Activity activity, ViewGroup sideMenu, ViewGroup sideMenuContainer) { + super(activity, sideMenu, sideMenuContainer); + this.activity = activity; + this.sideMenu = sideMenu; + this.sideMenuContainer = sideMenuContainer; + } + + public void updateFileProgress(Object[] args) { + if (updateTextView != null && SharedConfig.isAppUpdateAvailable()) { + String location = (String) args[0]; + String fileName = FileLoader.getAttachFileName(SharedConfig.pendingAppUpdate.document); + if (fileName != null && fileName.equals(location)) { + Long loadedSize = (Long) args[1]; + Long totalSize = (Long) args[2]; + float loadProgress = loadedSize / (float) totalSize; + updateLayoutIcon.setProgress(loadProgress, true); + updateTextView.setText(LocaleController.formatString("AppUpdateDownloading", R.string.AppUpdateDownloading, (int) (loadProgress * 100))); + } + } + } + + public void createUpdateUI(int currentAccount) { + if (sideMenuContainer == null) { + return; + } + updateLayout = new FrameLayout(activity) { + + private Paint paint = new Paint(); + private Matrix matrix = new Matrix(); + private LinearGradient updateGradient; + private int lastGradientWidth; + + @Override + public void draw(Canvas canvas) { + if (updateGradient != null) { + paint.setColor(0xffffffff); + paint.setShader(updateGradient); + updateGradient.setLocalMatrix(matrix); + canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint); + updateLayoutIcon.setBackgroundGradientDrawable(updateGradient); + updateLayoutIcon.draw(canvas); + } + super.draw(canvas); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + int width = MeasureSpec.getSize(widthMeasureSpec); + if (lastGradientWidth != width) { + updateGradient = new LinearGradient(0, 0, width, 0, new int[]{0xff69BF72, 0xff53B3AD}, new float[]{0.0f, 1.0f}, Shader.TileMode.CLAMP); + lastGradientWidth = width; + } + } + }; + updateLayout.setWillNotDraw(false); + updateLayout.setVisibility(View.INVISIBLE); + updateLayout.setTranslationY(AndroidUtilities.dp(44)); + if (Build.VERSION.SDK_INT >= 21) { + updateLayout.setBackground(Theme.getSelectorDrawable(0x40ffffff, false)); + } + sideMenuContainer.addView(updateLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 44, Gravity.LEFT | Gravity.BOTTOM)); + updateLayout.setOnClickListener(v -> { + if (!SharedConfig.isAppUpdateAvailable()) { + return; + } + if (updateLayoutIcon.getIcon() == MediaActionDrawable.ICON_DOWNLOAD) { + FileLoader.getInstance(currentAccount).loadFile(SharedConfig.pendingAppUpdate.document, "update", FileLoader.PRIORITY_NORMAL, 1); + updateAppUpdateViews(currentAccount, true); + } else if (updateLayoutIcon.getIcon() == MediaActionDrawable.ICON_CANCEL) { + FileLoader.getInstance(currentAccount).cancelLoadFile(SharedConfig.pendingAppUpdate.document); + updateAppUpdateViews(currentAccount, true); + } else { + ApkInstaller.installUpdate(activity, SharedConfig.pendingAppUpdate.document); + } + }); + updateLayoutIcon = new RadialProgress2(updateLayout); + updateLayoutIcon.setColors(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff); + updateLayoutIcon.setProgressRect(AndroidUtilities.dp(22), AndroidUtilities.dp(11), AndroidUtilities.dp(22 + 22), AndroidUtilities.dp(11 + 22)); + updateLayoutIcon.setCircleRadius(AndroidUtilities.dp(11)); + updateLayoutIcon.setAsMini(); + + updateTextView = new SimpleTextView(activity); + updateTextView.setTextSize(15); + updateTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + updateTextView.setText(LocaleController.getString("AppUpdate", R.string.AppUpdate)); + updateTextView.setTextColor(0xffffffff); + updateTextView.setGravity(Gravity.LEFT); + updateLayout.addView(updateTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL, 74, 0, 0, 0)); + + updateSizeTextView = new TextView(activity); + updateSizeTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); + updateSizeTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + updateSizeTextView.setGravity(Gravity.RIGHT); + updateSizeTextView.setTextColor(0xffffffff); + updateLayout.addView(updateSizeTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL | Gravity.RIGHT, 0, 0, 17, 0)); + } + + public void updateAppUpdateViews(int currentAccount, boolean animated) { + if (sideMenuContainer == null) { + return; + } + if (SharedConfig.isAppUpdateAvailable()) { + View prevUpdateLayout = updateLayout; + createUpdateUI(currentAccount); + updateSizeTextView.setText(AndroidUtilities.formatFileSize(SharedConfig.pendingAppUpdate.document.size)); + String fileName = FileLoader.getAttachFileName(SharedConfig.pendingAppUpdate.document); + File path = FileLoader.getInstance(currentAccount).getPathToAttach(SharedConfig.pendingAppUpdate.document, true); + boolean showSize; + if (path.exists()) { + updateLayoutIcon.setIcon(MediaActionDrawable.ICON_UPDATE, true, false); + updateTextView.setText(LocaleController.getString("AppUpdateNow", R.string.AppUpdateNow)); + showSize = false; + } else { + if (FileLoader.getInstance(currentAccount).isLoadingFile(fileName)) { + updateLayoutIcon.setIcon(MediaActionDrawable.ICON_CANCEL, true, false); + updateLayoutIcon.setProgress(0, false); + Float p = ImageLoader.getInstance().getFileProgress(fileName); + updateTextView.setText(LocaleController.formatString("AppUpdateDownloading", R.string.AppUpdateDownloading, (int) ((p != null ? p : 0.0f) * 100))); + showSize = false; + } else { + updateLayoutIcon.setIcon(MediaActionDrawable.ICON_DOWNLOAD, true, false); + updateTextView.setText(LocaleController.getString("AppUpdate", R.string.AppUpdate)); + showSize = true; + } + } + if (showSize) { + if (updateSizeTextView.getTag() != null) { + if (animated) { + updateSizeTextView.setTag(null); + updateSizeTextView.animate().alpha(1.0f).scaleX(1.0f).scaleY(1.0f).setDuration(180).start(); + } else { + updateSizeTextView.setAlpha(1.0f); + updateSizeTextView.setScaleX(1.0f); + updateSizeTextView.setScaleY(1.0f); + } + } + } else { + if (updateSizeTextView.getTag() == null) { + if (animated) { + updateSizeTextView.setTag(1); + updateSizeTextView.animate().alpha(0.0f).scaleX(0.0f).scaleY(0.0f).setDuration(180).start(); + } else { + updateSizeTextView.setAlpha(0.0f); + updateSizeTextView.setScaleX(0.0f); + updateSizeTextView.setScaleY(0.0f); + } + } + } + if (updateLayout.getTag() != null) { + return; + } + updateLayout.setVisibility(View.VISIBLE); + updateLayout.setTag(1); + if (animated) { + updateLayout.animate().translationY(0).setInterpolator(CubicBezierInterpolator.EASE_OUT).setListener(null).setDuration(180).withEndAction(() -> { + if (prevUpdateLayout != null) { + ViewGroup parent = (ViewGroup) prevUpdateLayout.getParent(); + if (parent != null) parent.removeView(prevUpdateLayout); + } + }).start(); + } else { + updateLayout.setTranslationY(0); + if (prevUpdateLayout != null) { + ViewGroup parent = (ViewGroup) prevUpdateLayout.getParent(); + if (parent != null) parent.removeView(prevUpdateLayout); + } + } + sideMenu.setPadding(0, 0, 0, AndroidUtilities.dp(44)); + } else { + if (updateLayout == null || updateLayout.getTag() == null) { + return; + } + updateLayout.setTag(null); + if (animated) { + updateLayout.animate().translationY(AndroidUtilities.dp(44)).setInterpolator(CubicBezierInterpolator.EASE_OUT).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (updateLayout.getTag() == null) { + updateLayout.setVisibility(View.INVISIBLE); + } + } + }).setDuration(180).start(); + } else { + updateLayout.setTranslationY(AndroidUtilities.dp(44)); + updateLayout.setVisibility(View.INVISIBLE); + } + sideMenu.setPadding(0, 0, 0, 0); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java index bc66f35b70..4e6010f4ae 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java @@ -223,6 +223,7 @@ import tw.nekomimi.nekogram.helpers.SettingsHelper; import tw.nekomimi.nekogram.helpers.remote.EmojiHelper; import tw.nekomimi.nekogram.helpers.remote.PeerColorHelper; +import tw.nekomimi.nekogram.helpers.remote.UpdateHelper; import tw.nekomimi.nekogram.helpers.remote.WallpaperHelper; import tw.nekomimi.nekogram.ui.BottomBuilder; import tw.nekomimi.nekogram.NekoConfig; @@ -5390,103 +5391,79 @@ private List findContacts(String userName, String userPhone, b } public void checkAppUpdate(boolean force, Browser.Progress progress) { - if (BuildVars.isFdroid || BuildVars.isPlay) return; - if (NekoXConfig.autoUpdateReleaseChannel == 0) return; - if (!force && System.currentTimeMillis() < NekoConfig.lastUpdateCheckTime.Long() + 48 * 60 * 60 * 1000L) return; - NekoConfig.lastUpdateCheckTime.setConfigLong(System.currentTimeMillis()); - FileLog.d("checking update"); +// if (BuildVars.isFdroid || BuildVars.isPlay) return; +// if (NekoXConfig.autoUpdateReleaseChannel == 0) return; +// if (!force && System.currentTimeMillis() < NekoConfig.lastUpdateCheckTime.Long() + 48 * 60 * 60 * 1000L) return; +// NekoConfig.lastUpdateCheckTime.setConfigLong(System.currentTimeMillis()); +// FileLog.d("checking update"); +// final int accountNum = currentAccount; +// InternalUpdater.checkUpdate((res, error) -> AndroidUtilities.runOnUIThread(() -> { +// if (res != null) { +// if (SharedConfig.setNewAppVersionAvailable(res)) { +// if (res.can_not_skip) { +// showUpdateActivity(accountNum, res, false); +// } else if (ApplicationLoader.isStandaloneBuild() || BuildVars.DEBUG_VERSION) { +// drawerLayoutAdapter.notifyDataSetChanged(); +// ApplicationLoader.applicationLoaderInstance.showUpdateAppPopup(LaunchActivity.this, res, accountNum); +// } +// NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.appUpdateAvailable); +// } +// } else { +// if (force) { +// if (error) +// Toast.makeText(LaunchActivity.this, LocaleController.getString("ErrorOccurred", R.string.ErrorOccurred), Toast.LENGTH_SHORT).show(); +// else +// Toast.makeText(LaunchActivity.this, LocaleController.getString("VersionUpdateNoUpdate", R.string.VersionUpdateNoUpdate), Toast.LENGTH_SHORT).show(); +// } +// SharedConfig.setNewAppVersionAvailable(null); +// drawerLayoutAdapter.notifyDataSetChanged(); +// } +// NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.appUpdateAvailable); +// })); + +// if (!force && BuildVars.DEBUG_VERSION || !force && !BuildVars.CHECK_UPDATES) { +// return; +// } + if (!force && Math.abs(System.currentTimeMillis() - SharedConfig.lastUpdateCheckTime) < MessagesController.getInstance(0).updateCheckDelay * 1000) { + return; + } final int accountNum = currentAccount; - InternalUpdater.checkUpdate((res, error) -> AndroidUtilities.runOnUIThread(() -> { - if (res != null) { - if (SharedConfig.setNewAppVersionAvailable(res)) { + if (progress != null) progress.init(); + UpdateHelper.getInstance().checkNewVersionAvailable((res, error) -> { + SharedConfig.lastUpdateCheckTime = System.currentTimeMillis(); + SharedConfig.saveConfig(); + AndroidUtilities.runOnUIThread(() -> { + if (res != null) { + SharedConfig.setNewAppVersionAvailable(res); if (res.can_not_skip) { showUpdateActivity(accountNum, res, false); - } else if (ApplicationLoader.isStandaloneBuild() || BuildVars.DEBUG_VERSION) { + } else { drawerLayoutAdapter.notifyDataSetChanged(); ApplicationLoader.applicationLoaderInstance.showUpdateAppPopup(LaunchActivity.this, res, accountNum); } - NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.appUpdateAvailable); + } else { + if (force) { + BaseFragment fragment = getLastFragment(); + if (fragment != null) { + if (error == null) { + BulletinFactory.of(fragment).createSimpleBulletin(R.raw.chats_infotip, LocaleController.getString(R.string.YourVersionIsLatest)).show(); + } else { + AlertsCreator.createSimpleAlert(this, LocaleController.getString("ErrorOccurred", R.string.ErrorOccurred) + "\n" + error).show(); + } + } + } + SharedConfig.setNewAppVersionAvailable(null); + drawerLayoutAdapter.notifyDataSetChanged(); } - } else { - if (force) { - if (error) - Toast.makeText(LaunchActivity.this, LocaleController.getString("ErrorOccurred", R.string.ErrorOccurred), Toast.LENGTH_SHORT).show(); - else - Toast.makeText(LaunchActivity.this, LocaleController.getString("VersionUpdateNoUpdate", R.string.VersionUpdateNoUpdate), Toast.LENGTH_SHORT).show(); + NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.appUpdateAvailable); + if (progress != null) { + progress.end(); } - SharedConfig.setNewAppVersionAvailable(null); - drawerLayoutAdapter.notifyDataSetChanged(); - } - NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.appUpdateAvailable); - })); - -// if (!force && BuildVars.DEBUG_VERSION || !force && !BuildVars.CHECK_UPDATES) { -// return; -// } -// if (!force && Math.abs(System.currentTimeMillis() - SharedConfig.lastUpdateCheckTime) < MessagesController.getInstance(0).updateCheckDelay * 1000) { -// return; -// } -// TLRPC.TL_help_getAppUpdate req = new TLRPC.TL_help_getAppUpdate(); -// try { -// req.source = ApplicationLoader.applicationContext.getPackageManager().getInstallerPackageName(ApplicationLoader.applicationContext.getPackageName()); -// } catch (Exception ignore) {} -// -// final int accountNum = currentAccount; -// int reqId = ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> { -// SharedConfig.lastUpdateCheckTime = System.currentTimeMillis(); -// SharedConfig.saveConfig(); -// if (response instanceof TLRPC.TL_help_appUpdate) { -// final TLRPC.TL_help_appUpdate res = (TLRPC.TL_help_appUpdate) response; -// AndroidUtilities.runOnUIThread(() -> { -// if (SharedConfig.pendingAppUpdate != null && SharedConfig.pendingAppUpdate.version.equals(res.version)) { -// return; -// } -// final boolean newVersionAvailable = SharedConfig.setNewAppVersionAvailable(res); -// if (newVersionAvailable) { -// if (res.can_not_skip) { -// showUpdateActivity(accountNum, res, false); -// } else if (ApplicationLoader.isStandaloneBuild() || BuildVars.DEBUG_VERSION) { -// drawerLayoutAdapter.notifyDataSetChanged(); -// ApplicationLoader.applicationLoaderInstance.showUpdateAppPopup(LaunchActivity.this, res, accountNum); -// } -// NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.appUpdateAvailable); -// } -// if (progress != null) { -// progress.end(); -// if (!newVersionAvailable) { -// BaseFragment fragment = getLastFragment(); -// if (fragment != null) { -// BulletinFactory.of(fragment).createSimpleBulletin(R.raw.chats_infotip, LocaleController.getString(R.string.YourVersionIsLatest)).show(); -// } -// } -// } -// }); -// } else if (response instanceof TLRPC.TL_help_noAppUpdate) { -// AndroidUtilities.runOnUIThread(() -> { -// if (progress != null) { -// progress.end(); -// BaseFragment fragment = getLastFragment(); -// if (fragment != null) { -// BulletinFactory.of(fragment).createSimpleBulletin(R.raw.chats_infotip, LocaleController.getString(R.string.YourVersionIsLatest)).show(); -// } -// } -// }); -// } else if (error != null) { -// AndroidUtilities.runOnUIThread(() -> { -// if (progress != null) { -// progress.end(); -// BaseFragment fragment = getLastFragment(); -// if (fragment != null) { -// BulletinFactory.of(fragment).showForError(error); -// } -// } -// }); -// } -// }); -// if (progress != null) { -// progress.init(); -// progress.onCancel(() -> ConnectionsManager.getInstance(currentAccount).cancelRequest(reqId, true)); -// } + }); + }); + if (progress != null) { + progress.init(); + } } public Dialog showAlertDialog(AlertDialog.Builder builder) { diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/ApkInstaller.java b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/ApkInstaller.java new file mode 100644 index 0000000000..fcd8ea3f82 --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/ApkInstaller.java @@ -0,0 +1,271 @@ +package tw.nekomimi.nekogram.helpers; + + +import static tw.nekomimi.nekogram.NekoXConfig.getNotificationColor; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageInstaller; +import android.net.Uri; +import android.os.Build; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.Gravity; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.core.app.NotificationChannelCompat; +import androidx.core.app.NotificationCompat; +import androidx.core.app.NotificationManagerCompat; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.R; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.Utilities; +import org.telegram.messenger.XiaomiUtilities; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.AlertDialog; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AlertsCreator; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.RLottieImageView; +import org.telegram.ui.LaunchActivity; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import tw.nekomimi.nekogram.NekoConfig; + +public final class ApkInstaller { + @SuppressLint("StaticFieldLeak") + private static AlertDialog dialog; + + // @WorkerThread + private static void installapk(Activity context, File apk) { + //noinspection InlinedApi + var flag = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE; + var action = ApkInstaller.class.getName(); + var intent = new Intent(action).setPackage(context.getPackageName()); + var pending = PendingIntent.getBroadcast(context, 0, intent, flag); + + var installer = context.getPackageManager().getPackageInstaller(); + var params = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + params.setRequireUserAction(PackageInstaller.SessionParams.USER_ACTION_NOT_REQUIRED); + } + try (PackageInstaller.Session session = installer.openSession(installer.createSession(params))) { + OutputStream out = session.openWrite(apk.getName(), 0, apk.length()); + try (var in = new FileInputStream(apk); out) { + transfer(in, out); + } + session.commit(pending.getIntentSender()); + } catch (IOException e) { + FileLog.e(e); + if (dialog != null) { + dialog.dismiss(); + dialog = null; + } + AlertsCreator.createSimpleAlert(context, LocaleController.getString(R.string.ErrorOccurred) + "\n" + e.getLocalizedMessage()).show(); + AndroidUtilities.openForView(apk, "install.apk", "application/vnd.android.package-archive", context, null); + } + } + + private static void transfer(InputStream in, OutputStream out) throws IOException { + int size = 8192; + var buffer = new byte[size]; + int read; + while ((read = in.read(buffer, 0, size)) >= 0) { + out.write(buffer, 0, read); + } + } + + public static void installUpdate(Activity context, TLRPC.Document document) { + if (context == null || document == null) { + return; + } + if (XiaomiUtilities.isMIUI()) { + AndroidUtilities.openForView(document, false, context); + return; + } + var apk = FileLoader.getInstance(UserConfig.selectedAccount).getPathToAttach(document, true); + if (apk == null) { + return; + } + if (dialog != null && dialog.isShowing()) { + return; + } + LinearLayout linearLayout = new LinearLayout(context); + linearLayout.setOrientation(LinearLayout.VERTICAL); + linearLayout.setLayoutParams(LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.LEFT, 4, 4, 4, 4)); + + RLottieImageView imageView = new RLottieImageView(context); + imageView.setAutoRepeat(true); + imageView.setAnimation(R.raw.db_migration_placeholder, 160, 160); + imageView.playAnimation(); + linearLayout.addView(imageView, LayoutHelper.createLinear(160, 160, Gravity.CENTER_HORIZONTAL | Gravity.TOP, 17, 24, 17, 0)); + + TextView textView = new TextView(context); + textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + textView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + textView.setSingleLine(true); + textView.setEllipsize(TextUtils.TruncateAt.END); + textView.setText(LocaleController.getString(R.string.UpdateInstalling)); + linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL | Gravity.TOP, 17, 20, 17, 0)); + + TextView textView2 = new TextView(context); + textView2.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12); + textView2.setTextColor(Theme.getColor(Theme.key_dialogTextGray)); + textView2.setText(Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || Settings.canDrawOverlays(context) ? + LocaleController.getString(R.string.UpdateInstallingRelaunch) : + LocaleController.getString(R.string.UpdateInstallingNotification)); + linearLayout.addView(textView2, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL | Gravity.TOP, 17, 4, 17, 24)); + + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setView(linearLayout); + dialog = builder.create(); + dialog.setCanceledOnTouchOutside(false); + dialog.setCancelable(false); + dialog.show(); + Utilities.globalQueue.postRunnable(() -> { + var receiver = register(context, () -> { + if (dialog != null) { + dialog.dismiss(); + dialog = null; + } + }); + installapk(context, apk); + Intent intent = receiver.waitIntent(); + if (intent != null) { + context.startActivity(intent); + } + }); + } + + private static InstallReceiver register(Context context, Runnable onSuccess) { + var receiver = new InstallReceiver(context, ApplicationLoader.getApplicationId(), onSuccess); + var filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); + filter.addDataScheme("package"); + context.registerReceiver(receiver, filter); + context.registerReceiver(receiver, new IntentFilter(ApkInstaller.class.getName())); + return receiver; + } + + private static class InstallReceiver extends BroadcastReceiver { + private final Context context; + private final String packageName; + private final Runnable onSuccess; + private final CountDownLatch latch = new CountDownLatch(1); + private Intent intent = null; + + private InstallReceiver(Context context, String packageName, Runnable onSuccess) { + this.context = context; + this.packageName = packageName; + this.onSuccess = onSuccess; + } + + @Override + public void onReceive(Context c, Intent i) { + if (Intent.ACTION_PACKAGE_ADDED.equals(i.getAction())) { + Uri data = i.getData(); + if (data == null || onSuccess == null) return; + String pkg = data.getSchemeSpecificPart(); + if (pkg.equals(packageName)) { + onSuccess.run(); + context.unregisterReceiver(this); + } + return; + } + int status = i.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE_INVALID); + switch (status) { + case PackageInstaller.STATUS_PENDING_USER_ACTION: + intent = i.getParcelableExtra(Intent.EXTRA_INTENT); + break; + case PackageInstaller.STATUS_FAILURE: + case PackageInstaller.STATUS_FAILURE_BLOCKED: + case PackageInstaller.STATUS_FAILURE_CONFLICT: + case PackageInstaller.STATUS_FAILURE_INCOMPATIBLE: + case PackageInstaller.STATUS_FAILURE_INVALID: + case PackageInstaller.STATUS_FAILURE_STORAGE: + int id = i.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, 0); + if (id > 0) { + var installer = context.getPackageManager().getPackageInstaller(); + var info = installer.getSessionInfo(id); + if (info != null) { + installer.abandonSession(info.getSessionId()); + } + } + if (context instanceof LaunchActivity) { + ((LaunchActivity) context).showBulletin(factory -> factory.createErrorBulletin(LocaleController.formatString(R.string.UpdateFailedToInstall, status))); + } + case PackageInstaller.STATUS_FAILURE_ABORTED: + case PackageInstaller.STATUS_SUCCESS: + default: + if (onSuccess != null) onSuccess.run(); + context.unregisterReceiver(this); + } + latch.countDown(); + } + + // @WorkerThread @Nullable + public Intent waitIntent() { + try { + //noinspection ResultOfMethodCallIgnored + latch.await(5, TimeUnit.SECONDS); + } catch (Exception ignored) { + } + return intent; + } + } + + public static class UpdateReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + if (!Intent.ACTION_MY_PACKAGE_REPLACED.equals(intent.getAction())) return; + + var packageName = context.getPackageName(); + var installer = context.getPackageManager().getInstallerPackageName(packageName); + if (!packageName.equals(installer)) return; + + var startIntent = new Intent(context, LaunchActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || Settings.canDrawOverlays(context)) { + context.startActivity(startIntent); + } else { + var channel = new NotificationChannelCompat.Builder("updated", NotificationManagerCompat.IMPORTANCE_HIGH) + .setName(LocaleController.getString(R.string.UpdateApp)) + .setLightsEnabled(false) + .setVibrationEnabled(false) + .setSound(null, null) + .build(); + var notificationManager = NotificationManagerCompat.from(context); + notificationManager.createNotificationChannel(channel); + var pendingIntent = PendingIntent.getActivity(context, 0, startIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); + notificationManager.notify(8732833, + new NotificationCompat.Builder(context, "updated") + .setSmallIcon(R.drawable.notification) + .setColor(getNotificationColor()) + .setShowWhen(false) + .setContentText(LocaleController.getString(R.string.UpdateInstalledNotification)) + .setCategory(NotificationCompat.CATEGORY_STATUS) + .setContentIntent(pendingIntent) + .build()); + } + } + } +} diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/remote/UpdateHelper.java b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/remote/UpdateHelper.java new file mode 100644 index 0000000000..648d6e87ab --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/helpers/remote/UpdateHelper.java @@ -0,0 +1,197 @@ +package tw.nekomimi.nekogram.helpers.remote; + +import android.os.Build; + +import org.json.JSONException; +import org.json.JSONObject; +import org.telegram.messenger.BuildConfig; +import org.telegram.messenger.BuildVars; +import org.telegram.tgnet.TLObject; +import org.telegram.tgnet.TLRPC; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import tw.nekomimi.nekogram.NekoXConfig; + +public class UpdateHelper extends BaseRemoteHelper { + public static final String UPDATE_TAG = NekoXConfig.autoUpdateReleaseChannel == 2 ? "updatetest" : "updatev1"; + + private static final class InstanceHolder { + private static final UpdateHelper instance = new UpdateHelper(); + } + + public static UpdateHelper getInstance() { + return InstanceHolder.instance; + } + + @Override + protected void onError(String text, Delegate delegate) { + delegate.onTLResponse(null, text); + } + + @Override + protected String getTag() { + return UPDATE_TAG; + } + + @SuppressWarnings("ConstantConditions") + private int getPreferredAbiFile(Map files) { + for (String abi : Build.SUPPORTED_ABIS) { + if (files.containsKey(abi)) { + return files.get(abi); + } + } + return files.get("arm64-v8a"); + } + + private Map jsonToMap(JSONObject obj) { + Map map = new HashMap<>(); + List abis = new ArrayList<>(); + abis.add("armeabi-v7a"); + abis.add("arm64-v8a"); + + try { + for(var abi: abis) { + map.put(abi, obj.getInt(abi)); + } + } catch (JSONException ignored) {} + return map; + } + + private Update getShouldUpdateVersion(List responses) { + long maxVersion = BuildConfig.VERSION_CODE; + Update ref = null; + for (var string : responses) { + try { + int version_code = string.getInt("version_code"); + if (version_code > maxVersion) { + maxVersion = version_code; + ref = new Update( + string.getBoolean("can_not_skip"), + string.getString("version"), + string.getInt("version_code"), + string.getInt("sticker"), + string.getInt("message"), + jsonToMap(string.getJSONObject("gcm")), + jsonToMap(string.getJSONObject("nogcm")), + string.getString("url") + ); + break; + } + } catch (JSONException ignored) {} + } + return ref; + } + + private void getNewVersionMessagesCallback(Delegate delegate, Update json, + HashMap ids, TLObject response) { + var update = new TLRPC.TL_help_appUpdate(); + update.version = json.version; + update.can_not_skip = json.canNotSkip; + if (json.url != null) { + update.url = json.url; + update.flags |= 4; + } + if (response != null) { + var res = (TLRPC.messages_Messages) response; + getMessagesController().removeDeletedMessagesFromArray(CHANNEL_METADATA_ID, res.messages); + var messages = new HashMap(); + for (var message : res.messages) { + messages.put(message.id, message); + } + + if (ids.containsKey("file")) { + var file = messages.get(ids.get("file")); + if (file != null && file.media != null) { + update.document = file.media.document; + update.flags |= 2; + } + } + if (ids.containsKey("message")) { + var message = messages.get(ids.get("message")); + if (message != null) { + update.text = message.message; + update.entities = message.entities; + } + } + if (ids.containsKey("sticker")) { + var sticker = messages.get(ids.get("sticker")); + if (sticker != null && sticker.media != null) { + update.sticker = sticker.media.document; + update.flags |= 8; + } + } + } + delegate.onTLResponse(update, null); + } + + @Override + protected void onLoadSuccess(ArrayList responses, Delegate delegate) { + var update = getShouldUpdateVersion(responses); + if (update == null) { + delegate.onTLResponse(null, null); + return; + } + + var ids = new HashMap(); + if (update.message != null) { + ids.put("message", update.message); + } + if (update.sticker != null) { + ids.put("sticker", update.sticker); + } + if (update.nogcm != null && BuildVars.isNoGCM) { + ids.put("file", getPreferredAbiFile(update.nogcm)); + } else if (update.gcm != null) { + ids.put("file", getPreferredAbiFile(update.gcm)); + } + + if (ids.isEmpty()) { + getNewVersionMessagesCallback(delegate, update, null, null); + } else { + var req = new TLRPC.TL_channels_getMessages(); + req.channel = getMessagesController().getInputChannel(CHANNEL_METADATA_ID); + req.id = new ArrayList<>(ids.values()); + getConnectionsManager().sendRequest(req, (response1, error1) -> { + if (error1 == null) { + getNewVersionMessagesCallback(delegate, update, ids, response1); + } else { + delegate.onTLResponse(null, error1.text); + } + }); + } + } + + public void checkNewVersionAvailable(Delegate delegate) { + if (NekoXConfig.autoUpdateReleaseChannel == 0) { + delegate.onTLResponse(null, null); + return; + } + load(delegate); + } + + public static class Update { + public Boolean canNotSkip; + public String version; + public Integer versionCode; + public Integer sticker; + public Integer message; + public Map gcm; + public Map nogcm; + public String url; + + public Update(Boolean canNotSkip, String version, int versionCode, int sticker, int message, Map gcm, Map nogcm, String url) { + this.canNotSkip = canNotSkip; + this.version = version; + this.versionCode = versionCode; + this.sticker = sticker; + this.message = message; + this.gcm = gcm; + this.nogcm = nogcm; + this.url = url; + } + } +} diff --git a/TMessagesProj/src/main/res/values-zh-rCN/strings_neko.xml b/TMessagesProj/src/main/res/values-zh-rCN/strings_neko.xml index fa9b44967f..5a88109814 100644 --- a/TMessagesProj/src/main/res/values-zh-rCN/strings_neko.xml +++ b/TMessagesProj/src/main/res/values-zh-rCN/strings_neko.xml @@ -216,4 +216,9 @@ 复制回调数据 复制 Inline 请求 复制 ID + 正在安装更新... + 更新完成后,将显示一个通知。 + 更新完成后,应用将重新启动。 + 更新安装完成,点击启动应用。 + 无法安装更新:%d diff --git a/TMessagesProj/src/main/res/values/strings_neko.xml b/TMessagesProj/src/main/res/values/strings_neko.xml index 234a6cfb9b..8045b0872c 100644 --- a/TMessagesProj/src/main/res/values/strings_neko.xml +++ b/TMessagesProj/src/main/res/values/strings_neko.xml @@ -218,4 +218,9 @@ Copy Callback Data Copy Inline Query Copy ID + Installing update... + A notification will be shown when the update completes. + The app will relaunch when the update completes. + Update installation finished, tap to launch the app. + Failed to install update: %d