Updated Magisk fragment to Kotlin

Exported old update card to special xml include where binding takes care of everything that had to be done in code beforehand.
Added several easing functions and enums.
Backported some classes and functions from the old fork

Expect major breakage. Literally nothing works as the functionality needs to be implemented
This commit is contained in:
Viktor De Pasquale 2019-04-13 00:14:37 +02:00
parent f309522268
commit bd00ae8ede
17 changed files with 1083 additions and 750 deletions

View File

@ -2,10 +2,12 @@ package com.topjohnwu.magisk.di
import android.content.Context
import com.skoumal.teanity.rxbus.RxBus
import com.topjohnwu.magisk.App
import org.koin.dsl.module
val applicationModule = module {
single { RxBus() }
single { get<Context>().resources }
single { get<Context>() as App }
}

View File

@ -1,10 +1,12 @@
package com.topjohnwu.magisk.di
import com.topjohnwu.magisk.ui.MainViewModel
import com.topjohnwu.magisk.ui.home.HomeViewModel
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
val viewModelModules = module {
viewModel { MainViewModel() }
viewModel { HomeViewModel(get(), get()) }
}

View File

@ -0,0 +1,14 @@
package com.topjohnwu.magisk.model.events
import com.skoumal.teanity.viewevents.ViewEvent
data class OpenLinkEvent(val url: String) : ViewEvent()
object ManagerInstallEvent : ViewEvent()
object MagiskInstallEvent : ViewEvent()
object ManagerChangelogEvent : ViewEvent()
object MagiskChangelogEvent : ViewEvent()
object UninstallEvent : ViewEvent()

View File

@ -0,0 +1,31 @@
package com.topjohnwu.magisk.model.observer
import androidx.databinding.Observable
import androidx.databinding.ObservableField
import java.io.Serializable
class Observer<T>(vararg dependencies: Observable, private val observer: () -> T) :
ObservableField<T>(*dependencies), Serializable {
val value: T get() = observer()
@Deprecated(
message = "Use KObservableField.value syntax from code",
replaceWith = ReplaceWith("value")
)
override fun get(): T {
return value
}
@Deprecated(
message = "Observer cannot be set",
level = DeprecationLevel.HIDDEN
)
override fun set(newValue: T) {
}
override fun toString(): String {
return "Observer(value=$value)"
}
}

View File

@ -1,8 +1,14 @@
package com.topjohnwu.magisk.ui.base
import androidx.core.net.toUri
import androidx.databinding.ViewDataBinding
import com.skoumal.teanity.view.TeanityActivity
import com.topjohnwu.magisk.utils.Utils
abstract class MagiskActivity<ViewModel : MagiskViewModel, Binding : ViewDataBinding> :
TeanityActivity<ViewModel, Binding>()
TeanityActivity<ViewModel, Binding>() {
fun openUrl(url: String) = Utils.openLink(this, url.toUri())
}

View File

@ -5,4 +5,12 @@ import com.skoumal.teanity.view.TeanityFragment
abstract class MagiskFragment<ViewModel : MagiskViewModel, Binding : ViewDataBinding> :
TeanityFragment<ViewModel, Binding>()
TeanityFragment<ViewModel, Binding>() {
protected val magiskActivity get() = activity as MagiskActivity<*, *>
fun openLink(url: String) {
magiskActivity.openUrl(url)
}
}

View File

@ -1,6 +1,6 @@
package com.topjohnwu.magisk.ui.base
import com.skoumal.teanity.viewmodel.TeanityViewModel
import com.skoumal.teanity.viewmodel.LoadingViewModel
abstract class MagiskViewModel : TeanityViewModel()
abstract class MagiskViewModel : LoadingViewModel()

View File

