From dbfbbbc4bfd2b784244faa6db9dd39c9522b4deb Mon Sep 17 00:00:00 2001 From: artdeell Date: Fri, 11 Nov 2022 21:48:17 +0300 Subject: [PATCH] W.I.P: progress reader rewrite --- .../src/main/AndroidManifest.xml | 4 +- .../java/com/kdt/mcgui/ProgressLayout.java | 127 ++++++++---------- .../net/kdt/pojavlaunch/LauncherActivity.java | 5 +- .../progresskeeper/ProgressKeeper.java | 98 ++++++++++++++ .../progresskeeper/ProgressListener.java | 7 + .../progresskeeper/ProgressState.java | 7 + .../progresskeeper/TaskCountListener.java | 5 + .../pojavlaunch/services/ProgressService.java | 57 ++++---- .../services/ProgressServiceKeeper.java | 19 +++ .../pojavlaunch/tasks/AsyncAssetManager.java | 1 + .../tasks/AsyncMinecraftDownloader.java | 4 - .../res/layout-land/fragment_launcher.xml | 104 ++++++++++++++ 12 files changed, 336 insertions(+), 102 deletions(-) create mode 100644 app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/progresskeeper/ProgressKeeper.java create mode 100644 app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/progresskeeper/ProgressListener.java create mode 100644 app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/progresskeeper/ProgressState.java create mode 100644 app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/progresskeeper/TaskCountListener.java create mode 100644 app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/services/ProgressServiceKeeper.java create mode 100644 app_pojavlauncher/src/main/res/layout-land/fragment_launcher.xml diff --git a/app_pojavlauncher/src/main/AndroidManifest.xml b/app_pojavlauncher/src/main/AndroidManifest.xml index a8467fef2..211eedd60 100644 --- a/app_pojavlauncher/src/main/AndroidManifest.xml +++ b/app_pojavlauncher/src/main/AndroidManifest.xml @@ -30,10 +30,8 @@ + android:name=".LauncherActivity"> diff --git a/app_pojavlauncher/src/main/java/com/kdt/mcgui/ProgressLayout.java b/app_pojavlauncher/src/main/java/com/kdt/mcgui/ProgressLayout.java index 90e57adee..5e9b1a5fc 100644 --- a/app_pojavlauncher/src/main/java/com/kdt/mcgui/ProgressLayout.java +++ b/app_pojavlauncher/src/main/java/com/kdt/mcgui/ProgressLayout.java @@ -3,6 +3,7 @@ package com.kdt.mcgui; import android.content.Context; import android.util.AttributeSet; +import android.util.Log; import android.view.View; import android.widget.ImageView; import android.widget.LinearLayout; @@ -16,8 +17,11 @@ import androidx.constraintlayout.widget.ConstraintLayout; import net.kdt.pojavlaunch.R; import net.kdt.pojavlaunch.extra.ExtraCore; +import net.kdt.pojavlaunch.progresskeeper.ProgressKeeper; +import net.kdt.pojavlaunch.progresskeeper.ProgressListener; +import net.kdt.pojavlaunch.services.ProgressService; -import java.util.Arrays; +import java.util.ArrayList; /** Class staring at specific values and automatically show something if the progress is present @@ -47,88 +51,38 @@ public class ProgressLayout extends ConstraintLayout implements View.OnClickList init(); } - private int mActiveProcesses = 0; - private final ArrayMap mMap = new ArrayMap<>(); + private final ArrayList mMap = new ArrayList<>(); private LinearLayout mLinearLayout; private TextView mTaskNumberDisplayer; private ImageView mFlipArrow; - private final Runnable mCheckProgressRunnable = new Runnable() { - @Override - public void run() { - for(String progressKey : mMap.keySet()){ - if(progressKey == null) continue; //TODO check wtf does this - - Object object = ExtraCore.consumeValue(progressKey); - - if(object != null){ - String[] progressStuff = ((String) object).split("¤"); - int progress = Integer.parseInt(progressStuff[0]); - int resourceString = Integer.parseInt(progressStuff[1]); - - // Prepare the progressbar - if(mMap.get(progressKey) == null){ - TextProgressBar textView = new TextProgressBar(getContext()); - LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, getResources().getDimensionPixelOffset(R.dimen._20sdp)); - params.bottomMargin = getResources().getDimensionPixelOffset(R.dimen._6sdp); - - mLinearLayout.addView(textView, params); - mMap.put(progressKey, textView); - mActiveProcesses++; - } - - mMap.get(progressKey).setProgress(progress); - if(resourceString != -1){ - // As an optimization, copy array content back 2 indexes instead of creating another object - System.arraycopy(progressStuff, 2, progressStuff, 0, progressStuff.length - 2); - mMap.get(progressKey).setText(getResources().getString(resourceString, progressStuff)); - }else{ - if(progressStuff.length >= 3) - mMap.get(progressStuff[2]); - } - - - // Remove when we don't have progress - if(progress < 0){ - if(progress <= -10) // Only remove the observer when it is explicitly told to do so ? - mMap.remove(progressKey); - - mLinearLayout.removeView(mMap.get(progressKey)); - mActiveProcesses--; - } - } - } - - setVisibility(hasProcesses() ? VISIBLE : GONE); - - mTaskNumberDisplayer.setText(getContext().getString(R.string.progresslayout_tasks_in_progress, mActiveProcesses)); - postDelayed(this, 1000); - } - }; public void observe(String progressKey){ - mMap.put(progressKey, null); + mMap.add(new LayoutProgressListener(progressKey)); } public boolean hasProcesses(){ - return mActiveProcesses > 0; + return ProgressKeeper.getTaskCount() > 0; } - public boolean hasProcess(String process){ - return mMap.get(process) != null; - } private void init(){ inflate(getContext(), R.layout.view_progress, this); mLinearLayout = findViewById(R.id.progress_linear_layout); mTaskNumberDisplayer = findViewById(R.id.progress_textview); mFlipArrow = findViewById(R.id.progress_flip_arrow); - postDelayed(mCheckProgressRunnable, 1000); - setBackgroundColor(getResources().getColor(R.color.background_bottom_bar)); - setVisibility(GONE); - + ProgressKeeper.addTaskCountListener((tc)->{ + post(()->{ + Log.i("ProgressLayout", "tc="+tc); + if(tc > 0) { + mTaskNumberDisplayer.setText(getContext().getString(R.string.progresslayout_tasks_in_progress, tc)); + setVisibility(VISIBLE); + }else + setVisibility(GONE); + }); + }); setOnClickListener(this); } @@ -138,13 +92,8 @@ public class ProgressLayout extends ConstraintLayout implements View.OnClickList } /** Update the text and progress content */ - public static void setProgress(String progressKey, int progress, @StringRes int resource, String... message){ - StringBuilder builder = new StringBuilder(); - for(String bit : message){ - builder.append(bit).append("¤"); - } - - ExtraCore.setValue(progressKey, progress + "¤" + resource + "¤" + builder); + public static void setProgress(String progressKey, int progress, @StringRes int resource, Object... message){ + ProgressKeeper.submitProgress(progressKey, progress, resource, message); } /** Update the text and progress content */ @@ -162,4 +111,40 @@ public class ProgressLayout extends ConstraintLayout implements View.OnClickList mLinearLayout.setVisibility(mLinearLayout.getVisibility() == GONE ? VISIBLE : GONE); mFlipArrow.setRotation(mLinearLayout.getVisibility() == GONE? 0 : 180); } + + class LayoutProgressListener implements ProgressListener { + final String progressKey; + final TextProgressBar textView; + final LinearLayout.LayoutParams params; + public LayoutProgressListener(String progressKey) { + this.progressKey = progressKey; + textView = new TextProgressBar(getContext()); + params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, getResources().getDimensionPixelOffset(R.dimen._20sdp)); + params.bottomMargin = getResources().getDimensionPixelOffset(R.dimen._6sdp); + ProgressKeeper.addListener(progressKey, this); + } + @Override + public void onProgressStarted() { + post(()-> { + Log.i("ProgressLayout", "onProgressStarted"); + mLinearLayout.addView(textView, params); + }); + } + + @Override + public void onProgressUpdated(int progress, int resid, Object... va) { + post(()-> { + textView.setProgress(progress); + if(resid != -1) textView.setText(getContext().getString(resid, va)); + else textView.setText(""); + }); + } + + @Override + public void onProgressEnded() { + post(()-> { + mLinearLayout.removeView(textView); + }); + } + } } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/LauncherActivity.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/LauncherActivity.java index 9ad320ca6..3d272852b 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/LauncherActivity.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/LauncherActivity.java @@ -41,6 +41,8 @@ import net.kdt.pojavlaunch.fragments.SelectAuthFragment; import net.kdt.pojavlaunch.multirt.MultiRTConfigDialog; import net.kdt.pojavlaunch.prefs.LauncherPreferences; import net.kdt.pojavlaunch.prefs.screens.LauncherPreferenceFragment; +import net.kdt.pojavlaunch.progresskeeper.ProgressKeeper; +import net.kdt.pojavlaunch.services.ProgressServiceKeeper; import net.kdt.pojavlaunch.tasks.AsyncAssetManager; import net.kdt.pojavlaunch.tasks.AsyncMinecraftDownloader; import net.kdt.pojavlaunch.tasks.AsyncVersionList; @@ -60,6 +62,7 @@ public class LauncherActivity extends BaseActivity { private FragmentContainerView mFragmentView; private ImageButton mSettingsButton, mDeleteAccountButton; private ProgressLayout mProgressLayout; + private ProgressServiceKeeper mProgressServiceKeeper; /* Allows to switch from one button "type" to another */ private final FragmentManager.FragmentLifecycleCallbacks mFragmentCallbackListener = new FragmentManager.FragmentLifecycleCallbacks() { @@ -151,7 +154,7 @@ public class LauncherActivity extends BaseActivity { setContentView(R.layout.activity_pojav_launcher); getWindow().setBackgroundDrawable(null); bindViews(); - + ProgressKeeper.addTaskCountListener((mProgressServiceKeeper = new ProgressServiceKeeper(this))); askForStoragePermission(); // Will wait here mSettingsButton.setOnClickListener(mSettingButtonListener); diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/progresskeeper/ProgressKeeper.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/progresskeeper/ProgressKeeper.java new file mode 100644 index 000000000..f949feb43 --- /dev/null +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/progresskeeper/ProgressKeeper.java @@ -0,0 +1,98 @@ +package net.kdt.pojavlaunch.progresskeeper; + +import android.util.Log; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + +public class ProgressKeeper { + private static final ConcurrentHashMap>> sProgressListeners = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap sProgressStates = new ConcurrentHashMap<>(); + private static final ArrayList> sTaskCountListeners = new ArrayList<>(); + + public static void submitProgress(String progressRecord, int progress, int resid, Object... va) { + ProgressState progressState = sProgressStates.get(progressRecord); + boolean shouldCallStarted = progressState == null; + boolean shouldCallEnded = resid == -1 && progress == -1; + if(shouldCallEnded) { + shouldCallStarted = false; + sProgressStates.remove(progressRecord); + updateTaskCount(); + }else if(shouldCallStarted){ + sProgressStates.put(progressRecord, (progressState = new ProgressState())); + updateTaskCount(); + } + if(progressState != null) { + progressState.progress = progress; + progressState.resid = resid; + progressState.varArg = va; + } + + Log.d("ProgressLayout", "shouldCallStarted="+shouldCallStarted+" shouldCallEnded="+shouldCallEnded); + ConcurrentLinkedQueue> listenerWeakReferenceList = sProgressListeners.get(progressRecord); + if(listenerWeakReferenceList != null) { + Iterator> iterator = listenerWeakReferenceList.iterator(); + while(iterator.hasNext()) { + ProgressListener listener = iterator.next().get(); + Log.i("ProgressLayout", listener+""); + if(listener != null) { + if(shouldCallStarted) listener.onProgressStarted(); + else if(shouldCallEnded) listener.onProgressEnded(); + else listener.onProgressUpdated(progress, resid, va); + } else iterator.remove(); + } + } + } + + private static void updateTaskCount() { + Log.i("ProgressKeeper","updateTaskCount()"); + int count = sProgressStates.size(); + Iterator> taskCountListeners = sTaskCountListeners.iterator(); + while(taskCountListeners.hasNext()) { + TaskCountListener countListener = taskCountListeners.next().get(); + if(countListener == null) { + Log.i("ProgressKeeper","Swooped a listener"); + taskCountListeners.remove(); + } + else countListener.onUpdateTaskCount(count); + } + } + + public static void addListener(String progressRecord, ProgressListener listener) { + ProgressState state = sProgressStates.get(progressRecord); + if(state != null && (state.resid != -1 || state.progress != -1)) { + listener.onProgressStarted(); + listener.onProgressUpdated(state.progress, state.resid, state.varArg); + Log.d("ProgressLayout", "Resubmitting UI state"); + }else{ + listener.onProgressEnded(); + } + ConcurrentLinkedQueue> listenerWeakReferenceList = sProgressListeners.get(progressRecord); + if(listenerWeakReferenceList == null) sProgressListeners.put(progressRecord, (listenerWeakReferenceList = new ConcurrentLinkedQueue<>())); + else { + Iterator> iterator = listenerWeakReferenceList.iterator(); + while(iterator.hasNext()) { + if(iterator.next().get() == null) iterator.remove(); + } + } + listenerWeakReferenceList.add(new WeakReference<>(listener)); + } + public static void addTaskCountListener(TaskCountListener listener) { + Iterator> taskCountListeners = sTaskCountListeners.iterator(); + while(taskCountListeners.hasNext()) { + TaskCountListener countListener = taskCountListeners.next().get(); + if(countListener == null){ + Log.i("ProgressKeeper","Swooped a listener"); + taskCountListeners.remove(); + } + } + listener.onUpdateTaskCount(sProgressStates.size()); + sTaskCountListeners.add(new WeakReference<>(listener)); + } + public static int getTaskCount() { + return sProgressStates.size(); + } +} diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/progresskeeper/ProgressListener.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/progresskeeper/ProgressListener.java new file mode 100644 index 000000000..f70cf8a4b --- /dev/null +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/progresskeeper/ProgressListener.java @@ -0,0 +1,7 @@ +package net.kdt.pojavlaunch.progresskeeper; + +public interface ProgressListener { + void onProgressStarted(); + void onProgressUpdated(int progress, int resid, Object... va); + void onProgressEnded(); +} diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/progresskeeper/ProgressState.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/progresskeeper/ProgressState.java new file mode 100644 index 000000000..fe10dc9b7 --- /dev/null +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/progresskeeper/ProgressState.java @@ -0,0 +1,7 @@ +package net.kdt.pojavlaunch.progresskeeper; + +public class ProgressState { + int progress; + int resid; + Object[] varArg; +} diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/progresskeeper/TaskCountListener.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/progresskeeper/TaskCountListener.java new file mode 100644 index 000000000..e26c22920 --- /dev/null +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/progresskeeper/TaskCountListener.java @@ -0,0 +1,5 @@ +package net.kdt.pojavlaunch.progresskeeper; + +public interface TaskCountListener { + void onUpdateTaskCount(int taskCount); +} diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/services/ProgressService.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/services/ProgressService.java index 3b54f0aba..284ec8b55 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/services/ProgressService.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/services/ProgressService.java @@ -5,54 +5,49 @@ import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.Build; +import android.os.Handler; import android.os.IBinder; import android.os.Process; +import android.util.Log; import androidx.annotation.Nullable; import androidx.core.app.NotificationCompat; +import androidx.core.app.NotificationManagerCompat; import androidx.core.content.ContextCompat; import net.kdt.pojavlaunch.R; import net.kdt.pojavlaunch.Tools; +import net.kdt.pojavlaunch.progresskeeper.ProgressKeeper; +import net.kdt.pojavlaunch.progresskeeper.ProgressListener; +import net.kdt.pojavlaunch.progresskeeper.TaskCountListener; import java.lang.ref.WeakReference; +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicInteger; /** * Lazy service which allows the process not to get killed. * Can be created from context, can be killed statically */ -public class ProgressService extends Service { +public class ProgressService extends Service implements TaskCountListener { - private static WeakReference sProgressService = new WeakReference<>(null); - private static final AtomicInteger sReferenceCount = new AtomicInteger(0); + private Handler mainThreadHandler = new Handler(); + private NotificationManagerCompat notificationManagerCompat; /** Simple wrapper to start the service */ public static void startService(Context context){ Intent intent = new Intent(context, ProgressService.class); - if(sReferenceCount.get() < 0) sReferenceCount.set(0); - sReferenceCount.getAndIncrement(); ContextCompat.startForegroundService(context, intent); } - /** Kill the service if it is still running */ - public static void killService(){ - Service service = sProgressService.get(); - int refcnt = sReferenceCount.decrementAndGet(); - if(service != null && refcnt <= 0) { - service.stopSelf(); - } - } - private NotificationCompat.Builder mNotificationBuilder; - public ProgressService(){ - super(); - sProgressService = new WeakReference<>(this); - } @Override public void onCreate() { Tools.buildNotificationChannel(getApplicationContext()); + notificationManagerCompat = NotificationManagerCompat.from(getApplicationContext()); Intent killIntent = new Intent(getApplicationContext(), ProgressService.class); killIntent.putExtra("kill", true); PendingIntent pendingKillIntent = PendingIntent.getService(this, 0, killIntent, Build.VERSION.SDK_INT >=23 ? PendingIntent.FLAG_IMMUTABLE : 0); @@ -60,16 +55,20 @@ public class ProgressService extends Service { .setContentTitle(getString(R.string.lazy_service_default_title)) .addAction(android.R.drawable.ic_menu_close_clear_cancel, getString(R.string.notification_terminate), pendingKillIntent) .setSmallIcon(R.mipmap.ic_launcher_round); + ProgressKeeper.addTaskCountListener(this); } @Override public int onStartCommand(Intent intent, int flags, int startId) { - if(intent != null && intent.getBooleanExtra("kill", false)) { - stopSelf(); // otherwise Android tries to restart the service since it "crashed" - Process.killProcess(Process.myPid()); - return super.onStartCommand(intent, flags, startId); + if(intent != null) { + if(intent.getBooleanExtra("kill", false)) { + stopSelf(); // otherwise Android tries to restart the service since it "crashed" + Process.killProcess(Process.myPid()); + return super.onStartCommand(intent, flags, startId); + } } - mNotificationBuilder.setContentText(getString(R.string.progresslayout_tasks_in_progress, sReferenceCount.get())); + Log.d("ProgressService", "Started!"); + mNotificationBuilder.setContentText(getString(R.string.progresslayout_tasks_in_progress, ProgressKeeper.getTaskCount())); startForeground(1,mNotificationBuilder.build()); return super.onStartCommand(intent, flags, startId); } @@ -79,4 +78,16 @@ public class ProgressService extends Service { public IBinder onBind(Intent intent) { return null; } + + @Override + public void onUpdateTaskCount(int taskCount) { + mainThreadHandler.post(()->{ + if(taskCount < 0) { + mNotificationBuilder.setContentText(getString(R.string.progresslayout_tasks_in_progress, taskCount)); + notificationManagerCompat.notify(1, mNotificationBuilder.build()); + }else{ + stopSelf(); + } + }); + } } diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/services/ProgressServiceKeeper.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/services/ProgressServiceKeeper.java new file mode 100644 index 000000000..65e3bd0f0 --- /dev/null +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/services/ProgressServiceKeeper.java @@ -0,0 +1,19 @@ +package net.kdt.pojavlaunch.services; + +import android.content.Context; +import android.util.Log; + +import net.kdt.pojavlaunch.progresskeeper.TaskCountListener; + +public class ProgressServiceKeeper implements TaskCountListener { + private Context context; + public ProgressServiceKeeper(Context ctx) { + this.context = ctx; + } + + @Override + public void onUpdateTaskCount(int taskCount) { + Log.d("ProgressServiceKeeper", taskCount+""); + if(taskCount > 0) ProgressService.startService(context); + } +} diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/tasks/AsyncAssetManager.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/tasks/AsyncAssetManager.java index 0bdf6b34d..ad4b8df3f 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/tasks/AsyncAssetManager.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/tasks/AsyncAssetManager.java @@ -54,6 +54,7 @@ public class AsyncAssetManager { // Install the runtime in an async manner, hope for the best String finalRt_version = rt_version; sExecutorService.execute(() -> { + try { MultiRTUtils.installRuntimeNamedBinpack( am.open("components/jre/universal.tar.xz"), diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/tasks/AsyncMinecraftDownloader.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/tasks/AsyncMinecraftDownloader.java index b8a5d2c51..c9a0e76cf 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/tasks/AsyncMinecraftDownloader.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/tasks/AsyncMinecraftDownloader.java @@ -47,13 +47,9 @@ public class AsyncMinecraftDownloader { public AsyncMinecraftDownloader(@NonNull Activity activity, JMinecraftVersionList.Version version, @NonNull DoneListener listener){ - ProgressService.startService(activity); sExecutorService.execute(() -> { - if(downloadGame(activity, version, version.id)) listener.onDownloadDone(); - - ProgressService.killService(); }); } diff --git a/app_pojavlauncher/src/main/res/layout-land/fragment_launcher.xml b/app_pojavlauncher/src/main/res/layout-land/fragment_launcher.xml new file mode 100644 index 000000000..f61212805 --- /dev/null +++ b/app_pojavlauncher/src/main/res/layout-land/fragment_launcher.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + +