Allow login with qr & Refine its scan

This commit is contained in:
世界 2020-11-28 03:32:39 +08:00
parent b36ec33936
commit 0072eaaab0
No known key found for this signature in database
GPG Key ID: CD109927C34A63C4
16 changed files with 256 additions and 54 deletions

View File

@ -1,8 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
def verName = "7.2.1"
def verCode = 117
def verName = "7.2.1.1"
def verCode = 118
def serviceAccountCredentialsFile = rootProject.file("service_account_credentials.json")

View File

@ -195,6 +195,12 @@ void setLayer(JNIEnv *env, jclass c, jint instanceNum, jint layer) {
}
void moveToDatacenter(JNIEnv *env, jclass c, jint instanceNum, jint datacenterId) {
ConnectionsManager::getInstance(instanceNum).moveToDatacenter((uint32_t) datacenterId);
}
void saveDatacenters(JNIEnv *env, jclass c,jint instanceNum) {
ConnectionsManager::getInstance(instanceNum).saveDatacenters();
@ -478,6 +484,7 @@ static JNINativeMethod ConnectionsManagerMethods[] = {
{"native_setDatacenterAddress", "(IILjava/lang/String;Ljava/lang/String;I)V", (void *) setDatacenterAddress},
{"native_saveDatacenters", "(I)V", (void *) saveDatacenters},
{"native_setLayer", "(II)V", (void *) setLayer},
{"native_moveToDatacenter", "(II)V", (void *) moveToDatacenter},
{"native_setProxySettings", "(ILjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", (void *) setProxySettings},
{"native_getConnectionState", "(I)I", (void *) getConnectionState},
{"native_setUserId", "(II)V", (void *) setUserId},

View File

@ -76,6 +76,8 @@ public:
void setPushConnectionEnabled(bool value);
void applyDnsConfig(NativeByteBuffer *buffer, std::string phone, int32_t date);
void setMtProtoVersion(int version);
void moveToDatacenter(uint32_t datacenterId);
int32_t getMtProtoVersion();
int64_t checkProxy(std::string address, uint16_t port, std::string username, std::string password, std::string secret, onRequestTimeFunc requestTimeFunc, jobject ptr1);
@ -101,7 +103,6 @@ private:
void clearRequestsForDatacenter(Datacenter *datacenter, HandshakeType type);
void registerForInternalPushUpdates();
void processRequestQueue(uint32_t connectionType, uint32_t datacenterId);
void moveToDatacenter(uint32_t datacenterId);
void authorizeOnMovingDatacenter();
void authorizedOnMovingDatacenter();
Datacenter *getDatacenterWithId(uint32_t datacenterId);

View File

@ -2189,7 +2189,7 @@ Telegram 团队</string>
<string name="ConvertGroupAlert">此操作是不可逆的。超级群组不可能降级为普通群组。</string>
<string name="EventLogDeletedMessages">un1 删除了这条消息:</string>
<string name="KMetersAway">千米以外</string>
<string name="AuthAnotherClient">通过二维码登录</string>
<string name="QRLoginConfirm">通过二维码登录</string>
<string name="VideoMessagesAutodownload">视频消息</string>
<string name="NotificationMessageStickerEmoji">%1$s 给您发送了一个 %2$s 表情</string>
<string name="StickersArchivedInfo">%1$s 已移动到您的归档中。</string>

View File

@ -16,7 +16,7 @@ import android.content.pm.PackageManager;
@SuppressWarnings("ConstantConditions")
public class BuildVars {
public static boolean DEBUG_VERSION = BuildConfig.BUILD_TYPE.equals("debug") || BuildConfig.VERSION_NAME.contains("preview");
public static boolean DEBUG_VERSION = BuildConfig.BUILD_TYPE.equals("debug");
public static boolean DEBUG_PRIVATE_VERSION = DEBUG_VERSION;
public static boolean LOGS_ENABLED;
public static boolean USE_CLOUD_STRINGS = true;

View File

@ -12144,6 +12144,11 @@ public class MessagesController extends BaseController implements NotificationCe
updatesOnMainThread = new ArrayList<>();
}
updatesOnMainThread.add(baseUpdate);
} else if (baseUpdate instanceof TLRPC.TL_updateLoginToken) {
if (updatesOnMainThread == null) {
updatesOnMainThread = new ArrayList<>();
}
updatesOnMainThread.add(baseUpdate);
}
}
if (messages != null) {
@ -12688,6 +12693,8 @@ public class MessagesController extends BaseController implements NotificationCe
} else if (baseUpdate instanceof TLRPC.TL_updateReadChannelDiscussionOutbox) {
TLRPC.TL_updateReadChannelDiscussionOutbox update = (TLRPC.TL_updateReadChannelDiscussionOutbox) baseUpdate;
getNotificationCenter().postNotificationName(NotificationCenter.threadMessagesRead, (long) -update.channel_id, update.top_msg_id, 0, update.read_max_id);
} else if (baseUpdate instanceof TLRPC.TL_updateLoginToken) {
getNotificationCenter().postNotificationName(NotificationCenter.updateLoginToken);
}
}
if (editor != null) {

View File

@ -204,6 +204,8 @@ public class NotificationCenter {
// custom
public static final int updateUserStatus = totalEvents++;
public static final int updateLoginToken = totalEvents++;
private SparseArray<ArrayList<NotificationCenterDelegate>> observers = new SparseArray<>();
private SparseArray<ArrayList<NotificationCenterDelegate>> removeAfterBroadcast = new SparseArray<>();

View File

@ -690,6 +690,8 @@ public class ConnectionsManager extends BaseController {
public static native void native_setLayer(int currentAccount, int layer);
public static native void native_moveToDatacenter(int currentAccount, int datacenterId);
public static native int native_getConnectionState(int currentAccount);
public static native void native_setUserId(int currentAccount, int id);

View File

@ -1137,7 +1137,7 @@ public class AlertDialog extends Dialog implements Drawable.Callback {
}
public Builder setMessage(CharSequence message) {
alertDialog.message = message;
alertDialog.message = message instanceof String ? AndroidUtilities.replaceTags((String) message) : message;
return this;
}

View File

@ -13,7 +13,6 @@ import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ImageFormat;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
@ -25,7 +24,6 @@ import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.text.TextUtils;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
@ -35,14 +33,15 @@ import android.widget.TextView;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.LuminanceSource;
import com.google.zxing.NotFoundException;
import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.RGBLuminanceSource;
import com.google.zxing.Result;
import com.google.zxing.common.GlobalHistogramBinarizer;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.ApplicationLoader;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.ImageLoader;
import org.telegram.messenger.LocaleController;
@ -63,7 +62,6 @@ import org.telegram.ui.Components.AnimationProperties;
import org.telegram.ui.Components.CubicBezierInterpolator;
import org.telegram.ui.Components.LayoutHelper;
import java.nio.ByteBuffer;
import java.util.ArrayList;
@TargetApi(18)
@ -89,7 +87,7 @@ public class CameraScanActivity extends BaseFragment implements Camera.PreviewCa
//private BarcodeDetector visionQrReader;
private boolean needGalleryButton;
private boolean any;
private boolean any;
private int currentType;
@ -664,21 +662,27 @@ public class CameraScanActivity extends BaseFragment implements Camera.PreviewCa
text = null;
}
} else {*/
LuminanceSource source;
if (bitmap != null) {
int[] intArray = new int[bitmap.getWidth() * bitmap.getHeight()];
bitmap.getPixels(intArray, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
source = new RGBLuminanceSource(bitmap.getWidth(), bitmap.getHeight(), intArray);
} else {
source = new PlanarYUVLuminanceSource(data, size.getWidth(), size.getHeight(), x, y, side, side, false);
}
Result result = qrReader.decode(new BinaryBitmap(new GlobalHistogramBinarizer(source)));
if (result == null) {
onNoQrFound();
return null;
}
text = result.getText();
LuminanceSource source;
if (bitmap != null) {
int[] intArray = new int[bitmap.getWidth() * bitmap.getHeight()];
bitmap.getPixels(intArray, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
source = new RGBLuminanceSource(bitmap.getWidth(), bitmap.getHeight(), intArray);
} else {
source = new PlanarYUVLuminanceSource(data, size.getWidth(), size.getHeight(), x, y, side, side, false);
}
Result result = null;
try {
result = qrReader.decode(new BinaryBitmap(new GlobalHistogramBinarizer(source)));
} catch (NotFoundException e) {
try {
result = qrReader.decode(new BinaryBitmap(new GlobalHistogramBinarizer(source.invert())));
} catch (NotFoundException ignore) {}
}
if (result == null) {
onNoQrFound();
return null;
}
text = result.getText();
//}
if (TextUtils.isEmpty(text)) {
onNoQrFound();
@ -692,11 +696,6 @@ public class CameraScanActivity extends BaseFragment implements Camera.PreviewCa
}
Uri uri = Uri.parse(text);
String path = uri.getPath().replace("/", "");
} else {
if (!text.startsWith("tg://login?token=")) {
onNoQrFound();
return null;
}
}
return text;
} catch (Throwable ignore) {

View File

@ -32,6 +32,7 @@ import android.os.Parcelable;
import android.os.StatFs;
import android.os.SystemClock;
import android.provider.ContactsContract;
import android.se.omapi.Session;
import android.text.TextUtils;
import android.util.Base64;
import android.view.ActionMode;
@ -119,6 +120,7 @@ import org.telegram.ui.Components.StickersAlert;
import org.telegram.ui.Components.Switch;
import org.telegram.ui.Components.TermsOfServiceView;
import org.telegram.ui.Components.ThemeEditorView;
import org.telegram.ui.Components.UndoView;
import org.telegram.ui.Components.voip.VoIPHelper;
import java.io.File;
@ -129,7 +131,9 @@ import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import kotlin.Unit;
import kotlin.text.StringsKt;
import tw.nekomimi.nekogram.BottomBuilder;
import tw.nekomimi.nekogram.ExternalGcm;
import tw.nekomimi.nekogram.NekoConfig;
import tw.nekomimi.nekogram.NekoXConfig;
@ -2472,11 +2476,47 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa
}
return;
} else if (loginToken != null) {
AlertDialog.Builder builder = new AlertDialog.Builder(LaunchActivity.this);
builder.setTitle(LocaleController.getString("AuthAnotherClient", R.string.AuthAnotherClient));
builder.setMessage(LocaleController.getString("AuthAnotherClientUrl", R.string.AuthAnotherClientUrl));
builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null);
showAlertDialog(builder);
BottomBuilder builder = new BottomBuilder(this);
builder.addTitle(LocaleController.getString("AuthAnotherClientScan", R.string.AuthAnotherClientScan), LocaleController.getString("QRLoginNotice",R.string.QRLoginNotice));
builder.addItem(LocaleController.getString("QRLoginConfirm", R.string.QRLoginConfirm), R.drawable.baseline_security_24, true, (c) -> {
AlertDialog progressDialog = new AlertDialog(this, 3);
progressDialog.setCanCacnel(false);
progressDialog.show();
byte[] token = Base64.decode(loginToken, Base64.URL_SAFE);
TLRPC.TL_auth_acceptLoginToken req = new TLRPC.TL_auth_acceptLoginToken();
req.token = token;
ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> {
try {
progressDialog.dismiss();
} catch (Exception ignore) {
}
if (response instanceof TLRPC.TL_authorization) {
SessionsActivity fragment = new SessionsActivity(0);
fragment.newAuthorizationToOpen = (TLRPC.TL_authorization) response;
presentFragment(fragment, false, false);
if (AndroidUtilities.isTablet()) {
actionBarLayout.showLastFragment();
rightActionBarLayout.showLastFragment();
drawerLayoutContainer.setAllowOpenDrawer(false, false);
} else {
drawerLayoutContainer.setAllowOpenDrawer(true, false);
}
} else {
AndroidUtilities.runOnUIThread(() -> {
final String text;
if (error.text.equals("AUTH_TOKEN_EXCEPTION")) {
text = LocaleController.getString("AccountAlreadyLoggedIn", R.string.AccountAlreadyLoggedIn);
} else {
text = LocaleController.getString("ErrorOccurred", R.string.ErrorOccurred) + "\n" + error.text;
}
AlertUtil.showSimpleAlert(this, LocaleController.getString("AuthAnotherClient", R.string.AuthAnotherClient), text);
});
}
}));
return Unit.INSTANCE;
});
builder.addCancelItem();
builder.show();
return;
}
final AlertDialog progressDialog = new AlertDialog(this, 3);