@ -0,0 +1,91 @@
package com.topjohnwu.magisk.ui.home
import android.content.res.Resources
import com.skoumal.teanity.util.KObservableField
import com.topjohnwu.magisk.App
import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.Const
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.model.events.*
import com.topjohnwu.magisk.model.observer.Observer
import com.topjohnwu.magisk.ui.base.MagiskViewModel
import com.topjohnwu.magisk.utils.toggle
class HomeViewModel(
private val resources: Resources,
private val app: App
) : MagiskViewModel() {
val isAdvancedExpanded = KObservableField(false)
val isForceEncryption = KObservableField(false /*todo*/)
val isKeepVerity = KObservableField(false /*todo*/)
private val prefsObserver = Observer(isForceEncryption, isKeepVerity) {
Config.keepEnc = isForceEncryption.value
Config.keepVerity = isKeepVerity.value
}
val magiskState = KObservableField(MagiskState.LOADING)
val magiskStateText = Observer(magiskState) {
@Suppress("WhenWithOnlyElse")
when (magiskState.value) {
MagiskState.NO_ROOT -> TODO()
MagiskState.NOT_INSTALLED -> TODO()
MagiskState.UP_TO_DATE -> TODO()
MagiskState.LOADING -> TODO()
MagiskState.OBSOLETE -> TODO()
}
}
val magiskCurrentVersion = KObservableField("")
val magiskLatestVersion = KObservableField("")
val magiskAdditionalInfo = Observer(magiskState) {
if (Config.get<Boolean>(Config.Key.COREONLY))
resources.getString(R.string.core_only_enabled)
else
""
}
val managerState = KObservableField(MagiskState.LOADING)
val managerStateText = Observer(managerState) {
@Suppress("WhenWithOnlyElse")
when (managerState.value) {
MagiskState.NO_ROOT -> TODO()
MagiskState.NOT_INSTALLED -> TODO()
MagiskState.UP_TO_DATE -> TODO()
MagiskState.LOADING -> TODO()
MagiskState.OBSOLETE -> TODO()
}
}
val managerCurrentVersion = KObservableField("")
val managerLatestVersion = KObservableField("")
val managerAdditionalInfo = Observer(managerState) {
if (app.packageName != BuildConfig.APPLICATION_ID)
"(${app.packageName})"
else
""
}
fun paypalPressed() = OpenLinkEvent(Const.Url.PAYPAL_URL).publish()
fun patreonPressed() = OpenLinkEvent(Const.Url.PATREON_URL).publish()
fun twitterPressed() = OpenLinkEvent(Const.Url.TWITTER_URL).publish()
fun githubPressed() = OpenLinkEvent(Const.Url.REPO_URL).publish()
fun xdaPressed() = OpenLinkEvent(Const.Url.XDA_THREAD).publish()
fun uninstallPressed() = UninstallEvent.publish()
fun refresh() {}
fun advancedPressed() = isAdvancedExpanded.toggle()
fun installPressed(item: MagiskItem) = when (item) {
MagiskItem.MANAGER -> ManagerInstallEvent.publish()
MagiskItem.MAGISK -> MagiskInstallEvent.publish()
}
fun cardPressed(item: MagiskItem) = when (item) {
MagiskItem.MANAGER -> ManagerChangelogEvent.publish()
MagiskItem.MAGISK -> MagiskChangelogEvent.publish()
}
}

View File

