应用内更新

This commit is contained in:
世界 2020-05-05 13:55:23 +08:00
parent 14eab97190
commit 8cba28feff
No known key found for this signature in database
GPG Key ID: CD109927C34A63C4
27 changed files with 1150 additions and 328 deletions

200
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,200 @@
name: Build
on:
push:
branches:
- master
jobs:
canaryBuild:
name: Canary Build
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[D]') && !contains(github.event.head_commit.message, '[RELEASE]') && !contains(github.event.head_commit.message, '[N]')"
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v1
with:
path: ~/.gradle
key: gradle-${{ hashFiles('**/*.gradle') }}
- uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Run Gradle Build
run: |
export LOCAL_PROPERTIES="${{ secrets.LOCAL_PROPERTIES }}"
./gradlew assembleMinApi21Release
echo ::set-env name=APK_FILE::$(find TMessagesProj/build/outputs/apk -name "*arm64-v8a*.apk")
# - uses: actions/upload-artifact@v1
# with:
# name: NekoX Canary
# path: ${{ env.APK_FILE }}
- name: Upload Canary Apk
uses: appleboy/telegram-action@master
with:
to: ${{ secrets.TELEGRAM_TO }}
token: ${{ secrets.TELEGRAM_TOKEN }}
document: ${{ env.APK_FILE }}
nativeBuild:
name: Native Build
runs-on: ubuntu-latest
if: "contains(github.event.head_commit.message, '[N]')"
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v1
with:
path: ~/.gradle
key: native-${{ hashFiles('**/*.gradle') }}
restore-keys: |
${{ runner.os }}-gradle-
- run: git submodule update --init --recursive
- uses: actions/cache@v1
with:
path: TMessagesProj/jni/boringssl/build
key: boringssl-${{ hashFiles('TMessagesProj/jni/boringssl/.git') }}
- uses: actions/cache@v1
with:
path: TMessagesProj/jni/ffmpeg/build
key: ffmpeg-${{ hashFiles('TMessagesProj/jni/ffmpeg/.git') }}
- uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Build native libraries
run: |
cd TMessagesProj/jni
while :
do
sudo apt-get install -y ninja-build && break
sleep 5
done
export NDK=$ANDROID_HOME/ndk-bundle
export NINJA_PATH=/usr/bin/ninja
export PATH=`echo $ANDROID_HOME/cmake/*/bin`:$PATH
[ -d "ffmpeg/build" ] || ./build_ffmpeg_clang.sh
./patch_ffmpeg.sh
./patch_boringssl.sh
[ -d "boringssl/build" ] || ./build_boringssl.sh
- name: assemble
run: |
sudo bash <<EOF
export LOCAL_PROPERTIES="${{ secrets.LOCAL_PROPERTIES }}" &&
./gradlew assembleAfatFoss
EOF
ls TMessagesProj/build/outputs/apk
echo ::set-env name=APK_FILE::$(find TMessagesProj/build/outputs/apk -name "*universal*")
- uses: actions/upload-artifact@master
with:
name: NekoX-Foss
path: ${{ env.APK_FILE }}
- uses: actions/upload-artifact@master
with:
name: Boringssl Library
path: "TMessagesProj/jni/boringssl/build"
- uses: actions/upload-artifact@master
with:
name: Ffmpeg Library
path: "TMessagesProj/jni/ffmpeg/build"
fossBuild:
name: Foss Build
runs-on: ubuntu-latest
if: "contains(github.event.head_commit.message, '[RELEASE]')"
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v1
with:
path: ~/.gradle
key: native-${{ hashFiles('**/*.gradle') }}
restore-keys: |
${{ runner.os }}-gradle-
- run: git submodule update --init --recursive
- uses: actions/cache@v1
with:
path: TMessagesProj/jni/boringssl/build
key: boringssl-${{ hashFiles('TMessagesProj/jni/boringssl/.git') }}
- uses: actions/cache@v1
with:
path: TMessagesProj/jni/ffmpeg/build
key: ffmpeg-${{ hashFiles('TMessagesProj/jni/ffmpeg/.git') }}
- uses: actions/setup-java@v1
with:
java-version: 1.8
- uses: actions/setup-go@v1
with:
go-version: 1.13
- name: Build native libraries
run: |
cd TMessagesProj/libs
go env -w GOPATH=$HOME/go
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
go get -u github.com/golang/protobuf/protoc-gen-go
go get -v golang.org/x/mobile/cmd/...
go get -v go.starlark.net/starlark
go get -v github.com/refraction-networking/utls
go get -v github.com/gorilla/websocket
go get -v -insecure v2ray.com/core
go get github.com/2dust/AndroidLibV2rayLite
gomobile init
rm libv2ray.aar
env GO111MODULE=off gomobile bind -v -ldflags='-s -w' github.com/2dust/AndroidLibV2rayLite
cd ../jni
while :
do
sudo apt-get install -y ninja-build && break
sleep 5
done
export NDK=$ANDROID_HOME/ndk-bundle
export NINJA_PATH=/usr/bin/ninja
export PATH=`echo $ANDROID_HOME/cmake/*/bin`:$PATH
[ -d "ffmpeg/build" ] || ./build_ffmpeg_clang.sh
./patch_ffmpeg.sh
./patch_boringssl.sh
[ -d "boringssl/build" ] || ./build_boringssl.sh
- name: assemble
run: |
sudo bash <<EOF
export LOCAL_PROPERTIES="${{ secrets.LOCAL_PROPERTIES }}"
./gradlew ss-libev:assembleRelease &&
./gradlew ssr-libev:assembleRelease &&
rm -rf TMessagesProj/libs/*-libev-release.aar &&
find . -name "*-libev-release.aar" -exec mv {} TMessagesProj/libs \; &&
./gradlew assembleAfatFoss
EOF
ls TMessagesProj/build/outputs/apk
echo ::set-env name=APK_FILE::$(find TMessagesProj/build/outputs/apk -name "*universal*")
- uses: actions/upload-artifact@master
with:
name: NekoX-Foss
path: ${{ env.APK_FILE }}
- uses: actions/upload-artifact@master
with:
name: V2ray Library
path: "TMessagesProj/libs/libv2ray.aar"
- uses: actions/upload-artifact@master
with:
name: Boringssl Library
path: "TMessagesProj/jni/boringssl/build"
- uses: actions/upload-artifact@master
with:
name: Ffmpeg Library
path: "TMessagesProj/jni/ffmpeg/build"

View File

@ -1,36 +0,0 @@
name: Canary Build
on:
push:
branches:
- master
jobs:
canaryBuild:
name: Canary Build
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[S]') && !contains(github.event.head_commit.message, '[RELEASE]') && !contains(github.event.head_commit.message, '[N]')"
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v1
with:
path: ~/.gradle
key: gradle-${{ hashFiles('**/*.gradle') }}
- uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Run Gradle Build
run: |
export LOCAL_PROPERTIES="${{ secrets.LOCAL_PROPERTIES }}"
./gradlew assembleMinApi21Release
echo ::set-env name=APK_FILE::$(find TMessagesProj/build/outputs/apk -name "*arm64-v8a*.apk")
# - uses: actions/upload-artifact@v1
# with:
# name: NekoX Canary
# path: ${{ env.APK_FILE }}
- name: Upload Canary Apk
uses: appleboy/telegram-action@master
with:
to: ${{ secrets.TELEGRAM_TO }}
token: ${{ secrets.TELEGRAM_TOKEN }}
document: ${{ env.APK_FILE }}

View File