View File

@ -135,6 +135,7 @@ import tw.nekomimi.nekogram.EditTextAutoFill;
import tw.nekomimi.nekogram.NekoXConfig;
import tw.nekomimi.nekogram.parts.PKCS1Pub;
import tw.nekomimi.nekogram.utils.AlertUtil;
import tw.nekomimi.nekogram.utils.ProxyUtil;
import tw.nekomimi.nekogram.utils.VibrateUtil;
@SuppressLint("HardwareIds")
@ -277,6 +278,11 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
private int menu_other = 5;
private int menu_custom_api = 6;
private int menu_custom_dc = 7;
private int menu_qr_login = 8;
TLRPC.TL_auth_exportLoginToken exportLoginTokenRequest = null;
AlertDialog exportLoginTokenProgress = null;
android.app.AlertDialog exportLoginTokenDialog = null;
@Override
public View createView(Context context) {
@ -899,6 +905,8 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
});
builder.show();
} else if (id == menu_qr_login) {
regenerateLoginToken(false);
}
}
@ -922,6 +930,7 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
Locale current = ConfigurationCompat.getLocales(getParentActivity().getResources().getConfiguration()).get(0);
otherItem.addSubItem(4, R.drawable.list_bot, LocaleController.getString("BotLogin", R.string.BotLogin));
otherItem.addSubItem(menu_qr_login, R.drawable.wallet_qr, LocaleController.getString("ImportLogin", R.string.ImportLogin));
otherItem.addSubItem(menu_custom_api, R.drawable.baseline_vpn_key_24, LocaleController.getString("CustomApi", R.string.CustomApi));
otherItem.addSubItem(menu_custom_dc, R.drawable.baseline_sync_24, LocaleController.getString("CustomBackend", R.string.CustomBackend));
@ -1080,6 +1089,128 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
return fragmentView;
}
private void regenerateLoginToken(Boolean refresh) {
getNotificationCenter().removeObserver(this, NotificationCenter.updateLoginToken);
if (getParentActivity() == null || isFinished) return;
if (exportLoginTokenDialog != null && exportLoginTokenDialog.isShowing()) {
exportLoginTokenDialog.dismiss();
} else if (refresh) return;
exportLoginTokenProgress = new AlertDialog(getParentActivity(), 3);
exportLoginTokenProgress.setCanCacnel(false);
exportLoginTokenProgress.show();
if (exportLoginTokenRequest == null) {
exportLoginTokenRequest = new TLRPC.TL_auth_exportLoginToken();
exportLoginTokenRequest.api_id = NekoXConfig.currentAppId();
exportLoginTokenRequest.api_hash = NekoXConfig.currentAppHash();
exportLoginTokenRequest.except_ids = new ArrayList<>();
for (int a = 0; a < UserConfig.MAX_ACCOUNT_COUNT; a++) {
UserConfig userConfig = UserConfig.getInstance(a);
if (!userConfig.isClientActivated()) {
continue;
}
exportLoginTokenRequest.except_ids.add(a);
}
}
getNotificationCenter().addObserver(this, NotificationCenter.updateLoginToken);
getConnectionsManager().sendRequest(exportLoginTokenRequest, (response, error) -> AndroidUtilities.runOnUIThread(() -> {
if (getParentActivity() == null) return;
try {
exportLoginTokenProgress.dismiss();
} catch (Exception ignore) {
}
if (response instanceof TLRPC.TL_auth_loginToken) {
exportLoginTokenDialog = ProxyUtil.showQrDialog(getParentActivity(), "tg://login?token=" + Base64.encodeUrlSafe(((TLRPC.TL_auth_loginToken) response).token));
int delay = (int) (((TLRPC.TL_auth_loginToken) response).expires - System.currentTimeMillis() / 1000);
if (BuildVars.DEBUG_VERSION) {
AlertUtil.showToast("Refresh after " + delay + "s");
}
AndroidUtilities.runOnUIThread(() -> regenerateLoginToken(true), ((TLRPC.TL_auth_loginToken) response).expires * 1000L - System.currentTimeMillis());
} else if (response instanceof TLRPC.TL_auth_loginTokenMigrateTo) {
checkMigrateTo((TLRPC.TL_auth_loginTokenMigrateTo) response);
} else if (response instanceof TLRPC.TL_auth_loginTokenSuccess) {
processLoginByTokenFinish((TLRPC.TL_auth_loginTokenSuccess) response);
} else {
if (error.text.contains("SESSION_PASSWORD_NEEDED")) {
exportLoginTokenProgress.show();
TLRPC.TL_account_getPassword req2 = new TLRPC.TL_account_getPassword();
ConnectionsManager.getInstance(currentAccount).sendRequest(req2, (response1, error1) -> AndroidUtilities.runOnUIThread(() -> {
exportLoginTokenProgress.dismiss();
showDoneButton(false, true);
if (error1 == null) {
TLRPC.TL_account_password password = (TLRPC.TL_account_password) response1;
if (!TwoStepVerificationActivity.canHandleCurrentPassword(password, true)) {
AlertsCreator.showUpdateAppAlert(getParentActivity(), LocaleController.getString("UpdateAppAlert", R.string.UpdateAppAlert), true);
return;
}
Bundle bundle = new Bundle();
if (password.current_algo instanceof TLRPC.TL_passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow) {
TLRPC.TL_passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow algo = (TLRPC.TL_passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow) password.current_algo;
bundle.putString("current_salt1", Utilities.bytesToHex(algo.salt1));
bundle.putString("current_salt2", Utilities.bytesToHex(algo.salt2));
bundle.putString("current_p", Utilities.bytesToHex(algo.p));
bundle.putInt("current_g", algo.g);
bundle.putString("current_srp_B", Utilities.bytesToHex(password.srp_B));
bundle.putLong("current_srp_id", password.srp_id);
bundle.putInt("passwordType", 1);
}
bundle.putString("hint", password.hint != null ? password.hint : "");
bundle.putString("email_unconfirmed_pattern", password.email_unconfirmed_pattern != null ? password.email_unconfirmed_pattern : "");
bundle.putInt("has_recovery", password.has_recovery ? 1 : 0);
setPage(6, true, bundle, false);
} else {
needShowAlert(LocaleController.getString("NekoX", R.string.NekoX), error1.text);
}
}), ConnectionsManager.RequestFlagFailOnServerErrors | ConnectionsManager.RequestFlagWithoutLogin);
} else {
AlertUtil.showToast(error);
}
}
}), ConnectionsManager.RequestFlagFailOnServerErrors | ConnectionsManager.RequestFlagWithoutLogin | ConnectionsManager.RequestFlagTryDifferentDc | ConnectionsManager.RequestFlagEnableUnauthorized);
}
private void checkMigrateTo(TLRPC.TL_auth_loginTokenMigrateTo response) {
getNotificationCenter().removeObserver(this, NotificationCenter.updateLoginToken);
ConnectionsManager.native_moveToDatacenter(currentAccount, response.dc_id);
exportLoginTokenProgress.show();
TLRPC.TL_auth_importLoginToken request = new TLRPC.TL_auth_importLoginToken();
request.token = response.token;
getConnectionsManager().sendRequest(request, (response1, error1) -> AndroidUtilities.runOnUIThread(() -> {
exportLoginTokenProgress.dismiss();
if (error1 != null) {
exportLoginTokenRequest = null;
regenerateLoginToken(false);
} else if (response1 instanceof TLRPC.TL_auth_loginTokenSuccess) {
processLoginByTokenFinish((TLRPC.TL_auth_loginTokenSuccess) response1);
}
}), ConnectionsManager.RequestFlagFailOnServerErrors | ConnectionsManager.RequestFlagWithoutLogin | ConnectionsManager.RequestFlagTryDifferentDc | ConnectionsManager.RequestFlagEnableUnauthorized);
}
private void processLoginByTokenFinish(TLRPC.TL_auth_loginTokenSuccess authLoginTokenSuccess) {
getNotificationCenter().removeObserver(this, NotificationCenter.updateLoginToken);
TLRPC.auth_Authorization authorization = authLoginTokenSuccess.authorization;
if (authorization instanceof TLRPC.TL_auth_authorizationSignUpRequired) {
TLRPC.TL_auth_authorizationSignUpRequired authorizationI = (TLRPC.TL_auth_authorizationSignUpRequired) authorization;
if (authorizationI.terms_of_service != null) {
currentTermsOfService = authorizationI.terms_of_service;
}
setPage(5, true, new Bundle(), false);
} else {
onAuthSuccess((TLRPC.TL_auth_authorization) authorization);
}
}
private int currentConnectionState;
@Override
@ -1090,6 +1221,8 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
currentConnectionState = state;
updateProxyButton(true);
}
} else if (id == NotificationCenter.updateLoginToken) {
regenerateLoginToken(false);
}
}