@ -1,332 +0,0 @@
package com.topjohnwu.magisk.ui.home;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.Config;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.tasks.CheckUpdates;
import com.topjohnwu.magisk.ui.MainActivity;
import com.topjohnwu.magisk.ui.base.BaseActivity;
import com.topjohnwu.magisk.ui.base.BaseFragment;
import com.topjohnwu.magisk.utils.Event;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.view.ArrowExpandable;
import com.topjohnwu.magisk.view.Expandable;
import com.topjohnwu.magisk.view.ExpandableViewHolder;
import com.topjohnwu.magisk.view.MarkDownWindow;
import com.topjohnwu.magisk.view.SafetyNet;
import com.topjohnwu.magisk.view.UpdateCardHolder;
import com.topjohnwu.magisk.view.dialogs.EnvFixDialog;
import com.topjohnwu.magisk.view.dialogs.MagiskInstallDialog;
import com.topjohnwu.magisk.view.dialogs.ManagerInstallDialog;
import com.topjohnwu.magisk.view.dialogs.UninstallDialog;
import com.topjohnwu.net.Networking;
import com.topjohnwu.superuser.Shell;
import java.util.Locale;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.cardview.widget.CardView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import androidx.transition.ChangeBounds;
import androidx.transition.Fade;
import androidx.transition.Transition;
import androidx.transition.TransitionManager;
import androidx.transition.TransitionSet;
import butterknife.BindColor;
import butterknife.BindView;
import butterknife.OnClick;
public class MagiskFragment extends BaseFragment implements SwipeRefreshLayout.OnRefreshListener {
private static boolean shownDialog = false;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
@BindView(R.id.linearLayout) LinearLayout root;
@BindView(R.id.install_option_card) CardView installOptionCard;
@BindView(R.id.keep_force_enc) CheckBox keepEncChkbox;
@BindView(R.id.keep_verity) CheckBox keepVerityChkbox;
@BindView(R.id.install_option_expand) ViewGroup optionExpandLayout;
@BindView(R.id.arrow) ImageView arrow;
@BindView(R.id.uninstall_button) CardView uninstallButton;
@BindColor(R.color.red500) int colorBad;
@BindColor(R.color.green500) int colorOK;
@BindColor(R.color.yellow500) int colorWarn;
@BindColor(R.color.green500) int colorNeutral;
@BindColor(R.color.blue500) int colorInfo;
private UpdateCardHolder magisk;
private UpdateCardHolder manager;
private SafetyNet safetyNet;
private Transition transition;
private Expandable optionExpand;
private void magiskInstall(View v) {
// Show Manager update first
if (Config.remoteManagerVersionCode > BuildConfig.VERSION_CODE) {
new ManagerInstallDialog(requireActivity()).show();
return;
}
new MagiskInstallDialog((BaseActivity) requireActivity()).show();
}
private void managerInstall(View v) {
new ManagerInstallDialog(requireActivity()).show();
}
private void openLink(String url) {
Utils.openLink(requireActivity(), Uri.parse(url));
}
@OnClick(R.id.paypal)
void paypal() {
openLink(Const.Url.PAYPAL_URL);
}
@OnClick(R.id.patreon)
void patreon() {
openLink(Const.Url.PATREON_URL);
}
@OnClick(R.id.twitter)
void twitter() {
openLink(Const.Url.TWITTER_URL);
}
@OnClick(R.id.github)
void github() {
openLink(Const.Url.SOURCE_CODE_URL);
}
@OnClick(R.id.xda)
void xda() {
openLink(Const.Url.XDA_THREAD);
}
@OnClick(R.id.uninstall_button)
void uninstall() {
new UninstallDialog(requireActivity()).show();
}
@OnClick(R.id.arrow)
void expandOptions() {
if (optionExpand.isExpanded())
optionExpand.collapse();
else optionExpand.expand();
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_magisk, container, false);
unbinder = new MagiskFragment_ViewBinding(this, v);
requireActivity().setTitle(R.string.magisk);
optionExpand = new ArrowExpandable(new ExpandableViewHolder(optionExpandLayout), arrow);
safetyNet = new SafetyNet(v);
magisk = new UpdateCardHolder(inflater, root);
manager = new UpdateCardHolder(inflater, root);
manager.setClickable(vv ->
MarkDownWindow.show(requireActivity(), null,
getResources().openRawResource(R.raw.changelog)));
root.addView(magisk.itemView, 1);
root.addView(manager.itemView, 2);
keepVerityChkbox.setChecked(Config.keepVerity);
keepVerityChkbox.setOnCheckedChangeListener((view, checked) -> Config.keepVerity = checked);
keepEncChkbox.setChecked(Config.keepEnc);
keepEncChkbox.setOnCheckedChangeListener((view, checked) -> Config.keepEnc = checked);
mSwipeRefreshLayout.setOnRefreshListener(this);
magisk.install.setOnClickListener(this::magiskInstall);
manager.install.setOnClickListener(this::managerInstall);
if (Config.get(Config.Key.COREONLY)) {
magisk.additional.setText(R.string.core_only_enabled);
magisk.additional.setVisibility(View.VISIBLE);
}
if (!app.getPackageName().equals(BuildConfig.APPLICATION_ID)) {
manager.additional.setText("(" + app.getPackageName() + ")");
manager.additional.setVisibility(View.VISIBLE);
}
transition = new TransitionSet()
.setOrdering(TransitionSet.ORDERING_TOGETHER)
.addTransition(new Fade(Fade.OUT))
.addTransition(new ChangeBounds())
.addTransition(new Fade(Fade.IN));
updateUI();
return v;
}
@Override
public void onDestroyView() {
super.onDestroyView();
safetyNet.unbinder.unbind();
magisk.unbinder.unbind();
manager.unbinder.unbind();
}
@Override
public void onRefresh() {
mSwipeRefreshLayout.setRefreshing(false);
TransitionManager.beginDelayedTransition(root, transition);
safetyNet.reset();
magisk.reset();
manager.reset();
Config.loadMagiskInfo();
updateUI();
Event.reset(this);
Config.remoteMagiskVersionString = null;
Config.remoteMagiskVersionCode = -1;
shownDialog = false;
// Trigger state check
if (Networking.checkNetworkStatus(app)) {
CheckUpdates.check();
}
}
@Override
public int[] getListeningEvents() {
return new int[] {Event.UPDATE_CHECK_DONE};
}
@Override
public void onEvent(int event) {
updateCheckUI();
}
private void updateUI() {
((MainActivity) requireActivity()).checkHideSection();
int image, color;
String status;
if (Config.magiskVersionCode < 0) {
color = colorBad;
image = R.drawable.ic_cancel;
status = getString(R.string.magisk_version_error);
magisk.status.setText(status);
magisk.currentVersion.setVisibility(View.GONE);
} else {
color = colorOK;
image = R.drawable.ic_check_circle;
status = getString(R.string.magisk);
magisk.currentVersion.setText(getString(R.string.current_installed,
String.format(Locale.US, "v%s (%d)",
Config.magiskVersionString, Config.magiskVersionCode)));
}
magisk.statusIcon.setColorFilter(color);
magisk.statusIcon.setImageResource(image);
manager.statusIcon.setColorFilter(colorOK);
manager.statusIcon.setImageResource(R.drawable.ic_check_circle);
manager.currentVersion.setText(getString(R.string.current_installed,
String.format(Locale.US, "v%s (%d)",
BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)));
if (!Networking.checkNetworkStatus(app)) {
// No network, updateCheckUI will not be triggered
magisk.status.setText(status);
manager.status.setText(R.string.app_name);
magisk.setValid(false);
manager.setValid(false);
}
}
private void updateCheckUI() {
int image, color;
String status, button = "";
TransitionManager.beginDelayedTransition(root, transition);
if (Config.remoteMagiskVersionCode < 0) {
color = colorNeutral;
image = R.drawable.ic_help;
status = getString(R.string.invalid_update_channel);
} else {
magisk.latestVersion.setText(getString(R.string.latest_version,
String.format(Locale.US, "v%s (%d)",
Config.remoteMagiskVersionString, Config.remoteMagiskVersionCode)));
if (Config.remoteMagiskVersionCode > Config.magiskVersionCode) {
color = colorInfo;
image = R.drawable.ic_update;
status = getString(R.string.magisk_update_title);
button = getString(R.string.update);
} else {
color = colorOK;
image = R.drawable.ic_check_circle;
status = getString(R.string.magisk_up_to_date);
button = getString(R.string.install);
}
}
if (Config.magiskVersionCode > 0) {
// Only override status if Magisk is installed
magisk.statusIcon.setImageResource(image);
magisk.statusIcon.setColorFilter(color);
magisk.status.setText(status);
magisk.install.setText(button);
}
if (Config.remoteManagerVersionCode < 0) {
color = colorNeutral;
image = R.drawable.ic_help;
status = getString(R.string.invalid_update_channel);
} else {
manager.latestVersion.setText(getString(R.string.latest_version,
String.format(Locale.US, "v%s (%d)",
Config.remoteManagerVersionString, Config.remoteManagerVersionCode)));
if (Config.remoteManagerVersionCode > BuildConfig.VERSION_CODE) {
color = colorInfo;
image = R.drawable.ic_update;
status = getString(R.string.manager_update_title);
manager.install.setText(R.string.update);
} else {
color = colorOK;
image = R.drawable.ic_check_circle;
status = getString(R.string.manager_up_to_date);
manager.install.setText(R.string.install);
}
}
manager.statusIcon.setImageResource(image);
manager.statusIcon.setColorFilter(color);
manager.status.setText(status);
magisk.setValid(Config.remoteMagiskVersionCode > 0);
manager.setValid(Config.remoteManagerVersionCode > 0);
if (Config.remoteMagiskVersionCode < 0) {
// Hide install related components
installOptionCard.setVisibility(View.GONE);
uninstallButton.setVisibility(View.GONE);
} else {
// Show install related components
installOptionCard.setVisibility(View.VISIBLE);
uninstallButton.setVisibility(Shell.rootAccess() ? View.VISIBLE : View.GONE);
}
if (!shownDialog && Config.magiskVersionCode > 0 &&
!Shell.su("env_check").exec().isSuccess()) {
shownDialog = true;
new EnvFixDialog(requireActivity()).show();
}
}
}

