- Added auto return to Home after importing rom and creating virtual machine in Rom store.
- Improved image viewer.
- Fixed Unknow display error in architecture in rom info if it is PowerPC architecture.
- New setup wizard interface that automatically changes according to screen size.
- New ID generator for virtual machine.
- Added dialog when deleting virtual machine.
This commit is contained in:
An Bui 2025-11-30 14:31:41 +07:00
parent 009ba90694
commit bf11a28d60
25 changed files with 817 additions and 89 deletions

19
.gitignore vendored
View file

@ -37,17 +37,18 @@ captures/
# IntelliJ # IntelliJ
*.iml *.iml
.idea/workspace.xml .idea/
.idea/tasks.xml #.idea/workspace.xml
.idea/gradle.xml #.idea/tasks.xml
.idea/assetWizardSettings.xml #.idea/gradle.xml
.idea/dictionaries #.idea/assetWizardSettings.xml
.idea/libraries #.idea/dictionaries
#.idea/libraries
# Android Studio 3 in .gitignore file. # Android Studio 3 in .gitignore file.
.idea/caches #.idea/caches
.idea/modules.xml #.idea/modules.xml
# Comment next line if keeping position of elements in Navigation Editor is relevant for you # Comment next line if keeping position of elements in Navigation Editor is relevant for you
.idea/navEditor.xml #.idea/navEditor.xml
# Keystore files # Keystore files
# Uncomment the following lines if you do not want to check your keystore files in. # Uncomment the following lines if you do not want to check your keystore files in.

View file

@ -4,10 +4,10 @@
<selectionStates> <selectionStates>
<SelectionState runConfigName="app"> <SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" /> <option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-11-29T08:52:45.170485500Z"> <DropdownSelection timestamp="2025-11-30T05:38:16.444348800Z">
<Target type="DEFAULT_BOOT"> <Target type="DEFAULT_BOOT">
<handle> <handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=8c847fb6" /> <DeviceId pluginId="Default" identifier="serial=192.168.1.173:46503;connection=00a6f494" />
</handle> </handle>
</Target> </Target>
</DropdownSelection> </DropdownSelection>

View file

