diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ae4138c..d420b88 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -187,7 +187,7 @@ android:exported="false" android:label="@string/dashboard" /> sc = service -> updateUi(); - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_focusmode); - setSupportActionBar(findViewById(R.id.topbar)); - ActionBar actionBar = getSupportActionBar(); - assert actionBar != null; - actionBar.setDisplayHomeAsUpEnabled(true); - LayoutTransition layoutTransition = ((LinearLayoutCompat) findViewById(R.id.focusModeRoot)).getLayoutTransition(); - layoutTransition.enableTransitionType(LayoutTransition.CHANGING); - - findViewById(R.id.schedule).setOnClickListener(v -> - startActivity(new Intent(this, ScheduleActivity.class).putExtra("type", "focus_mode").putExtra("name", getString(R.string.focus_mode)))); - - WellbeingService tw = WellbeingService.get(); - tw.addStateCallback(sc); - - RecyclerView r = findViewById(R.id.focusModePkgs); - r.setAdapter( - new PackageRecyclerViewAdapter(this, - tw.getInstalledApplications(PackageManager.GET_META_DATA), - "focus_mode", tw::onFocusModePreferenceChanged)); - - updateUi(); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - WellbeingService tw = WellbeingService.get(); - tw.removeStateCallback(sc); - } - - private void updateUi() { - WellbeingService tw = WellbeingService.get(); - State state = tw.getState(); - MaterialSwitch toggle = findViewById(R.id.topsw); - toggle.setChecked(state.isFocusModeEnabled()); - findViewById(R.id.topsc).setOnClickListener(v -> { - if (state.isFocusModeEnabled()) { - tw.disableFocusMode(); - } else { - tw.enableFocusMode(); - } - }); - View takeBreak = findViewById(R.id.takeBreak); - ((AppCompatTextView) findViewById(R.id.title)).setText(state.isOnFocusModeBreakGlobal() ? R.string.focus_mode_break_end : R.string.focus_mode_break); - takeBreak.setOnClickListener(v -> { - if (state.isOnFocusModeBreakGlobal()) { - tw.endFocusModeBreak(); - } else { - tw.takeFocusModeBreakWithDialog(FocusModeActivity.this, false, null); - } - }); - takeBreak.setVisibility(state.isFocusModeEnabled() ? View.VISIBLE : View.GONE); - } - - @Override - public boolean onSupportNavigateUp() { - finish(); - return true; - } -} \ No newline at end of file diff --git a/app/src/main/java/org/eu/droid_ng/wellbeing/prefs/PackageRecyclerViewAdapter.java b/app/src/main/java/org/eu/droid_ng/wellbeing/prefs/PackageRecyclerViewAdapter.java index 341e396..e4ef87d 100644 --- a/app/src/main/java/org/eu/droid_ng/wellbeing/prefs/PackageRecyclerViewAdapter.java +++ b/app/src/main/java/org/eu/droid_ng/wellbeing/prefs/PackageRecyclerViewAdapter.java @@ -32,7 +32,7 @@ import java.util.function.Consumer; import java.util.stream.Collectors; -class PackageRecyclerViewAdapter extends RecyclerView.Adapter { +public class PackageRecyclerViewAdapter extends RecyclerView.Adapter { private final Context mContext; private final LayoutInflater inflater; private final List mData; diff --git a/app/src/main/java/org/eu/droid_ng/wellbeing/ui/FocusModeActivity.kt b/app/src/main/java/org/eu/droid_ng/wellbeing/ui/FocusModeActivity.kt new file mode 100644 index 0000000..7481c8f --- /dev/null +++ b/app/src/main/java/org/eu/droid_ng/wellbeing/ui/FocusModeActivity.kt @@ -0,0 +1,101 @@ +package org.eu.droid_ng.wellbeing.ui + +import android.animation.LayoutTransition +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Bundle +import android.view.View +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.widget.LinearLayoutCompat +import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.appbar.MaterialToolbar +import com.google.android.material.textview.MaterialTextView +import org.eu.droid_ng.wellbeing.R +import org.eu.droid_ng.wellbeing.lib.WellbeingService +import org.eu.droid_ng.wellbeing.prefs.PackageRecyclerViewAdapter +import org.eu.droid_ng.wellbeing.prefs.ScheduleActivity +import org.eu.droid_ng.wellbeing.widget.MainSwitchBar +import java.util.function.Consumer + +class FocusModeActivity : AppCompatActivity() { + + private val service: WellbeingService by lazy { + WellbeingService.get() + } + + private val stateCallback = Consumer { _: WellbeingService -> + updateUi() + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_focus_mode) + // Set support action bar + val topAppBar = findViewById(R.id.topbar) + setSupportActionBar(topAppBar) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + // Enable layout transition + val layoutTransition = + (findViewById(R.id.focusModeRoot) as LinearLayoutCompat).layoutTransition + layoutTransition.enableTransitionType(LayoutTransition.CHANGING) + // Open schedule screen + findViewById(R.id.schedule)?.setOnClickListener { + startActivity( + Intent(this, ScheduleActivity::class.java) + .putExtra("type", "focus_mode") + .putExtra("name", getString(R.string.focus_mode)) + ) + } + // Add state call back to the service + service.addStateCallback(stateCallback) + val r = findViewById(R.id.focusModePkgs) + r.adapter = PackageRecyclerViewAdapter( + this, + service.getInstalledApplications(PackageManager.GET_META_DATA), + "focus_mode" + ) { packageName: String? -> + service.onFocusModePreferenceChanged( + packageName!! + ) + } + // Handle on click for main switch + findViewById(R.id.mainSwitchBar)?.setOnClickListener { + val state = service.getState() + if (state.isFocusModeEnabled()) { + service.disableFocusMode() + } else { + service.enableFocusMode() + } + } + updateUi() + } + + override fun onDestroy() { + super.onDestroy() + service.removeStateCallback(stateCallback) + } + + private fun updateUi() { + val state = service.getState() + updateMainSwitch(state.isFocusModeEnabled()) +// val takeBreak = findViewById(R.id.takeBreak) +// (findViewById(R.id.title) as AppCompatTextView).setText(if (state.isOnFocusModeBreakGlobal()) R.string.focus_mode_break_end else R.string.focus_mode_break) +// takeBreak.setOnClickListener { v: View? -> +// if (state.isOnFocusModeBreakGlobal()) { +// service.endFocusModeBreak() +// } else { +// service.takeFocusModeBreakWithDialog(this@FocusModeActivity, false, null) +// } +// } +// takeBreak.visibility = if (state.isFocusModeEnabled()) View.VISIBLE else View.GONE + } + + private fun updateMainSwitch(checked: Boolean) { + findViewById(R.id.mainSwitchBar)?.isChecked = checked + } + + override fun onSupportNavigateUp(): Boolean { + finish() + return true + } +} diff --git a/app/src/main/java/org/eu/droid_ng/wellbeing/utils/BuildUtils.kt b/app/src/main/java/org/eu/droid_ng/wellbeing/utils/BuildUtils.kt new file mode 100644 index 0000000..cf4db84 --- /dev/null +++ b/app/src/main/java/org/eu/droid_ng/wellbeing/utils/BuildUtils.kt @@ -0,0 +1,10 @@ +package org.eu.droid_ng.wellbeing.utils + +import android.os.Build + +object BuildUtils { + + fun isAtLeastS(): Boolean { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S + } +} diff --git a/app/src/main/java/org/eu/droid_ng/wellbeing/widget/MainSwitchBar.kt b/app/src/main/java/org/eu/droid_ng/wellbeing/widget/MainSwitchBar.kt new file mode 100644 index 0000000..af2aa8c --- /dev/null +++ b/app/src/main/java/org/eu/droid_ng/wellbeing/widget/MainSwitchBar.kt @@ -0,0 +1,252 @@ +package org.eu.droid_ng.wellbeing.widget + +import android.content.Context +import android.graphics.drawable.Drawable +import android.os.Parcel +import android.os.Parcelable +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.widget.CompoundButton +import android.widget.LinearLayout +import android.widget.TextView +import androidx.annotation.ColorInt +import androidx.core.content.ContextCompat +import com.google.android.material.materialswitch.MaterialSwitch +import org.eu.droid_ng.wellbeing.R +import org.eu.droid_ng.wellbeing.utils.BuildUtils + +class MainSwitchBar @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, + defStyleRes: Int = 0 +) : LinearLayout(context, attrs, defStyleAttr, defStyleRes), + CompoundButton.OnCheckedChangeListener { + + private val switchChangeListeners: MutableList = ArrayList() + + @ColorInt + private var backgroundColor = 0 + + @ColorInt + private var backgroundActivatedColor = 0 + + private var textView: TextView? = null + + /** + * Return the Switch + */ + var switch: MaterialSwitch? = null + private set + + + private var backgroundOn: Drawable? = null + private var backgroundOff: Drawable? = null + private var backgroundDisabled: Drawable? = null + private var frameView: View? = null + + /** + * Return the status of the Switch + */ + /** + * Update the switch status + */ + var isChecked: Boolean + get() = switch?.isChecked == true + set(checked) { + switch?.isChecked = checked + setBackground(checked) + } + + /** + * Return the displaying status of org.eu.droid_ng.wellbeing.widget.MainSwitchBar + */ + val isShowing: Boolean + get() = visibility == VISIBLE + + init { + LayoutInflater.from(context).inflate(R.layout.main_switch_bar, this) + if (!BuildUtils.isAtLeastS()) { + val a = context.obtainStyledAttributes(intArrayOf(android.R.attr.colorAccent)) + backgroundActivatedColor = a.getColor(0, 0) + backgroundColor = context.getColor(androidx.appcompat.R.color.material_grey_600) + a.recycle() + } + isFocusable = true + isClickable = true + frameView = findViewById(R.id.frame) + textView = findViewById(R.id.switch_text) + switch = findViewById(R.id.materialSwitch) + val switchChecked = switch?.isChecked ?: false + if (BuildUtils.isAtLeastS()) { + backgroundOn = ContextCompat.getDrawable(context, R.drawable.main_switch_bar_bg_on) + backgroundOff = ContextCompat.getDrawable(context, R.drawable.main_switch_bar_bg_off) + backgroundDisabled = ContextCompat.getDrawable( + context, + R.drawable.main_switch_bar_bg_disabled + ) + } + addOnSwitchChangeListener { _, isChecked -> + this@MainSwitchBar.isChecked = isChecked + } + if (switch?.visibility == VISIBLE) { + switch?.setOnCheckedChangeListener(this) + } + isChecked = switchChecked + + if (attrs != null) { + val a = context.obtainStyledAttributes( + attrs, + intArrayOf(android.R.attr.text) + ) + val title = a.getText(0) + setTitle(title) + a.recycle() + } + setBackground(switchChecked) + } + + override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) { + propagateChecked(isChecked) + } + + override fun performClick(): Boolean { + switch?.performClick() + return super.performClick() + } + + /** + * Set the title text + */ + fun setTitle(text: CharSequence?) { + textView?.text = text + } + + /** + * Show the MainSwitchBar + */ + fun show() { + visibility = VISIBLE + switch?.setOnCheckedChangeListener(this) + } + + /** + * Hide the MainSwitchBar + */ + fun hide() { + if (isShowing) { + visibility = GONE + switch?.setOnCheckedChangeListener(null) + } + } + + /** + * Adds a listener for switch changes + */ + fun addOnSwitchChangeListener(listener: OnMainSwitchChangeListener) { + if (!switchChangeListeners.contains(listener)) { + switchChangeListeners.add(listener) + } + } + + /** + * Remove a listener for switch changes + */ + fun removeOnSwitchChangeListener(listener: OnMainSwitchChangeListener) { + switchChangeListeners.remove(listener) + } + + /** + * Enable or disable the text and switch. + */ + override fun setEnabled(enabled: Boolean) { + super.setEnabled(enabled) + textView!!.isEnabled = enabled + switch!!.isEnabled = enabled + if (BuildUtils.isAtLeastS()) { + if (enabled) { + frameView?.background = if (isChecked) backgroundOn else backgroundOff + } else { + frameView?.background = backgroundDisabled + } + } + } + + private fun propagateChecked(isChecked: Boolean) { + setBackground(isChecked) + val count = switchChangeListeners.size + for (n in 0 until count) { + switchChangeListeners[n].onSwitchChanged(switch, isChecked) + } + } + + private fun setBackground(isChecked: Boolean) { + if (!BuildUtils.isAtLeastS()) { + setBackgroundColor(if (isChecked) backgroundActivatedColor else backgroundColor) + } else { + frameView?.background = if (isChecked) backgroundOn else backgroundOff + } + } + + internal class SavedState : BaseSavedState { + var mChecked = false + var mVisible = false + + constructor(superState: Parcelable?) : super(superState) + + /** + * Constructor called from [.CREATOR] + */ + private constructor(input: Parcel) : super(input) { + mChecked = input.readValue(null) as Boolean + mVisible = input.readValue(null) as Boolean + } + + override fun writeToParcel(out: Parcel, flags: Int) { + super.writeToParcel(out, flags) + out.writeValue(mChecked) + out.writeValue(mVisible) + } + + override fun toString(): String { + return ("MainSwitchBar.SavedState{" + + Integer.toHexString(System.identityHashCode(this)) + + " checked=" + mChecked + + " visible=" + mVisible + "}") + } + + object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): SavedState { + return SavedState(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + + override fun describeContents(): Int { + return 0 + } + } + + public override fun onSaveInstanceState(): Parcelable { + val superState = super.onSaveInstanceState() + val ss = SavedState(superState) + ss.mChecked = switch?.isChecked ?: false + ss.mVisible = isShowing + return ss + } + + public override fun onRestoreInstanceState(state: Parcelable) { + val ss = state as SavedState + super.onRestoreInstanceState(ss.superState) + switch?.isChecked = ss.mChecked + isChecked = ss.mChecked + setBackground(ss.mChecked) + visibility = if (ss.mVisible) VISIBLE else GONE + switch?.setOnCheckedChangeListener(if (ss.mVisible) this else null) + requestLayout() + } +} diff --git a/app/src/main/java/org/eu/droid_ng/wellbeing/widget/OnMainSwitchChangeListener.kt b/app/src/main/java/org/eu/droid_ng/wellbeing/widget/OnMainSwitchChangeListener.kt new file mode 100644 index 0000000..6941077 --- /dev/null +++ b/app/src/main/java/org/eu/droid_ng/wellbeing/widget/OnMainSwitchChangeListener.kt @@ -0,0 +1,14 @@ +package org.eu.droid_ng.wellbeing.widget + +import com.google.android.material.materialswitch.MaterialSwitch + +/** + * Called when the checked state of the Switch has changed. + */ +fun interface OnMainSwitchChangeListener { + /** + * @param switchView The Switch view whose state has changed. + * @param isChecked The new checked state of switchView. + */ + fun onSwitchChanged(switchView: MaterialSwitch?, isChecked: Boolean) +} diff --git a/app/src/main/res/drawable/main_switch_bar_bg_disabled.xml b/app/src/main/res/drawable/main_switch_bar_bg_disabled.xml new file mode 100644 index 0000000..e3ac7a3 --- /dev/null +++ b/app/src/main/res/drawable/main_switch_bar_bg_disabled.xml @@ -0,0 +1,25 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/main_switch_bar_bg_off.xml b/app/src/main/res/drawable/main_switch_bar_bg_off.xml new file mode 100644 index 0000000..5455bd0 --- /dev/null +++ b/app/src/main/res/drawable/main_switch_bar_bg_off.xml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/main_switch_bar_bg_on.xml b/app/src/main/res/drawable/main_switch_bar_bg_on.xml new file mode 100644 index 0000000..fc3688e --- /dev/null +++ b/app/src/main/res/drawable/main_switch_bar_bg_on.xml @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/app/src/main/res/layout/activity_focus_mode.xml b/app/src/main/res/layout/activity_focus_mode.xml new file mode 100644 index 0000000..609147f --- /dev/null +++ b/app/src/main/res/layout/activity_focus_mode.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_focusmode.xml b/app/src/main/res/layout/activity_focusmode.xml deleted file mode 100644 index 12e9206..0000000 --- a/app/src/main/res/layout/activity_focusmode.xml +++ /dev/null @@ -1,182 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/main_switch_bar.xml b/app/src/main/res/layout/main_switch_bar.xml new file mode 100644 index 0000000..b34c6b1 --- /dev/null +++ b/app/src/main/res/layout/main_switch_bar.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7e0381a..243ba06 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -7,6 +7,12 @@ @string/other Tap to set up + + Enable Focus mode + Focus mode is blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah + Focus mode settings + Set a schedule + Loading… Focus mode, bedtime mode, app timers App is blocked diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 5089b2e..1f57e53 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -33,4 +33,13 @@ @layout/preference_material_switch + diff --git a/app/src/main/res/xml/main_preferences.xml b/app/src/main/res/xml/main_preferences.xml index 77e0a5c..24e8c93 100644 --- a/app/src/main/res/xml/main_preferences.xml +++ b/app/src/main/res/xml/main_preferences.xml @@ -15,7 +15,7 @@ app:icon="@drawable/ic_baseline_person_24dp" app:summary="@string/main_preference_focus_mode_summary">