View File

@ -0,0 +1,279 @@
package com.topjohnwu.magisk.ui.home
import com.skoumal.teanity.viewevents.ViewEvent
import com.topjohnwu.magisk.Config
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.model.events.MagiskInstallEvent
import com.topjohnwu.magisk.model.events.ManagerInstallEvent
import com.topjohnwu.magisk.model.events.OpenLinkEvent
import com.topjohnwu.magisk.model.events.UninstallEvent
import com.topjohnwu.magisk.utils.Event
import com.topjohnwu.magisk.view.MarkDownWindow
import com.topjohnwu.magisk.view.dialogs.ManagerInstallDialog
import com.topjohnwu.magisk.view.dialogs.UninstallDialog
import org.koin.androidx.viewmodel.ext.android.viewModel
import com.topjohnwu.magisk.ui.base.MagiskFragment as NewMagiskFragment
class MagiskFragment : NewMagiskFragment<HomeViewModel, com.topjohnwu.magisk.databinding.FragmentMagiskBinding>() {
/*@BindView(R.id.swipeRefreshLayout)
internal var mSwipeRefreshLayout: SwipeRefreshLayout? = null
@BindView(R.id.linearLayout)
internal var root: LinearLayout? = null
@BindView(R.id.install_option_card)
internal var installOptionCard: CardView? = null
@BindView(R.id.keep_force_enc)
internal var keepEncChkbox: CheckBox? = null
@BindView(R.id.keep_verity)
internal var keepVerityChkbox: CheckBox? = null
@BindView(R.id.install_option_expand)
internal var optionExpandLayout: ViewGroup? = null
@BindView(R.id.arrow)
internal var arrow: ImageView? = null
@BindView(R.id.uninstall_button)
internal var uninstallButton: CardView? = null
@BindColor(R.color.red500)
internal var colorBad: Int = 0
@BindColor(R.color.green500)
internal var colorOK: Int = 0
@BindColor(R.color.yellow500)
internal var colorWarn: Int = 0
@BindColor(R.color.green500)
internal var colorNeutral: Int = 0
@BindColor(R.color.blue500)
internal var colorInfo: Int = 0*/
/*private var magisk: UpdateCardHolder? = null
private var manager: UpdateCardHolder? = null
private var safetyNet: SafetyNet? = null
private var transition: Transition? = null
private var optionExpand: Expandable? = null*/
override val layoutRes: Int = R.layout.fragment_magisk
override val viewModel: HomeViewModel by viewModel()
override fun onEventDispatched(event: ViewEvent) {
super.onEventDispatched(event)
when (event) {
is OpenLinkEvent -> openLink(event.url)
is ManagerInstallEvent -> installManager()
is MagiskInstallEvent -> installMagisk()
is UninstallEvent -> uninstall()
}
}
private fun installMagisk() {
// Show Manager update first
if (Config.remoteManagerVersionCode > BuildConfig.VERSION_CODE) {
ManagerInstallDialog(requireActivity()).show()
return
}
//FIXME dialog requires old base
//MagiskInstallDialog(requireActivity()).show()
}
private fun installManager() = ManagerInstallDialog(requireActivity()).show()
private fun uninstall() {
UninstallDialog(requireActivity()).show()
}
private fun changelogManager() {
MarkDownWindow.show(
requireActivity(), null,
resources.openRawResource(R.raw.changelog)
)
}
/*override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val v = inflater.inflate(R.layout.fragment_magisk, container, false)
requireActivity().setTitle(R.string.magisk)
//safetyNet = SafetyNet(v)
*//*transition = TransitionSet()
.setOrdering(TransitionSet.ORDERING_TOGETHER)
.addTransition(Fade(Fade.OUT))
.addTransition(ChangeBounds())
.addTransition(Fade(Fade.IN))*//*
updateUI()
return v
}*/
private fun onRefresh() {
/*mSwipeRefreshLayout!!.isRefreshing = false
TransitionManager.beginDelayedTransition(root!!, transition)
safetyNet!!.reset()
magisk!!.reset()
manager!!.reset()*/
Config.loadMagiskInfo()
updateUI()
//FIXME requires old base
/*Event.reset(this)
Config.remoteMagiskVersionString = null
Config.remoteMagiskVersionCode = -1*/
shownDialog = false
// Trigger state check
/*if (Networking.checkNetworkStatus(app)) {
CheckUpdates.check()
}*/
}
private fun getListeningEvents(): IntArray {
return intArrayOf(Event.UPDATE_CHECK_DONE)
}
private fun onEvent(event: Int) {
updateCheckUI()
}
private fun updateUI() {
/*(requireActivity() as MainActivity).checkHideSection()
val image: Int
val color: Int
val status: String
if (Config.magiskVersionCode < 0) {
color = colorBad
image = R.drawable.ic_cancel
status = getString(R.string.magisk_version_error)
magisk!!.status.text = status
magisk!!.currentVersion.visibility = View.GONE
} else {
color = colorOK
image = R.drawable.ic_check_circle
status = getString(R.string.magisk)
magisk!!.currentVersion.text = getString(
R.string.current_installed,
String.format(
Locale.US, "v%s (%d)",
Config.magiskVersionString, Config.magiskVersionCode
)
)
}
magisk!!.statusIcon.setColorFilter(color)
magisk!!.statusIcon.setImageResource(image)
manager!!.statusIcon.setColorFilter(colorOK)
manager!!.statusIcon.setImageResource(R.drawable.ic_check_circle)
manager!!.currentVersion.text = getString(
R.string.current_installed,
String.format(
Locale.US, "v%s (%d)",
BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE
)
)
if (!Networking.checkNetworkStatus(app)) {
// No network, updateCheckUI will not be triggered
magisk!!.status.text = status
manager!!.status.setText(R.string.app_name)
magisk!!.setValid(false)
manager!!.setValid(false)
}*/
}
private fun updateCheckUI() {
/*var image: Int
var color: Int
var status: String
var button = ""
if (Config.remoteMagiskVersionCode < 0) {
color = colorNeutral
image = R.drawable.ic_help
status = getString(R.string.invalid_update_channel)
} else {
magisk!!.latestVersion.text = getString(
R.string.latest_version,
String.format(
Locale.US, "v%s (%d)",
Config.remoteMagiskVersionString, Config.remoteMagiskVersionCode
)
)
if (Config.remoteMagiskVersionCode > Config.magiskVersionCode) {
color = colorInfo
image = R.drawable.ic_update
status = getString(R.string.magisk_update_title)
button = getString(R.string.update)
} else {
color = colorOK
image = R.drawable.ic_check_circle
status = getString(R.string.magisk_up_to_date)
button = getString(R.string.install)
}
}
if (Config.magiskVersionCode > 0) {
// Only override status if Magisk is installed
magisk!!.statusIcon.setImageResource(image)
magisk!!.statusIcon.setColorFilter(color)
magisk!!.status.text = status
magisk!!.install.text = button
}
if (Config.remoteManagerVersionCode < 0) {
color = colorNeutral
image = R.drawable.ic_help
status = getString(R.string.invalid_update_channel)
} else {
manager!!.latestVersion.text = getString(
R.string.latest_version,
String.format(
Locale.US, "v%s (%d)",
Config.remoteManagerVersionString, Config.remoteManagerVersionCode
)
)
if (Config.remoteManagerVersionCode > BuildConfig.VERSION_CODE) {
color = colorInfo
image = R.drawable.ic_update
status = getString(R.string.manager_update_title)
manager!!.install.setText(R.string.update)
} else {
color = colorOK
image = R.drawable.ic_check_circle
status = getString(R.string.manager_up_to_date)
manager!!.install.setText(R.string.install)
}
}
manager!!.statusIcon.setImageResource(image)
manager!!.statusIcon.setColorFilter(color)
manager!!.status.text = status
magisk!!.setValid(Config.remoteMagiskVersionCode > 0)
manager!!.setValid(Config.remoteManagerVersionCode > 0)
if (Config.remoteMagiskVersionCode < 0) {
// Hide install related components
installOptionCard!!.visibility = View.GONE
uninstallButton!!.visibility = View.GONE
} else {
// Show install related components
installOptionCard!!.visibility = View.VISIBLE
uninstallButton!!.visibility = if (Shell.rootAccess()) View.VISIBLE else View.GONE
}
if (!shownDialog && Config.magiskVersionCode > 0 &&
!Shell.su("env_check").exec().isSuccess
) {
shownDialog = true
EnvFixDialog(requireActivity()).show()
}*/
}
companion object {
private var shownDialog = false
}
}