@ -99,6 +99,7 @@ Help support the project by contributing!
- [PROOT](https://proot-me.github.io/) - [PROOT](https://proot-me.github.io/)
- [QEMU](https://github.com/qemu/qemu) - [QEMU](https://github.com/qemu/qemu)
- [Termux](https://github.com/termux) - [Termux](https://github.com/termux)
- [ZoomImageView](https://github.com/k1slay/ZoomImageView)
[ico-telegram]: https://img.shields.io/badge/Telegram-2CA5E0?logo=telegram&logoColor=white [ico-telegram]: https://img.shields.io/badge/Telegram-2CA5E0?logo=telegram&logoColor=white
[ico-discord]: https://img.shields.io/badge/Discord-%235865F2.svg?&logo=discord&logoColor=white [ico-discord]: https://img.shields.io/badge/Discord-%235865F2.svg?&logo=discord&logoColor=white

View file

@ -1,5 +1,8 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins { plugins {
id 'com.android.application' id 'com.android.application'
id 'org.jetbrains.kotlin.android'
} }
android { android {
@ -10,8 +13,8 @@ android {
applicationId "com.vectras.vm" applicationId "com.vectras.vm"
minSdk minApi minSdk minApi
targetSdk targetApi targetSdk targetApi
versionCode 43 versionCode 44
versionName "3.3.9" versionName "3.4.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true multiDexEnabled true
} }
@ -65,8 +68,15 @@ android {
sourceCompatibility JavaVersion.VERSION_21 sourceCompatibility JavaVersion.VERSION_21
targetCompatibility JavaVersion.VERSION_21 targetCompatibility JavaVersion.VERSION_21
} }
buildToolsVersion '36.1.0' buildToolsVersion '36.1.0'
ndkVersion '21' ndkVersion '21'
kotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_21)
}
}
} }
dependencies { dependencies {
@ -74,9 +84,9 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.7.1' implementation 'androidx.appcompat:appcompat:1.7.1'
implementation 'com.google.android.material:material:1.14.0-alpha07' implementation 'com.google.android.material:material:1.14.0-alpha07'
implementation "androidx.annotation:annotation:1.9.1" implementation "androidx.annotation:annotation:1.9.1"
implementation "androidx.core:core:1.17.0" implementation "androidx.core:core-ktx:1.17.0"
implementation "androidx.drawerlayout:drawerlayout:1.2.0" implementation "androidx.drawerlayout:drawerlayout:1.2.0"
implementation "androidx.preference:preference:1.2.1" implementation "androidx.preference:preference-ktx:1.2.1"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-rc01" implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-rc01"
implementation "androidx.viewpager:viewpager:1.1.0" implementation "androidx.viewpager:viewpager:1.1.0"
// implementation platform('com.google.firebase:firebase-bom:34.2.0') // implementation platform('com.google.firebase:firebase-bom:34.2.0')
@ -95,10 +105,11 @@ dependencies {
implementation 'com.airbnb.android:lottie:6.7.1' implementation 'com.airbnb.android:lottie:6.7.1'
implementation 'org.apache.commons:commons-compress:1.28.0' implementation 'org.apache.commons:commons-compress:1.28.0'
// implementation 'com.google.firebase:firebase-crashlytics-buildtools:3.0.6' // implementation 'com.google.firebase:firebase-crashlytics-buildtools:3.0.6'
implementation 'androidx.activity:activity:1.12.0' implementation 'androidx.activity:activity-ktx:1.12.0'
implementation 'androidx.constraintlayout:constraintlayout:2.2.1' implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
implementation 'androidx.preference:preference:1.2.1' implementation 'androidx.preference:preference-ktx:1.2.1'
implementation "androidx.documentfile:documentfile:1.1.0" implementation "androidx.documentfile:documentfile:1.1.0"
implementation 'androidx.core:core-ktx:1.17.0'
compileOnly project(':shell-loader:stub') compileOnly project(':shell-loader:stub')
implementation project(":terminal-view") implementation project(":terminal-view")
implementation project(":library") implementation project(":library")

View file

@ -0,0 +1,445 @@
package com.k2.zoomimageview
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.*
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.ScaleGestureDetector
import android.view.ViewConfiguration
import android.view.animation.AccelerateDecelerateInterpolator
import android.view.animation.DecelerateInterpolator
import android.widget.OverScroller
import androidx.core.animation.doOnCancel
import androidx.core.animation.doOnEnd
import androidx.core.view.ViewCompat
import kotlin.math.absoluteValue
/**
* @author Kislay [k.two.apps@gmail.com]
* @since 03/09/20
*/
class ZoomImageView : androidx.appcompat.widget.AppCompatImageView {
private val textPaint = Paint()
private val zoomMatrix = Matrix()
private val baseMatrix = Matrix()
private val preEventImgRect = RectF()
private val matrixValues = FloatArray(9)
private val zoomInterpolator = AccelerateDecelerateInterpolator()
private var logText = ""
private var handlingDismiss = false
private var touchSlop: Float = 0F
private var oldScale = MIN_SCALE
private var panAnimator: ValueAnimator? = null
private var zoomAnimator: ValueAnimator? = null
private var onClickListener: OnClickListener? = null
private var onLongClickListener: OnLongClickListener? = null
private var viewWidth = right - left - paddingLeft - paddingRight
private var viewHeight = bottom - top - paddingTop - paddingBottom
private lateinit var scroller: OverScroller
private lateinit var tapDetector: GestureDetector
private lateinit var scaleDetector: ScaleGestureDetector
var debugInfoVisible = false
var swipeToDismissEnabled = false
var disallowPagingWhenZoomed = false
var onDismiss: () -> Unit = {}
var onDrawableLoaded: () -> Unit = {}
var dismissProgressListener: (progress: Float) -> Unit = {}
constructor(context: Context) : super(context) {
initView()
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
initView()
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(
context, attrs, defStyleAttr
) {
initView()
}
private fun initView() {
touchSlop = ViewConfiguration.get(context).scaledTouchSlop.toFloat()
initTextPaint()
scaleType = ScaleType.MATRIX
scaleDetector = ScaleGestureDetector(context, scaleListener)
scroller = OverScroller(context, DecelerateInterpolator())
tapDetector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {
override fun onDoubleTap(e: MotionEvent): Boolean {
oldScale = currentScale
val scaleFactor = if (currentScale != MIN_SCALE) MIN_SCALE else MID_SCALE
setScaleAbsolute(scaleFactor, e.x, e.y)
return true
}
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
onClickListener?.onClick(this@ZoomImageView)
return true
}
override fun onLongPress(e: MotionEvent) {
onLongClickListener?.onLongClick(this@ZoomImageView)
}
override fun onScroll(
e1: MotionEvent?,
e2: MotionEvent,
distanceX: Float,
distanceY: Float
): Boolean {
if (scaleDetector.isInProgress) return false
val xAbs = distanceX.absoluteValue
val yAbs = distanceY.absoluteValue
if (currentScale <= MIN_SCALE) {
if (swipeToDismissEnabled && yAbs > xAbs) {
handlingDismiss = true
panImage(0F, distanceY)
dismissProgressListener.invoke(dismissProgress)
}
} else {
panImage(distanceX, distanceY)
}
var disallowParentIntercept = true
if (!disallowPagingWhenZoomed) {
if (handlingDismiss) {
disallowParentIntercept = true
} else if (xAbs > yAbs) {
// horizontal scroll
if (distanceX > 0F && preEventImgRect.right == viewWidth.toFloat())
disallowParentIntercept = false
else if (distanceX < 0F && preEventImgRect.left == 0F)
disallowParentIntercept = false
} else {
// vertical scroll
if (distanceY > 0F && preEventImgRect.bottom == viewHeight.toFloat())
disallowParentIntercept = false
else if (distanceY < 0F && preEventImgRect.top == 0F)
disallowParentIntercept = false
}
}
parent?.requestDisallowInterceptTouchEvent(disallowParentIntercept)
return (xAbs > touchSlop || yAbs > touchSlop)
}
override fun onFling(
e1: MotionEvent?,
e2: MotionEvent,
velocityX: Float,
velocityY: Float
): Boolean {
if (currentZoom <= MIN_SCALE) return false
val maxX = (preEventImgRect.width() - viewWidth).toInt()
val maxY = (preEventImgRect.height() - viewHeight).toInt()
flingRunnable.lastX = -preEventImgRect.left
flingRunnable.lastY = -preEventImgRect.top
scroller.fling(
flingRunnable.lastX.toInt(), flingRunnable.lastY.toInt(), -velocityX.toInt(),
-velocityY.toInt(), 0, maxX, 0, maxY
)
ViewCompat.postOnAnimation(this@ZoomImageView, flingRunnable)
return true
}
override fun onDown(e: MotionEvent): Boolean {
removeCallbacks(flingRunnable)
scroller.forceFinished(true)
displayRect?.let {
preEventImgRect.set(it)
}
panAnimator?.removeAllUpdateListeners()
panAnimator?.cancel()
return true
}
})
}
private val flingRunnable = object : Runnable {
var lastX = 0F
var lastY = 0F
override fun run() {
if (!scroller.isFinished && scroller.computeScrollOffset()) {
val curX = scroller.currX.toFloat()
val curY = scroller.currY.toFloat()
panImage((curX - lastX), (curY - lastY))
lastX = curX
lastY = curY
ViewCompat.postOnAnimation(this@ZoomImageView, this)
}
}
}
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
val disallowIntercept =
currentScale > MIN_SCALE || scaleDetector.isInProgress || handlingDismiss
if (event.action == MotionEvent.ACTION_UP) {
if (handlingDismiss) {
if (currentTransY.absoluteValue > dismissThreshold) {
onDismiss.invoke()
} else {
animatePan(0F, currentTransY, 0F, 0F, dismissProgress)
}
}
}
parent?.requestDisallowInterceptTouchEvent(disallowIntercept)
return tapDetector.onTouchEvent(event) || return scaleDetector.onTouchEvent(event) || return true
}
private fun setZoom(scale: Float, x: Float, y: Float) {
zoomMatrix.postScale(scale, scale, x, y)
setBounds()
updateMatrix(drawMatrix)
}
private fun updateMatrix(drawMatrix: Matrix) {
logText = "tX: $currentTransX tY: $currentTransY"
logText += " Scale: $currentScale"
imageMatrix = drawMatrix
}
private fun setScale(scale: Float, x: Float, y: Float) {
setZoom(scale, x, y)
}
private fun setScaleAbsolute(scale: Float, x: Float, y: Float) {
val zoom = when {
scale > MAX_SCALE -> MAX_SCALE
scale < MIN_SCALE -> MIN_SCALE
else -> scale
}
cancelAnimation()
animateZoom(oldScale, zoom, x, y)
}
private inline val drawableWidth: Int
get() = drawable?.intrinsicWidth ?: 0
private inline val drawableHeight: Int
get() = drawable?.intrinsicHeight ?: 0
override fun setImageDrawable(drawable: Drawable?) {
super.setImageDrawable(drawable)
if (drawable != null) {
onDrawableLoaded.invoke()
resetZoom()
zoomMatrix.set(imageMatrix)
}
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
viewWidth = right - left - paddingLeft - paddingRight
viewHeight = bottom - top - paddingTop - paddingBottom
if (changed) resetZoom()
}
fun resetZoom() {
val tempSrc = RectF(0F, 0F, drawableWidth.toFloat(), drawableHeight.toFloat())
val tempDst = RectF(0F, 0F, viewWidth.toFloat(), viewHeight.toFloat())
baseMatrix.setRectToRect(tempSrc, tempDst, Matrix.ScaleToFit.CENTER)
setScaleAbsolute(MIN_SCALE, viewWidth / 2F, viewHeight / 2F)
imageMatrix = baseMatrix
}
private val scaleListener = object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
override fun onScale(detector: ScaleGestureDetector): Boolean {
if (detector.scaleFactor.isNaN() || detector.scaleFactor.isInfinite())
return false
if (currentScale > MAX_SCALE && detector.scaleFactor > 1F)
return false
oldScale = currentScale
setScale(detector.scaleFactor, detector.focusX, detector.focusY)
return true
}
override fun onScaleEnd(detector: ScaleGestureDetector) {
super.onScaleEnd(detector)
oldScale = currentScale
var needsReset = false
var newScale = MIN_SCALE
if (currentScale < MIN_SCALE) {
newScale = MIN_SCALE
needsReset = true
}
if (needsReset) setScaleAbsolute(newScale, detector.focusX, detector.focusY)
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (debugInfoVisible) {
canvas.drawText(logText, 10F, height - 10F, textPaint)
val drawableBound = displayRect?.let {
"Drawable: $it"
} ?: ""
canvas.drawText(drawableBound, 10F, 40F, textPaint)
}
}
private fun initTextPaint() {
textPaint.color = Color.WHITE
textPaint.style = Paint.Style.FILL
textPaint.textSize = 40F
}
private fun animateZoom(startZoom: Float, endZoom: Float, x: Float, y: Float) {
zoomAnimator = ValueAnimator.ofFloat(startZoom, endZoom).apply {
duration = VALUE_ANIMATOR_DURATION
addUpdateListener {
val scale = (it.animatedValue as Float) / currentScale
setZoom(scale, x, y)
}
interpolator = zoomInterpolator
start()
}
}
private fun animatePan(
startX: Float, startY: Float, endX: Float, endY: Float, dismissProgress: Float? = null
) {
panAnimator = ValueAnimator.ofFloat(startX, startY, endX, endY).apply {
duration = VALUE_ANIMATOR_DURATION
addUpdateListener {
val newX = (startX - endX) * it.animatedFraction
val newY = (startY - endY) * it.animatedFraction
panImage(startX - newX, startY - newY, setAbsolute = true)
dismissProgress?.let { progress ->
if (1.0F - it.animatedFraction < progress) {
dismissProgressListener.invoke(1.0F - it.animatedFraction)
}
}
}
interpolator = zoomInterpolator
start()
doOnCancel {
panImage(0F, 0F, setAbsolute = true)
handlingDismiss = false
}
doOnEnd {
handlingDismiss = false
}
}
}
private fun cancelAnimation() {
zoomAnimator?.removeAllUpdateListeners()
zoomAnimator?.cancel()
}
private fun panImage(x: Float, y: Float, setAbsolute: Boolean = false) {
if (setAbsolute)
zoomMatrix.setTranslate(x, y)
else
zoomMatrix.postTranslate(-x, -y)
setBounds()
updateMatrix(drawMatrix)
}
private fun setBounds() {
val rect = displayRect ?: return
val height = rect.height()
val width = rect.width()
val viewHeight: Int = this.viewHeight
var deltaX = 0f
var deltaY = 0f
when {
height <= viewHeight -> {
if (!handlingDismiss)
deltaY = (viewHeight - height) / 2 - rect.top
}
rect.top > 0 -> {
deltaY = -rect.top
}
rect.bottom < viewHeight -> {
deltaY = viewHeight - rect.bottom
}
}
val viewWidth: Int = this.viewWidth
when {
width <= viewWidth -> {
deltaX = (viewWidth - width) / 2 - rect.left
}
rect.left > 0 -> {
deltaX = -rect.left
}
rect.right < viewWidth -> {
deltaX = viewWidth - rect.right
}
}
zoomMatrix.postTranslate(deltaX, deltaY)
}
private inline val dismissThreshold: Float
get() = viewHeight / 3F
private inline val currentScale: Float
get() {
zoomMatrix.getValues(matrixValues)
return matrixValues[Matrix.MSCALE_X]
}
private inline val currentTransX: Float
get() {
zoomMatrix.getValues(matrixValues)
return matrixValues[Matrix.MTRANS_X]
}
private inline val currentTransY: Float
get() {
zoomMatrix.getValues(matrixValues)
return matrixValues[Matrix.MTRANS_Y]
}
private inline val dismissProgress: Float
get() = currentTransY.absoluteValue / dismissThreshold
private val displayRect: RectF? = RectF()
get() {
drawable?.let { d ->
field?.set(
0f, 0f, d.intrinsicWidth.toFloat(), d.intrinsicHeight.toFloat()
)
drawMatrix.mapRect(field)
return field
}
return null
}
private val drawMatrix: Matrix = Matrix()
get() {
field.set(baseMatrix)
field.postConcat(zoomMatrix)
return field
}
var currentZoom: Float
get() = currentScale
set(value) {
oldScale = currentScale
setScaleAbsolute(value, viewWidth / 2F, viewHeight / 2F)
}
companion object {
const val MAX_SCALE = 3F
const val MIN_SCALE = 1F
const val MID_SCALE = 1.75F
private const val VALUE_ANIMATOR_DURATION = 300L
}
override fun setOnClickListener(l: OnClickListener?) {
this.onClickListener = l
}
override fun setOnLongClickListener(l: OnLongClickListener?) {
this.onLongClickListener = l
}
}

View file

@ -6,19 +6,30 @@ import android.widget.ImageView;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.google.android.material.color.MaterialColors;
import com.vectras.vm.databinding.IvPrvBinding;
import com.vectras.vm.utils.UIUtils; import com.vectras.vm.utils.UIUtils;
public class ImagePrvActivity extends AppCompatActivity { public class ImagePrvActivity extends AppCompatActivity {
public static String linkIv; IvPrvBinding binding;
public ImageView ivPrv; boolean isInvertedBackground;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
UIUtils.edgeToEdge(this); UIUtils.edgeToEdge(this);
setContentView(R.layout.iv_prv); binding = IvPrvBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
UIUtils.setOnApplyWindowInsetsListener(findViewById(R.id.main)); UIUtils.setOnApplyWindowInsetsListener(findViewById(R.id.main));
ivPrv = findViewById(R.id.ivPrv); if (getIntent().hasExtra("uri")) {
Glide.with(this).load(linkIv).into(ivPrv); Glide.with(this).load(getIntent().getStringExtra("uri")).placeholder(R.drawable.progress_activity_24px).error(R.drawable.broken_image_24px).into(binding.ivPrv);
}
binding.btnChangeBackground.setOnClickListener(v -> {
int newBackgroundColor = MaterialColors.getColor(binding.main, isInvertedBackground ? com.google.android.material.R.attr.colorSurface : com.google.android.material.R.attr.colorOnSurface);
binding.main.setBackgroundColor(newBackgroundColor);
UIUtils.setLightStatusBar(UIUtils.isColorLight(newBackgroundColor), ImagePrvActivity.this);
isInvertedBackground = !isInvertedBackground;
});
} }
} }