@ -1,105 +0,0 @@
name: Foss Build
on:
push:
branches:
- master
jobs:
fossBuild:
name: Foss Build
runs-on: ubuntu-latest
if: "contains(github.event.head_commit.message, '[RELEASE]')"
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v1
with:
path: ~/.gradle
key: native-${{ hashFiles('**/*.gradle') }}
restore-keys: |
${{ runner.os }}-gradle-
- run: git submodule update --init --recursive
- uses: actions/cache@v1
with:
path: TMessagesProj/jni/boringssl/build
key: boringssl-${{ hashFiles('TMessagesProj/jni/boringssl/.git') }}
- uses: actions/cache@v1
with:
path: TMessagesProj/jni/ffmpeg/build
key: ffmpeg-${{ hashFiles('TMessagesProj/jni/ffmpeg/.git') }}
- uses: actions/setup-java@v1
with:
java-version: 1.8
- uses: actions/setup-go@v1
with:
go-version: 1.13
- name: Build native libraries
run: |
cd TMessagesProj/libs
go env -w GOPATH=$HOME/go
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
go get -u github.com/golang/protobuf/protoc-gen-go
go get -v golang.org/x/mobile/cmd/...
go get -v go.starlark.net/starlark
go get -v github.com/refraction-networking/utls
go get -v github.com/gorilla/websocket
go get -v -insecure v2ray.com/core
go get github.com/2dust/AndroidLibV2rayLite
gomobile init
rm libv2ray.aar
env GO111MODULE=off gomobile bind -v -ldflags='-s -w' github.com/2dust/AndroidLibV2rayLite
cd ../jni
while :
do
sudo apt-get install -y ninja-build && break
sleep 5
done
export NDK=$ANDROID_HOME/ndk-bundle
export NINJA_PATH=/usr/bin/ninja
export PATH=`echo $ANDROID_HOME/cmake/*/bin`:$PATH
[ -d "ffmpeg/build" ] || ./build_ffmpeg_clang.sh
./patch_ffmpeg.sh
./patch_boringssl.sh
[ -d "boringssl/build" ] || ./build_boringssl.sh
- name: assemble
run: |
sudo bash <<EOF
export LOCAL_PROPERTIES="${{ secrets.LOCAL_PROPERTIES }}"
./gradlew ss-libev:assembleRelease &&
./gradlew ssr-libev:assembleRelease &&
rm -rf TMessagesProj/libs/*-libev-release.aar &&
find . -name "*-libev-release.aar" -exec mv {} TMessagesProj/libs \; &&
./gradlew assembleAfatFoss
EOF
ls TMessagesProj/build/outputs/apk
echo ::set-env name=APK_FILE::$(find TMessagesProj/build/outputs/apk -name "*universal*")
- uses: actions/upload-artifact@master
with:
name: NekoX-Foss
path: ${{ env.APK_FILE }}
- uses: actions/upload-artifact@master
with:
name: V2ray Library
path: "TMessagesProj/libs/libv2ray.aar"
- uses: actions/upload-artifact@master
with:
name: Boringssl Library
path: "TMessagesProj/jni/boringssl/build"
- uses: actions/upload-artifact@master
with:
name: Ffmpeg Library
path: "TMessagesProj/jni/ffmpeg/build"

View File

@ -1,75 +0,0 @@
name: Native Build
on:
push:
branches:
- master
jobs:
nativeBuild:
name: Native Build
runs-on: ubuntu-latest
if: "contains(github.event.head_commit.message, '[N]')"
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v1
with:
path: ~/.gradle
key: native-${{ hashFiles('**/*.gradle') }}
restore-keys: |
${{ runner.os }}-gradle-
- run: git submodule update --init --recursive
- uses: actions/cache@v1
with:
path: TMessagesProj/jni/boringssl/build
key: boringssl-${{ hashFiles('TMessagesProj/jni/boringssl/.git') }}
- uses: actions/cache@v1
with:
path: TMessagesProj/jni/ffmpeg/build
key: ffmpeg-${{ hashFiles('TMessagesProj/jni/ffmpeg/.git') }}
- uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Build native libraries
run: |
cd TMessagesProj/jni
while :
do
sudo apt-get install -y ninja-build && break
sleep 5
done
export NDK=$ANDROID_HOME/ndk-bundle
export NINJA_PATH=/usr/bin/ninja
export PATH=`echo $ANDROID_HOME/cmake/*/bin`:$PATH
[ -d "ffmpeg/build" ] || ./build_ffmpeg_clang.sh
./patch_ffmpeg.sh
./patch_boringssl.sh
[ -d "boringssl/build" ] || ./build_boringssl.sh
- name: assemble
run: |
sudo bash <<EOF
export LOCAL_PROPERTIES="${{ secrets.LOCAL_PROPERTIES }}" &&
./gradlew assembleAfatFoss
EOF
ls TMessagesProj/build/outputs/apk
echo ::set-env name=APK_FILE::$(find TMessagesProj/build/outputs/apk -name "*universal*")
- uses: actions/upload-artifact@master
with:
name: NekoX-Foss
path: ${{ env.APK_FILE }}
- uses: actions/upload-artifact@master
with:
name: Boringssl Library
path: "TMessagesProj/jni/boringssl/build"
- uses: actions/upload-artifact@master
with:
name: Ffmpeg Library
path: "TMessagesProj/jni/ffmpeg/build"

View File

@ -28,7 +28,7 @@ jobs:
- name: assembleRelease
run: |
export LOCAL_PROPERTIES="${{ secrets.LOCAL_PROPERTIES }}"
./gradlew TMessagesPro:assembleRelease \
./gradlew TMessagesProj:assembleRelease \
TMessagesProj:assembleReleaseNoGcm \
TMessagesProj:bundleAfatRelease
- name: Upload apks
@ -41,6 +41,24 @@ jobs:
ref=${ref/"refs/tags/"/}
msg="${{ github.event.head_commit.message }}"
ghr -delete -n "$ref" -b "$msg" "$ref" apks/
rm -rf $HOME/.ssh
mkdir -p $HOME/.ssh
echo "${{ secrets.SSH_KEY }}" > $HOME/.ssh/id_rsa
chmod 600 $HOME/.ssh/id_rsa
mv build/update.json apks
cd apks
rm *universal*
xz *.apk
export GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
git init
git config --global user.name "世界"
git config --global user.email "i@nekox.me"
git remote add github "git@github.com:NekoX-Dev/Resources.git"
git remote add gitlab "git@gitlab.com:NekoX/Resources.git"
git add . --all
git commit -m 喵
git push github master -f
git push gitlab master -f
- uses: r0adkll/upload-google-play@master
with:
serviceAccountJsonPlainText: ${{ secrets.GOOGLE_ACCOUNT_SERVICE }}

View File