View File

@ -0,0 +1,6 @@
package com.topjohnwu.magisk.ui.home
enum class MagiskItem {
MANAGER, MAGISK
}

View File

@ -0,0 +1,6 @@
package com.topjohnwu.magisk.ui.home
enum class MagiskState {
NO_ROOT, NOT_INSTALLED, UP_TO_DATE, OBSOLETE, LOADING
}

View File

@ -1,6 +1,8 @@
package com.topjohnwu.magisk.utils
import android.view.View
import androidx.annotation.DrawableRes
import androidx.appcompat.widget.AppCompatImageView
import androidx.appcompat.widget.Toolbar
import androidx.databinding.BindingAdapter
@ -9,3 +11,8 @@ import androidx.databinding.BindingAdapter
fun setOnNavigationClickedListener(view: Toolbar, listener: View.OnClickListener) {
view.setNavigationOnClickListener(listener)
}
@BindingAdapter("srcCompat")
fun setImageResource(view: AppCompatImageView, @DrawableRes resId: Int) {
view.setImageResource(resId)
}

View File

@ -0,0 +1,8 @@
package com.topjohnwu.magisk.utils
import com.skoumal.teanity.util.KObservableField
fun KObservableField<Boolean>.toggle() {
value = !value
}

View File

@ -1,10 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<import type="com.topjohnwu.magisk.ui.home.MagiskState" />
<import type="com.topjohnwu.magisk.ui.home.MagiskItem" />
<variable
name="viewModel"
type="com.topjohnwu.magisk.ui.home.HomeViewModel" />
</data>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
android:orientation="vertical"
app:onRefreshListener="@{() -> viewModel.refresh()}"
app:refreshing="@{viewModel.loading}">
<ScrollView
android:layout_width="match_parent"
@ -68,6 +84,26 @@
</androidx.cardview.widget.CardView>
<include
android:id="@+id/home_magisk_version"
additionalInfo="@{viewModel.magiskAdditionalInfo}"
currentVersion="@{viewModel.magiskCurrentVersion}"
item="@{MagiskItem.MAGISK}"
latestVersion="@{viewModel.magiskLatestVersion}"
layout="@layout/include_update_card"
state="@{viewModel.magiskState}"
text="@{viewModel.magiskStateText}" />
<include
android:id="@+id/home_manager_version"
additionalInfo="@{viewModel.managerAdditionalInfo}"
currentVersion="@{viewModel.managerCurrentVersion}"
item="@{MagiskItem.MANAGER}"
latestVersion="@{viewModel.managerLatestVersion}"
layout="@layout/include_update_card"
state="@{viewModel.managerState}"
text="@{viewModel.managerStateText}" />
<androidx.cardview.widget.CardView
android:id="@+id/install_option_card"
style="?attr/cardStyle"
@ -78,13 +114,13 @@
android:layout_marginTop="4dp"
android:layout_marginEnd="5dp"
android:layout_marginBottom="4dp"
android:visibility="gone"
app:cardCornerRadius="@dimen/card_corner_radius"
app:cardElevation="@dimen/card_elevation">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:onClick="@{() -> viewModel.advancedPressed()}"
android:orientation="vertical"
android:paddingTop="10dp"
android:paddingBottom="10dp">
@ -93,6 +129,18 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:rotation="@{viewModel.isAdvancedExpanded ? 180 : 0}"
android:tint="?attr/imageColorTint"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/title"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_arrow" />
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
@ -105,21 +153,11 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:tint="?attr/imageColorTint"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/title"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_arrow" />
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:id="@+id/install_option_expand"
gone="@{!viewModel.isAdvancedExpanded}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
@ -130,6 +168,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="50dp"
android:layout_marginEnd="50dp"
android:checked="@={viewModel.isForceEncryption}"
android:text="@string/keep_force_encryption" />
<CheckBox
@ -138,6 +177,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="50dp"
android:layout_marginEnd="50dp"
android:checked="@={viewModel.isKeepVerity}"
android:text="@string/keep_dm_verity" />
</LinearLayout>
@ -180,36 +220,6 @@
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_safetynet" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/sn_status_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.25" />
<TextView
android:id="@+id/safetyNet_status"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:maxLines="1"
android:minWidth="200dp"
android:text="@string/safetyNet_check_text"
android:textStyle="bold"
app:autoSizeMaxTextSize="14sp"
app:autoSizeTextType="uniform"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/sn_status_end"
app:layout_constraintStart_toStartOf="@+id/sn_status_start"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/sn_status_end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.75" />
<ImageView
android:id="@+id/safetyNet_refresh"
android:layout_width="25dp"
@ -230,6 +240,36 @@
app:layout_constraintStart_toStartOf="@+id/safetyNet_refresh"
app:layout_constraintTop_toTopOf="@+id/safetyNet_refresh" />
<TextView
android:id="@+id/safetyNet_status"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:maxLines="1"
android:minWidth="200dp"
android:text="@string/safetyNet_check_text"
android:textStyle="bold"
app:autoSizeMaxTextSize="14sp"
app:autoSizeTextType="uniform"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/sn_status_end"
app:layout_constraintStart_toStartOf="@+id/sn_status_start"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/sn_status_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.25" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/sn_status_end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.75" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
@ -247,16 +287,6 @@
app:layout_constraintEnd_toStartOf="@+id/cts_status"
app:layout_constraintTop_toTopOf="@+id/cts_status" />
<TextView
android:id="@+id/cts_status"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:padding="6dp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="@+id/basic_status"
app:layout_constraintStart_toStartOf="@+id/basic_status"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/basic_status_icon"
android:layout_width="25dp"
@ -269,6 +299,16 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/basic_status" />
<TextView
android:id="@+id/cts_status"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:padding="6dp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="@+id/basic_status"
app:layout_constraintStart_toStartOf="@+id/basic_status"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/basic_status"
android:layout_width="wrap_content"
@ -304,36 +344,72 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/paypal"
<FrameLayout
android:id="@+id/twitter"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:gravity="center_horizontal"
android:orientation="horizontal"
app:layout_constraintEnd_toStartOf="@+id/patreon"
android:foreground="?android:attr/selectableItemBackground"
android:onClick="@{() -> viewModel.twitterPressed()}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/github"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
app:layout_constraintTop_toBottomOf="@+id/paypal">
<ImageView
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_gravity="center"
android:layout_gravity="center_horizontal"
android:layout_margin="10dp"
android:tint="?attr/imageColorTint"
app:srcCompat="@drawable/ic_paypal" />
app:srcCompat="@drawable/ic_twitter" />
<TextView
android:layout_width="wrap_content"
</FrameLayout>
<FrameLayout
android:id="@+id/github"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="5dp"
android:text="PayPal"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="bold" />
</LinearLayout>
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
android:onClick="@{() -> viewModel.githubPressed()}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/xda"
app:layout_constraintStart_toEndOf="@+id/twitter">
<ImageView
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_gravity="center_horizontal"
android:layout_margin="10dp"
android:tint="?attr/imageColorTint"
app:srcCompat="@drawable/ic_github" />
</FrameLayout>
<FrameLayout
android:id="@+id/xda"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
android:onClick="@{() -> viewModel.xdaPressed()}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/github">
<ImageView
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_gravity="center_horizontal"
android:layout_margin="10dp"
android:tint="?attr/imageColorTint"
app:srcCompat="@drawable/ic_xda" />
</FrameLayout>
<LinearLayout
android:id="@+id/patreon"
@ -343,6 +419,7 @@
android:clickable="true"
android:focusable="true"
android:gravity="center_horizontal"
android:onClick="@{() -> viewModel.patreonPressed()}"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/paypal"
@ -366,69 +443,37 @@
android:textStyle="bold" />
</LinearLayout>
<FrameLayout
android:id="@+id/twitter"
<LinearLayout
android:id="@+id/paypal"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/github"
android:gravity="center_horizontal"
android:onClick="@{() -> viewModel.paypalPressed()}"
android:orientation="horizontal"
app:layout_constraintEnd_toStartOf="@+id/patreon"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/paypal">
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_gravity="center_horizontal"
android:layout_gravity="center"
android:layout_margin="10dp"
android:tint="?attr/imageColorTint"
app:srcCompat="@drawable/ic_twitter" />
app:srcCompat="@drawable/ic_paypal" />
</FrameLayout>
<FrameLayout
android:id="@+id/github"
android:layout_width="0dp"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/xda"
app:layout_constraintStart_toEndOf="@+id/twitter">
<ImageView
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_gravity="center_horizontal"
android:layout_margin="10dp"
android:tint="?attr/imageColorTint"
app:srcCompat="@drawable/ic_github" />
</FrameLayout>
<FrameLayout
android:id="@+id/xda"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/github">
<ImageView
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_gravity="center_horizontal"
android:layout_margin="10dp"
android:tint="?attr/imageColorTint"
app:srcCompat="@drawable/ic_xda" />
</FrameLayout>
android:layout_gravity="center"
android:layout_margin="5dp"
android:text="PayPal"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textStyle="bold" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
@ -447,6 +492,7 @@
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
android:onClick="@{() -> viewModel.uninstallPressed()}"
app:cardCornerRadius="@dimen/card_corner_radius"
app:cardElevation="@dimen/card_elevation">
@ -470,3 +516,5 @@
</ScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</layout>