View file

@ -233,6 +233,15 @@ public class RomInfo extends AppCompatActivity {
binding.tvFilename.setText(getIntent().getStringExtra("filename")); binding.tvFilename.setText(getIntent().getStringExtra("filename"));
} }
binding.ivIcon.setOnClickListener(v -> {
if (getIntent().hasExtra("icon")) {
Intent intent = new Intent();
intent.putExtra("uri", getIntent().getStringExtra("icon"));
intent.setClass(getApplicationContext(), ImagePrvActivity.class);
startActivity(intent);
}
});
binding.lnViews.setOnClickListener((v -> DialogUtils.oneDialog( binding.lnViews.setOnClickListener((v -> DialogUtils.oneDialog(
RomInfo.this, RomInfo.this,
getString(R.string.views), getString(R.string.views),
@ -540,7 +549,7 @@ public class RomInfo extends AppCompatActivity {
case "X86_64" -> getString(R.string.x86_64); case "X86_64" -> getString(R.string.x86_64);
case "i386" -> getString(R.string.i386_qemu); case "i386" -> getString(R.string.i386_qemu);
case "ARM64" -> getString(R.string.arm64_qemu); case "ARM64" -> getString(R.string.arm64_qemu);
case "PowerPC" -> getString(R.string.powerpc_qemu); case "PPC" -> getString(R.string.powerpc_qemu);
default -> getString(R.string.unknow); default -> getString(R.string.unknow);
}; };
} }

View file

@ -168,22 +168,22 @@ public class StoreItemActivity extends AppCompatActivity {
itemPrvMain.setOnClickListener(new View.OnClickListener() { itemPrvMain.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
ImagePrvActivity.linkIv = prvMain; // ImagePrvActivity.linkIv = prvMain;
startActivity(new Intent(activity, ImagePrvActivity.class)); // startActivity(new Intent(activity, ImagePrvActivity.class));
} }
}); });
itemPrv1.setOnClickListener(new View.OnClickListener() { itemPrv1.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
ImagePrvActivity.linkIv = prv1; // ImagePrvActivity.linkIv = prv1;
startActivity(new Intent(activity, ImagePrvActivity.class)); // startActivity(new Intent(activity, ImagePrvActivity.class));
} }
}); });
itemPrv2.setOnClickListener(new View.OnClickListener() { itemPrv2.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
ImagePrvActivity.linkIv = prv2; // ImagePrvActivity.linkIv = prv2;
startActivity(new Intent(activity, ImagePrvActivity.class)); // startActivity(new Intent(activity, ImagePrvActivity.class));
} }
}); });
} }

