package tk.wasdennnoch.androidn_ify.ui;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
import android.support.annotation.Keep;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import org.json.JSONArray;
import org.json.JSONException;
import java.io.File;
import java.io.IOException;
import tk.wasdennnoch.androidn_ify.BuildConfig;
import tk.wasdennnoch.androidn_ify.R;
import tk.wasdennnoch.androidn_ify.XposedHook;
import tk.wasdennnoch.androidn_ify.systemui.notifications.views.RemoteInputHelper;
import tk.wasdennnoch.androidn_ify.ui.emergency.PreferenceKeys;
import tk.wasdennnoch.androidn_ify.ui.preference.DropDownPreference;
import tk.wasdennnoch.androidn_ify.utils.ConfigUtils;
import tk.wasdennnoch.androidn_ify.utils.MiscUtils;
import tk.wasdennnoch.androidn_ify.utils.RomUtils;
import tk.wasdennnoch.androidn_ify.utils.UpdateUtils;
import tk.wasdennnoch.androidn_ify.utils.ViewUtils;
@Keep
public class SettingsActivity extends Activity implements View.OnClickListener {
private static final String TAG = "SettingsActivity";
public static final String ACTION_RECENTS_CHANGED = "tk.wasdennnoch.androidn_ify.action.ACTION_RECENTS_CHANGED";
public static final String EXTRA_RECENTS_DOUBLE_TAP_SPEED = "extra.recents.DOUBLE_TAP_SPEED";
public static final String ACTION_FIX_INVERSION = "tk.wasdennnoch.androidn_ify.action.ACTION_FIX_INVERSION";
public static final String ACTION_GENERAL = "tk.wasdennnoch.androidn_ify.action.ACTION_GENERAL";
public static final String EXTRA_GENERAL_DEBUG_LOG = "extra.general.DEBUG_LOG";
public static final String ACTION_KILL_SYSTEMUI = "tk.wasdennnoch.androidn_ify.action.ACTION_KILL_SYSTEMUI";
private boolean mExperimental;
@SuppressLint("ApplySharedPref")
@SuppressWarnings("ConstantConditions")
@Override
protected void onCreate(Bundle savedInstanceState) {
final SharedPreferences prefs = ConfigUtils.getPreferences(this);
ViewUtils.applyTheme(this, prefs);
super.onCreate(savedInstanceState);
if (!getPackageName().equals("tk.wasdennnoch.androidn_ify") && !BuildConfig.DEBUG && "com.android.vending".equals(getPackageManager().getInstallerPackageName(getPackageName()))) {
prefs.edit().putBoolean("pro", true).commit();
new AlertDialog.Builder(this)
.setTitle("\"Pro version\" warning")
.setMessage("The \"Pro version\" in the play store is not provided by the original developer (MrWasdennnoch). " +
"I (the original developer) do not get any penny from it. Please do yourself a favor and refund the purchase as " +
"fast as possible to not support people who just grab the work of others and want money for it.\n\n" +
"There are snapshot builds of Android N-ify available and linked in the XDA thread and on Github. " +
"These contain the same functionality as the play store version. The play version doesn't get any \"additional development\", " +
"it's exactly the same as one of these snapshots. Rating in the play store will be ignored by me as well as any bug reports.")
.setPositiveButton("Go to play store", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + getPackageName())));
}
})
.setCancelable(false)
.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
finish();
}
})
.show();
}
RomUtils.init(this);
setContentView(R.layout.activity_settings);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M)
Toast.makeText(this, "API" + Build.VERSION.SDK_INT + "?", Toast.LENGTH_SHORT).show();
if (!isActivated()) {
getActionBar().setSubtitle(R.string.not_activated);
} else if (!isPrefsFileReadable()) {
TextView warning = (TextView) findViewById(R.id.prefs_not_readable_warning);
//noinspection deprecation
warning.setText(Html.fromHtml(getString(R.string.prefs_not_readable)));
warning.setVisibility(View.VISIBLE);
warning.setOnClickListener(this);
}
mExperimental = ConfigUtils.isExperimental(prefs);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction().replace(R.id.fragment, new Fragment()).commit();
if (BuildConfig.AUTOMATED_BUILD && !prefs.contains(getWarningPrefsKey())) {
showDialog(R.string.warning, mExperimental ? R.string.experimental_build_warning : R.string.automated_build_warning, false, null, new Runnable() {
@Override
public void run() {
prefs.edit().putBoolean(getWarningPrefsKey(), true).apply();
}
}, android.R.string.ok, R.string.dont_show_again);
}
}
}
private String getWarningPrefsKey() {
return mExperimental ? "experimental_build_warning" : "automated_build_warning";
}
public static boolean isActivated() {
return false;
}
private boolean isPrefsFileReadable() {
return true;
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.prefs_not_readable_warning:
showDialog(0, R.string.prefs_not_readable_description, true, null);
break;
}
}
private void showDialog(int titleRes, int contentRes, boolean onlyOk, final Runnable okAction) {
showDialog(titleRes, contentRes, onlyOk, okAction, null, android.R.string.ok, android.R.string.cancel);
}
private void showDialog(int titleRes, int contentRes, boolean onlyOk, final Runnable okAction, final Runnable cancelAction, int okText, int cancelText) {
showDialog(titleRes, getString(contentRes), onlyOk, okAction, cancelAction, okText, cancelText);
}
private void showDialog(int titleRes, String content, boolean onlyOk, final Runnable okAction, final Runnable cancelAction, int okText, int cancelText) {
//noinspection deprecation
AlertDialog.Builder builder = new AlertDialog.Builder(this)
.setMessage(Html.fromHtml(content));
if (titleRes > 0)
builder.setTitle(titleRes);
if (!onlyOk)
builder.setNegativeButton(cancelText, cancelAction != null ? new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
cancelAction.run();
}
} : null);
builder.setPositiveButton(okText, okAction != null ? new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
okAction.run();
}
} : null);
View v = builder.show().findViewById(android.R.id.message);
if (v instanceof TextView)
((TextView) v).setMovementMethod(LinkMovementMethod.getInstance());
}
private void showRestartSystemUIDialog() {
showDialog(R.string.restart_systemui, R.string.restart_systemui_message, false, new Runnable() {
@Override
public void run() {
sendBroadcast(new Intent(ACTION_KILL_SYSTEMUI).setPackage(XposedHook.PACKAGE_SYSTEMUI));
Toast.makeText(SettingsActivity.this, R.string.restart_broadcast_sent, Toast.LENGTH_SHORT).show();
}
});
}
public static class Fragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener, UpdateUtils.UpdateListener {
/*private LoaderManager.LoaderCallbacks<String> updateLoaderCallbacks = new LoaderManager.LoaderCallbacks<String>() {
@Override
public Loader<String> onCreateLoader(int id, Bundle args) {
return new AsyncTaskLoader<String>(getActivity()) {
@Override
protected void onStartLoading() {
forceLoad();
}
@Override
public String loadInBackground() {
try {
URL url = new URL("https://raw.githubusercontent.com/Maxr1998/AndroidN-ify/master/app/src/main/assets/assistant_hooks");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
return MiscUtils.readInputStream(in);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
};
}
@SuppressLint("ApplySharedPref")
@Override
public void onLoadFinished(Loader loader, String data) {
try {
prefs.edit().putString(PreferenceKeys.GOOGLE_APP_HOOK_CONFIGS, MiscUtils.checkValidJSONArray(data).toString()).commit();
} catch (JSONException | NullPointerException e) {
e.printStackTrace();
}
}
@Override
public void onLoaderReset(Loader loader) {
}
};*/
private SharedPreferences prefs;
private boolean mExperimental;
private boolean mShowExperimental;
private boolean mAssistantSupported = false;
private String mGoogleAppVersionName;
@SuppressLint("ApplySharedPref")
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//noinspection deprecation
getPreferenceManager().setSharedPreferencesMode(Context.MODE_WORLD_READABLE);
addPreferencesFromResource(R.xml.preferences);
prefs = ConfigUtils.getPreferences(getActivity());
findPreference("theme_colorPrimary").setEnabled(!prefs.getString("app_theme", "light").equals("device"));
if (!MiscUtils.isGBInstalled(getActivity())) {
Preference p = findPreference("inject_gb_tiles");
p.setEnabled(false);
p.setSummary(R.string.inject_gb_tiles_not_installed);
}
mExperimental = ConfigUtils.isExperimental(prefs);
mShowExperimental = ConfigUtils.showExperimental(prefs);
if (UpdateUtils.isEnabled()) {
if (prefs.getBoolean("check_for_updates", true))
UpdateUtils.check(getActivity(), this);
} else {
PreferenceCategory appCategory = (PreferenceCategory) findPreference("settings_app");
Preference updatePref = getPreferenceScreen().findPreference("check_for_updates");
appCategory.removePreference(updatePref);
}
if (!mShowExperimental) {
PreferenceCategory tweaksCategory = (PreferenceCategory) findPreference("settings_tweaks");
Preference experimentalPref = getPreferenceScreen().findPreference("settings_experimental");
tweaksCategory.removePreference(experimentalPref);
}
// SELinux test, see XposedHook
prefs.edit().putBoolean("can_read_prefs", true).commit();
try {
mGoogleAppVersionName = getActivity().getPackageManager().getPackageInfo(XposedHook.PACKAGE_GOOGLE, 0).versionName;
} catch (PackageManager.NameNotFoundException e) {
mGoogleAppVersionName = "Error";
}
/*if (UpdateUtils.isConnected(getActivity())) {
// Always update from cloud if connected
getLoaderManager().initLoader(0, null, updateLoaderCallbacks).startLoading();
} else {*/
// Else load config from assets
try {
String result = MiscUtils.readInputStream(getResources().getAssets().open("assistant_hooks"));
JSONArray hookConfigs = MiscUtils.checkValidJSONArray(result);
// Only update if config from assets is newer
if (hookConfigs.optInt(0) > new JSONArray(prefs.getString(PreferenceKeys.GOOGLE_APP_HOOK_CONFIGS, "[]")).optInt(0)) {
prefs.edit().putString(PreferenceKeys.GOOGLE_APP_HOOK_CONFIGS, hookConfigs.toString()).commit();
}
} catch (IOException | JSONException e) {
e.printStackTrace();
}
//}
// Read version and check if supported
try {
JSONArray hookConfigs = new JSONArray(prefs.getString(PreferenceKeys.GOOGLE_APP_HOOK_CONFIGS, "[]"));
for (int i = 0; i < hookConfigs.length(); i++) {
if (hookConfigs.optInt(i, -1) != -1)
continue;
if (mGoogleAppVersionName.matches(hookConfigs.getJSONObject(i).optString("version"))) {
mAssistantSupported = true;
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
@Override
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
switch (key) {
case "app_theme":
case "theme_colorPrimary":
case "force_english":
getActivity().recreate();
break;
case "hide_launcher_icon":
int mode = prefs.getBoolean("hide_launcher_icon", false) ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED : PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
getActivity().getPackageManager().setComponentEnabledSetting(new ComponentName(getActivity(), "tk.wasdennnoch.androidn_ify.SettingsAlias"), mode, PackageManager.DONT_KILL_APP);
break;
default:
sendUpdateBroadcast(prefs, key);
break;
}
}
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
super.onPreferenceTreeClick(preferenceScreen, preference);
if (preference instanceof PreferenceScreen) {
PreferenceScreen screen = (PreferenceScreen) preference;
if (screen.getDialog() != null)
ViewUtils.applyTheme(screen.getDialog(), getActivity(), preference.getSharedPreferences());
switch (preference.getKey()) {
case "settings_recents":
DropDownPreference recentsBehaviorPref = (DropDownPreference) screen.findPreference("recents_button_behavior");
if (!ConfigUtils.M) {
lockPreference(recentsBehaviorPref);
} else {
final Preference delayPref = screen.findPreference("recents_navigation_delay");
if (recentsBehaviorPref.getValue().equals("2")) {
delayPref.setEnabled(true);
}
recentsBehaviorPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
delayPref.setEnabled(newValue.equals("2"));
return true;
}
});
}
break;
case "settings_qs":
if (getResources().getBoolean(R.bool.quick_settings_show_full_alarm)) { // Already showing full alarm
final Preference forceOldDatePosPref = screen.findPreference("force_old_date_position");
forceOldDatePosPref.setEnabled(false);
forceOldDatePosPref.setSummary(R.string.force_old_date_position_disabled_summary);
}
break;
case "settings_notifications":
if (!ConfigUtils.M) {
lockPreference(screen.findPreference("notification_experimental"));
lockPreference(screen.findPreference("enable_notifications_background")); // For now
}
if (!RemoteInputHelper.DIRECT_REPLY_ENABLED) {
Preference directReplyOnKeyguard = findPreference("allow_direct_reply_on_keyguard");
if (directReplyOnKeyguard != null)
screen.removePreference(directReplyOnKeyguard);
}
break;
case "settings_experimental":
Preference assistant = findPreference("enable_assistant");
if (!ConfigUtils.M) {
lockPreference(assistant);
} else {
if (!mAssistantSupported) {
assistant.setEnabled(false);
assistant.setSummary(getResources().getString(R.string.enable_assistant_summary_unsupported, mGoogleAppVersionName));
}
}
break;
}
} else {
switch (preference.getKey()) {
case "fix_stuck_inversion":
getActivity().sendBroadcast(new Intent(ACTION_FIX_INVERSION).setPackage("com.android.systemui"));
break;
}
}
return false;
}
private void lockPreference(Preference pref) {
if (pref == null) return;
pref.setEnabled(false);
pref.setSummary(getString(R.string.requires_android_version, "Marshmallow"));
}
@Override
public void onResume() {
super.onResume();
getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
if (mShowExperimental != ConfigUtils.showExperimental(ConfigUtils.getPreferences(getActivity()))) {
getActivity().recreate();
}
}
@SuppressLint("SetWorldReadable")
@Override
public void onPause() {
super.onPause();
getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
File sharedPrefsDir = new File(getActivity().getFilesDir(), "../shared_prefs");
File sharedPrefsFile = new File(sharedPrefsDir, getPreferenceManager().getSharedPreferencesName() + ".xml");
if (sharedPrefsFile.exists()) {
//noinspection ResultOfMethodCallIgnored
sharedPrefsFile.setReadable(true, false);
try {
Runtime.getRuntime().exec("chmod 664" + sharedPrefsFile.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
}
@SuppressLint("ApplySharedPref")
private void sendUpdateBroadcast(SharedPreferences prefs, String key) {
Intent intent = new Intent();
switch (key) {
case "double_tap_speed":
intent.setAction(ACTION_RECENTS_CHANGED);
intent.putExtra(EXTRA_RECENTS_DOUBLE_TAP_SPEED, prefs.getInt(key, 400));
break;
case "debug_log":
intent.setAction(ACTION_GENERAL);
intent.putExtra(EXTRA_GENERAL_DEBUG_LOG, prefs.getBoolean(key, false));
break;
}
if (intent.getAction() != null) {
prefs.edit().commit();
getActivity().sendBroadcast(intent);
}
}
@Override
public void onError(Exception e) {
Log.e(TAG, "Error fetching updates", e);
}
@Override
public void onFinish(UpdateUtils.UpdateData updateData) {
Context mContext = getActivity();
if (mContext == null) return;
if (updateData.getNumber() > BuildConfig.BUILD_NUMBER && updateData.hasArtifact())
UpdateUtils.showNotification(updateData, mContext, mExperimental);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_settings, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.restart_systemui:
showRestartSystemUIDialog();
return true;
case R.id.about:
startActivity(new Intent(this, AboutActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}
}