View File

@ -0,0 +1,160 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="com.topjohnwu.magisk.ui.home.MagiskState" />
<import type="com.topjohnwu.magisk.R" />
<variable
name="item"
type="com.topjohnwu.magisk.ui.home.MagiskItem" />
<variable
name="state"
type="com.topjohnwu.magisk.ui.home.MagiskState" />
<variable
name="text"
type="String" />
<variable
name="currentVersion"
type="String" />
<variable
name="latestVersion"
type="String" />
<variable
name="additionalInfo"
type="String" />
<variable
name="viewModel"
type="com.topjohnwu.magisk.ui.home.HomeViewModel" />
</data>
<androidx.cardview.widget.CardView
style="?attr/cardStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="5dp"
android:layout_marginBottom="4dp"
android:foreground="?android:attr/selectableItemBackground"
android:onClick="@{() -> viewModel.cardPressed(item)}"
app:cardCornerRadius="@dimen/card_corner_radius"
app:cardElevation="@dimen/card_elevation">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="12dp"
android:paddingBottom="12dp">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/install"
gone="@{state == MagiskState.LOADING}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:onClick="@{() -> viewModel.installPressed(item)}"
android:text="@{state != MagiskState.OBSOLETE ? @string/install : @string/update}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="@string/install" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/status_icon"
invisible="@{state == MagiskState.LOADING}"
srcCompat="@{state == MagiskState.UP_TO_DATE ? R.drawable.ic_check_circle : (state == MagiskState.OBSOLETE ? R.drawable.ic_update : R.drawable.ic_help)}"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/status"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ProgressBar
android:id="@+id/progress"
gone="state != MagiskState.LOADING"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="@+id/status_icon"
app:layout_constraintEnd_toEndOf="@+id/status_icon"
app:layout_constraintStart_toStartOf="@+id/status_icon"
app:layout_constraintTop_toTopOf="@+id/status_icon" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/current_version"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:maxLines="1"
android:text="@{state != MagiskState.LOADING ? currentVersion : @string/checking_for_updates}"
app:autoSizeMinTextSize="1sp"
app:autoSizeTextType="uniform"
app:layout_constraintEnd_toEndOf="@+id/status"
app:layout_constraintStart_toStartOf="@+id/status"
app:layout_constraintTop_toBottomOf="@+id/status"
tools:text="@string/checking_for_updates" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/latest_version"
gone="@{currentVersion == latestVersion}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:maxLines="1"
android:text="@{state != MagiskState.LOADING ? latestVersion : @string/checking_for_updates}"
app:autoSizeMinTextSize="1sp"
app:autoSizeTextType="uniform"
app:layout_constraintEnd_toEndOf="@+id/status"
app:layout_constraintStart_toStartOf="@+id/status"
app:layout_constraintTop_toBottomOf="@+id/current_version"
tools:text="@string/checking_for_updates" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/additional"
gone="@{additionalInfo.length == 0}"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:maxLines="1"
android:text="@{additionalInfo}"
app:autoSizeMinTextSize="1sp"
app:autoSizeTextType="uniform"
app:layout_constraintEnd_toEndOf="@+id/status"
app:layout_constraintStart_toStartOf="@+id/status"
app:layout_constraintTop_toBottomOf="@+id/latest_version"
tools:text="@string/checking_for_updates" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/status"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:maxLines="1"
android:paddingTop="3dp"
android:paddingBottom="3dp"
android:text="@{text}"
android:textAppearance="?android:attr/textAppearanceMedium"
app:autoSizeMinTextSize="1sp"
app:autoSizeTextType="uniform"
app:layout_constraintEnd_toStartOf="@+id/install"
app:layout_constraintStart_toEndOf="@+id/status_icon"
app:layout_constraintTop_toTopOf="parent"
tools:text="Magisk is up to date" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</layout>

View File

@ -75,7 +75,6 @@
android:layout_height="wrap_content"
android:maxLines="1"
android:text="@string/checking_for_updates"
android:visibility="gone"
app:autoSizeMinTextSize="1sp"
app:autoSizeTextType="uniform"
app:layout_constraintEnd_toEndOf="@+id/status"
@ -87,7 +86,6 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:maxLines="1"
android:visibility="gone"
app:autoSizeMinTextSize="1sp"
app:autoSizeTextType="uniform"
app:layout_constraintEnd_toEndOf="@+id/status"
@ -101,7 +99,6 @@
android:layout_marginEnd="8dp"
android:maxLines="1"
android:text="@string/install"
android:visibility="gone"
app:autoSizeMaxTextSize="14sp"
app:autoSizeTextType="uniform"
app:layout_constraintBottom_toBottomOf="parent"