View file

@ -561,8 +561,8 @@ public class VMCreatorActivity extends AppCompatActivity {
created = true; created = true;
if (getIntent().hasExtra("addromnow")) { if (getIntent().hasExtra("addromnow")) {
RomStoreActivity.isFinishNow = true;
RomInfo.isFinishNow = true; RomInfo.isFinishNow = true;
HomeActivity.isOpenHome = true;
} }
modify = false; modify = false;

View file

@ -10,6 +10,8 @@ import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -37,6 +39,7 @@ import com.vectras.vm.settings.VNCSettingsActivity;
import com.vectras.vm.utils.DialogUtils; import com.vectras.vm.utils.DialogUtils;
import com.vectras.vm.utils.FileUtils; import com.vectras.vm.utils.FileUtils;
import com.vectras.vm.utils.JSONUtils; import com.vectras.vm.utils.JSONUtils;
import com.vectras.vm.utils.TextUtils;
import com.vectras.vm.utils.UIUtils; import com.vectras.vm.utils.UIUtils;
import com.vectras.vterm.Terminal; import com.vectras.vterm.Terminal;
@ -131,17 +134,39 @@ public class VMManager {
DialogUtils.threeDialog(_activity, _activity.getString(R.string.remove)+ " " + _vmName, _activity.getString(R.string.remove_vm_content), _activity.getString(R.string.remove_and_do_not_keep_files), _activity.getString(R.string.remove_but_keep_files), _activity.getString(R.string.cancel),true, R.drawable.delete_24px, true, DialogUtils.threeDialog(_activity, _activity.getString(R.string.remove)+ " " + _vmName, _activity.getString(R.string.remove_vm_content), _activity.getString(R.string.remove_and_do_not_keep_files), _activity.getString(R.string.remove_but_keep_files), _activity.getString(R.string.cancel),true, R.drawable.delete_24px, true,
() -> { () -> {
View progressView = LayoutInflater.from(_activity).inflate(R.layout.dialog_progress_style, null);
TextView progress_text = progressView.findViewById(R.id.progress_text);
progress_text.setText(_activity.getString(R.string.just_a_moment));
AlertDialog progressDialog = new MaterialAlertDialogBuilder(_activity, R.style.CenteredDialogTheme)
.setView(progressView)
.setCancelable(false)
.create();
progressDialog.show();
new Thread(() -> {
isKeptSomeFiles = false; isKeptSomeFiles = false;
deleteVM(); deleteVM();
removeInRomsDataJson(_activity, _vmName, _position); removeInRomsDataJson(_activity, _vmName, _position);
_activity.runOnUiThread(() -> new Handler(Looper.getMainLooper()).postDelayed(progressDialog::dismiss, 500));
}).start();
}, },
() -> { () -> {
View progressView = LayoutInflater.from(_activity).inflate(R.layout.dialog_progress_style, null);
TextView progress_text = progressView.findViewById(R.id.progress_text);
progress_text.setText(_activity.getString(R.string.just_a_moment));
AlertDialog progressDialog = new MaterialAlertDialogBuilder(_activity, R.style.CenteredDialogTheme)
.setView(progressView)
.setCancelable(false)
.create();
progressDialog.show();
new Thread(() -> {
hideVMIDWithPosition(); hideVMIDWithPosition();
removeInRomsDataJson(_activity, _vmName, _position); removeInRomsDataJson(_activity, _vmName, _position);
_activity.runOnUiThread(() -> new Handler(Looper.getMainLooper()).postDelayed(progressDialog::dismiss, 500));
}).start();
}, },
() -> { null,
},
null); null);
} }
@ -181,35 +206,14 @@ public class VMManager {
@NonNull @NonNull
public static String startRamdomVMID() { public static String startRamdomVMID() {
String addAdb;
Random random = new Random(); Random random = new Random();
int randomAbc = random.nextInt(12); StringBuilder result = new StringBuilder();
if (randomAbc == 0) {
addAdb = "a"; for (int i = 0; i < 10; i++) {
} else if (randomAbc == 1) { result.append(random.nextInt(2) > 0 ? TextUtils.randomALetter() : String.valueOf(random.nextInt(10)));
addAdb = "b";
} else if (randomAbc == 2) {
addAdb = "c";
} else if (randomAbc == 3) {
addAdb = "d";
} else if (randomAbc == 4) {
addAdb = "e";
} else if (randomAbc == 5) {
addAdb = "f";
} else if (randomAbc == 6) {
addAdb = "g";
} else if (randomAbc == 7) {
addAdb = "h";
} else if (randomAbc == 8) {
addAdb = "i";
} else if (randomAbc == 9) {
addAdb = "j";
} else if (randomAbc == 10) {
addAdb = "k";
} else {
addAdb = "l";
} }
return addAdb + (long) (random.nextInt(65535));
return result.toString();
} }
public static int startRandomPort() { public static int startRandomPort() {

View file

@ -93,6 +93,7 @@ public class HomeActivity extends AppCompatActivity implements RomStoreFragment.
private final String TAG = "HomeActivity"; private final String TAG = "HomeActivity";
public static boolean isActivate = false; public static boolean isActivate = false;
public static boolean isNeedRecreate = false; public static boolean isNeedRecreate = false;
public static boolean isOpenHome = false;
public static boolean isOpenRomStore = false; public static boolean isOpenRomStore = false;
private final ExecutorService executor = Executors.newSingleThreadExecutor(); private final ExecutorService executor = Executors.newSingleThreadExecutor();
ActivityHomeBinding binding; ActivityHomeBinding binding;
@ -336,6 +337,10 @@ public class HomeActivity extends AppCompatActivity implements RomStoreFragment.
if (isOpenRomStore) { if (isOpenRomStore) {
isOpenRomStore = false; isOpenRomStore = false;
bindingContent.bottomNavigation.setSelectedItemId(R.id.item_romstore); bindingContent.bottomNavigation.setSelectedItemId(R.id.item_romstore);
} else if (isOpenHome) {
isOpenHome = false;
if (binding.searchview.isShowing()) binding.searchview.hide();
bindingContent.bottomNavigation.setSelectedItemId(R.id.item_home);
} }
} }

View file

@ -6,6 +6,7 @@ import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
@ -20,6 +21,7 @@ import android.widget.BaseAdapter;
import androidx.activity.OnBackPressedCallback; import androidx.activity.OnBackPressedCallback;
import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts; import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
@ -81,6 +83,7 @@ public class SetupWizard2Activity extends AppCompatActivity {
boolean isLibProotError = false; boolean isLibProotError = false;
boolean aria2Error = false; boolean aria2Error = false;
boolean isServerError = false; boolean isServerError = false;
boolean isNotEnoughStorageSpace = false;
boolean isCustomSetupMode = false; boolean isCustomSetupMode = false;
final ArrayList<HashMap<String, String>> mirrorList = new ArrayList<>(); final ArrayList<HashMap<String, String>> mirrorList = new ArrayList<>();
@ -117,6 +120,12 @@ public class SetupWizard2Activity extends AppCompatActivity {
} }
} }
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
loadingIndicatorController(currentStep);
}
private void initialize() { private void initialize() {
tarPath = getExternalFilesDir("data") + "/data.tar.gz"; tarPath = getExternalFilesDir("data") + "/data.tar.gz";
@ -237,9 +246,14 @@ public class SetupWizard2Activity extends AppCompatActivity {
binding.lnInstallingPackages.setVisibility(View.VISIBLE); binding.lnInstallingPackages.setVisibility(View.VISIBLE);
} else if (step == STEP_ERROR) { } else if (step == STEP_ERROR) {
binding.lnInstallingPackagesFailed.setVisibility(View.VISIBLE); binding.lnInstallingPackagesFailed.setVisibility(View.VISIBLE);
binding.tvErrorLogContent.setText(log); binding.tvErrorLogContent.setText(log.isEmpty() ? getString(R.string.there_are_no_logs) : log);
if (isLibProotError) { if (isNotEnoughStorageSpace) {
binding.ivErrorLarge.setImageResource(R.drawable.disc_full_100px);
binding.tvErrorTitle.setText(getString(R.string.not_enough_storage_space));
binding.tvErrorSubtitle.setText(getString(R.string.not_enough_storage_to_set_up_content));
binding.btnTryAgain.setText(getString(R.string.join_our_community));
} else if (isLibProotError) {
binding.ivErrorLarge.setImageResource(R.drawable.error_96px); binding.ivErrorLarge.setImageResource(R.drawable.error_96px);
binding.tvErrorTitle.setText(getString(R.string.vectras_vm_cannot_run_on_this_device)); binding.tvErrorTitle.setText(getString(R.string.vectras_vm_cannot_run_on_this_device));
binding.tvErrorSubtitle.setText(getString(R.string.a_serious_problem_has_occurred)); binding.tvErrorSubtitle.setText(getString(R.string.a_serious_problem_has_occurred));
@ -257,9 +271,54 @@ public class SetupWizard2Activity extends AppCompatActivity {
bindingFinalSteps.main.setVisibility(View.VISIBLE); bindingFinalSteps.main.setVisibility(View.VISIBLE);
} }
loadingIndicatorController(step);
currentStep = step; currentStep = step;
} }
private void loadingIndicatorController(int step) {
float dp = 200f;
float px = dp * getResources().getDisplayMetrics().density;
if (step == STEP_EXTRACTING_SYSTEM_FILES) {
binding.lnExtractingSystemFilesCpiContainer.post(() -> {
int heightPx = binding.lnExtractingSystemFilesCpiContainer.getHeight();
if (heightPx < px) {
binding.cpiExtractingSystemFiles.setVisibility(View.GONE);
binding.lpiExtractingSystemFiles.setVisibility(View.VISIBLE);
} else {
binding.cpiExtractingSystemFiles.setVisibility(View.VISIBLE);
binding.lpiExtractingSystemFiles.setVisibility(View.GONE);
}
});
} else if (step == STEP_GETTING_DATA) {
binding.lnGettingDataCpiContainer.post(() -> {
int heightPx = binding.lnGettingDataCpiContainer.getHeight();
if (heightPx < px) {
binding.cpiGettingData.setVisibility(View.GONE);
binding.lpiGettingData.setVisibility(View.VISIBLE);
} else {
binding.cpiGettingData.setVisibility(View.VISIBLE);
binding.lpiGettingData.setVisibility(View.GONE);
}
});
} else if (step == STEP_INSTALLING_PACKAGES) {
binding.lnInstallingPackagesCpiContainer.post(() -> {
int heightPx = binding.lnInstallingPackagesCpiContainer.getHeight();
if (heightPx < px) {
binding.cpiInstallingPackages.setVisibility(View.GONE);
binding.lpiInstallingPackages.setVisibility(View.VISIBLE);
} else {
binding.cpiInstallingPackages.setVisibility(View.VISIBLE);
binding.lpiInstallingPackages.setVisibility(View.GONE);
}
});
}
}
private void uiControllerFinalSteps(int step) { private void uiControllerFinalSteps(int step) {
TransitionManager.beginDelayedTransition(bindingFinalSteps.mainContent); TransitionManager.beginDelayedTransition(bindingFinalSteps.mainContent);
@ -287,7 +346,14 @@ public class SetupWizard2Activity extends AppCompatActivity {
} }
private void extractSystemFiles() { private void extractSystemFiles() {
isNotEnoughStorageSpace = DeviceUtils.isStorageLow(this, false);
if (isNotEnoughStorageSpace) {
uiController(STEP_ERROR);
return;
} else {
uiController(STEP_EXTRACTING_SYSTEM_FILES); uiController(STEP_EXTRACTING_SYSTEM_FILES);
}
new Thread(() -> { new Thread(() -> {
boolean result = SetupFeatureCore.startExtractSystemFiles(this); boolean result = SetupFeatureCore.startExtractSystemFiles(this);
@ -369,7 +435,7 @@ public class SetupWizard2Activity extends AppCompatActivity {
" mkdir -p $TMPDIR/pulse;" + " mkdir -p $TMPDIR/pulse;" +
" echo export PULSE_SERVER=127.0.0.1 >> /etc/profile;" + " echo export PULSE_SERVER=127.0.0.1 >> /etc/profile;" +
" mkdir -p ~/.vnc && echo -e \"555555\\n555555\" | vncpasswd -f > ~/.vnc/passwd && chmod 0600 ~/.vnc/passwd;" + " mkdir -p ~/.vnc && echo -e \"555555\\n555555\" | vncpasswd -f > ~/.vnc/passwd && chmod 0600 ~/.vnc/passwd;" +
" echo \"installation successful! xssFjnj58Id\""; " echo \"Installation successful! xssFjnj58Id\"";
executeShellCommand(cmd); executeShellCommand(cmd);
} }

View file

@ -3,14 +3,14 @@ package com.vectras.vm.utils;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.usage.StorageStatsManager; import android.app.usage.StorageStatsManager;
import android.content.Context; import android.content.Context;
import android.content.res.Configuration;
import android.os.Build; import android.os.Build;
import android.os.Environment; import android.os.Environment;
import android.os.StatFs; import android.os.StatFs;
import android.os.storage.StorageManager; import android.os.storage.StorageManager;
import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
import androidx.annotation.RequiresApi;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.UUID; import java.util.UUID;
@ -63,4 +63,16 @@ public class DeviceUtils {
public static boolean is64bit() { public static boolean is64bit() {
return Build.SUPPORTED_ABIS[0].contains("arm64"); return Build.SUPPORTED_ABIS[0].contains("arm64");
} }
public static boolean isLargeScreen(Context context) {
Configuration config = context.getResources().getConfiguration();
return config.smallestScreenWidthDp >= 600;
}
public static boolean isHighDpi(Context context) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
int currentDpi = metrics.densityDpi;
Log.i(TAG, "isHighDpi: " + currentDpi);
return currentDpi >= 600;
}
} }

View file

@ -0,0 +1,65 @@
package com.vectras.vm.utils;
import java.util.Random;
public class TextUtils {
public static String randomALetter() {
String addAdb;
Random random = new Random();
int randomAbc = random.nextInt(26);
if (randomAbc == 0) {
addAdb = "a";
} else if (randomAbc == 1) {
addAdb = "b";
} else if (randomAbc == 2) {
addAdb = "c";
} else if (randomAbc == 3) {
addAdb = "d";
} else if (randomAbc == 4) {
addAdb = "e";
} else if (randomAbc == 5) {
addAdb = "f";
} else if (randomAbc == 6) {
addAdb = "g";
} else if (randomAbc == 7) {
addAdb = "h";
} else if (randomAbc == 8) {
addAdb = "i";
} else if (randomAbc == 9) {
addAdb = "j";
} else if (randomAbc == 10) {
addAdb = "k";
} else if (randomAbc == 11) {
addAdb = "l";
} else if (randomAbc == 12) {
addAdb = "m";
} else if (randomAbc == 13) {
addAdb = "n";
} else if (randomAbc == 14) {
addAdb = "o";
} else if (randomAbc == 15) {
addAdb = "p";
} else if (randomAbc == 16) {
addAdb = "q";
} else if (randomAbc == 17) {
addAdb = "r";
} else if (randomAbc == 18) {
addAdb = "s";
} else if (randomAbc == 19) {
addAdb = "t";
} else if (randomAbc == 20) {
addAdb = "u";
} else if (randomAbc == 21) {
addAdb = "v";
} else if (randomAbc == 22) {
addAdb = "w";
} else if (randomAbc == 23) {
addAdb = "x";
} else if (randomAbc == 24) {
addAdb = "y";
} else {
addAdb = "z";
}
return addAdb;
}
}

View file

@ -8,6 +8,7 @@ import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.graphics.Insets; import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat; import androidx.core.view.ViewCompat;
import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsCompat; import androidx.core.view.WindowInsetsCompat;
import android.content.ClipData; import android.content.ClipData;
@ -32,6 +33,8 @@ import android.util.Log;
import android.view.Display; import android.view.Display;
import android.view.Gravity; import android.view.Gravity;
import android.view.View; import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.webkit.WebView; import android.webkit.WebView;
import android.widget.ScrollView; import android.widget.ScrollView;
@ -524,4 +527,13 @@ public class UIUtils {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
} }
} }
public static void setLightStatusBar(boolean isEnable, Activity _activity) {
Window window = _activity.getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
WindowCompat.getInsetsController(window, window.getDecorView())
.setAppearanceLightStatusBars(isEnable);
}
} }

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM240,503L400,343L560,503L720,343L760,383L760,200Q760,200 760,200Q760,200 760,200L200,200Q200,200 200,200Q200,200 200,200L200,463L240,503ZM200,760L760,760Q760,760 760,760Q760,760 760,760L760,496L720,456L560,616L400,456L240,616L200,576L200,760Q200,760 200,760Q200,760 200,760ZM200,760L200,760Q200,760 200,760Q200,760 200,760L200,496L200,576L200,463L200,383L200,200Q200,200 200,200Q200,200 200,200L200,200Q200,200 200,200Q200,200 200,200L200,463L200,463L200,576L200,576L200,760Q200,760 200,760Q200,760 200,760Z"/>
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="100dp"
android:height="100dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M480,660Q555,660 607.5,607.5Q660,555 660,480Q660,405 607.5,352.5Q555,300 480,300Q405,300 352.5,352.5Q300,405 300,480Q300,555 352.5,607.5Q405,660 480,660ZM480,520Q463,520 451.5,508.5Q440,497 440,480Q440,463 451.5,451.5Q463,440 480,440Q497,440 508.5,451.5Q520,463 520,480Q520,497 508.5,508.5Q497,520 480,520ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q600,80 699,145.5Q798,211 846,320L756,320Q714,245 640,202.5Q566,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800Q549,800 612,771.5Q675,743 720,690L720,800Q667,838 606,859Q545,880 480,880ZM800,720L800,400L880,400L880,720L800,720ZM840,880Q823,880 811.5,868.5Q800,857 800,840Q800,823 811.5,811.5Q823,800 840,800Q857,800 868.5,811.5Q880,823 880,840Q880,857 868.5,868.5Q857,880 840,880ZM480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z"/>
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M480,880Q398,880 325,848.5Q252,817 197.5,762.5Q143,708 111.5,635Q80,562 80,480Q80,397 111.5,324.5Q143,252 197.5,197.5Q252,143 325,111.5Q398,80 480,80Q497,80 508.5,91.5Q520,103 520,120Q520,137 508.5,148.5Q497,160 480,160Q347,160 253.5,253.5Q160,347 160,480Q160,613 253.5,706.5Q347,800 480,800Q613,800 706.5,706.5Q800,613 800,480Q800,463 811.5,451.5Q823,440 840,440Q857,440 868.5,451.5Q880,463 880,480Q880,562 848.5,635Q817,708 762.5,762.5Q708,817 635.5,848.5Q563,880 480,880Z"/>
</vector>

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M160,880Q127,880 103.5,856.5Q80,833 80,800L80,320Q80,287 103.5,263.5Q127,240 160,240L240,240L240,160Q240,127 263.5,103.5Q287,80 320,80L800,80Q833,80 856.5,103.5Q880,127 880,160L880,640Q880,673 856.5,696.5Q833,720 800,720L720,720L720,800Q720,833 696.5,856.5Q673,880 640,880L160,880ZM320,640L800,640Q800,640 800,640Q800,640 800,640L800,160Q800,160 800,160Q800,160 800,160L320,160Q320,160 320,160Q320,160 320,160L320,640Q320,640 320,640Q320,640 320,640Z"/>
</vector>

View file

@ -48,7 +48,7 @@
style="?attr/materialCardViewFilledStyle" style="?attr/materialCardViewFilledStyle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:cardBackgroundColor="?attr/colorSurfaceContainer"> app:cardBackgroundColor="?attr/colorCard">
<ImageView <ImageView
android:id="@+id/ivIcon" android:id="@+id/ivIcon"
@ -129,7 +129,7 @@
<View <View
android:layout_width="1dp" android:layout_width="1dp"
android:layout_height="25dp" android:layout_height="25dp"
android:background="?attr/colorSurfaceContainerHigh"/> android:background="?attr/colorSurfaceContainerHighest"/>
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
@ -159,7 +159,7 @@
<View <View
android:layout_width="1dp" android:layout_width="1dp"
android:layout_height="25dp" android:layout_height="25dp"
android:background="?attr/colorSurfaceContainerHigh"/> android:background="?attr/colorSurfaceContainerHighest"/>
<LinearLayout <LinearLayout
android:id="@+id/ln_creator" android:id="@+id/ln_creator"
@ -187,7 +187,7 @@
<View <View
android:layout_width="1dp" android:layout_width="1dp"
android:layout_height="25dp" android:layout_height="25dp"
android:background="?attr/colorSurfaceContainerHigh"/> android:background="?attr/colorSurfaceContainerHighest"/>
<LinearLayout <LinearLayout
android:id="@+id/ln_arch" android:id="@+id/ln_arch"
@ -215,7 +215,7 @@
<View <View
android:layout_width="1dp" android:layout_width="1dp"
android:layout_height="25dp" android:layout_height="25dp"
android:background="?attr/colorSurfaceContainerHigh"/> android:background="?attr/colorSurfaceContainerHighest"/>
<LinearLayout <LinearLayout
android:id="@+id/ln_size" android:id="@+id/ln_size"
@ -243,7 +243,7 @@
<View <View
android:layout_width="1dp" android:layout_width="1dp"
android:layout_height="25dp" android:layout_height="25dp"
android:background="?attr/colorSurfaceContainerHigh"/> android:background="?attr/colorSurfaceContainerHighest"/>
<LinearLayout <LinearLayout
android:id="@+id/ln_filename" android:id="@+id/ln_filename"

View file

@ -105,12 +105,22 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/just_a_sec" /> android:text="@string/just_a_sec" />
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/lpi_extracting_system_files"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingVertical="16dp"
android:indeterminate="true"
android:visibility="gone"/>
<LinearLayout <LinearLayout
android:id="@+id/ln_extracting_system_files_cpi_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:gravity="center"> android:gravity="center">
<com.google.android.material.progressindicator.CircularProgressIndicator <com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/cpi_extracting_system_files"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:indeterminate="true" android:indeterminate="true"
@ -141,13 +151,22 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/just_a_sec" /> android:text="@string/just_a_sec" />
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/lpi_getting_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingVertical="16dp"
android:indeterminate="true"
android:visibility="gone"/>
<LinearLayout <LinearLayout
android:id="@+id/ln_getting_data_cpi_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:gravity="center"> android:gravity="center">
<com.google.android.material.progressindicator.CircularProgressIndicator <com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/cpi_getting_data"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:indeterminate="true" android:indeterminate="true"
@ -372,13 +391,23 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/please_do_not_disconnect_the_network_this_may_take_a_few_minutes" /> android:text="@string/please_do_not_disconnect_the_network_this_may_take_a_few_minutes" />
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/lpi_installing_packages"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingVertical="16dp"
android:indeterminate="true"
android:visibility="gone"/>
<LinearLayout <LinearLayout
android:id="@+id/ln_installing_packages_cpi_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" android:layout_weight="1"
android:gravity="center"> android:gravity="center">
<com.google.android.material.progressindicator.CircularProgressIndicator <com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/cpi_installing_packages"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:indeterminate="true" android:indeterminate="true"

View file

@ -3,13 +3,23 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:fitsSystemWindows="true" android:fitsSystemWindows="true"
tools:context=".ImagePrvActivity" tools:context=".ImagePrvActivity"
android:id="@+id/main"> android:id="@+id/main">
<ImageView <com.k2.zoomimageview.ZoomImageView
android:id="@+id/ivPrv" android:id="@+id/ivPrv"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@android:color/black"
android:scaleType="fitCenter" /> android:scaleType="fitCenter" />
<Button
android:id="@+id/btn_change_background"
style="@style/Widget.Material3Expressive.Button.IconButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:padding="8dp"
android:layout_margin="16dp"
app:icon="@drawable/shadow_24px" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -3,7 +3,7 @@
<string name="app_name">Vectras VM</string> <string name="app_name">Vectras VM</string>
<!--======================VECTRAS STRINGS====================--> <!--======================VECTRAS STRINGS====================-->
<string name="app_version" translatable="false">3.3.9</string> <string name="app_version" translatable="false">3.4.0</string>
<string name="qemu_version" translatable="false">Stable</string> <string name="qemu_version" translatable="false">Stable</string>
<!-- logger --> <!-- logger -->
<string name="startvm">VM STARTED!</string> <string name="startvm">VM STARTED!</string>
@ -454,6 +454,8 @@
<string name="the_setup_could_not_be_completed_and_below_is_the_log">The setup could not be completed and below is the log.</string> <string name="the_setup_could_not_be_completed_and_below_is_the_log">The setup could not be completed and below is the log.</string>
<string name="this_option_is_temporarily_unavailable_because_the_server_cannot_be_connected">This option is temporarily unavailable because the server cannot be connected.</string> <string name="this_option_is_temporarily_unavailable_because_the_server_cannot_be_connected">This option is temporarily unavailable because the server cannot be connected.</string>
<string name="vectras_vm_cannot_run_on_this_device">Vectras VM cannot run on this device.</string> <string name="vectras_vm_cannot_run_on_this_device">Vectras VM cannot run on this device.</string>
<string name="not_enough_storage_space">Not enough storage space.</string>
<string name="there_are_no_logs">There are no logs.</string>
<!--======================TERMUX STRINGS====================--> <!--======================TERMUX STRINGS====================-->

View file

@ -1,12 +1,17 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext {
kotlin_version = '2.2.0'
}
repositories { repositories {
mavenCentral() mavenCentral()
google() google()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:8.13.1' // Android Gradle plugin classpath 'com.android.tools.build:gradle:8.13.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// Android Gradle plugin
// Add other classpaths like Google Services and Firebase Crashlytics if needed here. // Add other classpaths like Google Services and Firebase Crashlytics if needed here.
// classpath 'com.google.gms:google-services:4.4.3' // assuming you need it for your project // classpath 'com.google.gms:google-services:4.4.3' // assuming you need it for your project
// classpath 'com.google.firebase:firebase-crashlytics-gradle:3.0.6' // classpath 'com.google.firebase:firebase-crashlytics-gradle:3.0.6'

View file

@ -1,15 +1,15 @@
{ {
"versionCode":"34", "versionCode":"44",
"versionName":"3.3.0", "versionName":"3.4.0",
"size": "60 MB", "size": "55 MB",
"url": "https://github.com/xoureldeen/Vectras-VM-Android/releases", "url": "https://github.com/xoureldeen/Vectras-VM-Android/releases",
"Message": "<h2>3.3.0</h2>New Qemu 9.2.2 with 3dfx! And bugs fixed.", "Message": "<h2>3.4.0</h2>\n- Fixed the issue of exporting rom with virtual machine ID.\n- New dialog for copying files in creating new virtual machine.\n- New dialog showing information after successful rom import.\n- Fixed no sound playing with Termux.\n- Fixed invalid file path error when using Manual setup.\n- Fixed dialog showing content without line breaks.\n- Fixed rom export error.\n- Added Processes to System monitor.\n- Improved interface.\n- Thinner fonts.\n- Fixed an issue with interaction in Rom store.\n- Fixed the External VNC Server switch being incorrect in certain cases.\n- Removed unnecessary resources.\n- Updated Chinese (Simplified) language (contributed by @WeiguangTWK).\n- Fixed issue with installing system files after not completing the first time.\n- New setup wizard.\n- Added auto return to Home after importing rom and creating virtual machine in Rom store.\n- Improved image viewer.\n- Fixed Unknow display error in architecture in rom info if it is PowerPC architecture.\n- New setup wizard interface that automatically changes according to screen size.\n- New ID generator for virtual machine.\n- Added dialog when deleting virtual machine.",
"cancellable": true, "cancellable": true,
"versionCodeBeta":"43", "versionCodeBeta":"44",
"versionNameBeta":"3.3.9", "versionNameBeta":"3.4.0",
"versionNameBetas":"3.0.0,3.1.0,3.2.1,3.2.2,3.2.3,3.2.4,3.2.5,3.2.6,3.2.7,3.2.8,3.2.9,3.2.10,3.3.1,3.3.2,3.3.3,3.3.4,3.3.5,3.3.6,3.3.7,3.3.8,3.3.9", "versionNameBetas":"3.0.0,3.1.0,3.2.1,3.2.2,3.2.3,3.2.4,3.2.5,3.2.6,3.2.7,3.2.8,3.2.9,3.2.10,3.3.1,3.3.2,3.3.3,3.3.4,3.3.5,3.3.6,3.3.7,3.3.8,3.3.9",
"sizeBeta": "55 MB", "sizeBeta": "55 MB",
"urlBeta": "https://github.com/AnBui2004/Vectras-VM-Emu-Android/releases", "urlBeta": "https://github.com/AnBui2004/Vectras-VM-Emu-Android/releases",
"MessageBeta": "<h2>3.3.9</h2>Bugs fixed.", "MessageBeta": "<h2>3.4.0</h2>Bugs fixed.",
"cancellableBeta": true "cancellableBeta": true
} }