-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feat[launcher]: Add a quick settings menu (#6303)
- Loading branch information
Showing
44 changed files
with
2,319 additions
and
1,513 deletions.
There are no files selected for viewing
Binary file modified
BIN
+7.22 KB
(100%)
app_pojavlauncher/src/main/assets/components/lwjgl3/lwjgl-glfw-classes.jar
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
1720909595576 | ||
1732218529630 |
138 changes: 138 additions & 0 deletions
138
app_pojavlauncher/src/main/java/com/kdt/CustomSeekbar.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
package com.kdt; | ||
|
||
import android.annotation.SuppressLint; | ||
import android.content.Context; | ||
import android.util.AttributeSet; | ||
import android.widget.SeekBar; | ||
|
||
/** | ||
* Seekbar with ability to handle ranges and increments | ||
*/ | ||
@SuppressLint("AppCompatCustomView") | ||
public class CustomSeekbar extends SeekBar { | ||
private int mMin = 0; | ||
private int mIncrement = 1; | ||
private SeekBar.OnSeekBarChangeListener mListener; | ||
|
||
/** When using increments, this flag is used to prevent double calls to the listener */ | ||
private boolean mInternalChanges = false; | ||
|
||
public CustomSeekbar(Context context) { | ||
super(context); | ||
setup(); | ||
} | ||
|
||
public CustomSeekbar(Context context, AttributeSet attrs) { | ||
super(context, attrs); | ||
setup(); | ||
} | ||
|
||
public CustomSeekbar(Context context, AttributeSet attrs, int defStyleAttr) { | ||
super(context, attrs, defStyleAttr); | ||
setup(); | ||
} | ||
|
||
public CustomSeekbar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { | ||
super(context, attrs, defStyleAttr, defStyleRes); | ||
setup(); | ||
} | ||
|
||
public void setIncrement(int increment) { | ||
mIncrement = increment; | ||
} | ||
|
||
public void setRange(int min, int max) { | ||
mMin = min; | ||
setMax(max - min); | ||
} | ||
|
||
@Override | ||
public synchronized void setProgress(int progress) { | ||
super.setProgress(applyIncrement(progress - mMin)); | ||
} | ||
|
||
@Override | ||
public void setProgress(int progress, boolean animate) { | ||
super.setProgress(applyIncrement(progress - mMin), animate); | ||
} | ||
|
||
@Override | ||
public synchronized int getProgress() { | ||
return applyIncrement(super.getProgress() + mMin); | ||
} | ||
|
||
@Override | ||
public synchronized void setMin(int min) { | ||
super.setMin(min); | ||
mMin = min; | ||
} | ||
|
||
/** | ||
* Wrapper to allow for a listener to be set around the internal listener | ||
*/ | ||
@Override | ||
public void setOnSeekBarChangeListener(OnSeekBarChangeListener l) { | ||
mListener = l; | ||
} | ||
|
||
public void setup() { | ||
super.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { | ||
/** Store the previous progress to prevent double calls with increments */ | ||
private int previousProgress = 0; | ||
@Override | ||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { | ||
if (mInternalChanges) return; | ||
mInternalChanges = true; | ||
|
||
progress += mMin; | ||
progress = applyIncrement(progress); | ||
|
||
if (progress != previousProgress) { | ||
if (mListener != null) { | ||
previousProgress = progress; | ||
mListener.onProgressChanged(seekBar, progress, fromUser); | ||
} | ||
} | ||
|
||
// Forces the thumb to snap to the increment | ||
setProgress(progress); | ||
mInternalChanges = false; | ||
} | ||
|
||
@Override | ||
public void onStartTrackingTouch(SeekBar seekBar) { | ||
if (mInternalChanges) return; | ||
|
||
if (mListener != null) { | ||
mListener.onStartTrackingTouch(seekBar); | ||
} | ||
} | ||
|
||
@Override | ||
public void onStopTrackingTouch(SeekBar seekBar) { | ||
if (mInternalChanges) return; | ||
mInternalChanges = true; | ||
|
||
setProgress(seekBar.getProgress()); | ||
|
||
if (mListener != null) { | ||
mListener.onStopTrackingTouch(seekBar); | ||
} | ||
mInternalChanges = false; | ||
} | ||
}); | ||
} | ||
|
||
/** | ||
* Apply increment to the progress | ||
* @param progress Progress to apply increment to | ||
* @return Progress with increment applied | ||
*/ | ||
private int applyIncrement(int progress) { | ||
if (mIncrement < 1) return progress; | ||
|
||
progress = progress / mIncrement; | ||
progress = progress * mIncrement; | ||
return progress; | ||
} | ||
} |
246 changes: 246 additions & 0 deletions
246
app_pojavlauncher/src/main/java/com/kdt/SideDialogView.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,246 @@ | ||
package com.kdt; | ||
|
||
import static net.kdt.pojavlaunch.Tools.currentDisplayMetrics; | ||
|
||
import android.animation.Animator; | ||
import android.animation.AnimatorListenerAdapter; | ||
import android.animation.ObjectAnimator; | ||
import android.content.Context; | ||
import android.util.Log; | ||
import android.view.LayoutInflater; | ||
import android.view.View; | ||
import android.view.ViewGroup; | ||
import android.view.animation.AccelerateDecelerateInterpolator; | ||
import android.widget.Button; | ||
import android.widget.TextView; | ||
|
||
import androidx.annotation.CallSuper; | ||
import androidx.annotation.LayoutRes; | ||
import androidx.annotation.NonNull; | ||
import androidx.annotation.Nullable; | ||
import androidx.annotation.StringRes; | ||
import androidx.core.content.res.ResourcesCompat; | ||
|
||
import net.kdt.pojavlaunch.R; | ||
import net.kdt.pojavlaunch.Tools; | ||
|
||
/** | ||
* The base class for side dialog views | ||
* A side dialog is a dialog appearing from one side of the screen | ||
*/ | ||
public abstract class SideDialogView { | ||
|
||
private final ViewGroup mParent; | ||
private final @LayoutRes int mLayoutId; | ||
private ViewGroup mDialogLayout; | ||
private DefocusableScrollView mScrollView; | ||
protected View mDialogContent; | ||
|
||
protected final int mMargin; | ||
private ObjectAnimator mSideDialogAnimator; | ||
protected boolean mDisplaying = false; | ||
/* Whether the layout is built */ | ||
private boolean mIsInstantiated = false; | ||
|
||
/* UI elements */ | ||
private Button mStartButton, mEndButton; | ||
private TextView mTitleTextview; | ||
private View mTitleDivider; | ||
|
||
/* Data to store when the UI element has yet to be inflated */ | ||
private @StringRes int mStartButtonStringId, mEndButtonStringId, mTitleStringId; | ||
private View.OnClickListener mStartButtonListener, mEndButtonListener; | ||
|
||
|
||
public SideDialogView(Context context, ViewGroup parent, @LayoutRes int layoutId) { | ||
mMargin = context.getResources().getDimensionPixelOffset(R.dimen._20sdp); | ||
mParent = parent; | ||
mLayoutId = layoutId; | ||
} | ||
|
||
public void setTitle(@StringRes int textId) { | ||
mTitleStringId = textId; | ||
if (mIsInstantiated) { | ||
mTitleTextview.setText(textId); | ||
mTitleTextview.setVisibility(View.VISIBLE); | ||
mTitleDivider.setVisibility(View.VISIBLE); | ||
} | ||
} | ||
|
||
public final void setStartButtonListener(@StringRes int textId, @Nullable View.OnClickListener listener) { | ||
mStartButtonStringId = textId; | ||
mStartButtonListener = listener; | ||
if (mIsInstantiated) setButton(mStartButton, textId, listener); | ||
} | ||
|
||
public final void setEndButtonListener(@StringRes int textId, @Nullable View.OnClickListener listener) { | ||
mEndButtonStringId = textId; | ||
mEndButtonListener = listener; | ||
if (mIsInstantiated) setButton(mEndButton, textId, listener); | ||
} | ||
|
||
private void setButton(@NonNull Button button, @StringRes int textId, @Nullable View.OnClickListener listener) { | ||
button.setText(textId); | ||
button.setOnClickListener(listener); | ||
button.setVisibility(View.VISIBLE); | ||
} | ||
|
||
|
||
private void inflateLayout() { | ||
if(mIsInstantiated) { | ||
Log.w("SideDialogView", "Layout already inflated"); | ||
return; | ||
} | ||
|
||
// Inflate layouts | ||
mDialogLayout = (ViewGroup) LayoutInflater.from(mParent.getContext()).inflate(R.layout.dialog_side_dialog, mParent, false); | ||
mScrollView = mDialogLayout.findViewById(R.id.side_dialog_scrollview); | ||
mStartButton = mDialogLayout.findViewById(R.id.side_dialog_start_button); | ||
mEndButton = mDialogLayout.findViewById(R.id.side_dialog_end_button); | ||
mTitleTextview = mDialogLayout.findViewById(R.id.side_dialog_title_textview); | ||
mTitleDivider = mDialogLayout.findViewById(R.id.side_dialog_title_divider); | ||
|
||
LayoutInflater.from(mParent.getContext()).inflate(mLayoutId, mScrollView, true); | ||
mDialogContent = mScrollView.getChildAt(0); | ||
|
||
// Attach layouts | ||
mParent.addView(mDialogLayout); | ||
|
||
mSideDialogAnimator = ObjectAnimator.ofFloat(mDialogLayout, "x", 0).setDuration(600); | ||
mSideDialogAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); | ||
|
||
mDialogLayout.setElevation(10); | ||
mDialogLayout.setTranslationZ(10); | ||
|
||
mDialogLayout.setVisibility(View.VISIBLE); | ||
mDialogLayout.setBackground(ResourcesCompat.getDrawable(mDialogLayout.getResources(), R.drawable.background_control_editor, null)); | ||
|
||
//TODO offset better according to view width | ||
mDialogLayout.setX(-mDialogLayout.getResources().getDimensionPixelOffset(R.dimen._280sdp)); | ||
mIsInstantiated = true; | ||
|
||
// Set up UI elements | ||
if (mTitleStringId != 0) setTitle(mTitleStringId); | ||
if (mStartButtonStringId != 0) setStartButtonListener(mStartButtonStringId, mStartButtonListener); | ||
if (mEndButtonStringId != 0) setEndButtonListener(mEndButtonStringId, mEndButtonListener); | ||
} | ||
|
||
/** Destroy the layout, cleanup variables */ | ||
private void deflateLayout() { | ||
if(!mIsInstantiated) { | ||
Log.w("SideDialogView", "Layout not inflated"); | ||
return; | ||
} | ||
|
||
mParent.removeView(mDialogLayout); | ||
mIsInstantiated = false; | ||
|
||
mScrollView = null; | ||
mSideDialogAnimator = null; | ||
mDialogLayout = null; | ||
mDialogContent = null; | ||
mTitleTextview = null; | ||
mTitleDivider = null; | ||
mStartButton = null; | ||
mEndButton = null; | ||
} | ||
|
||
|
||
/** | ||
* Slide the layout into the visible screen area | ||
*/ | ||
@CallSuper | ||
public final void appear(boolean fromRight) { | ||
if (!mIsInstantiated) { | ||
inflateLayout(); | ||
onInflate(); | ||
} | ||
|
||
// To avoid UI sizing issue when the dialog is not fully inflated | ||
onAppear(); | ||
Tools.runOnUiThread(() -> { | ||
if (fromRight) { | ||
if (!mDisplaying || !isAtRight()) { | ||
mSideDialogAnimator.setFloatValues(currentDisplayMetrics.widthPixels, currentDisplayMetrics.widthPixels - mScrollView.getWidth() - mMargin); | ||
mSideDialogAnimator.start(); | ||
mDisplaying = true; | ||
} | ||
} else { | ||
if (!mDisplaying || isAtRight()) { | ||
mSideDialogAnimator.setFloatValues(-mDialogLayout.getWidth(), mMargin); | ||
mSideDialogAnimator.start(); | ||
mDisplaying = true; | ||
} | ||
} | ||
}); | ||
} | ||
|
||
protected final boolean isAtRight() { | ||
return mDialogLayout.getX() > currentDisplayMetrics.widthPixels / 2f; | ||
} | ||
|
||
/** | ||
* Slide out the layout | ||
* @param destroy Whether the layout should be destroyed after disappearing. | ||
* Recommended to be true if the layout is not going to be used anymore | ||
*/ | ||
@CallSuper | ||
public final void disappear(boolean destroy) { | ||
if (!mDisplaying) { | ||
if(destroy) { | ||
onDisappear(); | ||
onDestroy(); | ||
deflateLayout(); | ||
} | ||
return; | ||
} | ||
|
||
mDisplaying = false; | ||
if (isAtRight()) | ||
mSideDialogAnimator.setFloatValues(currentDisplayMetrics.widthPixels - mDialogLayout.getWidth() - mMargin, currentDisplayMetrics.widthPixels); | ||
else | ||
mSideDialogAnimator.setFloatValues(mMargin, -mDialogLayout.getWidth()); | ||
|
||
if(destroy) { | ||
onDisappear(); | ||
onDestroy(); | ||
mSideDialogAnimator.addListener(new AnimatorListenerAdapter() { | ||
@Override | ||
public void onAnimationEnd(Animator animation) { | ||
mSideDialogAnimator.removeListener(this); | ||
deflateLayout(); | ||
} | ||
}); | ||
} | ||
|
||
mSideDialogAnimator.start(); | ||
} | ||
|
||
/** @return Whether the dialog is currently displaying */ | ||
public final boolean isDisplaying(){ | ||
return mDisplaying; | ||
} | ||
|
||
/** | ||
* Called when the dialog is inflated, ideal for setting up UI elements bindings | ||
*/ | ||
protected void onInflate() {} | ||
|
||
/** | ||
* Called after the dialog has appeared | ||
*/ | ||
protected void onAppear() {} | ||
|
||
/** | ||
* Called after the dialog has disappeared | ||
*/ | ||
protected void onDisappear() {} | ||
|
||
/** | ||
* Called before the dialog gets destroyed (removing views from parent) | ||
* Ideal for cleaning up resources | ||
*/ | ||
protected void onDestroy() {} | ||
|
||
|
||
} |
Oops, something went wrong.