- Fixed the main screen being called twice when switching from light to dark or vice versa.
- Added subtitles in Settings.
- The Join the Beta channel will be disabled when you turn off Automatically check for update in Settings.
- The Memory capacity (MB) will be disabled when you turn off Custom memory in Settings.
- Removed the extra space on the left side of the X11 section in Settings.
- Updated some wording in Settings.
- Updated new dialog styles in VNC screen.
- Added subtitles in Create rom.
- Changed thumbnails and names in Create rom.
- Now, clicking Qemu params in Create rom will immediately open the editor.
- Updated new styles for some dialogs in the main screen.
- Now when opening X11 anywhere on the main screen, it will be checked for compatibility before continuing.
- Updated new card style for VM list.
- Updated architecture selection screen.
- Added drag and drop support for cvbi files to Import tab in architecture selection screen.
- Added Rom store button in architecture selection screen.
- Added copy result in command completion dialog.
- Default font is Google Sans.
- VM thumbnails are now saved as PNG.
- Updated Rom store interface.
- Updated Rom store list style.
- Updated ROM information interface.
- Updated Mini tools interface.
- Updated About interface.
- Replaced QEMU icon with PC when image cannot be loaded or VM has no image.
This commit is contained in:
An Bui 2025-07-31 00:24:40 +07:00
parent 237fdbb0e1
commit aa0ff6663e
30 changed files with 685 additions and 331 deletions

View file

@ -48,6 +48,7 @@ import com.google.android.gms.ads.MobileAds;
import com.google.android.gms.ads.initialization.InitializationStatus;
import com.google.android.gms.ads.initialization.OnInitializationCompleteListener;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
import com.google.android.material.progressindicator.LinearProgressIndicator;
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
@ -139,6 +140,8 @@ public class CustomRomActivity extends AppCompatActivity {
private AlertDialog alertDialog;
CollapsingToolbarLayout collapsingToolbarLayout;
public void onStart() {
super.onStart();
}
@ -192,16 +195,19 @@ public class CustomRomActivity extends AppCompatActivity {
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
setTitle(getString(R.string.rom_options));
//AdView mAdView = findViewById(R.id.adView);
//AdRequest adRequest = new AdRequest.Builder().build();
//mAdView.loadAd(adRequest);
MobileAds.initialize(this, new OnInitializationCompleteListener() {
@Override
public void onInitializationComplete(InitializationStatus initializationStatus) {
// MobileAds.initialize(this, new OnInitializationCompleteListener() {
// @Override
// public void onInitializationComplete(InitializationStatus initializationStatus) {
//
// }
// });
collapsingToolbarLayout = findViewById(R.id.collapsingToolbarLayout);
collapsingToolbarLayout.setSubtitle(MainSettingsManager.getArch(this));
}
});
ivAddThubnail = findViewById(R.id.ivAddThubnail);
cdromLayout = findViewById(R.id.cdromField);
driveLayout = findViewById(R.id.driveField);
@ -217,8 +223,6 @@ public class CustomRomActivity extends AppCompatActivity {
linearprogress = findViewById(R.id.linearprogress);
textviewprogress = findViewById(R.id.textviewprogress);
arch.setText(MainSettingsManager.getArch(this));
icon.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -391,7 +395,6 @@ public class CustomRomActivity extends AppCompatActivity {
}
});
TextView textName = findViewById(R.id.textName);
TextWatcher afterTextChangedListener = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@ -400,7 +403,6 @@ public class CustomRomActivity extends AppCompatActivity {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
textName.setText(Objects.requireNonNull(title.getText()).toString());
if (!thumbnailPath.isEmpty())
return;
@ -659,13 +661,13 @@ public class CustomRomActivity extends AppCompatActivity {
try {
if (MainSettingsManager.copyFile(activity)) {
try {
SaveImage(selectedImage, new File(AppConfig.vmFolder + vmID), "thumbnail.webp");
SaveImage(selectedImage, new File(AppConfig.vmFolder + vmID), "thumbnail.png");
} finally {
Runnable runnable = new Runnable() {
@Override
public void run() {
whenProcessing(false);
thumbnailPath = AppConfig.vmFolder + vmID + "/thumbnail.webp";
thumbnailPath = AppConfig.vmFolder + vmID + "/thumbnail.png";
thumbnailProcessing();
}
};
@ -806,7 +808,7 @@ public class CustomRomActivity extends AppCompatActivity {
if (file.exists()) file.delete();
try {
FileOutputStream out = new FileOutputStream(file);
finalBitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
finalBitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
out.flush();
out.close();

View file

@ -4,6 +4,7 @@ import static android.content.Intent.ACTION_OPEN_DOCUMENT;
import static android.content.Intent.ACTION_VIEW;
import static android.os.Build.VERSION.SDK_INT;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.progressindicator.LinearProgressIndicator;
import com.termux.app.TermuxService;
@ -41,6 +42,7 @@ import android.provider.DocumentsContract;
import android.provider.Settings;
import android.text.Html;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@ -149,6 +151,7 @@ public class MainActivity extends AppCompatActivity {
public static boolean isActivate = false;
public boolean skipIDEwithARM64DialogInStartVM = false;
BottomAppBar bottomAppBar;
AlertDialog progressDialog;
public static Timer timer = new Timer();
public static TimerTask timerTask;
@ -450,51 +453,7 @@ public class MainActivity extends AppCompatActivity {
startActivityForResult(intent, 1006);
}
} else if (id == R.id.navigation_item_desktop) {
if (SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle(R.string.x11_feature_not_supported)
.setMessage(R.string.the_x11_feature_is_currently_not_supported_on_android_14_and_above_please_use_a_device_with_android_13_or_below_for_x11_functionality)
.setPositiveButton("OK", (dialog, which) -> dialog.dismiss())
.create()
.show();
} else {
// XFCE4 meta-package
String xfce4Package = "xfce4";
// Check if XFCE4 is installed
isPackageInstalled2(activity, xfce4Package, (output, errors) -> {
boolean isInstalled = false;
// Check if the package exists in the installed packages output
if (output != null) {
Set<String> installedPackages = new HashSet<>();
for (String installedPackage : output.split("\n")) {
installedPackages.add(installedPackage.trim());
}
isInstalled = installedPackages.contains(xfce4Package.trim());
}
// If not installed, show a dialog to install it
if (!isInstalled) {
new AlertDialog.Builder(activity, R.style.MainDialogTheme)
.setTitle("Install XFCE4")
.setMessage("XFCE4 is not installed. Would you like to install it?")
.setCancelable(false)
.setPositiveButton("Install", (dialog, which) -> {
String installCommand = "apk add " + xfce4Package;
new Terminal(activity).executeShellCommand(installCommand, true, activity);
})
.setNegativeButton("Cancel", (dialog, which) -> dialog.dismiss())
.show();
} else {
new Terminal(activity).executeShellCommand2("killall xfce4-session", false, activity);
startActivity(new Intent(activity, X11Activity.class));
new Terminal(activity).executeShellCommand2("xfce4-session", false, MainActivity.activity);
}
});
}
launchX11(true);
} else if (id == R.id.navigation_item_terminal) {
/*com.vectras.vterm.TerminalBottomSheetDialog VTERM = new com.vectras.vterm.TerminalBottomSheetDialog(activity);
VTERM.showVterm();*/
@ -902,7 +861,7 @@ public class MainActivity extends AppCompatActivity {
if (MainSettingsManager.getVmUi(activity).equals("VNC"))
activity.startActivity(new Intent(activity, MainVNCActivity.class));
else if (MainSettingsManager.getVmUi(activity).equals("X11"))
activity.startActivity(new Intent(activity, X11Activity.class));
launchX11(false);
} else {
Toast.makeText(getApplicationContext(), activity.getResources().getString(R.string.there_is_nothing_here_because_there_is_no_vm_running), Toast.LENGTH_LONG).show();
}
@ -982,7 +941,7 @@ public class MainActivity extends AppCompatActivity {
romDir.mkdirs();
if (!VMManager.isthiscommandsafe(env, activity.getApplicationContext())) {
UIUtils.oneDialog(activity.getResources().getString(R.string.problem_has_been_detected), activity.getResources().getString(R.string.harmful_command_was_detected) + " " + activity.getResources().getString(R.string.reason) + ": " + VMManager.latestUnsafeCommandReason, true, false, activity);
DialogUtils.oneDialog(activity, activity.getString(R.string.problem_has_been_detected), activity.getString(R.string.harmful_command_was_detected) + " " + activity.getResources().getString(R.string.reason) + ": " + VMManager.latestUnsafeCommandReason, activity.getString(R.string.ok), true, R.drawable.verified_user_24px, true, null, null);
return;
}
@ -991,46 +950,24 @@ public class MainActivity extends AppCompatActivity {
if (MainSettingsManager.getVmUi(activity).equals("VNC"))
activity.startActivity(new Intent(activity, MainVNCActivity.class));
else if (MainSettingsManager.getVmUi(activity).equals("X11"))
activity.startActivity(new Intent(activity, X11Activity.class));
activity.launchX11(false);
return;
}
if (AppConfig.getSetupFiles().contains("arm") && !AppConfig.getSetupFiles().contains("arm64")) {
if (env.contains("tcg,thread=multi")) {
AlertDialog abiAlertDialog = new AlertDialog.Builder(activity, R.style.MainDialogTheme).create();
abiAlertDialog.setTitle(activity.getResources().getString(R.string.oops));
abiAlertDialog.setMessage(activity.getResources().getString(R.string.can_not_use_mttcg));
abiAlertDialog.setButton(DialogInterface.BUTTON_POSITIVE, activity.getResources().getString(R.string.continuetext), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
startVM(vmName, env.replace("tcg,thread=multi", "tcg,thread=single"), itemExtra, itemPath);
}
});
abiAlertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, activity.getResources().getString(R.string.cancel), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
});
abiAlertDialog.show();
DialogUtils.twoDialog(activity, activity.getResources().getString(R.string.problem_has_been_detected), activity.getResources().getString(R.string.can_not_use_mttcg), activity.getString(R.string.ok), activity.getString(R.string.cancel), true, R.drawable.warning_48px, true,
() -> startVM(vmName, env.replace("tcg,thread=multi", "tcg,thread=single"), itemExtra, itemPath), null, null);
return;
}
}
if (MainSettingsManager.getArch(activity).equals("ARM64") && MainSettingsManager.getIfType(activity).equals("ide") && !activity.skipIDEwithARM64DialogInStartVM) {
AlertDialog abiAlertDialog = new AlertDialog.Builder(activity, R.style.MainDialogTheme).create();
abiAlertDialog.setTitle(activity.getResources().getString(R.string.problem_has_been_detected));
abiAlertDialog.setMessage(activity.getResources().getString(R.string.you_cannot_use_IDE_hard_drive_type_with_ARM64));
abiAlertDialog.setButton(DialogInterface.BUTTON_POSITIVE, activity.getResources().getString(R.string.continuetext), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
activity.skipIDEwithARM64DialogInStartVM = true;
startVM(vmName, env, itemExtra, itemPath);
}
});
abiAlertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, activity.getResources().getString(R.string.cancel), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
});
abiAlertDialog.show();
DialogUtils.twoDialog(activity, activity.getString(R.string.problem_has_been_detected), activity.getString(R.string.you_cannot_use_IDE_hard_drive_type_with_ARM64), activity.getString(R.string.continuetext), activity.getString(R.string.cancel), true, R.drawable.warning_48px, true,
() -> {
activity.skipIDEwithARM64DialogInStartVM = true;
startVM(vmName, env, itemExtra, itemPath);
}, null, null);
return;
} else if (activity.skipIDEwithARM64DialogInStartVM) {
activity.skipIDEwithARM64DialogInStartVM = false;
@ -1041,11 +978,7 @@ public class MainActivity extends AppCompatActivity {
}
boolean isRunning = isMyServiceRunning(MainService.class);
ProgressDialog progressDialog = new ProgressDialog(activity, R.style.MainDialogTheme);
progressDialog.setMessage(activity.getString(R.string.booting_up));
progressDialog.setCancelable(false);
progressDialog.show();
activity.showProgressDialog(activity.getString(R.string.booting_up));
Handler handler = new Handler();
handler.postDelayed(
new Runnable() {
@ -1068,9 +1001,8 @@ public class MainActivity extends AppCompatActivity {
if (MainSettingsManager.getVncExternal(MainActivity.activity)) {
extVncLayout.setVisibility(View.VISIBLE);
appbar.setExpanded(true);
progressDialog.dismiss();
activity.progressDialog.dismiss();
} else {
progressDialog.show();
Handler handler = new Handler();
handler.postDelayed(
new Runnable() {
@ -1079,7 +1011,7 @@ public class MainActivity extends AppCompatActivity {
activity.startActivity(
new Intent(
activity, MainVNCActivity.class));
progressDialog.dismiss();
activity.progressDialog.dismiss();
}
},
2000);
@ -1088,14 +1020,12 @@ public class MainActivity extends AppCompatActivity {
// activity.startActivity(new Intent(activity,
// RemoteCanvasActivity.class));
} else if (MainSettingsManager.getVmUi(activity).equals("X11")) {
progressDialog.show();
Handler handler = new Handler();
handler.postDelayed(
new Runnable() {
public void run() {
activity.startActivity(
new Intent(activity, X11Activity.class));
progressDialog.dismiss();
activity.progressDialog.dismiss();
activity.launchX11(false);
}
},
3000);
@ -1194,11 +1124,11 @@ public class MainActivity extends AppCompatActivity {
if (!AppConfig.pendingCommand.isEmpty()) {
if (!VMManager.isthiscommandsafe(AppConfig.pendingCommand, getApplicationContext())) {
AppConfig.pendingCommand = "";
UIUtils.oneDialog(activity.getResources().getString(R.string.problem_has_been_detected), activity.getResources().getString(R.string.harmful_command_was_detected) + " " + activity.getResources().getString(R.string.reason) + ": " + VMManager.latestUnsafeCommandReason, true, false, activity);
DialogUtils.oneDialog(activity, getString(R.string.problem_has_been_detected), getString(R.string.harmful_command_was_detected) + " " + activity.getResources().getString(R.string.reason) + ": " + VMManager.latestUnsafeCommandReason, getString(R.string.ok), true, R.drawable.verified_user_24px, true, null, null);
} else {
if (AppConfig.pendingCommand.startsWith("qemu-img")) {
if (!VMManager.isthiscommandsafeimg(AppConfig.pendingCommand, getApplicationContext())) {
UIUtils.oneDialog(activity.getResources().getString(R.string.problem_has_been_detected), activity.getResources().getString(R.string.size_too_large_try_qcow2_format), true, false, activity);
DialogUtils.oneDialog(activity, getString(R.string.problem_has_been_detected), getString(R.string.size_too_large_try_qcow2_format), getString(R.string.ok), true, R.drawable.warning_48px, true, null, null);
} else {
Terminal _vterm = new Terminal(MainActivity.this);
_vterm.executeShellCommand2(AppConfig.pendingCommand, false, MainActivity.activity);
@ -1508,22 +1438,12 @@ public class MainActivity extends AppCompatActivity {
if (item.getItemId() == R.id.update) {
updateApp(true);
} else if (item.getItemId() == R.id.shutdown) {
alertDialog = new AlertDialog.Builder(activity, R.style.MainDialogTheme).create();
alertDialog.setTitle(getResources().getString(R.string.do_you_want_to_kill_all_qemu_processes));
alertDialog.setMessage(getResources().getString(R.string.all_running_vms_will_be_forcibly_shut_down));
alertDialog.setCancelable(true);
alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, getResources().getString(R.string.kill_all), (dialog, which) -> {
VMManager.killallqemuprocesses(getApplicationContext());
});
alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getResources().getString(R.string.cancel), (dialog, which) -> {
});
alertDialog.show();
VMManager.requestKillAllQemuProcess(activity);
} else if (item.getItemId() == R.id.backtothedisplay) {
if (MainSettingsManager.getVmUi(activity).equals("VNC")) {
startActivity(new Intent(activity, MainVNCActivity.class));
} else if (MainSettingsManager.getVmUi(activity).equals("X11")) {
startActivity(new Intent(activity, X11Activity.class));
launchX11(false);
}
} else if (item.getItemId() == R.id.importrom) {
Intent intent = new Intent();
@ -1538,4 +1458,55 @@ public class MainActivity extends AppCompatActivity {
_update.setVisibility(View.GONE);
}
private void launchX11(boolean isKillXFCE) {
if (SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
DialogUtils.oneDialog(activity, getString(R.string.x11_feature_not_supported), getString(R.string.the_x11_feature_is_currently_not_supported_on_android_14_and_above_please_use_a_device_with_android_13_or_below_for_x11_functionality), getString(R.string.ok), true, R.drawable.error_96px, true, null, null);
} else {
// XFCE4 meta-package
String xfce4Package = "xfce4";
// Check if XFCE4 is installed
isPackageInstalled2(activity, xfce4Package, (output, errors) -> {
boolean isInstalled = false;
// Check if the package exists in the installed packages output
if (output != null) {
Set<String> installedPackages = new HashSet<>();
for (String installedPackage : output.split("\n")) {
installedPackages.add(installedPackage.trim());
}
isInstalled = installedPackages.contains(xfce4Package.trim());
}
// If not installed, show a dialog to install it
if (!isInstalled) {
DialogUtils.twoDialog(activity, "Install XFCE4", "XFCE4 is not installed. Would you like to install it?", getString(R.string.install), getString(R.string.cancel), true, R.drawable.desktop_24px, true,
() -> {
String installCommand = "apk add " + xfce4Package;
new Terminal(activity).executeShellCommand(installCommand, true, activity);
}, null, null);
} else {
if (isKillXFCE)
new Terminal(activity).executeShellCommand2("killall xfce4-session", false, activity);
startActivity(new Intent(activity, X11Activity.class));
new Terminal(activity).executeShellCommand2("xfce4-session", false, MainActivity.activity);
}
});
}
}
private void showProgressDialog(String _content) {
View progressView = LayoutInflater.from(activity).inflate(R.layout.dialog_progress_style, null);
TextView progress_text = progressView.findViewById(R.id.progress_text);
progress_text.setText(_content);
progressDialog = new MaterialAlertDialogBuilder(activity, R.style.CenteredDialogTheme)
.setView(progressView)
.setCancelable(false)
.create();
progressDialog.show();
}
}

View file

@ -1,27 +1,25 @@
package com.vectras.vm;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.DragEvent;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
import androidx.cardview.widget.CardView;
import com.google.android.material.appbar.MaterialToolbar;
import com.vectras.qemu.MainSettingsManager;
import com.vectras.vm.databinding.ActivitySetArchBinding;
import com.vectras.vm.utils.PackageUtils;
import com.vectras.vm.utils.FileUtils;
import com.vectras.vm.utils.UIUtils;
import java.util.Objects;
import java.io.File;
public class SetArchActivity extends AppCompatActivity implements View.OnClickListener {
@ -61,9 +59,48 @@ public class SetArchActivity extends AppCompatActivity implements View.OnClickLi
return false;
});
if (PackageUtils.isInstalled("com.anbui.cqcm.app", this)) {
binding.buttongetcm.setText(getResources().getString(R.string.open));
}
// if (PackageUtils.isInstalled("com.anbui.cqcm.app", this)) {
// binding.buttongetcm.setText(getResources().getString(R.string.open));
// }
binding.bntimport.setOnDragListener(new View.OnDragListener() {
@Override
public boolean onDrag(View v, DragEvent event) {
Log.i("Drag", "onDrag: " + event.getAction());
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
ClipDescription description = event.getClipDescription();
if (description != null) {
Log.d("DRAG", "MIME: " + description.getMimeType(0));
return true; // Accept to go to event DragEvent.ACTION_DROP
}
return false;
case DragEvent.ACTION_DROP:
ClipData clipData = event.getClipData();
if (clipData != null && clipData.getItemCount() > 0) {
Uri uri = clipData.getItemAt(0).getUri();
String filePath = FileUtils.getFilePathFromUri(getApplicationContext(), uri);
File file = new File(filePath);
Intent intent = new Intent();
intent.setClass(getApplicationContext(), CustomRomActivity.class);
intent.putExtra("addromnow", "");
intent.putExtra("romextra", "");
intent.putExtra("romname", "");
intent.putExtra("romicon", "");
intent.putExtra("rompath", filePath);
intent.putExtra("romfilename", file.getName());
startActivity(intent);
finish();
}
return true;
}
return true;
}
});
}
public void onClick(View v) {

View file

@ -780,6 +780,11 @@ public class VMManager {
}
}
public static void requestKillAllQemuProcess(Activity activity) {
DialogUtils.twoDialog(activity, activity.getString(R.string.do_you_want_to_kill_all_qemu_processes), activity.getString(R.string.all_running_vms_will_be_forcibly_shut_down), activity.getString(R.string.kill_all), activity.getString(R.string.cancel), true, R.drawable.power_settings_new_24px, true,
() -> killallqemuprocesses(activity), null, null);
}
public static void killcurrentqemuprocess(Context context) {
Terminal vterm = new Terminal(context);
String env = "killall -9 ";

View file

@ -0,0 +1,20 @@
package com.vectras.vm.utils;
import static android.content.Context.CLIPBOARD_SERVICE;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.widget.Toast;
import com.vectras.vm.R;
public class ClipboardUltils {
public static void copyToClipboard(Context context, String text) {
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("From Vectras VM", text);
assert clipboard != null;
clipboard.setPrimaryClip(clip);
Toast.makeText(context, context.getString(R.string.copied), Toast.LENGTH_SHORT).show();
}
}

View file

@ -781,4 +781,28 @@ public class FileUtils {
return 0;
}
}
public static String getFilePathFromUri(Context context, Uri uri) {
String filePath = null;
if ("content".equalsIgnoreCase(uri.getScheme())) {
String[] projection = {MediaStore.Files.FileColumns.DATA};
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(uri, projection, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
int index = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA);
filePath = cursor.getString(index);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null)
cursor.close();
}
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
filePath = uri.getPath();
}
return filePath;
}
}