@ -1,3 +1,7 @@
import cn.hutool.core.io.FileUtil
import cn.hutool.json.JSONObject
import cn.hutool.system.SystemUtil
import java.security.MessageDigest
apply plugin: 'com.android.application'
@ -9,6 +13,22 @@ configurations {
compile.exclude module: 'support-v4'
}
buildscript {
repositories {
jcenter()
mavenCentral()
google()
}
dependencies {
classpath 'cn.hutool:hutool-all:5.3.2'
}
}
dependencies {
implementation 'androidx.core:core:1.3.0-rc01'
@ -19,7 +39,7 @@ dependencies {
implementation 'androidx.dynamicanimation:dynamicanimation:1.0.0'
compileOnly 'com.android.support:multidex:1.0.3'
compileOnly 'org.checkerframework:checker-qual:3.3.0'
compileOnly 'org.checkerframework:checker-qual:3.4.0'
compileOnly 'org.checkerframework:checker-compat-qual:2.5.5'
implementation 'com.googlecode.mp4parser:isoparser:1.1.22'
implementation 'com.google.code.gson:gson:2.8.6'
@ -36,24 +56,46 @@ dependencies {
implementation 'org.dizitart:nitrite:3.4.1'
implementation 'net.lingala.zip4j:zip4j:2.5.2'
implementation 'cn.hutool:hutool-core:5.3.2'
implementation 'cn.hutool:hutool-crypto:5.3.2'
implementation 'cn.hutool:hutool-all:5.3.2'
implementation 'org.tukaani:xz:1.8'
implementation files('libs/libv2ray.aar')
implementation files('libs/ss-libev-release.aar')
implementation files('libs/ssr-libev-release.aar')
implementation 'androidx.dynamicanimation:dynamicanimation:1.0.0'
implementation 'com.android.support:multidex:1.0.3'
compileOnly 'com.google.firebase:firebase-messaging:20.1.6'
compileOnly 'com.google.firebase:firebase-crashlytics:17.0.0'
def fcmVersion = '20.1.4'
releaseImplementation 'com.google.firebase:firebase-messaging:20.1.6'
//noinspection GradleDependency
compileOnly "com.google.firebase:firebase-messaging:$fcmVersion"
compileOnly "com.google.firebase:firebase-crashlytics:17.0.0"
compileOnly 'com.google.android.play:core:1.7.2'
//noinspection GradleDependency
releaseImplementation "com.google.firebase:firebase-messaging:$fcmVersion"
releaseImplementation 'com.google.firebase:firebase-crashlytics:17.0.0'
releaseImplementation 'com.google.android.play:core:1.7.2'
}
def verName = "6.1.1.0-rc03"
def verCode = 29
task writeUpdateInfo {
def info = new JSONObject()
info.set("version", verName)
info.set("versionCode", verCode)
info.set("defaultApkName","NekoX-afat-armeabi-v7a-release.apk.xz")
FileUtil.writeUtf8String(info.toStringPretty(), new File("build/update.json"))
}
tasks.findByName("preBuild").finalizedBy(writeUpdateInfo)
android {
compileSdkVersion 29
buildToolsVersion '29.0.3'
@ -65,8 +107,8 @@ android {
minSdkVersion 16
targetSdkVersion 28
versionName "6.1.1.0-rc03"
versionCode = 29
versionName verName
versionCode verCode
vectorDrawables.generatedDensities = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi']
@ -246,7 +288,7 @@ android {
def tgVoipDexClassesPath = "org/telegram/messenger/voip"
def dxUtilPath = "${sdkDirectory.path}/build-tools/${buildToolsVersion}/dx"
if (System.getProperty("os.name").toLowerCase().contains("windows")) {
if (SystemUtil.getOsInfo().isWindows()) {
dxUtilPath += ".bat"
}

View File

@ -126,6 +126,7 @@ import tw.nekomimi.nekogram.NekoXSettingActivity;
import tw.nekomimi.nekogram.utils.AlertUtil;
import tw.nekomimi.nekogram.utils.PrivacyUtil;
import tw.nekomimi.nekogram.utils.ProxyUtil;
import tw.nekomimi.nekogram.utils.UpdateUtil;
public class LaunchActivity extends Activity implements ActionBarLayout.ActionBarLayoutDelegate, NotificationCenter.NotificationCenterDelegate, DialogsActivity.DialogsActivityDelegate {
@ -799,6 +800,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa
FileLog.e(e);
}
MediaController.getInstance().setBaseActivity(this, true);
UpdateUtil.checkUpdate(this);
}
private void checkSystemBarColors() {

View File

@ -55,6 +55,7 @@ import androidx.core.content.FileProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.checkerframework.common.subtyping.qual.Bottom;
import org.telegram.PhoneFormat.PhoneFormat;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.ApplicationLoader;
@ -124,9 +125,14 @@ import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import kotlin.Unit;
import tw.nekomimi.nekogram.BottomBuilder;
import tw.nekomimi.nekogram.NekoConfig;
import tw.nekomimi.nekogram.NekoSettingsActivity;
import tw.nekomimi.nekogram.NekoXConfig;
import tw.nekomimi.nekogram.NekoXSettingActivity;
import tw.nekomimi.nekogram.parts.UpdateChecksKt;
import tw.nekomimi.nekogram.utils.AlertUtil;
public class SettingsActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate, ImageUpdater.ImageUpdaterDelegate {
@ -501,10 +507,41 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter
} else if (position == numberRow) {
presentFragment(new ActionIntroActivity(ActionIntroActivity.ACTION_TYPE_CHANGE_PHONE_NUMBER));
} else if (position == versionRow) {
TextInfoPrivacyCell cell = (TextInfoPrivacyCell) view;
pressCount++;
if (pressCount == 8) {
NekoXConfig.developerModeEntrance = true;
Toast.makeText(getParentActivity(), "¯\\_(ツ)_/¯", Toast.LENGTH_SHORT).show();
} else {
BottomBuilder builder = new BottomBuilder(getParentActivity());
builder.addTitle(cell.getTextView().getText().toString(), false);
builder.addItem(LocaleController.getString("CopyDetails", R.string.CopyDetails), R.drawable.baseline_content_copy_24, (it) -> {
builder.dismiss();
AndroidUtilities.addToClipboard(cell.getTextView().getText().toString());
AlertUtil.showToast(LocaleController.getString("TextCopied", R.string.TextCopied));
return Unit.INSTANCE;
});
builder.addItem(LocaleController.getString("CheckUpdate", R.string.CheckUpdate), R.drawable.baseline_system_update_24, (it) -> {
builder.dismiss();
UpdateChecksKt.checkUpdate(SettingsActivity.this);
return Unit.INSTANCE;
});
if (NekoXConfig.developerModeEntrance) {
builder.addItem(LocaleController.getString("DeveloperSettings", R.string.DeveloperSettings),R.drawable.baseline_developer_mode_24,(it) -> {
builder.dismiss();
BottomBuilder devBuilder = new BottomBuilder(getParentActivity());
devBuilder.addTitle("**Your telegram account may be banned**","We are not responsible for any improper use of developer features.");
devBuilder.addItem(LocaleController.getString("Continue",R.string.Continue),R.drawable.baseline_warning_24,true,(__) -> {
devBuilder.dismiss();
presentFragment(new NekoXSettingActivity());
return Unit.INSTANCE;
});
devBuilder.addCancelItem();
devBuilder.show();
return Unit.INSTANCE;
});
}
builder.show();
}
}
}
@ -532,8 +569,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter
LocaleController.getString("DebugMenuClearMediaCache", R.string.DebugMenuClearMediaCache),
LocaleController.getString("DebugMenuCallSettings", R.string.DebugMenuCallSettings),
LocaleController.getString("DebugMenuReadAllDialogs", R.string.DebugMenuReadAllDialogs),
SharedConfig.pauseMusicOnRecord ? LocaleController.getString("DebugMenuDisablePauseMusic", R.string.DebugMenuDisablePauseMusic) : LocaleController.getString("DebugMenuEnablePauseMusic", R.string.DebugMenuEnablePauseMusic),
NekoXConfig.developerModeEntrance ? (NekoXConfig.developerMode ? LocaleController.getString("DisableDeveloperMode", R.string.DisableDeveloperMode) : LocaleController.getString("EnableDeveloperMode", R.string.EnableDeveloperMode)) : null
SharedConfig.pauseMusicOnRecord ? LocaleController.getString("DebugMenuDisablePauseMusic", R.string.DebugMenuDisablePauseMusic) : LocaleController.getString("DebugMenuEnablePauseMusic", R.string.DebugMenuEnablePauseMusic)
};
builder.setItems(items, (dialog, which) -> {
if (which == 0) {
@ -567,8 +603,6 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter
MessagesStorage.getInstance(currentAccount).readAllDialogs(-1);
} else if (which == 9) {
SharedConfig.togglePauseMusicOnRecord();
} else if (which == 10) {
NekoXConfig.toggleDeveloperMode();
}
});
builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null);
@ -595,9 +629,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter
};
searchListView.setVerticalScrollBarEnabled(false);
searchListView.setLayoutManager(new
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false));
searchListView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false));
searchListView.setGlowColor(Theme.getColor(Theme.key_avatar_backgroundActionBarBlue));
frameLayout.addView(searchListView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT));
searchListView.setAdapter(searchAdapter);
@ -652,9 +684,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter
});
searchListView.setVisibility(View.GONE);
emptyView = new
EmptyTextProgressView(context);
emptyView = new EmptyTextProgressView(context);
emptyView.showTextView();
emptyView.setTextSize(18);
emptyView.setVisibility(View.GONE);
@ -662,9 +692,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter
emptyView.setPadding(0, AndroidUtilities.dp(50), 0, 0);
frameLayout.addView(emptyView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
topView = new
TopView(context);
topView = new TopView(context);
topView.setBackgroundColor(Theme.getColor(Theme.key_avatar_backgroundActionBarBlue));
frameLayout.addView(topView);

View File

@ -82,7 +82,7 @@ class BottomBuilder(val ctx: Context) {
val headerCell = if (bigTitle) HeaderCell(ctx, Theme.key_dialogTextBlue2, 21, 15, subTitle != null) else HeaderCell(ctx)
headerCell.setText(title)
headerCell.setText(if (title is String) AndroidUtilities.replaceTags(title) else title)
subTitle?.also {
@ -202,13 +202,13 @@ class BottomBuilder(val ctx: Context) {
}
@JvmOverloads
fun addRadioItems(text: Array<String>, value: (Int,String) -> Boolean, valueText: ((Int,String) -> String)? = null, listener: (index: Int, text: String, cell: RadioButtonCell) -> Unit): List<RadioButtonCell> {
fun addRadioItems(text: Array<String>, value: (Int, String) -> Boolean, valueText: ((Int, String) -> String)? = null, listener: (index: Int, text: String, cell: RadioButtonCell) -> Unit): List<RadioButtonCell> {
val list = mutableListOf<RadioButtonCell>()
text.forEachIndexed { index, textI ->
list.add(addRadioItem(textI, value(index,textI), valueText?.invoke(index,textI)) { cell ->
list.add(addRadioItem(textI, value(index, textI), valueText?.invoke(index, textI)) { cell ->
listener(index, textI, cell)
@ -273,7 +273,8 @@ class BottomBuilder(val ctx: Context) {
}
fun addItem(text: String, icon: Int = 0, red: Boolean = false, listener: (cell: TextCell) -> Unit): TextCell {
@JvmOverloads
fun addItem(text: String, icon: Int = 0, red: Boolean = false, listener: ((cell: TextCell) -> Unit)?): TextCell {
return TextCell(ctx).apply {
@ -283,7 +284,7 @@ class BottomBuilder(val ctx: Context) {
setOnClickListener {
listener(this)
if (listener == null) dismiss() else listener(this)
}
@ -325,7 +326,7 @@ class BottomBuilder(val ctx: Context) {
setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14f)
setTextColor(Theme.getColor(Theme.key_dialogTextBlack))
setHintTextColor(Theme.getColor(Theme.key_dialogTextBlue4))
hintText?.also { hint = it }
hintText?.also { hint = it }
isSingleLine = true
isFocusable = true
setBackgroundDrawable(null)

View File

@ -1,10 +1,17 @@
package tw.nekomimi.nekogram;
import android.app.Activity;
import android.content.IntentSender;
import android.text.TextUtils;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.play.core.appupdate.AppUpdateManager;
import com.google.android.play.core.appupdate.AppUpdateManagerFactory;
import com.google.android.play.core.install.InstallStateUpdatedListener;
import com.google.android.play.core.install.model.AppUpdateType;
import com.google.android.play.core.install.model.InstallStatus;
import com.google.android.play.core.install.model.UpdateAvailability;
import com.google.firebase.crashlytics.FirebaseCrashlytics;
import com.google.firebase.iid.FirebaseInstanceId;
@ -14,26 +21,27 @@ import org.telegram.messenger.BuildConfig;
import org.telegram.messenger.BuildVars;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.GcmPushListenerService;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.R;
import org.telegram.messenger.SharedConfig;
import org.telegram.messenger.Utilities;
import javax.validation.constraints.NotNull;
import kotlin.Unit;
import tw.nekomimi.nekogram.utils.UIUtil;
public class ExternalGcm {
@SuppressWarnings("ConstantConditions")
public static boolean noGcm = !"release".equals(BuildConfig.BUILD_TYPE);
private static boolean noGcm = !"release".equals(BuildConfig.BUILD_TYPE);
private static boolean hasPlayServices;
private static Boolean hasPlayServices;
public static void initPlayServices() {
if (noGcm) return;
AndroidUtilities.runOnUIThread(() -> {
if (hasPlayServices = checkPlayServices()) {
if (checkPlayServices()) {
final String currentPushString = SharedConfig.pushString;
if (!TextUtils.isEmpty(currentPushString)) {
if (BuildVars.DEBUG_PRIVATE_VERSION && BuildVars.LOGS_ENABLED) {
@ -74,34 +82,81 @@ public class ExternalGcm {
}
private static boolean checkPlayServices() {
public static boolean checkPlayServices() {
if (noGcm) return false;
if (hasPlayServices != null) return hasPlayServices;
try {
int resultCode = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(ApplicationLoader.applicationContext);
return resultCode == ConnectionResult.SUCCESS;
hasPlayServices = resultCode == ConnectionResult.SUCCESS;
} catch (Exception e) {
hasPlayServices = false;
FileLog.e(e);
}
return true;
return hasPlayServices;
}
public static void sendRegistrationToServer() {
if (noGcm) return;
if (!checkPlayServices()) return;
GcmPushListenerService.sendRegistrationToServer(SharedConfig.pushString);
}
public static void reportLog(@NotNull String report) {
if (noGcm) return;
if (!checkPlayServices()) return;
UIUtil.runOnIoDispatcher(() -> FirebaseCrashlytics.getInstance().log(report));
}
public static void recordException(@NotNull Throwable throwable) {
if (noGcm) return;
if (!checkPlayServices()) return;
UIUtil.runOnIoDispatcher(() -> FirebaseCrashlytics.getInstance().recordException(throwable));
}
public static void checkUpdate(Activity ctx) {
if (!checkPlayServices()) return;
AppUpdateManager manager = AppUpdateManagerFactory.create(ctx);
InstallStateUpdatedListener listener = (installState) -> {
if (installState.installStatus() == InstallStatus.DOWNLOADED) {
BottomBuilder builder = new BottomBuilder(ctx);
builder.addTitle(LocaleController.getString("UpdateDownloaded", R.string.UpdateDownloaded), false);
builder.addItem(LocaleController.getString("Update", R.string.Update), R.drawable.baseline_system_update_24, false, (it) -> {
manager.completeUpdate();
return Unit.INSTANCE;
});
builder.addItem(LocaleController.getString("Later", R.string.Later), R.drawable.baseline_watch_later_24, false, null);
builder.show();
}
};
manager.registerListener(listener);
manager.getAppUpdateInfo().addOnSuccessListener((appUpdateInfo) -> {
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) {
if (appUpdateInfo.availableVersionCode() <= BuildConfig.VERSION_CODE) return;
try {
manager.startUpdateFlowForResult(appUpdateInfo, AppUpdateType.FLEXIBLE, ctx, 114514);
} catch (IntentSender.SendIntentException ignored) {
}
}
});
}

View File

@ -11,7 +11,7 @@ public class NekoXConfig {
public static String FAQ_URL = "https://telegra.ph/NekoX-FAQ-03-31";
private static SharedPreferences preferences = NitritesKt.openMainSharedPreference("nekox_config");
public static SharedPreferences preferences = NitritesKt.openMainSharedPreference("nekox_config");
public static boolean developerModeEntrance;
public static boolean developerMode = preferences.getBoolean("developer_mode", false);
@ -23,6 +23,15 @@ public class NekoXConfig {
preferences.edit().putBoolean("developer_mode", developerMode = !developerMode).apply();
if (!developerMode) {
preferences.edit()
.putBoolean("disable_flag_secure", disableFlagSecure = false)
.putBoolean("disable_screenshot_detection", disableScreenshotDetection = false)
.apply();
}
}
public static void toggleDisableFlagSecure() {
@ -98,10 +107,10 @@ public class NekoXConfig {
preferences.edit()
.putString("custom_dc_v4", customDcIpv4)
.putString("custom_dc_v6", customDcIpv6)
.putInt("custom_dc_port",customDcPort)
.putInt("custom_dc_layer",customDcLayer)
.putString("custom_dc_public_key",customDcPublicKey)
.putLong("custom_dc_fingerprint",customDcFingerprint)
.putInt("custom_dc_port", customDcPort)
.putInt("custom_dc_layer", customDcLayer)
.putString("custom_dc_public_key", customDcPublicKey)
.putLong("custom_dc_fingerprint", customDcFingerprint)
.apply();
}

View File

@ -1,6 +1,5 @@
package tw.nekomimi.nekogram;
import android.annotation.SuppressLint;
import android.content.Context;
import android.view.Gravity;
import android.view.View;
@ -25,7 +24,6 @@ import org.telegram.ui.Cells.NotificationsCheckCell;
import org.telegram.ui.Cells.ShadowSectionCell;
import org.telegram.ui.Cells.TextCheckCell;
import org.telegram.ui.Cells.TextDetailSettingsCell;
import org.telegram.ui.Cells.TextInfoPrivacyCell;
import org.telegram.ui.Cells.TextSettingsCell;
import org.telegram.ui.Components.LayoutHelper;
import org.telegram.ui.Components.RecyclerListView;
@ -49,8 +47,8 @@ public class NekoXSettingActivity extends BaseFragment {
private int developerSettingsRow;
private int enableRow;
private int fetchAndExportLangRow;
private int disableFlagSecureRow;
private int disableScreenshotDetectionRow;
@ -61,7 +59,6 @@ public class NekoXSettingActivity extends BaseFragment {
return true;
}
@SuppressLint("NewApi")
@Override
public View createView(Context context) {
actionBar.setBackButtonImage(R.drawable.ic_ab_back);
@ -101,12 +98,13 @@ public class NekoXSettingActivity extends BaseFragment {
listView.setOnItemClickListener((view, position, x, y) -> {
if (position == fetchAndExportLangRow) {
fetchAndExportLang();
}
if (position == disableFlagSecureRow) {
if (position == enableRow) {
NekoXConfig.toggleDeveloperMode();
updateRows();
} else if (position == disableFlagSecureRow) {
NekoXConfig.toggleDisableFlagSecure();
if (view instanceof TextCheckCell) {
((TextCheckCell) view).setChecked(NekoXConfig.disableFlagSecure);
@ -136,6 +134,7 @@ public class NekoXSettingActivity extends BaseFragment {
developerSettingsRow = rowCount++;
enableRow = rowCount++;
fetchAndExportLangRow = rowCount++;
disableFlagSecureRow = rowCount++;
@ -258,16 +257,26 @@ public class NekoXSettingActivity extends BaseFragment {
case 3: {
TextCheckCell textCell = (TextCheckCell) holder.itemView;
textCell.setEnabled(true, null);
if (position == disableFlagSecureRow) {
textCell.setTextAndCheck("Disable Flag Secure", NekoXConfig.disableFlagSecure, true);
} else if (position == disableScreenshotDetectionRow) {
textCell.setTextAndCheck("Disable Screenshot Detection", NekoXConfig.disableScreenshotDetection, false);
if (position == enableRow) {
textCell.setTextAndCheck("Enable", NekoXConfig.developerMode, true);
} else {
if (!NekoXConfig.developerMode) {
textCell.setEnabled(false);
}
if (position == disableFlagSecureRow) {
textCell.setTextAndCheck("Disable Flag Secure", NekoXConfig.disableFlagSecure, true);
} else if (position == disableScreenshotDetectionRow) {
textCell.setTextAndCheck("Disable Screenshot Detection", NekoXConfig.disableScreenshotDetection, false);
}
}
break;
}
case 2: {
TextSettingsCell textCell = (TextSettingsCell) holder.itemView;
if (!NekoXConfig.developerMode) {
textCell.setEnabled(false);
}
if (position == fetchAndExportLangRow) {
textCell.setText("Export Builtin Languages", true);
}
@ -279,10 +288,8 @@ public class NekoXSettingActivity extends BaseFragment {
@Override
public boolean isEnabled(RecyclerView.ViewHolder holder) {
int position = holder.getAdapterPosition();
return position == fetchAndExportLangRow ||
position == disableFlagSecureRow ||
position == disableScreenshotDetectionRow;
int type = holder.getItemViewType();
return type == 2 || type == 3;
}
@Override
@ -312,10 +319,6 @@ public class NekoXSettingActivity extends BaseFragment {
view = new TextDetailSettingsCell(mContext);
view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite));
break;
case 7:
view = new TextInfoPrivacyCell(mContext);
view.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow));
break;
}
view.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT));
return new RecyclerListView.Holder(view);

View File

@ -23,27 +23,10 @@ import kotlin.random.Random
class VmessLoader {
private val point: V2RayPoint = Libv2ray.newV2RayPoint(EmptyCallback(), true)
private val point = Libv2ray.newV2RayPoint(EmptyCallback(), true)
companion object {
@JvmStatic
val public = VmessBean().apply {
address = "nekox.me"
port = 443
configType = V2RayConfig.EConfigType.Vmess
id = "73670f86-6046-4ffd-b468-6cd73cea1f29"
security = "none"
network = "ws"
streamSecurity = "tls"
requestHost = "nekox.me"
path = "/internet"
remarks = LocaleController.getString("NekoXProxy", R.string.NekoXProxy)
}
fun parseVmess1Link(server: String): VmessBean {
val lnk = ("https://" + server.substringAfter(VMESS1_PROTOCOL)).toHttpUrl()

View File

@ -0,0 +1,158 @@
package tw.nekomimi.nekogram.parts
import android.content.IntentSender.SendIntentException
import com.google.android.play.core.appupdate.AppUpdateManagerFactory
import com.google.android.play.core.install.InstallStateUpdatedListener
import com.google.android.play.core.install.model.AppUpdateType
import com.google.android.play.core.install.model.InstallStatus
import com.google.android.play.core.install.model.UpdateAvailability
import org.json.JSONObject
import org.telegram.messenger.BuildConfig
import org.telegram.messenger.LocaleController
import org.telegram.messenger.R
import org.telegram.ui.SettingsActivity
import tw.nekomimi.nekogram.BottomBuilder
import tw.nekomimi.nekogram.ExternalGcm
import tw.nekomimi.nekogram.NekoXConfig
import tw.nekomimi.nekogram.utils.*
import java.util.*
fun SettingsActivity.checkUpdate() {
val ctx = parentActivity
val progress = AlertUtil.showProgress(ctx)
progress.show()
UIUtil.runOnIoDispatcher {
if (ExternalGcm.checkPlayServices()) {
progress.uUpdate(LocaleController.getString("Checking", R.string.Checking) + " (Play Store)")
val manager = AppUpdateManagerFactory.create(ctx)
manager.registerListener(InstallStateUpdatedListener {
if (it.installStatus() == InstallStatus.DOWNLOADED) {
val builder = BottomBuilder(ctx)
builder.addTitle(LocaleController.getString("UpdateDownloaded", R.string.UpdateDownloaded), false)
builder.addItem(LocaleController.getString("Update", R.string.Update), R.drawable.baseline_system_update_24, false) {
manager.completeUpdate()
}
builder.addItem(LocaleController.getString("Later", R.string.Later), R.drawable.baseline_watch_later_24, false, null)
builder.show()
}
})
manager.appUpdateInfo.addOnSuccessListener {
progress.dismiss()
if (it.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && it.availableVersionCode() > BuildConfig.VERSION_CODE) {
try {
manager.startUpdateFlowForResult(it, AppUpdateType.FLEXIBLE, ctx, 114514)
} catch (ignored: SendIntentException) { }
} else {
AlertUtil.showToast(LocaleController.getString("NoUpdate", R.string.NoUpdate))
}
}.addOnFailureListener {
progress.uDismiss()
AlertUtil.showToast(it.message ?: it.javaClass.simpleName)
}
return@runOnIoDispatcher
}
progress.uUpdate(LocaleController.getString("Checking", R.string.Checking) + " (Repo)")
val ex = LinkedList<Throwable>()
UpdateUtil.updateUrls.forEach { url ->
runCatching {
val updateInfo = JSONObject(HttpUtil.get("$url/update.json"))
val code = updateInfo.getInt("versionCode")
progress.uDismiss()
if (code > BuildConfig.VERSION_CODE) UIUtil.runOnUIThread {
val builder = BottomBuilder(ctx)
builder.addTitle(LocaleController.getString("UpdateAvailable", R.string.UpdateAvailable), updateInfo.getString("version"))
builder.addItem(LocaleController.getString("Update", R.string.Update), R.drawable.baseline_system_update_24, false) {
UpdateUtil.doUpdate(ctx, code, updateInfo.getString("defaultApkName"))
builder.dismiss()
NekoXConfig.preferences.edit().remove("ignored_update_at").remove("ignore_update_at").apply()
}
builder.addItem(LocaleController.getString("Later", R.string.Later), R.drawable.baseline_watch_later_24, false) {
builder.dismiss()
NekoXConfig.preferences.edit().putLong("ignored_update_at", System.currentTimeMillis()).apply()
}
builder.addItem(LocaleController.getString("Ignore", R.string.Ignore), R.drawable.baseline_block_24, true) {
builder.dismiss()
NekoXConfig.preferences.edit().putInt("ignore_update", code).apply()
}
builder.show()
} else {
AlertUtil.showToast(LocaleController.getString("NoUpdate", R.string.NoUpdate))
}
return@runOnIoDispatcher
}.onFailure {
ex.add(it)
}
}
progress.uDismiss()
AlertUtil.showToast(ex.map { it.message ?: it.javaClass.simpleName }.joinToString("\n"))
}
}

View File

@ -29,11 +29,19 @@ object AlertUtil {
@JvmStatic
@JvmOverloads
fun showSimpleAlert(ctx: Context?, text: String, listener: ((AlertDialog.Builder) -> Unit)? = null) = UIUtil.runOnUIThread(Runnable {
fun showSimpleAlert(ctx: Context, text: String, listener: ((AlertDialog.Builder) -> Unit)? = null) {
val builder = AlertDialog.Builder(ctx ?: ApplicationLoader.applicationContext)
builder.setTitle(LocaleController.getString("NekoX", R.string.NekoX))
showSimpleAlert(ctx,null, text, listener)
}
@JvmStatic
fun showSimpleAlert(ctx: Context,title: String?, text: String, listener: ((AlertDialog.Builder) -> Unit)? = null) = UIUtil.runOnUIThread(Runnable {
val builder = AlertDialog.Builder(ctx)
builder.setTitle(title ?: LocaleController.getString("NekoX", R.string.NekoX))
builder.setMessage(text)
builder.setPositiveButton(LocaleController.getString("OK", R.string.OK)) { _, _ ->

View File

@ -1,8 +1,10 @@
package tw.nekomimi.nekogram.utils
import android.os.Build
import org.telegram.messenger.ApplicationLoader
import org.telegram.messenger.BuildVars
import org.telegram.messenger.FileLog
import java.io.File
import java.io.FileInputStream
object FileUtil {
@ -86,6 +88,30 @@ object FileUtil {
}
@Suppress("DEPRECATION")
private fun getAbi() = try {
if (Build.CPU_ABI.equals("x86_64", ignoreCase = true)) {
"x86_64"
} else if (Build.CPU_ABI.equals("arm64-v8a", ignoreCase = true)) {
"arm64-v8a"
} else if (Build.CPU_ABI.equals("armeabi-v7a", ignoreCase = true)) {
"armeabi-v7a"
} else if (Build.CPU_ABI.equals("armeabi", ignoreCase = true)) {
"armeabi"
} else if (Build.CPU_ABI.equals("x86", ignoreCase = true)) {
"x86"
} else {
if (BuildVars.LOGS_ENABLED) {
FileLog.e("Unsupported arch: " + Build.CPU_ABI)
}
"armeabi"
}
} catch (e: Exception) {
FileLog.e(e)
"armeabi"
}
@JvmStatic
fun extLib(name: String): File {
@ -93,21 +119,18 @@ object FileUtil {
if (!execFile.isFile) {
execFile = File(ApplicationLoader.getDataDirFixed(), "cache/lib/lib$name.so")
System.loadLibrary(name)
if (!execFile.isFile) {
val abi = when (execFile.parentFile!!.name) {
execFile = File(ApplicationLoader.getDataDirFixed(), "cache/lib/${execFile.name}")
"arm64", "aarch64" -> "arm64-v8a"
"x86", "i386", "i486", "i586", "i686" -> "x86"
"x86_64", "amd64" -> "x86_64"
else -> "armeabi-v7a"
if (!execFile.isFile) {
saveNonAsset("lib/${getAbi()}/${execFile.name}", execFile);
}
saveNonAsset("lib/$abi/${execFile.name}", execFile);
}
}
@ -127,11 +150,11 @@ object FileUtil {
saveTo.parentFile?.also { initDir(it) }
val assets = ApplicationLoader.applicationContext.assets
saveTo.createNewFile()
saveTo.outputStream().use {
(ApplicationLoader::class.java.getResourceAsStream(path) ?: error("not found")).use {
IoUtil.copy(FileInputStream(assets.openNonAssetFd(path).fileDescriptor), it)
IoUtil.copy(it, saveTo)
}

View File

@ -3,6 +3,7 @@ package tw.nekomimi.nekogram.utils
import cn.hutool.core.collection.CollUtil
import cn.hutool.core.util.ArrayUtil
import cn.hutool.core.util.StrUtil
import org.telegram.ui.ActionBar.AlertDialog
import java.math.BigInteger
import java.util.*
import java.util.concurrent.atomic.AtomicBoolean
@ -142,4 +143,7 @@ operator fun AtomicLong.getValue(thisRef: Any?, property: KProperty<*>): Long =
operator fun AtomicLong.setValue(thisRef: Any?, property: KProperty<*>, value: Long) = set(value)
operator fun <T> AtomicReference<T>.getValue(thisRef: Any?, property: KProperty<*>): T = get()
operator fun <T> AtomicReference<T>.setValue(thisRef: Any?, property: KProperty<*>, value: T) = set(value)
operator fun <T> AtomicReference<T>.setValue(thisRef: Any?, property: KProperty<*>, value: T) = set(value)
fun AlertDialog.uUpdate(message: String) = UIUtil.runOnUIThread { setMessage(message) }
fun AlertDialog.uDismiss() = UIUtil.runOnUIThread { dismiss() }

View File

@ -462,7 +462,7 @@ object ProxyUtil {
} catch (ex: NoSuchMethodError) {
AlertUtil.showSimpleAlert(ctx, "很抱歉, 這是一個已知的問題, 但您現在無法掃碼, 因爲您正在使用糟糕的Android系統, 直到 Google Zxing 為您的設備做出優化.")
AlertUtil.showSimpleAlert(ctx,"很抱歉, 這是一個已知的問題, 但您現在無法掃碼, 因爲您正在使用糟糕的Android系統, 直到 Google Zxing 為您的設備做出優化.")
}

View File

@ -4,9 +4,9 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.webkit.MimeTypeMap
import androidx.core.content.FileProvider
import org.telegram.messenger.BuildConfig
import org.telegram.messenger.ContactsController
import org.telegram.ui.LaunchActivity
import java.io.File
@ -14,7 +14,7 @@ object ShareUtil {
@JvmStatic
@JvmOverloads
fun shareText(ctx: Context,text: String,choose: Boolean = false) {
fun shareText(ctx: Context, text: String, choose: Boolean = false) {
val intent = Intent(Intent.ACTION_SEND).apply {
type = "text/plain"
@ -23,7 +23,7 @@ object ShareUtil {
if (!choose) {
intent.setClass(ctx,LaunchActivity::class.java)
intent.setClass(ctx, LaunchActivity::class.java)
ctx.startActivity(intent)
} else {
@ -64,4 +64,42 @@ object ShareUtil {
}
@JvmOverloads
@JvmStatic
fun openFile(ctx: Context, fileToOpen: File) {
val uri = if (Build.VERSION.SDK_INT >= 24) {
FileProvider.getUriForFile(ctx, BuildConfig.APPLICATION_ID + ".provider", fileToOpen)
} else {
Uri.fromFile(fileToOpen)
}
val intent = Intent(Intent.ACTION_VIEW)
if (Build.VERSION.SDK_INT >= 24) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
if (fileToOpen.extension.isBlank()) {
intent.type = "application/octet-stream"
} else {
intent.type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileToOpen.extension)
}
intent.data = uri
ctx.startActivity(intent)
}
}

View File

@ -10,6 +10,8 @@ object UIUtil {
@JvmStatic
fun runOnUIThread(runnable: Runnable) = ApplicationLoader.applicationHandler.post(runnable)
fun runOnUIThread(runnable: () -> Unit) = ApplicationLoader.applicationHandler.post(runnable)
@JvmStatic
fun runOnIoDispatcher(runnable: Runnable) {
@ -21,4 +23,14 @@ object UIUtil {
}
fun runOnIoDispatcher(runnable: () -> Unit) {
GlobalScope.launch(Dispatchers.IO) {
runnable()
}
}
}

View File

@ -0,0 +1,256 @@
package tw.nekomimi.nekogram.utils
import android.app.Activity
import android.os.Build
import cn.hutool.core.io.IoUtil
import cn.hutool.core.io.StreamProgress
import okhttp3.Request
import okhttp3.Response
import okhttp3.internal.closeQuietly
import org.json.JSONObject
import org.telegram.messenger.BuildConfig
import org.telegram.messenger.LocaleController
import org.telegram.messenger.R
import org.tukaani.xz.XZInputStream
import tw.nekomimi.nekogram.BottomBuilder
import tw.nekomimi.nekogram.ExternalGcm
import tw.nekomimi.nekogram.NekoXConfig
import java.io.File
import java.util.zip.ZipInputStream
object UpdateUtil {
val updateUrls = arrayOf(
"https://gitee.com/nekoshizuku/nekox-resources/raw/master",
"https://gitlab.com/NekoX/Resources/-/raw/master",
"https://raw.githubusercontent.com/NekoX-Dev/Resources/master"
)
@JvmStatic
fun checkUpdate(ctx: Activity) = UIUtil.runOnIoDispatcher {
if (ExternalGcm.checkPlayServices()) {
ExternalGcm.checkUpdate(ctx)
return@runOnIoDispatcher
}
if (System.currentTimeMillis() - NekoXConfig.preferences.getLong("ignored_update_at", -1) > 1 * 60 * 60 * 1000L) {
// ignored
return@runOnIoDispatcher
}
updateUrls.forEach { url ->
runCatching {
val updateInfo = JSONObject(HttpUtil.get("$url/update.json"))
val code = updateInfo.getInt("versionCode")
if (code > BuildConfig.VERSION_CODE.coerceAtLeast(NekoXConfig.preferences.getInt("ignore_update", -1))) UIUtil.runOnUIThread {
val builder = BottomBuilder(ctx)
builder.addTitle(LocaleController.getString("UpdateAvailable", R.string.UpdateAvailable), updateInfo.getString("version"))
builder.addItem(LocaleController.getString("Update", R.string.Update), R.drawable.baseline_system_update_24, false) {
doUpdate(ctx, code, updateInfo.getString("defaultApkName"))
builder.dismiss()
NekoXConfig.preferences.edit().remove("ignored_update_at").remove("ignore_update_at").apply()
}
builder.addItem(LocaleController.getString("Later", R.string.Later), R.drawable.baseline_watch_later_24, false) {
builder.dismiss()
NekoXConfig.preferences.edit().putLong("ignored_update_at", System.currentTimeMillis()).apply()
}
builder.addItem(LocaleController.getString("Ignore", R.string.Ignore), R.drawable.baseline_block_24, true) {
builder.dismiss()
NekoXConfig.preferences.edit().putInt("ignore_update", code).apply()
}
builder.show()
}
return@runOnIoDispatcher
}
}
}
fun doUpdate(ctx: Activity, targetVer: Int, defFileName: String) {
val pro = AlertUtil.showProgress(ctx)
pro.setCanCacnel(false)
pro.show()
fun update(message: String) = UIUtil.runOnUIThread { pro.setMessage(message) }
fun dismiss() = UIUtil.runOnUIThread { pro.dismiss() }
var exception: Exception? = null
UIUtil.runOnIoDispatcher {
val fileName = "NekoX-${BuildConfig.FLAVOR}-${Build.CPU_ABI}-${BuildConfig.BUILD_TYPE}.apk.xz"
var response: Response? = null
runCatching {
for (url in updateUrls) {
try {
response = HttpUtil.okHttpClient.newCall(Request.Builder().url("$url/$fileName").build()).execute()
if (response!!.code != 200) error("HTTP ${response!!.code} :${response!!.body!!.string()}")
break
} catch (e: Exception) {
exception = e
}
}
if (response == null) {
for (url in updateUrls) {
try {
response = HttpUtil.okHttpClient.newCall(Request.Builder().url("$url/$defFileName").build()).execute()
if (response!!.code != 200) error("HTTP ${response!!.code} :${response!!.body!!.string()}")
break
} catch (e: Exception) {
exception = e
}
}
}
}.onFailure {
dismiss()
AlertUtil.showSimpleAlert(ctx, LocaleController.getString("DownloadFailed", R.string.DownloadFailed), it.toString())
return@runOnIoDispatcher
}
if (response == null) {
dismiss()
AlertUtil.showSimpleAlert(ctx, LocaleController.getString("DownloadFailed", R.string.DownloadFailed), exception!!.toString())
return@runOnIoDispatcher
}
val body = response!!.body!!
val size = body.contentLength()
val target = File(ctx.externalCacheDir, "nekox-$targetVer.apk")
update("Downloading...")
if (target.isFile) {
runCatching {
ZipInputStream(target.inputStream()).use { it.nextEntry }
dismiss()
ShareUtil.openFile(ctx, target)
return@runOnIoDispatcher
}.onFailure {
target.delete()
}
}
runCatching {
val input = XZInputStream(body.byteStream())
FileUtil.initFile(target)
target.outputStream().use {
IoUtil.copy(input, it, IoUtil.DEFAULT_BUFFER_SIZE, object : StreamProgress {
override fun progress(progressSize: Long) {
update("$progressSize / $size ( ${((progressSize / size) * 100).toInt()}% )")
}
override fun start() {
update("0 / $size")
}
override fun finish() {
update("Finish")
}
})
}
input.closeQuietly()
dismiss()
ShareUtil.openFile(ctx, target)
}.onFailure {
dismiss()
AlertUtil.showSimpleAlert(ctx, LocaleController.getString("DownloadFailed", R.string.DownloadFailed), it.toString())
}
}
}
}

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#ff000000"
android:pathData="M7,5h10v2h2L19,3c0,-1.1 -0.9,-1.99 -2,-1.99L7,1c-1.1,0 -2,0.9 -2,2v4h2L7,5zM15.41,16.59L20,12l-4.59,-4.59L14,8.83 17.17,12 14,15.17l1.41,1.42zM10,15.17L6.83,12 10,8.83 8.59,7.41 4,12l4.59,4.59L10,15.17zM17,19L7,19v-2L5,17v4c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2v-4h-2v2z" />
</vector>

View File

@ -0,0 +1,143 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="CustomApi">API Kustom</string>
<string name="UseCustomApiNotice">Gunakan api kustom untuk masuk, ini mungkin membantu jika anda tidak dapat mendaftar atau masuk.\n\nCatatan: fcm tidak akan bekerja jika anda menggunakan versi rilisan.</string>
<string name="CustomApiNo">Jangan gunakan API kustom</string>
<string name="CustomApiOfficial">Telegram Android</string>
<string name="CustomApiTGX">Telegram X Android</string>
<string name="CustomApiInput">Manual input</string>
<string name="CustomBackend">Kustom Backend</string>
<string name="CustomBackendNotice">Fungsi ini hanya tersedia untuk pengguna profesional, jika Anda tidak tahu apa yang mewakili opsi-opsi berikut, abaikan saja.</string>
<string name="CustomBackendProduction">Official Production DataCenter</string>
<string name="CustomBackendTestDC">Official Test DataCenter</string>
<string name="CustomBackendIpv4">Ipv4 Address</string>
<string name="CustomBackendIpv6">Ipv6 Address</string>
<string name="CustomBackendLayer">Layer</string>
<string name="CustomBackendPublicKey">Public Key</string>
<string name="CustomBackendFingerprint">Key Fingerprint</string>
<string name="CachePath">Direktori cache</string>
<string name="AllowFlashCall">Izinkan panggilan cepat</string>
<string name="ChangeTranslateProvider">Ubah Penyedia</string>
<string name="ProviderYandex">Yandex.Terjemahan</string>
<string name="GoogleCloudTransKey">Google Cloud Translate Key</string>
<string name="GoogleCloudTransKeyNotice">Jika anda menyiapkan Google Cloud Trans Key, API terjemahan awan akan digunakan, alih-alih mengirimkan formulir palsu ke versi web saat menerjemahkan (lalu lintas yang lebih cepat, stabil, dan hemat).</string>
<string name="TransToLang">Bahasa target terjemahan</string>
<string name="TransInputToLang">Input bahasa target</string>
<string name="More">Lainnya</string>
<string name="NekoXUpdatesChannel">Channel Pembaruan NekoX</string>
<string name="ShowIdAndDc">Tampilkan ID / DC di profil</string>
<string name="UseDefaultTheme">Gunakan tema default *</string>
<string name="NightMode">Mode Malam</string>
<string name="PrivacyNotice">Peringatan privasi</string>
<string name="PrivacyNoticePhoneVisible">Terdeteksi bahwa nomor ponsel anda dapat dilihat oleh siapa saja, yang dapat menyebabkan peretas yang dimiliki pemerintah dapat menemukan identitas anda yang sebenarnya, harap matikan!</string>
<string name="PrivacyNoticeAddByPhone">Terdeteksi bahwa anda tidak mematikan pengaturan \"Izinkan menemukan saya melalui nomor telepon\", yang dapat menyebabkan peretas yang dimiliki pemerintah menemukan identitas anda yang sebenarnya, harap matikan!</string>
<string name="PrivacyNoticeP2p">Terdeteksi bahwa anda belum mematikan pengaturan \"Izinkan panggilan P2p\", yang dapat menyebabkan peretas yang dimiliki pemerintah menemukan identitas anda yang sebenarnya, harap matikan!</string>
<string name="PrivacyNotice2fa">Terdeteksi bahwa anda belum menetapkan kata sandi, yang dapat menyebabkan peretas yang dimiliki pemerintah menemukan identitas anda yang sebenarnya, harap setel!</string>
<string name="ApplySuggestion">Oke, Terapkan</string>
<string name="DoNotRemindAgain">Jangan Ingatkan lagi</string>
<string name="InstantViewTransWithWeb">Tidak dapat menerjemahkan InstantView menggunakan penerjemah web</string>
<string name="RemoveTitleEmoji">Hapus emoji dari judul Nekogram X</string>
<string name="NekoXProxy">NekoX Publik Proxy</string>
<string name="PublicPrefix">Publik</string>
<string name="DisableChatAction">Jangan kirim status input saya</string>
<string name="FakeScreenshot">Simulasikan tangkapan layar</string>
<string name="Import">Impor</string>
<string name="FilterNameUsers">Pengguna</string>
<string name="FilterNameUsersDescription">Hanya pesan dari obrolan pribadi</string>
<string name="FilterNameContacts">Kontak</string>
<string name="FilterNameContactsDescription">Hanya pesan dari pengguna yang disimpan</string>
<string name="FilterNameGroups">Grup</string>
<string name="FilterNameGroupsDescription">Hanya pesan dari obrolan grup</string>
<string name="FilterNameChannels">Channel</string>
<string name="FilterNameChannelsDescription">Hanya pesan dari channel</string>
<string name="FilterNameBots">Bot</string>
<string name="FilterNameBotsDescription">Hanya pesan dari bot</string>
<string name="FilterNameUnmuted">Tidak dibisukan</string>
<string name="FilterNameUnmutedDescription">Hanya pesan dari obrolan yang tidak dibisukan</string>
<string name="FilterNameUnread2">Belum dibaca</string>
<string name="FilterNameUnreadDescription">Hanya pesan yang belum dibaca</string>
<string name="FilterNameUnmutedAndUnread">Tidak dibisukan &amp; Belum dibaca</string>
<string name="FilterNameUnmutedAndUnreadDescription">Hanya pesan yang belum dibaca dari chat yang tidak dibisukan</string>
<string name="IgnoreMutedCount">Abaikan hitungan pesan dibisukan / belum dibaca di tab folder</string>
<string name="DialogsSettings">Pengaturan Obrolan</string>
<string name="SortMenu">Pengaturan Urutan Obrolan</string>
<string name="SortByUnread">Urutkan berdasarkan Belum dibaca</string>
<string name="SortByUnmuted">Urutkan berdasarkan Tidak Dibisukan</string>
<string name="SortByUser">Urutkan berdasarkan Pengguna</string>
<string name="SortByContacts">Urutkan berdasarkan Kontak</string>
<string name="NekoXPushService">Layanan Push NekoX</string>
<string name="EnablePushAlert">Harap aktifkan \"Layanan Push NekoX\"</string>
<string name="DisablePushAlert">Harap nonaktifkan \"Layanan Push NekoX\"</string>
<string name="MIUIPermissionNote">Untuk pengguna MIUI, harap aktifkan izin mulai otomatis untuk NekoX untuk menerima pemberitahuan secara normal.</string>
<string name="DisableUndo">Nonaktifkan Batalkan</string>
<string name="DisableSystemAccount">Nonaktifkan Akun Sistem</string>
<string name="FilterMenu">Menu Filter Obrolan</string>
<string name="DisableProxyWhenVpnEnabled">Nonaktifkan proxy saat VPN diaktifkan</string>
<string name="SkipOpenLinkConfirm">Lewati konfirmasi saat membuka tautan</string>
<string name="DeleteAllInChat">Hapus semua pesan dalam obrolan</string>
<string name="DeleteAllInChatAlert">Peringatan! Ini akan menghapus **semua pesan** di obrolan ini untuk **semua** peserta.</string>
<string name="UnblockAll">Buka Semua Blokiran</string>
<string name="UnblockAllWarn">Anda yakin ingin membuka blokir untuk **semua pengguna dan bot**?</string>
<string name="BlockedListEmpty">Anda belum memblokir siapa pun :)</string>
<string name="ProxyTypeVmess">Vmess Proxy</string>
<string name="AddProxySocks5">Tambahkan Socks5 Proxy</string>
<string name="AddProxyTelegram">Tambahkan MTProto Proxy</string>
<string name="AddProxyVmess">Tambahkan Vmess Proxy</string>
<string name="AddProxySS">Tambahkan Shadowsocks Proxy</string>
<string name="AddProxySSR">Tambahkan ShadowsocksR Proxy</string>
<string name="EditProxy">Edit Proxy</string>
<string name="ShareProxy">Bagikan Proxy</string>
<string name="ProxyDelete">Hapus Proxy</string>
<string name="ProxyInfoVmess">Pengaturan Vmess Proxy</string>
<string name="VmessUserId">ID pengguna</string>
<string name="VmessAlterId">Ubah ID</string>
<string name="VmessSecurity">Keamanan</string>
<string name="VmessNetwork">Jaringan</string>
<string name="VmessHeadType">Head Type</string>
<string name="VmessRequestHost">Permintaan Host / Keamanan QUIC</string>
<string name="VmessPath">Path / Kunci QUIC</string>
<string name="VmessTls">Gunakan TLS</string>
<string name="ProxyInfoSS">Pengaturan Shadowsocks Proxy</string>
<string name="SSPassword">Kata sandi</string>
<string name="SSMethod">Metode Enkripsi</string>
<string name="ProxyInfoSSR">Pengaturan ShadowsocksR Proxy</string>
<string name="SSRProtocol">Protokol</string>
<string name="SSRProtocolParams">Parameter protokol</string>
<string name="SSRObfs">Obfs</string>
<string name="SSRObfsParam">Parameter Obfs</string>
<string name="ProxyRemarks">Catatan</string>
<string name="RetestPing">Test ulang ping ke semua server</string>
<string name="ReorderByPing">Susun ulang server dari ping</string>
<string name="ExportProxies">Ekspor server ke berkas</string>
<string name="ImportProxies">Impor server dari berkas</string>
<string name="ImportProxyList">Impor server proxy</string>
<string name="ProxySubscription">Berlangganan Proxy</string>
<string name="ProxySubDetails">Detail Berlangganan</string>
<string name="SubscriptionUrls">Url</string>
<string name="SubscriptionUpdating">Memperbarui Langganan</string>
<string name="SubscriptionDelete">Hapus Langganan</string>
<string name="ExportStickers">Ekspor Stiker</string>
<string name="ImportStickers">Impor stiker dari berkas</string>
<string name="ImportStickersList">Impor stiker</string>
<string name="StickerSets">Set Stiker</string>
<string name="InvalidStickersFile">File stiker tidak valid: </string>
<string name="InvalidProxyFile">File daftar proxy tidak valid: </string>
<string name="ImportedProxies">Impor proxy server: </string>
<string name="ErrorsInImport">Kesalahan impor: </string>
<string name="NoProxy">Anda belum menambahkan server proxy.</string>
<string name="DeleteAllServer">Hapus semua server</string>
<string name="DeleteUnavailableServer">Hapus server yang tidak tersedia</string>
<string name="DeleteAllServerConfirm">Anda yakin ingin **menghapus semua server**?</string>
<string name="DeleteUnavailableServerConfirm">Anda yakin ingin **menghapus semua server tidak tersedia**?</string>
<string name="MinApi21Required">Maaf, anda memerlukan setidaknya Android 5 (API 21).</string>
<string name="ImportProxyFromClipboard">Impor Dari Papan Klip</string>
<string name="BrokenLink">Tidak Dikenal / Tautan Rusak</string>
<string name="ShareQRCode">Kode QR</string>
<string name="ScanQRCode">Pindai Kode QR</string>
<string name="NoQrFound">Tidak ditemukan Kode QR</string>
<string name="EnableDeveloperMode">Aktifkan Mode Pengembang</string>
<string name="DisableDeveloperMode">Nonaktifkan Mode Pengembang</string>
<string name="DeveloperSettings">Pengaturan Pengembang</string>
<string name="LoginSettings">Pengaturan Login</string>
<string name="NekoXFaq">NekoX FAQ</string>
</resources>

View File

@ -1,20 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="CustomApi">API personalizada</string>
<string name="UseCustomApiNotice">Efetue login usando a API personalizada. Se você não conseguir se registrar ou efetuar login, isso pode ajudar.\n\nNota: o fcm não vai funcionar se você estiver usando a versão de final.</string>
<string name="CustomApiNo">Não use a API personalizada</string>
<string name="CustomApiOfficial">Telegram Android</string>
<string name="CustomApiTGX">Telegram Android X</string>
<string name="CustomApiInput">Entrada manual</string>
<string name="CustomBackend">Back-end personalizado</string>
<string name="CustomBackendNotice">Essa função é fornecida apenas para usuários experientes; se você não souber o que as elas representam, apenas ignore.</string>
<string name="CustomBackendProduction">Data Center de Produção Oficial</string>
<string name="CustomBackendTestDC">DataCenter Oficial de Teste</string>
<string name="CustomBackendIpv4">Endereço Ipv4</string>
<string name="CustomBackendIpv6">Endereço Ipv6</string>
<string name="CustomBackendLayer">Camada</string>
<string name="CustomBackendPublicKey">Chave pública</string>
<string name="CustomBackendFingerprint">Chave Impressão digital</string>
<string name="CachePath">Diretório de cache</string>
<string name="AllowFlashCall">Permitir chamada rápida</string>
<string name="ChangeTranslateProvider">Alterar Provedor</string>
<string name="ProviderYandex">Yandex.Translate</string>
<string name="GoogleCloudTransKey">Chave de tradução do Google Cloud</string>
<string name="GoogleCloudTransKeyNotice">Se você configurar um Google Cloud Trans Key, a API de conversão em nuvem será chamada em vez de enviar um formulário falso para a versão da Web durante a tradução (tráfego mais rápido, estável e leve).</string>
<string name="TransToLang">Idioma de destino para tradução</string>
<string name="TransInputToLang">Idioma de destino da tradução para envio de mensagens</string>
<string name="More">Mais</string>
<string name="NekoXUpdatesChannel">Canal de atualizações do NekoX</string>
<string name="ShowIdAndDc">Mostrar ID / DC no perfil</string>
<string name="UseDefaultTheme">Usar o tema padrão *</string>
<string name="NightMode">Modo Noturno</string>
<string name="PrivacyNotice">Aviso de privacidade</string>
<string name="PrivacyNoticePhoneVisible">Identificamos que seu número de celular está visível para qualquer pessoa, o que pode fazer com que hackers controlados pelo governo encontrem sua verdadeira identidade. Por favor, desative!</string>
<string name="PrivacyNoticeAddByPhone">Identificamos que você não desativou a opção \"Permitir que me encontrem através do número de telefone\", o que pode fazer com que um hacker controlado pelo governo encontre sua verdadeira identidade. Por favor, desative!</string>
<string name="PrivacyNoticeP2p">Identificamos que você não desativou a configuração \"Permitir chamadas P2p\", o que pode fazer com que hackers controlados pelo governo encontrem sua verdadeira identidade, desligue-a!</string>
<string name="PrivacyNotice2fa">Foi detectado que você não definiu uma senha, o que pode fazer com que os hackers controlados pelo governo encontrem sua verdadeira identidade, por favor, defina uma!</string>
<string name="ApplySuggestion">OK, Aplicar</string>
<string name="DoNotRemindAgain">Não lembre novamente</string>
<string name="InstantViewTransWithWeb">Não é possível traduzir InstantView com o tradutor web</string>
<string name="RemoveTitleEmoji">Remover emoji no título</string>
<string name="NekoXProxy">Proxy Público do NekoX</string>
<string name="PublicPrefix">Público</string>
<string name="DisableChatAction">Não enviar meu status de entrada</string>
<string name="FakeScreenshot">Simular captura de tela</string>
<string name="Import">Importar</string>
<string name="FilterNameUsers">Usuários</string>
<string name="FilterNameUsersDescription">Apenas mensagens de conversas privadas</string>
<string name="FilterNameContacts">Contatos</string>
@ -84,6 +111,16 @@
<string name="ExportProxies">Exportar servidores para arquivo</string>
<string name="ImportProxies">Importar servidores do arquivo</string>
<string name="ImportProxyList">Importar servidores proxy</string>
<string name="ProxySubscription">Assinatura do Proxy</string>
<string name="ProxySubDetails">Detalhes da assinatura</string>
<string name="SubscriptionUrls">URLs</string>
<string name="SubscriptionUpdating">Atualização da assinatura</string>
<string name="SubscriptionDelete">Excluir assinatura</string>
<string name="ExportStickers">Exportar Stickers</string>
<string name="ImportStickers">Importar stickers de arquivo</string>
<string name="ImportStickersList">Importar Stickers</string>
<string name="StickerSets">Pacote de Sticker</string>
<string name="InvalidStickersFile">Arquivo de stickers inválido: </string>
<string name="InvalidProxyFile">Arquivo de lista de proxy inválido: </string>
<string name="ImportedProxies">Servidores proxy importados: </string>
<string name="ErrorsInImport">Erros de importação: </string>

View File

@ -10,6 +10,15 @@
<string name="CustomApiTGX">Telegram Android X</string>
<string name="CustomApiInput">Manual input</string>
<string name="CheckUpdate">Check Update</string>
<string name="NoUpdate">No update found</string>
<string name="UpdateAvailable">New update available</string>
<string name="Download">Download</string>
<string name="DownloadFailed">Download Failed</string>
<string name="Install">Install</string>
<string name="Ignore">Ignore</string>
<string name="UpdateDownloaded">An update has just been downloaded.</string>
<string name="CustomBackend">Custom Backend</string>
<string name="CustomBackendNotice">This function is only provided for expert users, if you don\'t know what the following options represent, just ignore it.</string>
<string name="CustomBackendProduction">Official Production DataCenter</string>

View File

@ -9,7 +9,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:3.6.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72"
classpath 'com.google.gms:google-services:4.3.3'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.0.0-beta04'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.0.0'
}
}

View File

@ -1,6 +1,6 @@
#Fri May 01 23:57:46 CST 2020
#Mon May 04 18:56:10 CST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.4-rc-4-all.zip