View File

@ -112,6 +112,8 @@ public class SessionsActivity extends BaseFragment implements NotificationCenter
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.newSessionReceived);
}
TLRPC.TL_authorization newAuthorizationToOpen;
@Override
public View createView(Context context) {
actionBar.setBackButtonImage(R.drawable.ic_ab_back);
@ -421,6 +423,10 @@ public class SessionsActivity extends BaseFragment implements NotificationCenter
frameLayout.addView(undoView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | Gravity.LEFT, 8, 0, 8, 8));
}
if (newAuthorizationToOpen != null) {
AndroidUtilities.runOnUIThread(() -> undoView.showWithAction(0, UndoView.ACTION_QR_SESSION_ACCEPTED, newAuthorizationToOpen), 3000L);
}
return fragmentView;
}

View File

@ -247,7 +247,7 @@ public class NekoConfig {
mediaPreview = preferences.getBoolean("mediaPreview", false);
proxyAutoSwitch = preferences.getBoolean("proxy_auto_switch", false);
usePersianCalender = preferences.getBoolean("usePersianCalender", false);
usePersianCalender = false;//preferences.getBoolean("usePersianCalender", false);
openPGPApp = preferences.getString("openPGPApp","");
openPGPKeyId = preferences.getLong("openPGPKeyId",0L);

View File

@ -2,6 +2,7 @@ package tw.nekomimi.nekogram.utils
import android.Manifest
import android.app.Activity
import android.app.AlertDialog
import android.content.ClipboardManager
import android.content.Context
import android.content.ContextWrapper
@ -349,13 +350,13 @@ object ProxyUtil {
}
@JvmStatic
fun showQrDialog(ctx: Context, text: String) {
fun showQrDialog(ctx: Context, text: String): AlertDialog {
val code = createQRCode(text)
ctx.setTheme(R.style.Theme_TMessages)
android.app.AlertDialog.Builder(ctx).setView(LinearLayout(ctx).apply {
return AlertDialog.Builder(ctx).setView(LinearLayout(ctx).apply {
addView(LinearLayout(ctx).apply {
@ -461,28 +462,30 @@ object ProxyUtil {
@JvmStatic
fun tryReadQR(ctx: Activity, bitmap: Bitmap) {
val intArray = IntArray(bitmap.getWidth() * bitmap.getHeight())
bitmap.getPixels(intArray, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight())
val source = RGBLuminanceSource(bitmap.getWidth(), bitmap.getHeight(), intArray)
val intArray = IntArray(bitmap.width * bitmap.height)
bitmap.getPixels(intArray, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)
val source = RGBLuminanceSource(bitmap.width, bitmap.height, intArray)
try {
val result = qrReader.decode(BinaryBitmap(GlobalHistogramBinarizer(source)))
if (result == null || result.text.isBlank()) {
AlertUtil.showToast(LocaleController.getString("NoQrFound", R.string.NoQrFound))
} else {
showLinkAlert(ctx, result.text)
val result = try {
qrReader.decode(BinaryBitmap(GlobalHistogramBinarizer(source)))
} catch (e: NotFoundException) {
qrReader.decode(BinaryBitmap(GlobalHistogramBinarizer(source.invert())))
}
showLinkAlert(ctx, result.text)
val intArr = arrayListOf<Int>().toIntArray()
} catch (ex: NoSuchMethodError) {
AlertUtil.showSimpleAlert(ctx, "很抱歉, 這是一個已知的問題, 但您現在無法掃碼, 因爲您正在使用糟糕的Android系統, 直到 Google Zxing 為您的設備做出優化.")
} catch (e: Throwable) {
AlertUtil.showToast(LocaleController.getString("NoQrFound", R.string.NoQrFound))
}
}
@ -495,7 +498,7 @@ object ProxyUtil {
var isUrl = false
runCatching {
text.toHttpUrlOrNull()!!
text.replace("tg://", "https://t.me/").toHttpUrlOrNull()!!
if (Browser.isInternalUrl(text, booleanArrayOf(false))) {
Browser.openUrl(ctx, text)
return

View File

@ -252,6 +252,8 @@
<string name="UnpinMessageX">Unpin message</string>
<string name="UnpinMessagesAll">Unpin all messages</string>
<string name="DismissForYourself">Dismiss for yourself</string>
<string name="ImportLogin">QR Login</string>
<string name="QRLoginNotice">This QR code / link allows someone to log in to your Telegram account. Is this your own operation?</string>
<string name="QRLoginConfirm">Confirm Login</string>
</resources>