diff --git a/.github/workflows/delete-workflow-runs.yml b/.github/workflows/delete-workflow-runs.yml new file mode 100644 index 000000000..accc54494 --- /dev/null +++ b/.github/workflows/delete-workflow-runs.yml @@ -0,0 +1,16 @@ +--- +name: Delete old workflow runs +on: + workflow_dispatch: + +jobs: + del_runs: + runs-on: ubuntu-latest + steps: + - name: Delete workflow runs + uses: Mattraks/delete-workflow-runs@v2 + with: + token: ${{ github.token }} + repository: ${{ github.repository }} + retain_days: 15 + keep_minimum_runs: 6 diff --git a/app/build.gradle b/app/build.gradle index bab71e916..8d201fcf8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,61 +1,57 @@ plugins { - id 'com.android.application' - id 'org.jetbrains.kotlin.android' + id 'com.android.application' + id 'org.jetbrains.kotlin.android' } android { - namespace 'com.pranav.java.ide' - compileSdk 32 + namespace 'com.pranav.java.ide' + compileSdk 32 - defaultConfig { - applicationId 'com.pranav.java.ide' - minSdk 26 - targetSdk 32 - versionCode 3 - versionName '0.0.3' - } + defaultConfig { + applicationId 'com.pranav.java.ide' + minSdk 26 + targetSdk 32 + versionCode 3 + versionName '0.0.3' + } - buildTypes { - release { - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - - debug { - multiDexEnabled true - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_11 - targetCompatibility JavaVersion.VERSION_11 - } - - kotlinOptions { - jvmTarget = '1.8' - } - - packagingOptions { - resources.excludes.add("plugin.xml") - resources.excludes.add(".options") - resources.excludes.add("about.xml") - resources.excludes.add("plugin.properties") - resources.excludes.add(".api_description") - resources.excludes.add("about_files/*") - resources.excludes.add("META-INF/*") - } + buildTypes { + release { + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + + debug { + multiDexEnabled true + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + packagingOptions { + resources.excludes.add("plugin.xml") + resources.excludes.add(".options") + resources.excludes.add("about.xml") + resources.excludes.add("plugin.properties") + resources.excludes.add(".api_description") + resources.excludes.add("about_files/*") + resources.excludes.add("META-INF/*") + } } dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'com.google.guava:guava:31.1-android' - implementation 'org.benf:cfr:0.152' - implementation 'org.smali:dexlib2:2.5.2' - implementation 'io.github.Rosemoe.sora-editor:editor:0.10.11' - implementation 'io.github.Rosemoe.sora-editor:language-java:0.10.11' - implementation 'androidx.appcompat:appcompat:1.6.0-alpha01' - implementation 'com.google.android.material:material:1.6.0-beta01' - implementation project(path: ':lib-android') - testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.4-alpha05' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0-alpha05' + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'com.google.guava:guava:31.1-android' + implementation 'org.benf:cfr:0.152' + implementation 'org.smali:dexlib2:2.5.2' + implementation 'io.github.Rosemoe.sora-editor:editor:0.10.11' + implementation 'io.github.Rosemoe.sora-editor:language-java:0.10.11' + implementation 'androidx.appcompat:appcompat:1.6.0-alpha01' + implementation 'com.google.android.material:material:1.6.0-beta01' + implementation project(path: ':lib-android') + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.4-alpha05' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0-alpha05' } diff --git a/app/src/androidTest/java/com/pranav/java/ide/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/pranav/java/ide/ExampleInstrumentedTest.kt index 11ac87bb9..2475037ee 100644 --- a/app/src/androidTest/java/com/pranav/java/ide/ExampleInstrumentedTest.kt +++ b/app/src/androidTest/java/com/pranav/java/ide/ExampleInstrumentedTest.kt @@ -14,11 +14,11 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class ExampleInstrumentedTest { // Check the package name of project. - @Test - // Check if app context is accessible. - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("com.pranav.java.ide", appContext.packageName) - } + @Test + // Check if app context is accessible. + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.pranav.java.ide", appContext.packageName) + } } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 75eb16db1..bb476bb5e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,19 +1,19 @@ + xmlns:tools="http://schemas.android.com/tools"> - - + @@ -27,5 +27,6 @@ - + + diff --git a/app/src/main/java/com/pranav/java/ide/ApplicationLoader.java b/app/src/main/java/com/pranav/java/ide/ApplicationLoader.java deleted file mode 100644 index 09ef06cff..000000000 --- a/app/src/main/java/com/pranav/java/ide/ApplicationLoader.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.pranav.java.ide; - -import android.app.AlarmManager; -import android.app.Application; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.os.Process; -import android.util.Log; -import com.pranav.lib_android.util.FileUtil; - -public final class ApplicationLoader extends Application { - - private static Context mContext; - - @Override - public void onCreate() { - mContext = getApplicationContext(); - FileUtil.initializeContext(mContext); - Thread.UncaughtExceptionHandler uncaughtExceptionHandler = - Thread.getDefaultUncaughtExceptionHandler(); - - Thread.setDefaultUncaughtExceptionHandler( - (thread, throwable) -> { - Intent intent = new Intent(getApplicationContext(), DebugActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); - intent.putExtra("error", Log.getStackTraceString(throwable)); - PendingIntent pendingIntent = - PendingIntent.getActivity( - getApplicationContext(), 11111, intent, PendingIntent.FLAG_ONE_SHOT); - - AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE); - am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 0, pendingIntent); - - Process.killProcess(Process.myPid()); - System.exit(1); - - uncaughtExceptionHandler.uncaughtException(thread, throwable); - }); - super.onCreate(); - } - - public static Context getContext() { - return mContext; - } -} diff --git a/app/src/main/java/com/pranav/java/ide/ApplicationLoader.kt b/app/src/main/java/com/pranav/java/ide/ApplicationLoader.kt new file mode 100644 index 000000000..900d5c1b6 --- /dev/null +++ b/app/src/main/java/com/pranav/java/ide/ApplicationLoader.kt @@ -0,0 +1,42 @@ +package com.pranav.java.ide + +import android.app.AlarmManager +import android.app.Application +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.os.Process +import android.util.Log +import com.pranav.lib_android.util.FileUtil + +class ApplicationLoader : Application() { + + private lateinit var mContext: Context + + override fun onCreate() { + super.onCreate() + mContext = getApplicationContext() + FileUtil.initializeContext(mContext) + val uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler() + + Thread.setDefaultUncaughtExceptionHandler { + thread, throwable -> + val intent = Intent(mContext, DebugActivity::class.java) + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) + intent.putExtra("error", Log.getStackTraceString(throwable)) + val pendingIntent = + PendingIntent.getActivity( + mContext, 11111, intent, PendingIntent.FLAG_ONE_SHOT) + + val am = getSystemService(Context.ALARM_SERVICE) as AlarmManager + am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 0, pendingIntent) + + Process.killProcess(Process.myPid()) + System.exit(0) + + uncaughtExceptionHandler?.uncaughtException(thread, throwable) + } + } + + fun getContext(): Context = mContext +} diff --git a/app/src/main/java/com/pranav/java/ide/DebugActivity.java b/app/src/main/java/com/pranav/java/ide/DebugActivity.java deleted file mode 100644 index b33b7d203..000000000 --- a/app/src/main/java/com/pranav/java/ide/DebugActivity.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.pranav.java.ide; - -import android.content.Intent; -import android.os.Bundle; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; - -public final class DebugActivity extends AppCompatActivity { - - @Override - protected void onCreate(final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - Intent intent = getIntent(); - String errorMessage = intent.getStringExtra("error"); - - new AlertDialog.Builder(this) - .setTitle("An error occurred") - .setMessage(errorMessage) - .setPositiveButton("Quit", (dialog, which) -> finish()) - .create() - .show(); - } -} diff --git a/app/src/main/java/com/pranav/java/ide/DebugActivity.kt b/app/src/main/java/com/pranav/java/ide/DebugActivity.kt new file mode 100644 index 000000000..bcc1811eb --- /dev/null +++ b/app/src/main/java/com/pranav/java/ide/DebugActivity.kt @@ -0,0 +1,23 @@ +package com.pranav.java.ide + +import android.content.Intent +import android.os.Bundle +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity + +class DebugActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val intent: Intent? = getIntent() + val errorMessage = intent?.getStringExtra("error")!! + + AlertDialog.Builder(this) + .setTitle("An error occurred") + .setMessage(errorMessage) + .setPositiveButton("Quit", {_, _ -> finish()}) + .create() + .show() + } +} diff --git a/app/src/main/java/com/pranav/java/ide/MainActivity.java b/app/src/main/java/com/pranav/java/ide/MainActivity.java deleted file mode 100644 index 521ae455f..000000000 --- a/app/src/main/java/com/pranav/java/ide/MainActivity.java +++ /dev/null @@ -1,482 +0,0 @@ -package com.pranav.java.ide; - -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.graphics.Typeface; -import android.os.Bundle; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.widget.LinearLayout; - -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; - -import com.google.android.material.dialog.MaterialAlertDialogBuilder; -import com.google.android.material.snackbar.Snackbar; -import com.google.common.io.ByteStreams; -import com.google.common.io.Files; -import com.google.common.base.Charsets; -import com.googlecode.d2j.smali.BaksmaliCmd; -import com.pranav.lib_android.exception.CompilationFailedException; -import com.pranav.lib_android.task.JavaBuilder; -import com.pranav.lib_android.task.java.CompileJavaTask; -import com.pranav.lib_android.task.java.D8Task; -import com.pranav.lib_android.task.java.ExecuteJavaTask; -import com.pranav.lib_android.code.disassembler.ClassFileDisassembler; -import com.pranav.lib_android.code.formatter.Formatter; -import com.pranav.lib_android.util.ZipUtil; -import com.pranav.lib_android.util.FileUtil; -import com.pranav.lib_android.util.ConcurrentUtilKt; - -import io.github.rosemoe.sora.langs.java.JavaLanguage; -import io.github.rosemoe.sora.widget.CodeEditor; -import io.github.rosemoe.sora.widget.schemes.SchemeDarcula; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; - -import org.jf.dexlib2.DexFileFactory; -import org.jf.dexlib2.Opcodes; -import org.jf.dexlib2.iface.ClassDef; -import org.jf.dexlib2.iface.DexFile; - -public final class MainActivity extends AppCompatActivity { - - private CodeEditor editor; - - private long d8Time = 0; - private long ecjTime = 0; - - private boolean errorsArePresent = false; - - @Override - protected void onCreate(final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - setSupportActionBar(findViewById(R.id.toolbar)); - getSupportActionBar().setDisplayHomeAsUpEnabled(false); - getSupportActionBar().setHomeButtonEnabled(false); - - editor = findViewById(R.id.editor); - - editor.setTypefaceText(Typeface.MONOSPACE); - editor.setEditorLanguage(new JavaLanguage()); - editor.setColorScheme(new SchemeDarcula()); - editor.setTextSize(12); - - final File file = file(FileUtil.getJavaDir() + "Main.java"); - - if (file.exists()) { - try { - editor.setText( - Files.asCharSource(file, Charsets.UTF_8) - .read() - ); - } catch (Exception e) { - dialog("Cannot read file", getString(e), true); - } - } else { - editor.setText( - "package com.example;\n\nimport java.util.*;\n\n" + - "public class Main {\n\n" + - "\tpublic static void main(String[] args) {\n" + - "\t\tSystem.out.print(\"Hello, World!\");\n" + "\t}\n" + - "}\n" - ); - } - - final JavaBuilder builder = new JavaBuilder(getApplicationContext(), - getClassLoader()); - - ConcurrentUtilKt.executeInBackground(() -> { - if (!file(FileUtil.getClasspathDir() + "android.jar").exists()) { - ZipUtil.unzipFromAssets(MainActivity.this, - "android.jar.zip", FileUtil.getClasspathDir()); - } - File output = file(FileUtil.getClasspathDir() + "/core-lambda-stubs.jar"); - if (!output.exists() && - getSharedPreferences("compiler_settings", Context.MODE_PRIVATE) - .getString("javaVersion", "7.0") - .equals("8.0")) { - try { - Files.write( - ByteStreams.toByteArray( - getAssets().open("core-lambda-stubs.jar") - ), - output); - } catch (Exception e) { - showErr(getString(e)); - } - } - }); - - findViewById(R.id.btn_disassemble).setOnClickListener(v -> disassemble()); - findViewById(R.id.btn_smali2java).setOnClickListener(v -> decompile()); - findViewById(R.id.btn_smali).setOnClickListener(v -> smali()); - findViewById(R.id.btn_run).setOnClickListener(view -> { - try { - // Delete previous build files - FileUtil.deleteFile(FileUtil.getBinDir()); - file(FileUtil.getBinDir()).mkdirs(); - final File mainFile = file( - FileUtil.getJavaDir() + "Main.java"); - Files.createParentDirs(mainFile); - // a simple workaround to prevent calls to system.exit - Files.write(editor.getText().toString() - .replace("System.exit(", - "System.err.print(\"Exit code \" + ") - .getBytes(), mainFile); - } catch (final IOException e) { - dialog("Cannot save program", getString(e), true); - } - - // code that runs ecj - long time = System.currentTimeMillis(); - errorsArePresent = true; - try { - CompileJavaTask javaTask = new CompileJavaTask(builder); - javaTask.doFullTask(); - errorsArePresent = false; - } catch (CompilationFailedException e) { - showErr(e.getMessage()); - } catch (Throwable e) { - showErr(getString(e)); - } - if (errorsArePresent) { - return; - } - - ecjTime = System.currentTimeMillis() - time; - time = System.currentTimeMillis(); - - // run d8 - try { - new D8Task().doFullTask(); - } catch (Exception e) { - errorsArePresent = true; - showErr(e.toString()); - return; - } - d8Time = System.currentTimeMillis() - time; - // code that loads the final dex - try { - final String[] classes = getClassesFromDex(); - if (classes == null) { - return; - } - listDialog("Select a class to execute", classes, - (dialog, pos) -> { - ExecuteJavaTask task = new ExecuteJavaTask( - builder, classes[pos]); - try { - task.doFullTask(); - } catch (java.lang.reflect.InvocationTargetException e) { - dialog("Failed...", - "Runtime error: " + - e.getMessage() + - "\n\n" + - getString(e), - true); - } catch (Exception e) { - dialog("Failed..", - "Couldn't execute the dex: " - + e.toString() - + "\n\nSystem logs:\n" - + task.getLogs(), - true); - } - StringBuilder s = new StringBuilder(); - s.append("Success! ECJ took: "); - s.append(String.valueOf(ecjTime)); - s.append("ms, "); - s.append("D8"); - s.append(" took: "); - s.append(String.valueOf(d8Time)); - s.append("ms"); - dialog(s.toString(), task.getLogs(), true); - }); - } catch (Throwable e) { - showErr(getString(e)); - } - }); - } - - public void showErr(final String e) { - Snackbar.make((LinearLayout) findViewById(R.id.container), "An error occurred", - Snackbar.LENGTH_INDEFINITE) - .setAction("Show error", (view) -> dialog("Failed...", e, true)) - .show(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - menu.add(0, 0, 0, "Format"); - menu.add(0, 1, 0, "Settings"); - return super.onCreateOptionsMenu(menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case 0 : - Formatter formatter = new Formatter( - editor.getText().toString()); - editor.setText(formatter.format()); - break; - - case 1 : - Intent intent = getIntent(); - intent.setClass(this, SettingActivity.class); - startActivity(intent); - default : - break; - } - return super.onOptionsItemSelected(item); - } - - public void smali() { - try { - final String[] classes = getClassesFromDex(); - if (classes == null) - return; - listDialog("Select a class to extract source", classes, - (d, pos) -> { - final String claz = classes[pos]; - final String[] args = new String[] { - "-f", - "-o", - FileUtil.getBinDir() - .concat("smali/"), - FileUtil.getBinDir() - .concat("classes.dex") - }; - ConcurrentUtilKt.execute(() -> BaksmaliCmd.main(args)); - - CodeEditor edi = new CodeEditor(MainActivity.this); - edi.setTypefaceText(Typeface.MONOSPACE); - edi.setEditorLanguage(new JavaLanguage()); - edi.setColorScheme(new SchemeDarcula()); - edi.setTextSize(13); - - File smaliFile = file(FileUtil.getBinDir() + "smali/" + claz.replace(".", "/") + ".smali"); - - try { - edi.setText(formatSmali(Files.asCharSource(smaliFile, Charsets.UTF_8).read())); - } catch (IOException e) { - dialog("Cannot read file", getString(e), true); - } - - final AlertDialog dialog = new AlertDialog.Builder( - MainActivity.this).setView(edi).create(); - dialog.setCanceledOnTouchOutside(true); - dialog.show(); - }); - } catch (Throwable e) { - dialog("Failed to extract smali source", getString(e), - true); - } - } - - public void decompile() { - final String[] classes = getClassesFromDex(); - if (classes == null) - return; - listDialog("Select a class to extract source", classes, - (dialog, pos) -> { - final String claz = classes[pos].replace(".", "/"); - String[] args = { - FileUtil.getBinDir() + - "classes/" + - claz + // full class name - ".class", - "--extraclasspath", - FileUtil.getClasspathDir() + "android.jar", - "--outputdir", - FileUtil.getBinDir() + "cfr/" - }; - - ConcurrentUtilKt.execute(() -> { - try { - org.benf.cfr.reader.Main.main(args); - } catch (Exception e) { - dialog("Failed to decompile...", - getString(e), true); - } - }); - - final CodeEditor edi = new CodeEditor(MainActivity.this); - edi.setTypefaceText(Typeface.MONOSPACE); - edi.setEditorLanguage(new JavaLanguage()); - edi.setColorScheme(new SchemeDarcula()); - edi.setTextSize(12); - - File decompiledFile = file( - FileUtil.getBinDir() + "cfr/" + claz + ".java"); - - try { - edi.setText(Files - .asCharSource(decompiledFile, Charsets.UTF_8) - .read()); - } catch (IOException e) { - dialog("Cannot read file", getString(e), - true); - } - - final AlertDialog d = new AlertDialog.Builder( - MainActivity.this).setView(edi).create(); - d.setCanceledOnTouchOutside(true); - d.show(); - }); - } - - public void disassemble() { - final String[] classes = getClassesFromDex(); - if (classes == null) - return; - listDialog("Select a class to disassemble", classes, (dialog, pos) -> { - final String claz = classes[pos].replace(".", "/"); - - final CodeEditor edi = new CodeEditor(MainActivity.this); - edi.setTypefaceText(Typeface.MONOSPACE); - edi.setEditorLanguage(new JavaLanguage()); - edi.setColorScheme(new SchemeDarcula()); - edi.setTextSize(12); - - try { - final String disassembled = new ClassFileDisassembler( - FileUtil.getBinDir() + "classes/" + claz + ".class") - .disassemble(); - - edi.setText(disassembled); - - AlertDialog d = new AlertDialog.Builder(MainActivity.this) - .setView(edi).create(); - d.setCanceledOnTouchOutside(true); - d.show(); - } catch (Throwable e) { - dialog("Failed to disassemble", getString(e), - true); - } - }); - } - - private String formatSmali(String in) { - - ArrayList lines = new ArrayList<>( - Arrays.asList(in.split("\n")) - ); - - boolean insideMethod = false; - - for (int i = 0; i < lines.size(); i++) { - - String line = lines.get(i); - - if (line.startsWith(".method")) - insideMethod = true; - - if (line.startsWith(".end method")) - insideMethod = false; - - if (insideMethod && !shouldSkip(line)) - lines.set(i, line + "\n"); - } - - StringBuilder result = new StringBuilder(); - - for (int i = 0; i < lines.size(); i++) { - if (i != 0) - result.append("\n"); - - result.append(lines.get(i)); - } - - return result.toString(); - } - - private boolean shouldSkip(String smaliLine) { - - String[] ops = {".line", ":", ".prologue"}; - - for (String op : ops) { - if (smaliLine.trim().startsWith(op)) - return true; - } - return false; - } - - public void listDialog(String title, String[] items, - DialogInterface.OnClickListener listener) { - new MaterialAlertDialogBuilder(MainActivity.this) - .setTitle(title) - .setItems(items, listener) - .create() - .show(); - } - - public void dialog(String title, final String message, boolean copyButton) { - final MaterialAlertDialogBuilder dialog = new MaterialAlertDialogBuilder( - MainActivity.this).setTitle(title).setMessage(message) - .setPositiveButton("GOT IT", null) - .setNegativeButton("CANCEL", null); - if (copyButton) - dialog.setNeutralButton("COPY", (dialogInterface, i) -> { - ((ClipboardManager) getSystemService( - getApplicationContext().CLIPBOARD_SERVICE)) - .setPrimaryClip(ClipData - .newPlainText("clipboard", message)); - }); - dialog.create().show(); - } - -// for granting execute permissions to file/folder (currently unused) - public void grantChmod(File file) { - File[] files = file.listFiles(); - try { - for (File f : files) { - if (f.isDirectory()) { - grantChmod(f); - } else { - f.setExecutable(true, true); - } - } - } catch (Throwable e) { - e.printStackTrace(); - } - } - - public String[] getClassesFromDex() { - try { - final ArrayList classes = new ArrayList<>(); - DexFile dexfile = DexFileFactory.loadDexFile(FileUtil.getBinDir().concat("classes.dex"), - Opcodes.forApi(26) - ); - for (ClassDef f : dexfile.getClasses() - .toArray(new ClassDef[0]) - ) { - String name = f.getType().replace("/", "."); // convert class name to standard form - classes.add(name.substring(1, name.length() - 1)); - } - return classes.toArray(new String[0]); - } catch (Exception e) { - dialog("Failed to get available classes in dex...", - getString(e), true); - return null; - } - } - - public File file(final String path) { - return new File(path); - } - - private String getString(final Throwable e) { - return Log.getStackTraceString(e); - } -} diff --git a/app/src/main/java/com/pranav/java/ide/MainActivity.kt b/app/src/main/java/com/pranav/java/ide/MainActivity.kt new file mode 100644 index 000000000..fc2efc11b --- /dev/null +++ b/app/src/main/java/com/pranav/java/ide/MainActivity.kt @@ -0,0 +1,448 @@ +package com.pranav.java.ide + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.content.DialogInterface +import android.content.Intent +import android.graphics.Typeface +import android.os.Bundle +import android.util.Log +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.widget.LinearLayout + +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity + +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.android.material.snackbar.Snackbar +import com.google.common.io.ByteStreams +import com.google.common.io.Files +import com.google.common.base.Charsets +import com.googlecode.d2j.smali.BaksmaliCmd +import com.pranav.lib_android.exception.CompilationFailedException +import com.pranav.lib_android.task.JavaBuilder +import com.pranav.lib_android.task.java.CompileJavaTask +import com.pranav.lib_android.task.java.D8Task +import com.pranav.lib_android.task.java.ExecuteJavaTask +import com.pranav.lib_android.code.disassembler.ClassFileDisassembler +import com.pranav.lib_android.code.formatter.Formatter +import com.pranav.lib_android.util.ZipUtil +import com.pranav.lib_android.util.FileUtil +import com.pranav.lib_android.util.ConcurrentUtil + +import io.github.rosemoe.sora.langs.java.JavaLanguage +import io.github.rosemoe.sora.widget.CodeEditor +import io.github.rosemoe.sora.widget.schemes.SchemeDarcula + +import java.io.File +import java.io.IOException +import java.util.ArrayList +import java.util.Arrays + +import org.jf.dexlib2.DexFileFactory +import org.jf.dexlib2.Opcodes +import org.jf.dexlib2.iface.ClassDef +import org.jf.dexlib2.iface.DexFile + +class MainActivity: AppCompatActivity() { + + private lateinit var editor: CodeEditor + + private var d8Time: Long = 0 + private var ecjTime: Long = 0 + + private var errorsArePresent = false + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + setSupportActionBar(findViewById(R.id.toolbar)) + getSupportActionBar()?.setDisplayHomeAsUpEnabled(false) + getSupportActionBar()?.setHomeButtonEnabled(false) + + editor = findViewById(R.id.editor) + + editor.setTypefaceText(Typeface.MONOSPACE) + editor.setEditorLanguage(JavaLanguage()) + editor.setColorScheme(SchemeDarcula()) + editor.setTextSize(12f) + + val file = File(FileUtil.getJavaDir(), "Main.java") + + if (file.exists()) { + try { + editor.setText( + file.readText() + ) + } catch (e: Exception) { + dialog("Cannot read file", getString(e), true) + } + } else { + editor.setText( + "package com.example;\n\nimport java.util.*;\n\n" + + "public class Main {\n\n" + + "\tpublic static void main(String[] args) {\n" + + "\t\tSystem.out.print(\"Hello, World!\");\n" + "\t}\n" + + "}\n" + ) + } + + val builder = JavaBuilder(getApplicationContext(), + getClassLoader()) + + ConcurrentUtil.executeInBackground { + if (!File(FileUtil.getClasspathDir(), "android.jar").exists()) { + ZipUtil.unzipFromAssets(this, + "android.jar.zip", FileUtil.getClasspathDir()) + } + val output = File(FileUtil.getClasspathDir(), "core-lambda-stubs.jar") + if (!output.exists() && + getSharedPreferences("compiler_settings", Context.MODE_PRIVATE) + .getFloat("javaVersion", 7.0f) >= 8.0f) { + try { + output.writeBytes(getAssets().open("core-lambda-stubs.jar").readBytes()) + } catch (e: Exception) { + showErr(getString(e)) + } + } + } + + (findViewById(R.id.btn_disassemble) as View).setOnClickListener { _ -> disassemble()} + (findViewById(R.id.btn_smali2java) as View).setOnClickListener { _ -> decompile()} + (findViewById(R.id.btn_smali) as View).setOnClickListener { _ -> smali()} + (findViewById(R.id.btn_run) as View).setOnClickListener { _ -> + try { + // Delete previous build files + FileUtil.deleteFile(FileUtil.getBinDir()) + File(FileUtil.getBinDir()).mkdirs() + val mainFile = File( + FileUtil.getJavaDir() + "Main.java") + Files.createParentDirs(mainFile) + // a simple workaround to prevent calls to system.exit + mainFile.writeText(editor.getText().toString() + .replace("System.exit(", + "System.err.print(\"Exit code \" + ") + ) + } catch (e: IOException) { + dialog("Cannot save program", getString(e), true) + } + + // code that runs ecj + var time = System.currentTimeMillis() + errorsArePresent = true + try { + val javaTask = CompileJavaTask(builder) + javaTask.doFullTask() + errorsArePresent = false + } catch (e: CompilationFailedException) { + showErr(e.message) + } catch (e: Throwable) { + showErr(getString(e)) + } + if (errorsArePresent) { + return@setOnClickListener + } + + ecjTime = System.currentTimeMillis() - time + time = System.currentTimeMillis() + + // run d8 + try { + D8Task().doFullTask() + } catch (e: Throwable) { + errorsArePresent = true + showErr(e.toString()) + return@setOnClickListener + } + d8Time = System.currentTimeMillis() - time + // code that loads the final dex + try { + val classes = getClassesFromDex() + if (classes == null) { + return@setOnClickListener + } + listDialog("Select a class to execute", classes, + { _, pos -> + val task = ExecuteJavaTask( + builder, classes[pos]) + try { + task.doFullTask() + } catch (e: java.lang.reflect.InvocationTargetException) { + dialog("Failed...", + "Runtime error: " + + e.message + + "\n\n" + + getString(e), + true) + } catch (e: Exception) { + dialog("Failed..", + "Couldn't execute the dex: " + + e.toString() + + "\n\nSystem logs:\n" + + task.getLogs(), + true) + } + val s = StringBuilder() + s.append("Success! ECJ took: ") + s.append(ecjTime.toString()) + s.append("ms, ") + s.append("D8") + s.append(" took: ") + s.append(d8Time.toString()) + s.append("ms") + dialog(s.toString(), task.getLogs(), true) + }) + } catch (e: Throwable) { + showErr(getString(e)) + } + } + } + + fun showErr(e: String?) { + Snackbar.make(findViewById(R.id.container) as LinearLayout, "An error occurred", + Snackbar.LENGTH_INDEFINITE) + .setAction("Show error", { _ -> dialog("Failed...", e, true)}) + .show() + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menu.add(0, 0, 0, "Format") + menu.add(0, 1, 0, "Settings") + return super.onCreateOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.getItemId()) { + 0 -> { + val formatter = Formatter( + editor.getText().toString()) + ConcurrentUtil.execute { + editor.setText(formatter.format()) + } + } + + 1 -> { + val intent = getIntent() + intent.setClass(this, SettingActivity::class.java) + startActivity(intent) + } + } + return super.onOptionsItemSelected(item) + } + + fun smali() { + try { + val classes = getClassesFromDex() + if (classes == null) + return + listDialog("Select a class to extract source", classes, + { _, pos -> + val claz = classes[pos] + val args = listOf( + "-f", + "-o", + FileUtil.getBinDir() + "smali/", + FileUtil.getBinDir() + "classes.dex" + ) + ConcurrentUtil.execute { + BaksmaliCmd.main(args.toTypedArray()) + } + + val edi = CodeEditor(this@MainActivity) + edi.setTypefaceText(Typeface.MONOSPACE) + edi.setEditorLanguage(JavaLanguage()) + edi.setColorScheme(SchemeDarcula()) + edi.setTextSize(13f) + + val smaliFile = File(FileUtil.getBinDir() + "smali/" + claz.replace(".", "/") + ".smali") + + try { + edi.setText(formatSmali(smaliFile.readText())) + } catch (e: IOException) { + dialog("Cannot read file", getString(e), true) + } + + val dialog = AlertDialog.Builder( + this).setView(edi).create() + dialog.setCanceledOnTouchOutside(true) + dialog.show() + }) + } catch (e: Throwable) { + dialog("Failed to extract smali source", getString(e), + true) + } + } + + fun decompile() { + val classes = getClassesFromDex() + if (classes == null) + return + listDialog("Select a class to extract source", classes, + { _, pos -> + val claz = classes[pos].replace(".", "/") + val args = listOf( + FileUtil.getBinDir() + + "classes/" + + claz + // full class name + ".class", + "--extraclasspath", + FileUtil.getClasspathDir() + "android.jar", + "--outputdir", + FileUtil.getBinDir() + "cfr/" + ) + + ConcurrentUtil.execute { + try { + } catch (e: Exception) { + dialog("Failed to decompile...", + getString(e), true) + } + } + + val edi = CodeEditor(this) + edi.setTypefaceText(Typeface.MONOSPACE) + edi.setEditorLanguage(JavaLanguage()) + edi.setColorScheme(SchemeDarcula()) + edi.setTextSize(12f) + + val decompiledFile = File( + FileUtil.getBinDir() + "cfr/" + claz + ".java") + + try { + edi.setText(decompiledFile.readText()) + } catch (e: IOException) { + dialog("Cannot read file", getString(e), + true) + } + + val d = AlertDialog.Builder( + this).setView(edi).create() + d.setCanceledOnTouchOutside(true) + d.show() + }) + } + + fun disassemble() { + val classes = getClassesFromDex() + if (classes == null) + return + listDialog("Select a class to disassemble", classes, { _, pos -> + val claz = classes[pos].replace(".", "/") + + val edi = CodeEditor(this) + edi.setTypefaceText(Typeface.MONOSPACE) + edi.setEditorLanguage(JavaLanguage()) + edi.setColorScheme(SchemeDarcula()) + edi.setTextSize(12f) + + try { + val disassembled = ClassFileDisassembler( + FileUtil.getBinDir() + "classes/" + claz + ".class") + .disassemble() + + edi.setText(disassembled) + + val d = AlertDialog.Builder(this) + .setView(edi).create() + d.setCanceledOnTouchOutside(true) + d.show() + } catch (e: Throwable) { + dialog("Failed to disassemble", getString(e), + true) + } + }) + } + + private fun formatSmali(inp: String): String { + + val lines: List = Arrays.asList(inp.split("\n")) + + var insideMethod = false + + lines.forEach { + + if (it.startsWith(".method")) + insideMethod = true + + if (it.startsWith(".end method")) + insideMethod = false + + if (insideMethod && !shouldSkip(it)) + lines.set(lines.indexOf(it), it + "\n") + } + + val result = StringBuilder() + + for (line in lines.toTypedArray()) { + result.append("\n") + + result.append(line) + } + + return result.toString() + } + + private fun shouldSkip(smaliLine: String?): Boolean { + + val ops = listOf(".line", ":", ".prologue") + + for (op in ops) { + if (smaliLine?.trim().startsWith(op)) + return true + } + return false + } + + fun listDialog(title: String, items: Array, + listener: DialogInterface.OnClickListener) { + MaterialAlertDialogBuilder(this) + .setTitle(title) + .setItems(items, listener) + .create() + .show() + } + + fun dialog(title: String, message: String?, copyButton: Boolean) { + val dialog = MaterialAlertDialogBuilder(this) + .setTitle(title) + .setMessage(message) + .setPositiveButton("GOT IT", null) + .setNegativeButton("CANCEL", null) + if (copyButton) + dialog.setNeutralButton("COPY", { _, _ -> + (getSystemService( + Context.CLIPBOARD_SERVICE) as ClipboardManager) + .setPrimaryClip(ClipData + .newPlainText("content", message)) + }) + dialog.create().show() + } + + fun getClassesFromDex(): Array? { + try { + val classes = ArrayList() + val dexfile = DexFileFactory.loadDexFile(FileUtil.getBinDir().plus("classes.dex"), + Opcodes.forApi(26) + ) + for (f in dexfile.getClasses() + .toTypedArray() + ) { + val name = f.getType().replace("/", ".") // convert class name to standard form + classes.add(name.substring(1, name.length - 1)) + } + return classes.toTypedArray() + } catch (e: Exception) { + dialog("Failed to get available classes in dex...", + getString(e), true) + return null + } + } + + private fun getString(e: Throwable): String { + return Log.getStackTraceString(e) + } +} diff --git a/app/src/main/java/com/pranav/java/ide/SettingActivity.java b/app/src/main/java/com/pranav/java/ide/SettingActivity.java deleted file mode 100644 index d1feb92ad..000000000 --- a/app/src/main/java/com/pranav/java/ide/SettingActivity.java +++ /dev/null @@ -1,155 +0,0 @@ -package com.pranav.java.ide; - -import android.content.SharedPreferences; -import android.os.Bundle; - -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.AppCompatEditText; -import androidx.appcompat.widget.Toolbar; - -import com.google.android.material.radiobutton.MaterialRadioButton; - -public final class SettingActivity extends AppCompatActivity { - - private MaterialRadioButton java3; - private MaterialRadioButton java4; - private MaterialRadioButton java5; - private MaterialRadioButton java6; - private MaterialRadioButton java8; - private MaterialRadioButton java9; - private MaterialRadioButton java10; - private MaterialRadioButton java11; - private MaterialRadioButton java12; - private MaterialRadioButton java13; - private MaterialRadioButton java14; - private MaterialRadioButton java15; - private MaterialRadioButton java16; - private MaterialRadioButton java17; - - private AppCompatEditText classpath; - - private SharedPreferences settings; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_setting); - - final Toolbar toolbar = findViewById(R.id.toolbar); - - setSupportActionBar(toolbar); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setHomeButtonEnabled(true); - toolbar.setNavigationOnClickListener(v -> onBackPressed()); - - java3 = findViewById(R.id.java3); - java4 = findViewById(R.id.java4); - java5 = findViewById(R.id.java5); - java6 = findViewById(R.id.java6); - java8 = findViewById(R.id.java8); - java9 = findViewById(R.id.java9); - java10 = findViewById(R.id.java10); - java11 = findViewById(R.id.java11); - java12 = findViewById(R.id.java12); - java13 = findViewById(R.id.java13); - java14 = findViewById(R.id.java14); - java15 = findViewById(R.id.java15); - java16 = findViewById(R.id.java16); - java17 = findViewById(R.id.java17); - - final MaterialRadioButton java7 = findViewById(R.id.java7); - classpath = findViewById(R.id.classpath); - settings = getSharedPreferences("compiler_settings", MODE_PRIVATE); - - switch (settings.getString("javaVersion", "7.0")) { - - case "1.3": - java3.setChecked(true); - break; - - case "1.4": - java4.setChecked(true); - break; - - case "5.0": - java5.setChecked(true); - break; - - case "6.0": - java6.setChecked(true); - break; - - case "8.0": - java8.setChecked(true); - break; - - case "9.0": - java9.setChecked(true); - break; - - case "10.0": - java10.setChecked(true); - break; - - case "11.0": - java11.setChecked(true); - break; - - case "12.0": - java12.setChecked(true); - break; - - case "13.0": - java13.setChecked(true); - break; - - case "14.0": - java14.setChecked(true); - break; - - case "15.0": - java15.setChecked(true); - break; - - case "16.0": - java16.setChecked(true); - break; - - case "17.0": - java17.setChecked(true); - break; - - case "7.0": - default: - java7.setChecked(true); - break; - } - - classpath.setText(settings.getString("classpath", "")); - } - - @Override - public void onDestroy() { - double version = 1.7; - - if (java3.isChecked()) version = 1.3; - else if (java4.isChecked()) version = 1.4; - else if (java5.isChecked()) version = 5.0; - else if (java6.isChecked()) version = 6.0; - else if (java8.isChecked()) version = 8.0; - else if (java9.isChecked()) version = 9.0; - else if (java10.isChecked()) version = 10.0; - else if (java11.isChecked()) version = 11.0; - else if (java12.isChecked()) version = 12.0; - else if (java13.isChecked()) version = 13.0; - else if (java14.isChecked()) version = 14.0; - else if (java15.isChecked()) version = 15.0; - else if (java16.isChecked()) version = 16.0; - else if (java17.isChecked()) version = 17.0; - - settings.edit().putString("javaVersion", String.valueOf(version)).apply(); - - settings.edit().putString("classpath", classpath.getText().toString()).apply(); - super.onDestroy(); - } -} diff --git a/app/src/main/java/com/pranav/java/ide/SettingActivity.kt b/app/src/main/java/com/pranav/java/ide/SettingActivity.kt new file mode 100644 index 000000000..08a50ba7a --- /dev/null +++ b/app/src/main/java/com/pranav/java/ide/SettingActivity.kt @@ -0,0 +1,125 @@ +package com.pranav.java.ide + +import android.content.SharedPreferences +import android.os.Bundle + +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.widget.AppCompatEditText +import androidx.appcompat.widget.Toolbar + +import com.google.android.material.radiobutton.MaterialRadioButton + +class SettingActivity : AppCompatActivity() { + + lateinit var java3: MaterialRadioButton + lateinit var java4: MaterialRadioButton + lateinit var java5: MaterialRadioButton + lateinit var java6: MaterialRadioButton + lateinit var java8: MaterialRadioButton + lateinit var java9: MaterialRadioButton + lateinit var java10: MaterialRadioButton + lateinit var java11: MaterialRadioButton + lateinit var java12: MaterialRadioButton + lateinit var java13: MaterialRadioButton + lateinit var java14: MaterialRadioButton + lateinit var java15: MaterialRadioButton + lateinit var java16: MaterialRadioButton + lateinit var java17: MaterialRadioButton + + lateinit var classpath: AppCompatEditText + + lateinit var settings: SharedPreferences + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_setting) + + val toolbar: Toolbar = findViewById(R.id.toolbar) + + setSupportActionBar(toolbar) + getSupportActionBar()?.setDisplayHomeAsUpEnabled(true) + getSupportActionBar()?.setHomeButtonEnabled(true) + toolbar.setNavigationOnClickListener { _ -> + onBackPressed() + } + + java3 = findViewById(R.id.java3) + java4 = findViewById(R.id.java4) + java5 = findViewById(R.id.java5) + java6 = findViewById(R.id.java6) + java8 = findViewById(R.id.java8) + java9 = findViewById(R.id.java9) + java10 = findViewById(R.id.java10) + java11 = findViewById(R.id.java11) + java12 = findViewById(R.id.java12) + java13 = findViewById(R.id.java13) + java14 = findViewById(R.id.java14) + java15 = findViewById(R.id.java15) + java16 = findViewById(R.id.java16) + java17 = findViewById(R.id.java17) + + classpath = findViewById(R.id.classpath) + + val java7: MaterialRadioButton = findViewById(R.id.java7) + settings = getSharedPreferences("compiler_settings", MODE_PRIVATE) + + when (settings.getFloat("javaVersion", 7.0f)) { + + 1.3f -> java3.setChecked(true) + + 1.4f -> java4.setChecked(true) + + 5.0f -> java5.setChecked(true) + + 6.0f -> java6.setChecked(true) + + 8.0f -> java8.setChecked(true) + + 9.0f -> java9.setChecked(true) + + 10.0f -> java10.setChecked(true) + + 11.0f -> java11.setChecked(true) + + 12.0f -> java12.setChecked(true) + + 13.0f -> java13.setChecked(true) + + 14.0f -> java14.setChecked(true) + + 15.0f -> java15.setChecked(true) + + 16.0f -> java16.setChecked(true) + + 17.0f -> java17.setChecked(true) + + else -> java7.setChecked(true) + } + + classpath.setText(settings.getString("classpath", "")) + } + + override fun onDestroy() { + super.onDestroy() + var version = 1.7f + + if (java3.isChecked()) version = 1.3f + else if (java4.isChecked()) version = 1.4f + else if (java5.isChecked()) version = 5.0f + else if (java6.isChecked()) version = 6.0f + else if (java8.isChecked()) version = 8.0f + else if (java9.isChecked()) version = 9.0f + else if (java10.isChecked()) version = 10.0f + else if (java11.isChecked()) version = 11.0f + else if (java12.isChecked()) version = 12.0f + else if (java13.isChecked()) version = 13.0f + else if (java14.isChecked()) version = 14.0f + else if (java15.isChecked()) version = 15.0f + else if (java16.isChecked()) version = 16.0f + else if (java17.isChecked()) version = 17.0f + + settings.edit().putFloat("javaVersion", version).apply() + + settings.edit().putString("classpath", classpath.getText().toString()).apply() + } +} diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml index 2b068d114..c6aee64ec 100644 --- a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -1,30 +1,30 @@ - - - - - - - - - + xmlns:aapt="http://schemas.android.com/aapt" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml index 07d5da9cb..4e12b433a 100644 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -1,170 +1,170 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index fdb31e640..0ba5db7c9 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -4,6 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> + + + diff --git a/app/src/main/res/layout/activity_setting.xml b/app/src/main/res/layout/activity_setting.xml index 3f0913ec3..63cbabd74 100644 --- a/app/src/main/res/layout/activity_setting.xml +++ b/app/src/main/res/layout/activity_setting.xml @@ -5,6 +5,7 @@ android:id="@+id/coordinator" android:layout_width="match_parent" android:layout_height="match_parent"> + + - - + - + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index eca70cfe5..65bd9d3e4 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index eca70cfe5..65bd9d3e4 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index 9565cde1c..f2b854ac7 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -1,16 +1,16 @@ - - + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index f8c6127d3..d95bf93e8 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,10 +1,10 @@ - #FFBB86FC - #FF6200EE - #FF3700B3 - #FF03DAC5 - #FF018786 - #FF000000 - #FFFFFFFF + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 28a4581a3..14171904d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,14 +1,14 @@ - Java IDE - Next - Previous - - - Run - Smali - Disassemble - Smali2Java - - - Java Version + Java IDE + Next + Previous + + + Run + Smali + Disassemble + Smali2Java + + + Java Version \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index ffd4d71a1..539ffd2c6 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,25 +1,25 @@ - - + + - + -