Commit 531fc810 authored by Administrator's avatar Administrator

增加app闭环的逻辑

parent 19e78913
......@@ -9,8 +9,8 @@ android {
minSdkVersion 19
//noinspection ExpiredTargetSdkVersion
targetSdkVersion 23
versionCode 2
versionName "1.2"
versionCode 3
versionName "1.3"
archivesBaseName = "Majora_${versionName}".replace(' ', '_')
}
......@@ -52,10 +52,12 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support:design: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 'org.apache.commons:commons-lang3:3.6'
// implementation 'commons-io:commons-io:2.6'
implementation "com.virjar.majora:majora-sdk:1.0"
implementation 'eu.chainfire:libsuperuser:1.0.0.201608240809'
// 权限请求框架:https://github.com/getActivity/XXPermissions
......@@ -65,4 +67,8 @@ dependencies {
// 悬浮窗框架:https://github.com/getActivity/XToast
implementation 'com.github.getActivity:XToast:8.1'
implementation 'de.psdev.licensesdialog:licensesdialog:1.8.1'
implementation 'com.afollestad.material-dialogs:commons:0.9.0.2'
implementation 'com.android.support:customtabs:26.1.0'
implementation 'com.android.support:cardview-v7:26.1.0'
}
......@@ -9,6 +9,7 @@
<!-- 前台服务,保持安排的优先级 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
......@@ -18,23 +19,35 @@
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">
<activity android:name=".ui.WelcomeActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SettingsActivity" />
<activity android:name=".ui.SettingsActivity" />
<activity android:name=".ui.AboutActivity" />
<activity android:name=".ui.SupportActivity" />
<service
android:name="com.virjar.majora.adr.KeepAliveService"
android:enabled="true"
android:exported="false" />
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.virjar.majora.adr.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
......
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.BitmapFactory;
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.adr.majora.MajoraClientService;
import com.virjar.majora.adr.ui.WelcomeActivity;
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 static MajoraClient majoraClient;
// 重播任务定时器
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);
Intent launchIntent = new Intent(this, WelcomeActivity.class);
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);
}
String channelId = BuildConfig.APPLICATION_ID;
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")
.setLargeIcon(BitmapFactory.decodeResource(this.getResources(), R.mipmap.ic_launcher))
.setContentTitle("majora")
.setContentText("majora")
.setWhen(System.currentTimeMillis());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder.setChannelId(packageName + ":majora");
builder.setChannelId(channelId);
}
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);
startForeground(channelId.hashCode(), notification);
MajoraClientService.startup();
start = true;
}
public static void reDial() {
if (majoraClient == null) {
reDialInternal();
} else {
majoraClient.prepareReDial(KeepAliveService::reDialInternal);
}
}
private static void reDialInternal() {
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);
majoraClient = new MajoraClient(serverHost, serverPort, ClientIdentifier.id());
}
// 5. Drawable----> Bitmap
public static Bitmap drawableToBitmap(Drawable drawable) {
......
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 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;
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.annotation.SuppressLint;
import android.app.Application;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.graphics.Color;
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.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import eu.chainfire.libsuperuser.Shell;
public class TheApp extends Application {
private File logFile;
private static TheApp theApp;
public static Application getApplication() {
public static TheApp getApplication() {
return theApp;
}
public File getLogFile() {
return logFile;
}
@Override
public void onCreate() {
super.onCreate();
......@@ -29,71 +33,43 @@ public class TheApp extends Application {
new Thread("makeSu") {
@Override
public void run() {
Shell.executeCmd("pwd");
Shell.SU.available();
}
}.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);
}
}
createLogFile();
setNotifyChannel(this);
KeepAliveService.startService(this);
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 void createLogFile() {
File file = new File(getFilesDir(), "log");
if (!file.exists()) {
file.mkdirs();
}
logFile = new File(file, "majora.log");
}
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;
private static void setNotifyChannel(Context context) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return;
}
@SuppressLint("WrongConstant") NotificationChannel notificationChannel = new NotificationChannel(
BuildConfig.APPLICATION_ID,
"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);
}
}
package com.virjar.majora.adr.majora;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import com.virjar.majora.adr.TheApp;
import com.virjar.majora.adr.utils.ClientIdentifier;
import com.virjar.majora.adr.utils.CommonUtils;
import com.virjar.majora.adr.utils.PermissionUtils;
import com.virjar.majora.client.sdk.client.MajoraClient;
import com.virjar.majora.client.sdk.log.MajoraLogger;
import java.util.Timer;
import java.util.TimerTask;
import eu.chainfire.libsuperuser.Shell;
public class MajoraClientService {
private static boolean started = false;
private static MajoraClient majoraClient;
// 重播任务定时器
private static final Timer timer = new Timer("reDial");
public static void startup() {
if (started) {
return;
}
if (!PermissionUtils.checkPermission()) {
return;
}
startProxyService();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
reDial();
}
}, 5 * 60 * 1000, 5 * 60 * 1000);
started = true;
}
public static void reDial() {
if (!Shell.SU.available()) {
// 没有root权限,那就无法进行飞行模式切换
return;
}
if (majoraClient == null) {
reDialInternal();
} else {
majoraClient.prepareReDial(MajoraClientService::reDialInternal);
}
}
private static void reDialInternal() {
try {
Shell.SU.run("settings put global airplane_mode_on 1 && am broadcast -a android.intent.action.AIRPLANE_MODE --ez state true");
Thread.sleep(5000);
Shell.SU.run("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 static void startProxyService() {
SharedPreferences spf = PreferenceManager.getDefaultSharedPreferences(TheApp.getApplication());
String serverHost = spf.getString("server_host", "majora.virjar.com");
int serverPort = CommonUtils.toInt(spf.getString("server_port", "5879"), 5879);
majoraClient = new MajoraClient(serverHost, serverPort, ClientIdentifier.id());
UILoggerHelper.setupLogger();
}
}
package com.virjar.majora.adr.majora;
import android.annotation.SuppressLint;
import android.util.Log;
import com.virjar.majora.adr.TheApp;
import com.virjar.majora.adr.utils.CommonUtils;
import com.virjar.majora.client.sdk.log.ILogger;
import com.virjar.majora.client.sdk.log.MajoraLogger;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.SimpleFormatter;
public class UILoggerHelper {
public static void setupLogger() {
ILogger originLogger = MajoraLogger.getLogger();
if (originLogger instanceof Logger) {
originLogger = ((Logger) originLogger).delegate;
}
MajoraLogger.setLogger(new Logger(originLogger));
logConsumeThread.start();
}
private static void appendLog(String level, String msg, Throwable throwable) {
blockingQueue.offer(new LogMessage(level, msg, throwable));
}
@SuppressLint("ConstantLocale")
private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
private static void appendLog2(String level, String msg, Throwable throwable) throws IOException {
BufferedWriter bufferedWriter = adaptLogWriter();
bufferedWriter.write(simpleDateFormat.format(new Date()));
bufferedWriter.write(level);
bufferedWriter.write(":");
bufferedWriter.write(msg);
bufferedWriter.newLine();
if (throwable != null) {
PrintWriter printWriter = new PrintWriter(bufferedWriter);
throwable.printStackTrace(printWriter);
printWriter.flush();
}
if (blockingQueue.isEmpty()) {
bufferedWriter.flush();
} else {
if (System.currentTimeMillis() - lastFlushLogTimestamp > 10000) {
lastFlushLogTimestamp = System.currentTimeMillis();
bufferedWriter.flush();
}
}
}
public static void forceCloseWriter() {
lastLogWriterCreateTimestamp = 0;
appendLog(Logger.INFO, "do close logFile", null);
}
private static BufferedWriter adaptLogWriter() throws IOException {
if (System.currentTimeMillis() - lastLogWriterCreateTimestamp < 25000) {
logWriter.close();
clearLogFileIfNeed();
} else if (logWriter != null) {
return logWriter;
}
lastLogWriterCreateTimestamp = System.currentTimeMillis();
logWriter = new BufferedWriter(new FileWriter(logFile, true));
return logWriter;
}
private static long skipLargeFile(BufferedReader is, long length) throws IOException {
if (length < MAX_LOG_SIZE)
return 0;
long skipped = length - MAX_LOG_SIZE;
long yetToSkip = skipped;
do {
yetToSkip -= is.skip(yetToSkip);
} while (yetToSkip > 0);
int c;
do {
c = is.read();
if (c == -1)
break;
skipped++;
} while (c != '\n');
return skipped;
}
private static void clearLogFileIfNeed() throws IOException {
if (logFile.length() < MAX_LOG_SIZE) {
return;
}
StringBuilder llog = new StringBuilder(15 * 10 * 1024);
try {
BufferedReader br;
br = new BufferedReader(new FileReader(logFile));
long skipped = skipLargeFile(br, logFile.length());
if (skipped > 0) {
llog.append("-----------------\n");
llog.append("Log too long");
llog.append("\n-----------------\n\n");
}
char[] temp = new char[1024];
int read;
while ((read = br.read(temp)) > 0) {
llog.append(temp, 0, read);
}
br.close();
} catch (IOException e) {
llog.append("Cannot read log");
llog.append(e.getMessage());
}
CommonUtils.writeFile(logFile, llog.toString());
}
private static final int MAX_LOG_SIZE = 1024 * 1024; // 1Mb
private static long lastFlushLogTimestamp = 0;
private static long lastLogWriterCreateTimestamp = 0;
private static BufferedWriter logWriter = null;
private static final File logFile = TheApp.getApplication().getLogFile();
private static final BlockingQueue<LogMessage> blockingQueue = new LinkedBlockingQueue<>();
private static final Thread logConsumeThread = new Thread("logConsumeThread") {
@Override
public void run() {
while (Thread.currentThread().isAlive()) {
try {
LogMessage message = blockingQueue.take();
appendLog2(message.level, message.msg, message.throwable);
} catch (InterruptedException e) {
//ignore
} catch (IOException e) {
Log.e("Majora", "log component error", e);
}
}
}
};
private static class LogMessage {
private final String level;
private final String msg;
private final Throwable throwable;
public LogMessage(String level, String msg, Throwable throwable) {
this.level = level;
this.msg = msg;
this.throwable = throwable;
}
}
public static class Logger implements ILogger {
private final ILogger delegate;
private static final String DEBUG = "DEBUG";
private static final String INFO = "INFO";
private static final String WARNING = "WARNING";
private static final String ERROR = "ERROR";
public Logger(ILogger delegate) {
this.delegate = delegate;
}
@Override
public void info(String msg) {
delegate.info(msg);
appendLog(INFO, msg, null);
}
@Override
public void info(String msg, Throwable throwable) {
delegate.info(msg, throwable);
appendLog(INFO, msg, throwable);
}
@Override
public void warn(String msg) {
delegate.warn(msg);
appendLog(WARNING, msg, null);
}
@Override
public void warn(String msg, Throwable throwable) {
delegate.warn(msg, throwable);
appendLog(WARNING, msg, throwable);
}
@Override
public void error(String msg) {
delegate.error(msg);
appendLog(ERROR, msg, null);
}
@Override
public void error(String msg, Throwable throwable) {
delegate.error(msg, throwable);
appendLog(ERROR, msg, throwable);
}
@Override
public void debug(String msg) {
delegate.debug(msg);
appendLog(DEBUG, msg, null);
}
@Override
public void debug(String msg, Throwable throwable) {
delegate.debug(msg, throwable);
appendLog(DEBUG, msg, throwable);
}
}
}
package com.virjar.majora.adr.ui;
import android.app.Fragment;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.afollestad.materialdialogs.MaterialDialog;
import com.virjar.majora.adr.R;
import com.virjar.majora.adr.utils.NavUtil;
import com.virjar.majora.adr.utils.ThemeUtil;
import de.psdev.licensesdialog.LicensesDialog;
import de.psdev.licensesdialog.licenses.ApacheSoftwareLicense20;
import de.psdev.licensesdialog.licenses.MITLicense;
import de.psdev.licensesdialog.licenses.SILOpenFontLicense11;
import de.psdev.licensesdialog.model.Notice;
import de.psdev.licensesdialog.model.Notices;
public class AboutActivity extends MajoraBaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeUtil.setTheme(this);
setContentView(R.layout.activity_container);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
});
ActionBar ab = getSupportActionBar();
if (ab != null) {
ab.setTitle(R.string.nav_item_about);
ab.setDisplayHomeAsUpEnabled(true);
}
setFloating(toolbar, R.string.nav_item_about);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction().add(R.id.container, new AboutFragment()).commit();
}
}
public static class AboutFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.tab_about, container, false);
View developersView = v.findViewById(R.id.developersView);
View licensesView = v.findViewById(R.id.licensesView);
View sourceCodeView = v.findViewById(R.id.sourceCodeView);
String packageName = getActivity().getPackageName();
try {
String version = getActivity().getPackageManager().getPackageInfo(packageName, 0).versionName;
((TextView) v.findViewById(R.id.app_version)).setText(version);
} catch (NameNotFoundException ignored) {
}
licensesView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
createLicenseDialog();
}
});
developersView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MaterialDialog dialog = new MaterialDialog.Builder(getActivity())
.title(R.string.about_developers_label)
.content(R.string.about_developers)
.positiveText(android.R.string.ok)
.show();
((TextView) dialog.findViewById(R.id.md_content)).setMovementMethod(LinkMovementMethod.getInstance());
}
});
sourceCodeView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
NavUtil.startURL(getActivity(), getString(R.string.about_source));
}
});
return v;
}
private void createLicenseDialog() {
Notices notices = new Notices();
notices.addNotice(new Notice("material-dialogs", "https://github.com/afollestad/material-dialogs", "Copyright (c) 2014-2016 Aidan Michael Follestad", new MITLicense()));
notices.addNotice(new Notice("StickyListHeaders", "https://github.com/emilsjolander/StickyListHeaders", "Emil Sjölander", new ApacheSoftwareLicense20()));
notices.addNotice(new Notice("PreferenceFragment-Compat", "https://github.com/Machinarius/PreferenceFragment-Compat", "machinarius", new ApacheSoftwareLicense20()));
notices.addNotice(new Notice("picasso", "https://github.com/square/picasso", "Copyright 2013 Square, Inc.", new ApacheSoftwareLicense20()));
notices.addNotice(new Notice("materialdesignicons", "http://materialdesignicons.com", "Copyright (c) 2014, Austin Andrews", new SILOpenFontLicense11()));
new LicensesDialog.Builder(getActivity())
.setNotices(notices)
.setIncludeOwnLicense(true)
.build()
.show();
}
}
}
package com.virjar.majora.adr.ui;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
public class DefaultSharedPreferenceHolder {
private SharedPreferences mPref;
private DefaultSharedPreferenceHolder(Context context) {
mPref = PreferenceManager.getDefaultSharedPreferences(context);
}
private static DefaultSharedPreferenceHolder instance = null;
public static DefaultSharedPreferenceHolder getInstance(Context context) {
if (instance != null) {
return instance;
}
synchronized (DefaultSharedPreferenceHolder.class) {
if (instance != null) {
return instance;
}
instance = new DefaultSharedPreferenceHolder(context);
}
return instance;
}
public SharedPreferences getPreferences() {
return mPref;
}
private static final String switchKey = "totalSwitchKey";
public void totalSwitch(Boolean enabled) {
if (enabled == null) {
enabled = false;
}
getPreferences().edit().putBoolean(switchKey, enabled).apply();
}
}
This diff is collapsed.
package com.virjar.majora.adr.ui;
import android.app.Fragment;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.virjar.majora.adr.R;
import com.virjar.majora.adr.TheApp;
import com.virjar.majora.adr.majora.MajoraClientService;
import com.virjar.majora.adr.utils.ClientIdentifier;
import com.virjar.majora.adr.utils.CommonUtils;
import eu.chainfire.libsuperuser.Shell;
public class MainPanelFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.tab_main_panel, container, false);
boolean rootAvailable = Shell.SU.available();
TextView rootAv = view.findViewById(R.id.tv_root_available);
rootAv.setText(rootAvailable ? "YES" : "NO");
if (!rootAvailable) {
rootAv.setTextColor(Color.RED);
}
TextView tvClientId = view.findViewById(R.id.tv_client_id);
tvClientId.setText(ClientIdentifier.id());
TextView tvServerConfig = view.findViewById(R.id.tv_server_config);
tvServerConfig.setText(genServerConfigText());
TextView tvBindingAccount = view.findViewById(R.id.tv_binding_account);
tvBindingAccount.setText(getBindingAccountConf());
Button btnReDial = view.findViewById(R.id.btn_redial);
btnReDial.setOnClickListener(view1 -> {
if (Shell.SU.available()) {
MajoraClientService.reDial();
} else {
Toast.makeText(getActivity(), "重播需要root权限", Toast.LENGTH_SHORT).show();
}
});
Button btnResetClientId = view.findViewById(R.id.btn_reset_device_id);
btnResetClientId.setOnClickListener(view12 -> {
if (ClientIdentifier.reset()) {
Toast.makeText(getActivity(), "重置成功,请重启app", Toast.LENGTH_LONG).show();
getActivity().runOnUiThread(() -> System.exit(0));
} else {
Toast.makeText(getActivity(), "重置失败", Toast.LENGTH_SHORT).show();
}
});
return view;
}
private String genServerConfigText() {
SharedPreferences spf = PreferenceManager.getDefaultSharedPreferences(TheApp.getApplication());
String serverHost = spf.getString("server_host", "majora.virjar.com");
int serverPort = CommonUtils.toInt(spf.getString("server_port", "5879"), 5879);
return serverHost + ":" + serverPort;
}
private String getBindingAccountConf() {
SharedPreferences spf = PreferenceManager.getDefaultSharedPreferences(TheApp.getApplication());
return spf.getString("account_identifier", "");
}
}
package com.virjar.majora.adr.ui;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.StringRes;
import android.support.v7.app.AppCompatActivity;
import android.view.WindowManager;
import com.virjar.majora.adr.R;
import com.virjar.majora.adr.utils.ThemeUtil;
public abstract class MajoraBaseActivity extends AppCompatActivity {
public int mTheme = -1;
@Override
protected void onCreate(Bundle savedInstanceBundle) {
super.onCreate(savedInstanceBundle);
ThemeUtil.setTheme(this);
}
@Override
protected void onResume() {
super.onResume();
ThemeUtil.reloadTheme(this);
}
public void setFloating(android.support.v7.widget.Toolbar toolbar, @StringRes int details) {
boolean isTablet = getResources().getBoolean(R.bool.isTablet);
if (isTablet) {
WindowManager.LayoutParams params = getWindow().getAttributes();
params.height = getResources().getDimensionPixelSize(R.dimen.floating_height);
params.width = getResources().getDimensionPixelSize(R.dimen.floating_width);
params.alpha = 1.0f;
params.dimAmount = 0.6f;
params.flags |= 2;
getWindow().setAttributes(params);
if (details != 0) {
toolbar.setTitle(details);
}
toolbar.setNavigationIcon(R.drawable.ic_close);
setFinishOnTouchOutside(true);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().setStatusBarColor(getResources().getColor(R.color.colorPrimaryDark));
}
}
}
\ No newline at end of file
package com.virjar.majora.adr.ui;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
import android.view.View;
import com.virjar.majora.adr.R;
import com.virjar.majora.adr.utils.ThemeUtil;
public class SettingsActivity extends MajoraBaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeUtil.setTheme(this);
setContentView(R.layout.activity_container);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
});
ActionBar ab = getSupportActionBar();
if (ab != null) {
ab.setTitle(R.string.nav_item_settings);
ab.setDisplayHomeAsUpEnabled(true);
}
setFloating(toolbar, 0);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(R.id.container, new SettingsFragment()).commit();
}
}
public static class SettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener, Preference.OnPreferenceClickListener {
public SettingsFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.prefs);
}
@Override
public void onResume() {
super.onResume();
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
}
@Override
public void onPause() {
super.onPause();
getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals("theme")) getActivity().recreate();
}
@Override
public boolean onPreferenceClick(Preference preference) {
return true;
}
}
}
package com.virjar.majora.adr.ui;
import android.app.Fragment;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import com.virjar.majora.adr.R;
import com.virjar.majora.adr.utils.NavUtil;
import com.virjar.majora.adr.utils.RomUtils;
import com.virjar.majora.adr.utils.ThemeUtil;
public class SupportActivity extends MajoraBaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeUtil.setTheme(this);
setContentView(R.layout.activity_container);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
});
ActionBar ab = getSupportActionBar();
if (ab != null) {
ab.setTitle(R.string.nav_item_support);
ab.setDisplayHomeAsUpEnabled(true);
}
setFloating(toolbar, 0);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction().add(R.id.container, new SupportFragment()).commit();
}
}
public static class SupportFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.tab_support, container, false);
View installerSupportView = v.findViewById(R.id.installerSupportView);
View faqView = v.findViewById(R.id.faqView);
View donateView = v.findViewById(R.id.donateView);
setupView(installerSupportView, R.string.about_support);
setupView(faqView, R.string.support_faq_url);
setupView(donateView, R.string.support_donate_url);
LinearLayout linearLayout = v.findViewById(R.id.donateWithAliPay);
if (!RomUtils.checkApkExist(getActivity(), "com.eg.android.AlipayGphone")) {
linearLayout.setVisibility(View.GONE);
} else {
linearLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String intentFullUrl = "intent://platformapi/startapp?saId=10000007&" +
"clientVersion=3.7.0.0718&qrcode=https%3A%2F%2Fqr.alipay.com%2FFKX05428F2CIZP9P1BZN9A%3F_s" + //这里的URLcode换成扫码得到的结果
"%3Dweb-other&_t=" + System.currentTimeMillis() + "#Intent;" +
"scheme=alipayqr;package=com.eg.android.AlipayGphone;end";
try {
Intent intent = Intent.parseUri(intentFullUrl, Intent.URI_INTENT_SCHEME);
getActivity().startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
return v;
}
public void setupView(View v, final int url) {
v.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
NavUtil.startURL(getActivity(), getString(url));
}
});
}
}
}
package com.virjar.majora.adr.ui.widget;
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.preference.ListPreference;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckedTextView;
import android.widget.ImageView;
import android.widget.ListAdapter;
import com.virjar.majora.adr.R;
import java.util.ArrayList;
import java.util.List;
public class IconListPreference extends ListPreference {
private List<Drawable> mEntryDrawables = new ArrayList<>();
public IconListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.IconListPreference, 0, 0);
CharSequence[] drawables;
try {
drawables = a.getTextArray(R.styleable.IconListPreference_icons);
} finally {
a.recycle();
}
for (CharSequence drawable : drawables) {
int resId = context.getResources().getIdentifier(drawable.toString(), "mipmap", context.getPackageName());
Drawable d = context.getResources().getDrawable(resId);
mEntryDrawables.add(d);
}
setWidgetLayoutResource(R.layout.color_icon_preview);
}
protected ListAdapter createListAdapter() {
final String selectedValue = getValue();
int selectedIndex = findIndexOfValue(selectedValue);
return new AppArrayAdapter(getContext(), R.layout.icon_preference_item, getEntries(), mEntryDrawables, selectedIndex);
}
@Override
protected void onBindView(View view) {
super.onBindView(view);
String selectedValue = getValue();
int selectedIndex = findIndexOfValue(selectedValue);
Drawable drawable = mEntryDrawables.get(selectedIndex);
((ImageView) view.findViewById(R.id.preview)).setImageDrawable(drawable);
}
@Override
protected void onPrepareDialogBuilder(Builder builder) {
builder.setAdapter(createListAdapter(), this);
super.onPrepareDialogBuilder(builder);
}
public class AppArrayAdapter extends ArrayAdapter<CharSequence> {
private List<Drawable> mImageDrawables = null;
private int mSelectedIndex = 0;
public AppArrayAdapter(Context context, int textViewResourceId,
CharSequence[] objects, List<Drawable> imageDrawables,
int selectedIndex) {
super(context, textViewResourceId, objects);
mSelectedIndex = selectedIndex;
mImageDrawables = imageDrawables;
}
@Override
@SuppressLint("ViewHolder")
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = ((Activity) getContext()).getLayoutInflater();
View view = inflater.inflate(R.layout.icon_preference_item, parent, false);
CheckedTextView textView = (CheckedTextView) view.findViewById(R.id.label);
textView.setText(getItem(position));
textView.setChecked(position == mSelectedIndex);
ImageView imageView = (ImageView) view.findViewById(R.id.icon);
imageView.setImageDrawable(mImageDrawables.get(position));
return view;
}
}
}
\ No newline at end of file
package com.virjar.majora.adr.ui.widget;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.AttributeSet;
import com.afollestad.materialdialogs.prefs.MaterialListPreference;
public class IntegerListPreference extends MaterialListPreference {
public IntegerListPreference(Context context) {
super(context);
}
public IntegerListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public static int getIntValue(String value) {
if (value == null)
return 0;
return (int) ((value.startsWith("0x"))
? Long.parseLong(value.substring(2), 16)
: Long.parseLong(value));
}
@Override
public void setValue(String value) {
super.setValue(value);
notifyChanged();
}
@Override
protected boolean persistString(String value) {
return value != null && persistInt(getIntValue(value));
}
@Override
protected String getPersistedString(String defaultReturnValue) {
SharedPreferences pref = getPreferenceManager().getSharedPreferences();
String key = getKey();
if (!shouldPersist() || !pref.contains(key))
return defaultReturnValue;
return String.valueOf(pref.getInt(key, 0));
}
@Override
public int findIndexOfValue(String value) {
CharSequence[] entryValues = getEntryValues();
int intValue = getIntValue(value);
if (value != null && entryValues != null) {
for (int i = entryValues.length - 1; i >= 0; i--) {
if (getIntValue(entryValues[i].toString()) == intValue) {
return i;
}
}
}
return -1;
}
}
\ No newline at end of file
package com.virjar.majora.adr.ui.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ListView;
public class MyListView extends ListView {
public MyListView(Context paramContext) {
super(paramContext);
}
public MyListView(Context paramContext, AttributeSet paramAttributeSet) {
super(paramContext, paramAttributeSet);
}
public MyListView(Context paramContext, AttributeSet paramAttributeSet,
int paramInt) {
super(paramContext, paramAttributeSet, paramInt);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
\ No newline at end of file
package com.virjar.majora.adr;
package com.virjar.majora.adr.utils;
import android.Manifest;
import android.app.Application;
......@@ -9,15 +9,11 @@ import android.os.Process;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import com.virjar.majora.adr.TheApp;
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;
......@@ -25,10 +21,11 @@ import java.util.Random;
import java.util.UUID;
public class ClientIdentifier {
private static final String clientIdFileName = "echo_client_id.txt";
private static final String clientIdFileName = "majora_client_id.txt";
private static final String UN_RESOLVE = "un_resolve_";
public static boolean isAndroid = isAndroidEnv();
private static String clientIdInMemory;
public static String id() {
......@@ -39,7 +36,7 @@ public class ClientIdentifier {
File file = resolveIdCacheFile();
if (file.exists()) {
try {
String s = readFile(file);
String s = CommonUtils.readFile(file);
if (s != null && !s.isEmpty() && !s.startsWith(UN_RESOLVE)) {
clientIdInMemory = s;
return clientIdInMemory;
......@@ -51,41 +48,19 @@ public class ClientIdentifier {
clientIdInMemory = generateClientId() + "_" + new Random().nextInt(10000);
try {
writeFile(file, clientIdInMemory);
CommonUtils.writeFile(file, clientIdInMemory);
} catch (IOException e) {
MajoraLogger.getLogger().error("can not write id file: " + file.getAbsolutePath(), e);
}
return 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 s = generateClientIdForAndroid();
if (s != null && !s.isEmpty()) {
return s;
}
String mac = generateClientIdForNormalJVM();
......@@ -169,36 +144,13 @@ public class ClientIdentifier {
}
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;
public static boolean reset() {
return resolveIdCacheFile().delete();
}
}
package com.virjar.majora.adr.utils;
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;
public class CommonUtils {
public static int toInt(final String str, final int defaultValue) {
if(str == null) {
return defaultValue;
}
try {
return Integer.parseInt(str);
} catch (final NumberFormatException nfe) {
return defaultValue;
}
}
public static void writeFile(File file, String data) throws IOException {
try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
fileOutputStream.write(data.getBytes());
}
}
public 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);
}
}
}
package com.virjar.majora.adr.utils;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.provider.Browser;
import android.support.customtabs.CustomTabsIntent;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.URLSpan;
import android.text.util.Linkify;
import android.util.Log;
import android.widget.Toast;
import com.virjar.majora.adr.R;
import com.virjar.majora.adr.ui.DefaultSharedPreferenceHolder;
import java.util.List;
public final class NavUtil {
public static final String SETTINGS_CATEGORY = "de.robv.android.xposed.category.MODULE_SETTINGS";
private static Uri parseURL(String str) {
if (str == null || str.isEmpty())
return null;
Spannable spannable = new SpannableString(str);
Linkify.addLinks(spannable, Linkify.WEB_URLS | Linkify.EMAIL_ADDRESSES);
URLSpan[] spans = spannable.getSpans(0, spannable.length(), URLSpan.class);
return (spans.length > 0) ? Uri.parse(spans[0].getURL()) : null;
}
private static void startURL(Activity activity, Uri uri) {
if (!DefaultSharedPreferenceHolder.getInstance(activity).getPreferences().getBoolean("chrome_tabs", true)) {
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.putExtra(Browser.EXTRA_APPLICATION_ID, activity.getPackageName());
activity.startActivity(intent);
return;
}
CustomTabsIntent.Builder customTabsIntent = new CustomTabsIntent.Builder();
customTabsIntent.setShowTitle(true);
customTabsIntent.setToolbarColor(activity.getResources().getColor(R.color.colorPrimary));
customTabsIntent.build().launchUrl(activity, uri);
}
public static void startURL(Activity activity, String url) {
startURL(activity, parseURL(url));
}
public static void startApp(Context context, String packageName) {
Intent launchIntent = getSettingsIntent(context, packageName);
if (launchIntent != null) {
// 优先使用 Shizuku
// launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// ComponentName component = launchIntent.getComponent();
// if (component != null) {
// Intent intent = new Intent();
// intent.setComponent(component);
// if (ShizukuToolkit.startActivity(intent)) {
// Log.i(RatelManagerApp.TAG, "start app use Shizuku success");
// return;
// }
// }
context.startActivity(launchIntent);
}
// else {
// Toast.makeText(context,
// context.getString(R.string.module_no_ui),
// Toast.LENGTH_LONG).show();
// }
}
private static Intent getSettingsIntent(Context activity, String packageName) {
// taken from
// ApplicationPackageManager.getLaunchIntentForPackage(String)
// first looks for an Xposed-specific category, falls back to
// getLaunchIntentForPackage
PackageManager pm = activity.getPackageManager();
Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
intentToResolve.addCategory(SETTINGS_CATEGORY);
intentToResolve.setPackage(packageName);
List<ResolveInfo> ris = pm.queryIntentActivities(intentToResolve, 0);
if (ris == null || ris.size() <= 0) {
return pm.getLaunchIntentForPackage(packageName);
}
Intent intent = new Intent(intentToResolve);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClassName(ris.get(0).activityInfo.packageName, ris.get(0).activityInfo.name);
return intent;
}
}
package com.virjar.majora.adr.utils;
import android.app.Activity;
import android.app.AppOpsManager;
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.hjq.permissions.Permission;
import com.hjq.permissions.XXPermissions;
import com.virjar.majora.adr.TheApp;
import com.virjar.majora.client.sdk.log.MajoraLogger;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class PermissionUtils {
public static boolean checkPermission() {
return XXPermissions.isGranted(TheApp.getApplication(), minPermissions);
}
public static final String[] minPermissions = new String[]{
Permission.READ_PHONE_STATE,
Permission.SYSTEM_ALERT_WINDOW,
// Permission.SYSTEM_OVERLAY_WINDOW,
// Permission.FOREGROUND_SERVICE
};
public static void doGrantPermission(Activity context) {
if (!checkFloatPermission(context)) {
MajoraLogger.getLogger().info("请给软件设置悬浮窗权限,否则可能影响后台网络!");
Toast.makeText(context, "请给软件设置悬浮窗权限,否则可能影响后台网络!", 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);
context.startActivity(intent);
}
}
if (!canBackgroundStart(context)) {
Toast.makeText(context, "请给软件设置允许后台启动页面权限,否则可能影响后台网络!", Toast.LENGTH_SHORT).show();
}
}
private static 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 static 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;
}
}
}
package com.virjar.majora.adr.utils;
import android.content.Context;
import android.content.pm.PackageManager;
public class RomUtils {
//检测手机上是否安装某应用
public static boolean checkApkExist(Context context, String packageName) {
if (packageName == null || "".equals(packageName))
return false;
try {
context.getPackageManager().getApplicationInfo(packageName,
PackageManager.GET_UNINSTALLED_PACKAGES);
return true;
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
}
\ No newline at end of file
package com.virjar.majora.adr.utils;
import android.content.Context;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.text.TextUtils;
import android.view.View;
import android.widget.TextView;
import com.virjar.majora.adr.R;
import com.virjar.majora.adr.TheApp;
import com.virjar.majora.adr.ui.DefaultSharedPreferenceHolder;
import com.virjar.majora.adr.ui.MajoraBaseActivity;
public final class ThemeUtil {
private static final int[] THEMES = new int[]{
R.style.Theme_Majora_Light,
R.style.Theme_Majora_Dark,
R.style.Theme_Majora_Dark_Black,};
private ThemeUtil() {
}
private static int getSelectTheme() {
int theme = DefaultSharedPreferenceHolder.getInstance(TheApp.getApplication()).getPreferences().getInt("theme", 0);
return (theme >= 0 && theme < THEMES.length) ? theme : 0;
}
public static void setTheme(MajoraBaseActivity activity) {
activity.mTheme = getSelectTheme();
activity.setTheme(THEMES[activity.mTheme]);
}
public static void reloadTheme(MajoraBaseActivity activity) {
int theme = getSelectTheme();
if (theme != activity.mTheme)
activity.recreate();
}
public static int getThemeColor(Context context, int id) {
Theme theme = context.getTheme();
TypedArray a = theme.obtainStyledAttributes(new int[]{id});
int result = a.getColor(0, 0);
a.recycle();
return result;
}
public static void setTextView(View root, int id, String value) {
if (TextUtils.isEmpty(value)) {
return;
}
TextView certificateIdTextView = root.findViewById(id);
certificateIdTextView.setText(value);
}
}
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_shortAnimTime"
android:interpolator="@android:interpolator/accelerate_quad"
android:propertyName="alpha"
android:valueFrom="0"
android:valueTo="1"
/>
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_shortAnimTime"
android:interpolator="@android:interpolator/accelerate_quad"
android:propertyName="alpha"
android:valueFrom="1"
android:valueTo="0"
/>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#ffffff"
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#ffffff"
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#757575"
android:pathData="M14,2L6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2L18,22c1.1,0 2,-0.9 2,-2L20,8l-6,-6zM16,18L8,18v-2h8v2zM16,14L8,14v-2h8v2zM13,9L13,3.5L18.5,9L13,9z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#757575"
android:pathData="M11.8,10.9c-2.27,-0.59 -3,-1.2 -3,-2.15 0,-1.09 1.01,-1.85 2.7,-1.85 1.78,0 2.44,0.85 2.5,2.1h2.21c-0.07,-1.72 -1.12,-3.3 -3.21,-3.81V3h-3v2.16c-1.94,0.42 -3.5,1.68 -3.5,3.61 0,2.31 1.91,3.46 4.7,4.13 2.5,0.6 3,1.48 3,2.41 0,0.69 -0.49,1.79 -2.7,1.79 -2.06,0 -2.87,-0.92 -2.98,-2.1h-2.2c0.12,2.19 1.76,3.42 3.68,3.83V21h3v-2.15c1.95,-0.37 3.5,-1.5 3.5,-3.55 0,-2.84 -2.43,-3.81 -4.7,-4.4z"/>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24.0dip"
android:height="24.0dip"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#757575"
android:pathData="M12,2A10,10 0 0,0 2,12C2,16.42 4.87,20.17 8.84,21.5C9.34,21.58 9.5,21.27 9.5,21C9.5,20.77 9.5,20.14 9.5,19.31C6.73,19.91 6.14,17.97 6.14,17.97C5.68,16.81 5.03,16.5 5.03,16.5C4.12,15.88 5.1,15.9 5.1,15.9C6.1,15.97 6.63,16.93 6.63,16.93C7.5,18.45 8.97,18 9.54,17.76C9.63,17.11 9.89,16.67 10.17,16.42C7.95,16.17 5.62,15.31 5.62,11.5C5.62,10.39 6,9.5 6.65,8.79C6.55,8.54 6.2,7.5 6.75,6.15C6.75,6.15 7.59,5.88 9.5,7.17C10.29,6.95 11.15,6.84 12,6.84C12.85,6.84 13.71,6.95 14.5,7.17C16.41,5.88 17.25,6.15 17.25,6.15C17.8,7.5 17.45,8.54 17.35,8.79C18,9.5 18.38,10.39 18.38,11.5C18.38,15.32 16.04,16.16 13.81,16.41C14.17,16.72 14.5,17.33 14.5,18.26C14.5,19.6 14.5,20.68 14.5,21C14.5,21.27 14.66,21.59 15.17,21.5C19.14,20.16 22,16.42 22,12A10,10 0 0,0 12,2Z"/>
</vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#757575"
android:pathData="M11,18h2v-2h-2v2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM12,6c-2.21,0 -4,1.79 -4,4h2c0,-1.1 0.9,-2 2,-2s2,0.9 2,2c0,2 -3,1.75 -3,5h2c0,-2.25 3,-2.5 3,-5 0,-2.21 -1.79,-4 -4,-4z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#757575"
android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#ffffff"
android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#757575"
android:pathData="M20,8h-2.81c-0.45,-0.78 -1.07,-1.45 -1.82,-1.96L17,4.41 15.59,3l-2.17,2.17C12.96,5.06 12.49,5 12,5c-0.49,0 -0.96,0.06 -1.41,0.17L8.41,3 7,4.41l1.62,1.63C7.88,6.55 7.26,7.22 6.81,8L4,8v2h2.09c-0.05,0.33 -0.09,0.66 -0.09,1v1L4,12v2h2v1c0,0.34 0.04,0.67 0.09,1L4,16v2h2.81c1.04,1.79 2.97,3 5.19,3s4.15,-1.21 5.19,-3L20,18v-2h-2.09c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1L20,10L20,8zM14,16h-4v-2h4v2zM14,12h-4v-2h4v2z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M544,832H64V384h704v192c0,17.07 14.93,32 32,32S832,593.07 832,576V160c0,-17.07 -14.93,-32 -32,-32h-768C14.93,128 0,142.93 0,160v704c0,17.07 14.93,32 32,32h512c17.07,0 32,-14.93 32,-32s-14.93,-32 -32,-32zM64,192h704v128H64V192z"
android:fillColor="#212121"/>
<path
android:pathData="M704,480c0,-17.07 -14.93,-32 -32,-32h-512c-17.07,0 -32,14.93 -32,32S142.93,512 160,512h512c17.07,0 32,-14.93 32,-32zM160,576c-17.07,0 -32,14.93 -32,32S142.93,640 160,640h256c17.07,0 32,-14.93 32,-32s-14.93,-32 -32,-32h-256zM128,224h64v64H128zM256,224h64v64h-64zM384,224h64v64h-64zM1015.47,535.47c-12.8,-12.8 -32,-12.8 -44.8,0L699.73,804.27l-96,-96c-12.8,-12.8 -32,-12.8 -44.8,0 -12.8,12.8 -12.8,32 0,44.8l119.47,117.33c6.4,6.4 14.93,8.53 23.47,8.53s17.07,-2.13 23.47,-8.53l292.27,-290.13c8.53,-10.67 8.53,-32 -2.13,-44.8z"
android:fillColor="#212121"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#ffffff"
android:pathData="M11,18h2v-2h-2v2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM12,6c-2.21,0 -4,1.79 -4,4h2c0,-1.1 0.9,-2 2,-2s2,0.9 2,2c0,2 -3,1.75 -3,5h2c0,-2.25 3,-2.5 3,-5 0,-2.21 -1.79,-4 -4,-4z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#757575"
android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#ffffff"
android:pathData="M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM12,19c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM15,9L5,9L5,5h10v4z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="#757575"
android:pathData="M16.59,5.59L18,7L12,13L6,7L7.41,5.59L12,10.17L16.59,5.59M16.59,11.59L18,13L12,19L6,13L7.41,11.59L12,16.17L16.59,11.59Z"/>
</vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="#757575"
android:pathData="M7.41,18.41L6,17L12,11L18,17L16.59,18.41L12,13.83L7.41,18.41M7.41,12.41L6,11L12,5L18,11L16.59,12.41L12,7.83L7.41,12.41Z"/>
</vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#ffffff"
android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
</vector>
<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"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/background_card_pressed_black"
android:state_pressed="true"/>
<item android:drawable="@drawable/background_card_normal_black"
android:state_focused="false"/>
</selector>
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/background_card_pressed_dark"
android:state_pressed="true"/>
<item android:drawable="@drawable/background_card_normal_dark"
android:state_focused="false"/>
</selector>
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/background_card_pressed_light"
android:state_pressed="true"/>
<item android:drawable="@drawable/background_card_normal_light"
android:state_focused="false"/>
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!-- modified from from https://gist.github.com/iheanyi/5774841 -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:dither="true"
android:shape="rectangle">
<corners android:radius="2dp"/>
<solid android:color="@color/card_shadow_black"/>
</shape>
</item>
<item android:bottom="2dp">
<shape
android:dither="true"
android:shape="rectangle">
<corners android:radius="2dp"/>
<solid android:color="@color/card_background_black"/>
<padding
android:bottom="8dp"
android:left="8dp"
android:right="8dp"
android:top="8dp"/>
</shape>
</item>
</layer-list>
<?xml version="1.0" encoding="utf-8"?>
<!-- modified from from https://gist.github.com/iheanyi/5774841 -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:dither="true"
android:shape="rectangle">
<corners android:radius="2dp"/>
<solid android:color="@color/card_shadow_dark"/>
</shape>
</item>
<item android:bottom="2dp">
<shape
android:dither="true"
android:shape="rectangle">
<corners android:radius="2dp"/>
<solid android:color="@color/card_background_dark"/>
<padding
android:bottom="8dp"
android:left="8dp"
android:right="8dp"
android:top="8dp"/>
</shape>
</item>
</layer-list>
<?xml version="1.0" encoding="utf-8"?>
<!-- modified from from https://gist.github.com/iheanyi/5774841 -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:dither="true"
android:shape="rectangle">
<corners android:radius="2dp"/>
<solid android:color="@color/card_shadow_light"/>
</shape>
</item>
<item android:bottom="2dp">
<shape
android:dither="true"
android:shape="rectangle">
<corners android:radius="2dp"/>
<solid android:color="@color/card_background_light"/>
<padding
android:bottom="8dp"
android:left="8dp"
android:right="8dp"
android:top="8dp"/>
</shape>
</item>
</layer-list>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!-- modified from from https://gist.github.com/iheanyi/5774841 -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:dither="true"
android:shape="rectangle">
<corners android:radius="2dp"/>
<solid android:color="@color/card_shadow_black"/>
</shape>
</item>
<item android:bottom="2dp">
<shape
android:dither="true"
android:shape="rectangle">
<corners android:radius="2dp"/>
<solid android:color="@color/card_background_pressed_black"/>
<padding
android:bottom="8dp"
android:left="8dp"
android:right="8dp"
android:top="8dp"/>
</shape>
</item>
</layer-list>
<?xml version="1.0" encoding="utf-8"?>
<!-- modified from from https://gist.github.com/iheanyi/5774841 -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:dither="true"
android:shape="rectangle">
<corners android:radius="2dp"/>
<solid android:color="@color/card_shadow_dark"/>
</shape>
</item>
<item android:bottom="2dp">
<shape
android:dither="true"
android:shape="rectangle">
<corners android:radius="2dp"/>
<solid android:color="@color/card_background_pressed_dark"/>
<padding
android:bottom="8dp"
android:left="8dp"
android:right="8dp"
android:top="8dp"/>
</shape>
</item>
</layer-list>
<?xml version="1.0" encoding="utf-8"?>
<!-- modified from from https://gist.github.com/iheanyi/5774841 -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:dither="true"
android:shape="rectangle">
<corners android:radius="2dp"/>
<solid android:color="@color/card_shadow_light"/>
</shape>
</item>
<item android:bottom="2dp">
<shape
android:dither="true"
android:shape="rectangle">
<corners android:radius="2dp"/>
<solid android:color="@color/card_background_pressed_light"/>
<padding
android:bottom="8dp"
android:left="8dp"
android:right="8dp"
android:top="8dp"/>
</shape>
</item>
</layer-list>
\ No newline at end of file
<?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"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:angle="90"
android:endColor="#40000000"
android:startColor="@android:color/transparent"/>
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:windowBackground"
android:orientation="vertical">
<include layout="@layout/toolbar"/>
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
</LinearLayout>
\ No newline at end of file
......@@ -36,7 +36,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="25dp"
android:text="@string/majoraapp"
android:text="@string/majora_app"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
......
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
android:id="@+id/drawer_layout"
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"
android:fitsSystemWindows="true"
tools:context="com.virjar.majora.adr.ui.WelcomeActivity">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:windowBackground"
android:orientation="vertical">
<FrameLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<include layout="@layout/toolbar"/>
</FrameLayout>
<android.support.design.widget.NavigationView
android:id="@+id/navigation_view"
android:layout_width="@dimen/nav_drawer_width"
android:layout_height="match_parent"
android:layout_gravity="start"
android:clickable="true"
android:clipToPadding="false"
android:elevation="4dp"
android:fitsSystemWindows="true"
app:insetForeground="#4000"
app:menu="@menu/drawer"/>
</android.support.v4.widget.DrawerLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<ImageView android:id="@+id/preview"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="48dp"
android:layout_height="48dp"/>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 2013, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:padding="6dip">
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:padding="6dip"/>
<CheckedTextView
android:id="@+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_toRightOf="@+id/icon"
android:checkMark="?android:attr/listChoiceIndicatorSingle"
android:ellipsize="marquee"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeight"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:attr/textColorAlertDialogListItem"
android:textIsSelectable="false"/>
</RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
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="fill_parent"
android:layout_height="wrap_content"
android:padding="8dp"
tools:ignore="UseCompoundDrawables,ContentDescription">
<LinearLayout
android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.CardView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="8dp"
app:cardBackgroundColor="?attr/list_download_item_color"
app:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:orientation="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="72dp"
android:orientation="horizontal"
android:padding="16dp">
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center_vertical"
android:src="@mipmap/ic_launcher"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:text="@string/app_name"
android:textAppearance="@style/TextAppearance.AppCompat.Headline"/>
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:gravity="center_vertical"
android:minHeight="48dp"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_info"/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="32dp"
android:layout_marginStart="32dp"
android:orientation="vertical"
android:paddingBottom="8dp"
android:paddingTop="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/about_version_label"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"/>
<TextView
android:id="@+id/app_version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:textAppearance="@style/TextAppearance.AppCompat.Caption"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/developersView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:gravity="center_vertical"
android:minHeight="48dp"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_person"/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="32dp"
android:layout_marginStart="32dp"
android:orientation="vertical"
android:paddingBottom="8dp"
android:paddingTop="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/about_developers_label"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/licensesView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:gravity="center_vertical"
android:minHeight="@dimen/md_listitem_height"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_description"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="32dp"
android:layout_marginStart="32dp"
android:paddingBottom="8dp"
android:paddingTop="8dp"
android:text="@string/about_libraries_label"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"/>
</LinearLayout>
<LinearLayout
android:id="@+id/sourceCodeView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:gravity="center_vertical"
android:minHeight="48dp"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_github"/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="32dp"
android:layout_marginStart="32dp"
android:orientation="vertical"
android:paddingBottom="8dp"
android:paddingTop="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/about_source_label"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
</ScrollView>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="@+id/container"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize">
<ScrollView
android:id="@+id/svLog"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<HorizontalScrollView
android:id="@+id/hsvLog"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/txtLog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:textIsSelectable="true"/>
</HorizontalScrollView>
</ScrollView>
<!--
<android.support.design.widget.FloatingActionButton
android:layout_width="40dp"
android:layout_height="40dp"
app:backgroundTint="#fff"
android:id="@+id/scroll_top"
android:src="@drawable/ic_scroll_top"
android:layout_marginBottom="72dp"
android:layout_alignParentBottom="true"
android:layout_alignLeft="@+id/scroll_down"
android:layout_alignStart="@+id/scroll_down"/>
<android.support.design.widget.FloatingActionButton
android:layout_width="40dp"
android:layout_height="40dp"
app:backgroundTint="#fff"
android:id="@+id/scroll_down"
android:layout_margin="16dp"
android:src="@drawable/ic_scroll_down"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"/>
-->
</RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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="fill_parent"
android:layout_height="wrap_content"
android:padding="8dp"
tools:ignore="UseCompoundDrawables,ContentDescription">
<LinearLayout
android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="50dp"
android:layout_marginBottom="8dp"
app:cardBackgroundColor="?attr/list_download_item_color"
app:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="6dp"
android:text="@string/majora_status"
android:textAllCaps="true"
android:textStyle="bold" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<TextView
android:layout_width="90dp"
android:layout_height="24dp"
android:text="@string/root_available" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:orientation="vertical"
android:paddingTop="8dp"
android:paddingBottom="8dp">
<TextView
android:id="@+id/tv_root_available"
android:layout_width="90dp"
android:layout_height="24dp"
android:text="false" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<TextView
android:layout_width="90dp"
android:layout_height="24dp"
android:text="@string/client_id" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:orientation="vertical"
android:paddingTop="8dp"
android:paddingBottom="8dp">
<TextView
android:id="@+id/tv_client_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
tools:text="1" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<TextView
android:layout_width="90dp"
android:layout_height="24dp"
android:text="@string/server_config" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:orientation="vertical"
android:paddingTop="8dp"
android:paddingBottom="8dp">
<TextView
android:id="@+id/tv_server_config"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
tools:text="1" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<TextView
android:layout_width="90dp"
android:layout_height="24dp"
android:text="@string/binding_account" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:orientation="vertical"
android:paddingTop="8dp"
android:paddingBottom="8dp">
<TextView
android:id="@+id/tv_binding_account"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginBottom="6dp"
app:cardBackgroundColor="?attr/list_download_item_color"
app:cardElevation="2dp"
app:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="6dp"
android:text="@string/op_panel"
android:textAllCaps="true"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="false"
android:orientation="horizontal">
<!-- 重播按钮 -->
<Button
android:id="@+id/btn_redial"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/txt_reDial"
tools:ignore="ButtonStyle" />
<Button
android:id="@+id/btn_reset_device_id"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/txt_reset_device_id"
tools:ignore="ButtonStyle" />
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
</ScrollView>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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"
android:padding="8dp"
tools:ignore="UseCompoundDrawables,ContentDescription">
<LinearLayout
android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.CardView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="8dp"
app:cardBackgroundColor="?attr/list_download_item_color"
app:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:orientation="vertical">
<LinearLayout
android:id="@+id/installerSupportView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:gravity="center_vertical"
android:minHeight="48dp"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_help" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginLeft="32dp"
android:orientation="vertical"
android:paddingTop="8dp"
android:paddingBottom="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/support_framework_label"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/faqView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:gravity="center_vertical"
android:minHeight="48dp"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_nav_logs" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginLeft="32dp"
android:orientation="vertical"
android:paddingTop="8dp"
android:paddingBottom="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/support_faq_label"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/donateView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:gravity="center_vertical"
android:minHeight="48dp"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_donate" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginLeft="32dp"
android:orientation="vertical"
android:paddingTop="8dp"
android:paddingBottom="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/support_donate_label"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/support_donate_description"
android:textAppearance="@style/TextAppearance.AppCompat.Caption" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/donateWithAliPay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:gravity="center_vertical"
android:minHeight="48dp"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_donate" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginLeft="32dp"
android:orientation="vertical"
android:paddingTop="8dp"
android:paddingBottom="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="点击这里直接支付宝捐赠"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
</ScrollView>
\ 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:layout_width="40dp"
android:layout_height="40dp"
android:src="@mipmap/ic_launcher" />
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/Theme.Majora.Toolbar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
<View
android:id="@+id/elevation"
android:layout_width="match_parent"
android:layout_height="@dimen/toolbar_elevation"
android:background="@drawable/toolbar_shadow"/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group
android:id="@+id/main_items"
android:checkableBehavior="single">
<item
android:id="@+id/nav_item_main_status"
android:icon="@drawable/ic_nav_main_status"
android:title="@string/nav_item_main" />
<item
android:id="@+id/nav_item_logs"
android:icon="@drawable/ic_nav_logs"
android:title="@string/nav_item_logs" />
</group>
<group
android:id="@+id/option_items"
android:checkableBehavior="all">
<item
android:id="@+id/nav_item_settings"
android:icon="@drawable/ic_nav_settings"
android:title="@string/nav_item_settings" />
<item
android:id="@+id/nav_item_support"
android:icon="@drawable/ic_nav_support"
android:title="@string/nav_item_support" />
<item
android:id="@+id/nav_item_about"
android:icon="@drawable/ic_nav_about"
android:title="@string/nav_item_about" />
</group>
</menu>
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_save"
android:icon="@drawable/ic_save"
android:title="@string/menuSaveToSd"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/menu_send"
android:icon="@drawable/ic_send"
android:title="@string/menuSend"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/menu_clear"
android:icon="@drawable/ic_delete"
android:title="@string/menuClearLog"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/menu_refresh"
android:icon="@drawable/ic_menu_refresh"
android:title="@string/menuReload"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/menu_scroll_top"
android:icon="@drawable/ic_scroll_top"
android:title="@string/scroll_top"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/menu_scroll_down"
android:icon="@drawable/ic_scroll_down"
android:title="@string/scroll_bottom"
app:showAsAction="ifRoom"/>
</menu>
\ 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 xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<string-array name="theme_texts" translatable="false">
<item>@string/settings_theme_light</item>
<item>@string/settings_theme_dark</item>
<item>@string/settings_theme_black</item>
</string-array>
<string-array name="default_view_texts" translatable="false">
<item>@string/nav_item_main</item>
<item>@string/nav_item_logs</item>
</string-array>
<string-array name="theme_values" translatable="false">
<item>0</item>
<item>1</item>
<item>2</item>
</string-array>
<string-array name="default_view_values" translatable="false">
<item>0</item>
<item>1</item>
<item>2</item>
</string-array>
<string-array name="release_type_values" translatable="false">
<item>stable</item>
<item>beta</item>
<item>experimental</item>
</string-array>
<string-array name="module_release_type_values" translatable="false">
<item></item>
<item>stable</item>
<item>beta</item>
<item>experimental</item>
</string-array>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="background_card" format="reference"/>
<attr name="background_card_pressed" format="reference"/>
<attr name="pager_tab_strip_bg" format="reference"/>
<attr name="text_important" format="reference|color"/>
<attr name="download_status_installed" format="reference|color"/>
<attr name="sticky_header_background" format="reference|color"/>
<attr name="list_download_item_color" format="reference|color"/>
<declare-styleable name="IconListPreference">
<attr name="icons" format="reference"/>
</declare-styleable>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="isTablet">false</bool>
</resources>
\ No newline at end of file
......@@ -4,4 +4,31 @@
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
<color name="white">#FFF</color>
<color name="red_500">#F44336</color>
<!-- light theme-->
<color name="background_material_light">#ffeeeeee</color>
<color name="list_download_item_color_light">#fff</color>
<color name="card_background_light">#fff</color>
<color name="card_background_pressed_light">#dfdfdf</color>
<color name="card_shadow_light">#dbdbdb</color>
<color name="pager_tab_strip_bg_light">#222</color>
<color name="sticky_header_background_light">#e5e5e5</color>
<!-- dark theme -->
<color name="background_material_dark">#ff303030</color>
<color name="list_download_item_color_dark">#363636</color>
<color name="card_background_dark">#363636</color>
<color name="card_background_pressed_dark">#505050</color>
<color name="card_shadow_dark">#161616</color>
<color name="pager_tab_strip_bg_dark">#222</color>
<color name="sticky_header_background_dark">#505050</color>
<!-- black theme -->
<color name="app_background_black">#000000</color>
<color name="list_download_item_color_black">#181818</color>
<color name="card_background_black">#181818</color>
<color name="card_background_pressed_black">#303030</color>
<color name="card_shadow_black">#121212</color>
</resources>
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="toolbar_elevation">4dp</dimen>
<!-- Default Navigation Drawer. -->
<dimen name="nav_drawer_top_padding">0dp</dimen>
<dimen name="nav_drawer_width">264dp</dimen>
<dimen name="nav_drawer_header_height">148dp</dimen>
<dimen name="floating_height">650dp</dimen>
<dimen name="floating_width">500dp</dimen>
</resources>
......@@ -7,5 +7,64 @@
<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>
<string name="majora_app">MajoraApp</string>
<string name="nav_item_main">面板</string>
<string name="nav_item_logs">日志</string>
<string name="nav_item_settings">设置</string>
<string name="nav_item_support">支持</string>
<string name="nav_item_about">关于</string>
<string name="settings_group_app">应用</string>
<string name="settings_group_user">用户</string>
<string name="phone_identifier">结算账户</string>
<string name="support_framework_label">框架/安装支持</string>
<string name="support_faq_label">常见问题/已知问题</string>
<string name="support_donate_label">捐赠</string>
<string name="support_donate_description">如果您想感谢Majora作者,可以在此向 virjar 捐赠</string>
<string name="support_faq_url" translatable="false">https://git.virjar.com/majora</string>
<string name="support_donate_url" translatable="false">https://git.virjar.com/majora</string>
<string name="about_support" translatable="false">https://git.virjar.com/majora</string>
<string name="navigation_drawer_open">打开导航抽屉</string>
<string name="navigation_drawer_close">关闭导航抽屉</string>
<string name="about_developers_label">主要开发者</string>
<string name="about_developers"> <a href="https://github.com/virjar">virjar</a> </string>
<string name="about_source" translatable="false">http://git.virjar.com/majora</string>
<string name="settings_theme">主题</string>
<string name="settings_theme_light">亮色</string>
<string name="settings_theme_dark">暗色</string>
<string name="settings_theme_black">黑色(对 AMOLED 屏幕有益)</string>
<string name="settings_default_view">默认视图</string>
<string name="menuSend">发送</string>
<string name="menuSaveToSd">保存到 SD 卡</string>
<string name="logs_save_failed">无法将日志写入 SD 卡:</string>
<string name="sdcard_not_writable">SD 卡未找到或不可写入</string>
<string name="menuClearLog">立刻清理日志</string>
<string name="logs_cleared">日志清理成功</string>
<string name="logs_clear_failed">无法清理日志:</string>
<string name="log_is_empty">日志为空</string>
<string name="scroll_top">滚动页面至顶部</string>
<string name="permissionNotGranted">没有写入外部存储权限该功能将不能工作。</string>
<string name="scroll_bottom">拖动至到底端</string>
<string name="loading">载入中…</string>
<string name="menuReload">重新载入</string>
<string name="about_version_label">版本</string>
<string name="about_libraries_label">使用的库文件</string>
<string name="about_source_label">源码</string>
<string name="op_panel">操作面板</string>
<string name="txt_reDial">重播</string>
<string name="txt_reset_device_id">重制设备id</string>
<string name="majora_status">Majora状态</string>
<string name="root_available">root</string>
<string name="client_id">clientId</string>
<string name="server_config">服务器配置</string>
<string name="binding_account">结算账号</string>
</resources>
......@@ -8,4 +8,55 @@
<item name="colorAccent">@color/colorAccent</item>
</style>
<!-- Base application light theme. -->
<style name="Theme.Majora.Light.Base" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<!-- Base application dark theme. -->
<style name="Theme.Majora.Dark.Base" parent="Theme.AppCompat.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="Theme.Majora.Light" parent="Theme.Majora.Light.Base">
<item name="android:windowBackground">@color/background_material_light</item>
<item name="sticky_header_background">@color/sticky_header_background_light</item>
<item name="list_download_item_color">@color/list_download_item_color_light</item>
<item name="background_card">@drawable/background_card_light</item>
<item name="background_card_pressed">@drawable/background_card_pressed_light</item>
<item name="pager_tab_strip_bg">@color/pager_tab_strip_bg_light</item>
<item name="text_important">@color/red_500</item>
<item name="download_status_installed">@android:color/holo_orange_dark</item>
</style>
<style name="Theme.Majora.Dark" parent="Theme.Majora.Dark.Base">
<item name="android:windowBackground">@color/background_material_dark</item>
<item name="sticky_header_background">@color/sticky_header_background_dark</item>
<item name="list_download_item_color">@color/list_download_item_color_dark</item>
<item name="background_card">@drawable/background_card_dark</item>
<item name="background_card_pressed">@drawable/background_card_pressed_dark</item>
<item name="pager_tab_strip_bg">@color/pager_tab_strip_bg_dark</item>
<item name="text_important">@android:color/holo_orange_light</item>
<item name="download_status_installed">@android:color/holo_orange_light</item>
</style>
<style name="Theme.Majora.Dark.Black" parent="Theme.Majora.Dark">
<item name="android:windowBackground">@color/app_background_black</item>
<item name="list_download_item_color">@color/list_download_item_color_black</item>
<item name="background_card">@drawable/background_card_black</item>
<item name="background_card_pressed">@drawable/background_card_pressed_black</item>
</style>
<style name="Theme.Majora.Toolbar" parent="ThemeOverlay.AppCompat.Light">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="android:textColorPrimary">@android:color/white</item>
<item name="actionMenuTextColor">@android:color/white</item>
<item name="android:textColorSecondary">@android:color/white</item>
</style>
<style name="Theme.Majora.Transparent" parent="Theme.AppCompat.NoActionBar"></style>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-cache-path
name="downloads"
path="downloads" />
<root-path
name="log_legacy"
path="/data/data/com.virjar.majora.adr/files/log/" />
<root-path
name="log"
path="/data/user_de/0/com.virjar.majora.adr/files/log/" />
</paths>
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<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:key="group_app"
android:title="@string/settings_group_app">
<com.virjar.majora.adr.ui.widget.IntegerListPreference
android:defaultValue="0"
android:entries="@array/theme_texts"
android:entryValues="@array/theme_values"
android:key="theme"
android:summary="%s"
android:title="@string/settings_theme"
app:useStockLayout="true" />
<com.virjar.majora.adr.ui.widget.IntegerListPreference
android:defaultValue="0"
android:entries="@array/default_view_texts"
android:entryValues="@array/default_view_values"
android:key="default_view"
android:summary="%s"
android:title="@string/settings_default_view"
app:useStockLayout="true" />
</PreferenceCategory>
<PreferenceCategory
android:key="group_user"
android:title="@string/settings_group_user">
<EditTextPreference
android:defaultValue=""
android:key="account_identifier"
android:title="@string/phone_identifier">
</EditTextPreference>
</PreferenceCategory>
</PreferenceScreen>
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