Commit 5e88de3c authored by 符瀚's avatar 符瀚

fix chagne ip

parents
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.externalNativeBuild
# kdiff3 ignore
*.orig
# maven ignore
target/
# eclipse ignore
.settings/
.project
.classpath
# idea ignore
.idea/
*.ipr
*.iml
*.iws
# temp ignore
*.log
*.cache
*.diff
*.patch
*.tmp
# system ignore
.DS_Store
Thumbs.db
*.apk
build/
apps/
ratelConfig.properties
script/dist
script/dist.zip
container-builder-repkg/src/main/resources/ratel_engine.properties
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
defaultConfig {
applicationId "com.virjar.majora.adr"
minSdkVersion 19
targetSdkVersion 23
versionCode 1
versionName "1.0"
archivesBaseName = "Majora_${versionName}".replace(' ', '_')
}
signingConfigs {
release {
storeFile rootProject.file('script/hermes_key')
storePassword "hermes"
keyAlias "hermes"
keyPassword "hermes"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
//debug ,也使用release的key,hermes代码无法在线调试,所以没有debug的意义
debug {
signingConfig signingConfigs.release
}
}
lintOptions {
abortOnError false
}
compileOptions {
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_1_8
}
packagingOptions {
exclude 'META-INF/INDEX.LIST'
exclude 'META-INF/io.netty.versions.properties'
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'org.apache.commons:commons-lang3:3.6'
implementation 'commons-io:commons-io:2.6'
implementation "com.virjar.majora:majora-sdk:1.0"
// 权限请求框架:https://github.com/getActivity/XXPermissions
implementation 'com.github.getActivity:XXPermissions:11.6'
// 悬浮窗框架:https://github.com/getActivity/XToast
implementation 'com.github.getActivity:XToast:8.1'
}
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.virjar.majora.adr">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- 前台服务,保持安排的优先级 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".TheApp"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SettingsActivity" />
<service
android:name="com.virjar.majora.adr.KeepAliveService"
android:enabled="true"
android:exported="false" />
</application>
</manifest>
\ No newline at end of file
package com.virjar.majora.adr;
import android.Manifest;
import android.app.Application;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Process;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import com.virjar.majora.client.sdk.log.MajoraLogger;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.Random;
import java.util.UUID;
public class ClientIdentifier {
private static final String clientIdFileName = "echo_client_id.txt";
private static final String UN_RESOLVE = "un_resolve_";
public static boolean isAndroid = isAndroidEnv();
private static String clientIdInMemory;
public static String id() {
if (clientIdInMemory != null) {
return clientIdInMemory;
}
// from cache file
File file = resolveIdCacheFile();
if (file.exists()) {
try {
String s = readFile(file);
if (s != null && !s.isEmpty() && !s.startsWith(UN_RESOLVE)) {
clientIdInMemory = s;
return clientIdInMemory;
}
} catch (IOException e) {
MajoraLogger.getLogger().error("can not read id file: " + file.getAbsolutePath(), e);
}
}
clientIdInMemory = generateClientId() + "_" + new Random().nextInt(10000);
try {
writeFile(file, clientIdInMemory);
} catch (IOException e) {
MajoraLogger.getLogger().error("can not write id file: " + file.getAbsolutePath(), e);
}
return "66_"+clientIdInMemory;
}
private static void writeFile(File file, String data) throws IOException {
try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
fileOutputStream.write(data.getBytes());
}
}
private static String readFile(File file) throws IOException {
FileInputStream stream = new FileInputStream(file);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
copyStream(stream, byteArrayOutputStream);
return byteArrayOutputStream.toString();
}
private static void copyStream(InputStream inputStream, OutputStream outputStream) throws IOException {
byte[] buf = new byte[2048];
int len;
while ((len = inputStream.read(buf)) > 0) {
outputStream.write(buf, 0, len);
}
}
private static String generateClientId() {
if (isAndroid) {
String s = generateClientIdForAndroid();
if (s != null && !s.isEmpty()) {
return s;
}
}
String mac = generateClientIdForNormalJVM();
if (!TextUtils.isEmpty(mac)) {
return mac;
}
return UN_RESOLVE + UUID.randomUUID().toString();
}
private static String generateClientIdForNormalJVM() {
try {
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
while (networkInterfaces.hasMoreElements()) {
NetworkInterface networkInterface = networkInterfaces.nextElement();
if (networkInterface.isVirtual()) {
continue;
}
if (networkInterface.isLoopback()) {
continue;
}
byte[] hardwareAddress = networkInterface.getHardwareAddress();
if (hardwareAddress == null) {
continue;
}
return parseByte(hardwareAddress[0]) + ":" +
parseByte(hardwareAddress[1]) + ":" +
parseByte(hardwareAddress[2]) + ":" +
parseByte(hardwareAddress[3]) + ":" +
parseByte(hardwareAddress[4]) + ":" +
parseByte(hardwareAddress[5]);
}
return null;
} catch (SocketException e) {
return null;
}
}
private static String parseByte(byte b) {
int intValue;
if (b >= 0) {
intValue = b;
} else {
intValue = 256 + b;
}
return Integer.toHexString(intValue);
}
private static String generateClientIdForAndroid() {
Application application = TheApp.getApplication();
if (application.checkPermission(Manifest.permission.READ_PHONE_STATE, Process.myPid(), Process.myUid())
== PackageManager.PERMISSION_GRANTED
) {
TelephonyManager telephonyManager = (TelephonyManager) application.getSystemService(Context.TELEPHONY_SERVICE);
String imei;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
imei = telephonyManager.getImei();
} else {
imei = telephonyManager.getDeviceId();
}
if (!TextUtils.isEmpty(imei)) {
return imei;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
String serial = Build.getSerial();
if (!TextUtils.isEmpty(serial)) {
return serial;
}
}
} else {
MajoraLogger.getLogger().warn("need permission :" + Manifest.permission.READ_PHONE_STATE);
}
String serial = Build.SERIAL;
if ("unknown".equalsIgnoreCase(serial)) {
return "";
}
return serial;
}
private static File resolveIdCacheFile() {
if (isAndroid) {
return resolveAndroidCacheIdFile();
}
return resolveJvmEnvCacheIdFile();
}
private static File resolveJvmEnvCacheIdFile() {
String userHome = System.getProperty("user.home");
File base;
if (userHome != null && !userHome.trim().isEmpty()) {
base = new File(userHome);
} else {
base = new File(".");
}
return new File(base, clientIdFileName);
}
private static File resolveAndroidCacheIdFile() {
Application application = TheApp.getApplication();
return new File(application.getFilesDir(), clientIdFileName);
}
private static boolean isAndroidEnv() {
try {
Class.forName("android.util.Log");
return true;
} catch (Throwable throwable) {
//ignore
}
return false;
}
}
package com.virjar.majora.adr;
import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.IBinder;
import android.preference.PreferenceManager;
import com.virjar.majora.client.sdk.client.MajoraClient;
import com.virjar.majora.client.sdk.log.MajoraLogger;
import org.apache.commons.lang3.math.NumberUtils;
import java.util.Timer;
import java.util.TimerTask;
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
public class KeepAliveService extends Service {
private static boolean start = false;
// 重播任务定时器
private Timer timer = new Timer("reDial");
public static void startService(Context context) {
if (start) {
return;
}
setNotifyChannel(context);
Intent intent = new Intent(context, KeepAliveService.class);
context.startService(intent);
}
private static void setNotifyChannel(Context context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return;
}
String packageName = context.getPackageName();
@SuppressLint("WrongConstant") NotificationChannel notificationChannel = new NotificationChannel(
packageName + ":majora",
"channel", NotificationManager.IMPORTANCE_HIGH);
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.YELLOW);
notificationChannel.setShowBadge(true);
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
NotificationManager manager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
if (manager == null) {
return;
}
manager.createNotificationChannel(notificationChannel);
}
private void onServiceStartupInternal() {
String packageName = getPackageName();
PackageManager pm = getPackageManager();
Intent launchIntent = pm.getLaunchIntentForPackage(packageName);
if (launchIntent == null) {
MajoraLogger.getLogger()
.warn("no launchIntent for package: " + getPackageName());
return;
}
Bitmap icon;
try {
icon = drawableToBitmap(
pm.getPackageInfo(packageName, PackageManager.GET_META_DATA)
.applicationInfo.loadIcon(pm)
);
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(e);
}
Notification.Builder builder = new Notification.Builder(this);
// 设置PendingIntent
builder.setContentIntent(PendingIntent.getActivity(this, 0, launchIntent, FLAG_UPDATE_CURRENT))
.setLargeIcon(icon)
.setContentTitle("majora service")
.setContentText("to make sure majora service alive")
.setWhen(System.currentTimeMillis());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder.setChannelId(packageName + ":majora");
}
Notification notification = builder.build();
notification.defaults = Notification.DEFAULT_SOUND;
startForeground(110, notification);
startProxyService();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
reDial();
}
}, 5 * 60 * 1000, 5 * 60 * 1000);
start = true;
}
public static void reDial() {
try {
Shell.doCmds("settings put global airplane_mode_on 1 && am broadcast -a android.intent.action.AIRPLANE_MODE --ez state true");
Thread.sleep(5000);
Shell.doCmds("settings put global airplane_mode_on 0 && am broadcast -a android.intent.action.AIRPLANE_MODE --ez state false");
} catch (Exception e) {
MajoraLogger.getLogger().info("reDial error", e);
}
}
private void startProxyService() {
SharedPreferences spf = PreferenceManager.getDefaultSharedPreferences(this);
String serverHost = spf.getString("server_host", "majora.virjar.com");
int serverPort = NumberUtils.toInt(spf.getString("server_port", "5879"), 5879);
new MajoraClient(serverHost, serverPort, ClientIdentifier.id()).startUp();
}
// 5. Drawable----> Bitmap
public static Bitmap drawableToBitmap(Drawable drawable) {
// 获取 drawable 长宽
int width = drawable.getIntrinsicWidth();
int heigh = drawable.getIntrinsicHeight();
drawable.setBounds(0, 0, width, heigh);
// 获取drawable的颜色格式
Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
: Bitmap.Config.RGB_565;
// 创建bitmap
Bitmap bitmap = Bitmap.createBitmap(width, heigh, config);
// 创建bitmap画布
Canvas canvas = new Canvas(bitmap);
// 将drawable 内容画到画布中
drawable.draw(canvas);
return bitmap;
}
@Override
public IBinder onBind(Intent intent) {
onServiceStartupInternal();
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
onServiceStartupInternal();
return START_STICKY;
}
}
package com.virjar.majora.adr;
import android.app.Activity;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.hjq.permissions.OnPermissionCallback;
import com.hjq.permissions.Permission;
import com.hjq.permissions.XXPermissions;
import com.hjq.xtoast.XToast;
import com.hjq.xtoast.draggable.SpringDraggable;
import java.util.List;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = findViewById(R.id.clientId);
textView.setText(ClientIdentifier.id());
Button btn = findViewById(R.id.goSetting);
Button changeIpBt = findViewById(R.id.changeIpBt);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SettingsActivity.go(MainActivity.this);
}
});
changeIpBt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
KeepAliveService.reDial();
}
});
showRatelFloatWindow();
}
/**
* 设置一个应用外悬浮窗,因为安卓10之后对后台启动activity具有限制
* https://developer.android.com/guide/components/activities/background-starts
* <p>
*/
private void showRatelFloatWindow() {
XXPermissions.with(this)
.permission(Permission.SYSTEM_ALERT_WINDOW)
.request(new OnPermissionCallback() {
@Override
public void onGranted(List<String> granted, boolean all) {
// 传入 Application 表示这个是一个全局的 Toast
new XToast<>(TheApp.getApplication())
.setView(R.layout.toast_phone)
.setGravity(Gravity.END | Gravity.BOTTOM)
.setYOffset(200)
// 设置指定的拖拽规则
.setDraggable(new SpringDraggable())
.show();
}
@Override
public void onDenied(List<String> denied, boolean never) {
}
});
}
}
package com.virjar.majora.adr;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.FragmentManager;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceFragment;
import android.support.v7.app.AppCompatActivity;
public class SettingsActivity extends AppCompatActivity {
public static void go(Context context) {
Intent intent = new Intent(context, SettingsActivity.class);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
if (savedInstanceState == null) {
SettingsFragment mSettingsFragment = new SettingsFragment();
replaceFragment(R.id.settings_container, mSettingsFragment);
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void replaceFragment(int viewId, android.app.Fragment fragment) {
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction().replace(viewId, fragment).commit();
}
/**
* A placeholder fragment containing a settings view.
*/
@SuppressLint("WorldReadableFiles")
public static class SettingsFragment extends PreferenceFragment {
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pref_setting);
}
}
}
package com.virjar.majora.adr;
import android.util.Log;
import com.virjar.majora.client.sdk.log.MajoraLogger;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
public class Shell {
public static void doCmds(String cmd) throws Exception {
MajoraLogger.getLogger().info("doCmds:" + cmd);
Process process = null;
String result = "";
DataOutputStream os = null;
DataInputStream is = null;
try {
process = Runtime.getRuntime().exec("su");
os = new DataOutputStream(process.getOutputStream());
is = new DataInputStream(process.getInputStream());
os.writeBytes(cmd + "\n");
os.writeBytes("exit\n");
os.flush();
String line = null;
while ((line = is.readLine()) != null) {
result += line;
}
process.waitFor();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (process != null) {
process.destroy();
}
}
MajoraLogger.getLogger().info("doCmds result:" + result);
}
public static void executeCmd(String cmd) {
try {
MajoraLogger.getLogger().info("execute cmd: " + cmd);
Process process = Runtime.getRuntime().exec("su");
OutputStream outputStream = process.getOutputStream();
outputStream.write(cmd.getBytes());
outputStream.write("\n".getBytes());
StringBuffer out = new StringBuffer();
StringBuffer error = new StringBuffer();
autoFillOutput(process.getInputStream(), out);
autoFillOutput(process.getErrorStream(), error);
process.waitFor();
} catch (Throwable throwable) {
MajoraLogger.getLogger().info("executeCmd error", throwable);
}
}
private static void autoFillOutput(final InputStream inputStream, final StringBuffer stringBuffer) {
new Thread() {
@Override
public void run() {
try {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = bufferedReader.readLine()) != null) {
MajoraLogger.getLogger().info(line);
stringBuffer.append(line).append("\n");
}
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
package com.virjar.majora.adr;
import android.app.AppOpsManager;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Build;
import android.os.Process;
import android.provider.Settings;
import android.widget.Toast;
import com.virjar.majora.client.sdk.log.MajoraLogger;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class TheApp extends Application {
private static TheApp theApp;
public static Application getApplication() {
return theApp;
}
@Override
public void onCreate() {
super.onCreate();
theApp = this;
new Thread("makeSu") {
@Override
public void run() {
Shell.executeCmd("pwd");
}
}.start();
KeepAliveService.startService(this);
if (!checkFloatPermission(this)) {
MajoraLogger.getLogger().info("请给软件设置悬浮窗权限,否则可能影响后台网络!");
Toast.makeText(getApplicationContext(), "请给软件设置悬浮窗权限,否则可能影响后台网络!", Toast.LENGTH_SHORT).show();
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
startActivity(intent);
}
}
if (!canBackgroundStart(this)) {
Toast.makeText(getApplicationContext(), "请给软件设置允许后台启动页面权限,否则可能影响后台网络!", Toast.LENGTH_SHORT).show();
}
}
private boolean checkFloatPermission(Context context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
try {
Class cls = Class.forName("android.content.Context");
Field declaredField = cls.getDeclaredField("APP_OPS_SERVICE");
declaredField.setAccessible(true);
Object obj = declaredField.get(cls);
if (!(obj instanceof String)) {
return false;
}
String str2 = (String) obj;
obj = cls.getMethod("getSystemService", String.class).invoke(context, str2);
cls = Class.forName("android.app.AppOpsManager");
Field declaredField2 = cls.getDeclaredField("MODE_ALLOWED");
declaredField2.setAccessible(true);
Method checkOp = cls.getMethod("checkOp", Integer.TYPE, Integer.TYPE, String.class);
int result = (Integer) checkOp.invoke(obj, 24, Binder.getCallingUid(), context.getPackageName());
return result == declaredField2.getInt(cls);
} catch (Exception e) {
return false;
}
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
AppOpsManager appOpsMgr = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
if (appOpsMgr == null)
return false;
int mode = appOpsMgr.checkOpNoThrow("android:system_alert_window", android.os.Process.myUid(), context
.getPackageName());
return mode == AppOpsManager.MODE_ALLOWED || mode == AppOpsManager.MODE_IGNORED;
} else {
return Settings.canDrawOverlays(context);
}
}
}
private boolean canBackgroundStart(Context context) {
AppOpsManager ops = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
try {
int op = 10021; // >= 23
// ops.checkOpNoThrow(op, uid, packageName)
Method method = ops.getClass().getMethod("checkOpNoThrow", int.class, int.class, String.class);
Integer result = (Integer) method.invoke(ops, op, Process.myUid(), context.getPackageName());
return result == AppOpsManager.MODE_ALLOWED;
} catch (Exception e) {
MajoraLogger.getLogger().info("not support exec checkOpNoThrow", e);
return true;
}
}
}
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeColor="#00000000"
android:strokeWidth="1">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeColor="#00000000"
android:strokeWidth="1" />
</vector>
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillColor="#26A69A"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8" />
</vector>
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent">
<Button
android:id="@+id/goSetting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="设置参数" />
</LinearLayout>
<TextView
android:id="@+id/clientId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="MajoraApp"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/index_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="25dp"
android:text="@string/majoraapp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<Button
android:id="@+id/changeIpBt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="切换ip"
tools:layout_editor_absoluteX="151dp"
tools:layout_editor_absoluteY="425dp" />
</android.support.constraint.ConstraintLayout>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/settings_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"/>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/icon"
android:layout_width="60dp"
android:layout_height="60dp"
android:src="@mipmap/ic_launcher" />
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
<color name="white">#FFF</color>
</resources>
<resources>
<string name="app_name">Majora</string>
<string name="about">关于</string>
<string name="author">virjar</string>
<string name="server">服务器</string>
<string name="server_host">服务器host</string>
<string name="port">端口</string>
<string name="default_server_host">majora.virjar.com</string>
<string name="default_server_port">5879</string>
<string name="majoraapp">MajoraApp</string>
</resources>
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="@string/server">
<EditTextPreference
android:defaultValue="@string/default_server_host"
android:key="server_host"
android:persistent="true"
android:title="@string/server_host" />
<EditTextPreference
android:defaultValue="@string/default_server_port"
android:digits="0123456789"
android:inputType="numberDecimal"
android:key="server_port"
android:title="@string/port" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/about">
<Preference
android:key="author"
android:summary="virjar"
android:title="@string/author" />
</PreferenceCategory>
</PreferenceScreen>
import java.nio.charset.StandardCharsets
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
repositories {
maven {
name "aliyunmaven"
url "https://maven.aliyun.com/repository/public"
}
maven {
name "aliyunGoogle"
url "https://maven.aliyun.com/repository/google"
}
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.1'
classpath 'net.sf.proguard:proguard-gradle:6.1.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
tasks.withType(JavaCompile) {
// 在windows上面,可能会用gbk编译项目。导致乱码
options.encoding(StandardCharsets.UTF_8.name())
}
repositories {
repositories {
maven {
name "aliyunmaven"
url "https://maven.aliyun.com/repository/public"
}
maven {
name "aliyunGoogle"
url "https://maven.aliyun.com/repository/google"
}
maven {
name "aliyunJCenter"
url "https://maven.aliyun.com/repository/jcenter"
}
maven { url 'https://jitpack.io' }
maven {
name "IntMaven"
url "http://nexus.virjar.com/repository/maven-public/"
}
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
#Fri Oct 05 17:01:22 CST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
include ':app'
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment