diff --git a/.gitignore b/.gitignore index 39fb081..5393359 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,8 @@ *.iml .gradle /local.properties -/.idea/workspace.xml -/.idea/libraries .DS_Store /build /captures .externalNativeBuild +/.idea \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index 96cc43e..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml deleted file mode 100644 index e7bedf3..0000000 --- a/.idea/copyright/profiles_settings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml deleted file mode 100644 index 97626ba..0000000 --- a/.idea/encodings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index 7ac24c7..0000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 635999d..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 1fff92a..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 7f68460..0000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index c4613a2..88f9d8b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,15 +1,16 @@ apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' android { - compileSdkVersion 26 - buildToolsVersion "26.0.1" + compileSdkVersion 30 + buildToolsVersion "30.0.2" defaultConfig { applicationId "com.franco.demomode" minSdkVersion 24 - targetSdkVersion 26 - versionName "1.2" - versionCode getVersionCodeTimestamp() + targetSdkVersion 30 + versionName "1.6" + versionCode 1910030257 archivesBaseName = "DemoModeTile-${android.defaultConfig.versionName}" } @@ -46,15 +47,20 @@ android { shrinkResources true signingConfig signingConfigs.release proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - manifestPlaceholders = [vmSafeModeEnabled: "false"] } debug { minifyEnabled false shrinkResources false - manifestPlaceholders = [vmSafeModeEnabled: "true"] } } + + buildFeatures { + viewBinding = true + } + kotlinOptions { + jvmTarget = '1.8' + } } def readKeystoreProperties(def configName) { @@ -70,13 +76,21 @@ def readKeystoreProperties(def configName) { } } -static def getVersionCodeTimestamp() { - return new Date().format('yyMMddHHmm').toInteger() +dependencies { + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'com.google.android.material:material:1.2.1' + implementation 'androidx.preference:preference-ktx:1.1.1' + implementation "androidx.fragment:fragment-ktx:1.2.5" + implementation "androidx.core:core-ktx:1.3.1" + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9' + + def lifecycle_version = "2.3.0-alpha07" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" + implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" + implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" } -dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:design:26.1.0' - compile 'com.jakewharton:butterknife:8.8.1' - annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' +repositories { + mavenCentral() } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index cdbe115..59f7652 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,31 +1,26 @@ - + + tools:ignore="ProtectedPermissions" /> + tools:ignore="ProtectedPermissions" /> + android:theme="@style/DemoModeTheme"> - + - + - + @@ -35,7 +30,7 @@ android:label="@string/demo_mode" android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"> - + diff --git a/app/src/main/java/com/franco/demomode/Utils.java b/app/src/main/java/com/franco/demomode/Utils.java deleted file mode 100644 index 536ab2d..0000000 --- a/app/src/main/java/com/franco/demomode/Utils.java +++ /dev/null @@ -1,112 +0,0 @@ -package com.franco.demomode; - -import android.Manifest; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.provider.Settings; - -import com.franco.demomode.application.App; - -public class Utils { - private static final String DEMO_MODE_ALLOWED = "sysui_demo_allowed"; - private static final String DEMO_MODE_ON = "sysui_tuner_demo_on"; - public static final String MISSING_PERMISSION = "missing_permission"; - - public static void enableDemoMode() { - if (!isDemoModeAllowed()) { - Settings.Global.putInt(App.CONTEXT.getContentResolver(), DEMO_MODE_ALLOWED, 1); - } - - Settings.Global.putInt(App.CONTEXT.getContentResolver(), DEMO_MODE_ON, 1); - - // clock with the latest release string - Intent clock = new Intent("com.android.systemui.demo"); - clock.putExtra("command", "clock"); - clock.putExtra("hhmm", "0800"); - App.CONTEXT.sendBroadcast(clock); - - // battery icon needs to be perfect - Intent battery = new Intent("com.android.systemui.demo"); - battery.putExtra("command", "battery"); - battery.putExtra("level", "100"); - battery.putExtra("plugged", "false"); - App.CONTEXT.sendBroadcast(battery); - - // signal icon - Intent data = new Intent("com.android.systemui.demo"); - data.putExtra("command", "network"); - data.putExtra("mobile", "show"); - data.putExtra("datatype", "hide"); - data.putExtra("level", "4"); - App.CONTEXT.sendBroadcast(data); - - // mock sim carrier connection - Intent signal = new Intent("com.android.systemui.demo"); - signal.putExtra("command", "network"); - signal.putExtra("fully", "true"); - App.CONTEXT.sendBroadcast(signal); - - // WiFi icon - Intent wifi = new Intent("com.android.systemui.demo"); - wifi.putExtra("command", "network"); - wifi.putExtra("wifi", "show"); - wifi.putExtra("level", "4"); - App.CONTEXT.sendBroadcast(wifi); - - // rip icons - Intent miscNetwork = new Intent("com.android.systemui.demo"); - miscNetwork.putExtra("command", "network"); - miscNetwork.putExtra("airplane", "hide"); - miscNetwork.putExtra("nosim", "hide"); - miscNetwork.putExtra("sims", 1); - App.CONTEXT.sendBroadcast(miscNetwork); - - - // if there's one thing I hate is cluttered statusbar with notifs - Intent notifs = new Intent("com.android.systemui.demo"); - notifs.putExtra("command", "notifications"); - notifs.putExtra("visible", "false"); - App.CONTEXT.sendBroadcast(notifs); - - // goodbye more icons! - Intent miscIcons = new Intent("com.android.systemui.demo"); - miscIcons.putExtra("command", "status"); - miscIcons.putExtra("bluetooth", "hide"); - miscIcons.putExtra("volume", "hide"); - miscIcons.putExtra("mute", "hide"); - App.CONTEXT.sendBroadcast(miscIcons); - } - - public static void disableDemoMode() { - Settings.Global.putInt(App.CONTEXT.getContentResolver(), Utils.DEMO_MODE_ON, 0); - - Intent enableDemoMode = new Intent("com.android.systemui.demo"); - enableDemoMode.putExtra("command", "exit"); - App.CONTEXT.sendBroadcast(enableDemoMode); - } - - public static boolean isDemoModeAllowed() { - try { - return Settings.Global.getInt(App.CONTEXT.getContentResolver(), DEMO_MODE_ALLOWED) == 1; - } catch (Settings.SettingNotFoundException e) { - e.printStackTrace(); - return false; - } - } - - public static boolean isDemoModeOn() { - return Settings.Global.getInt(App.CONTEXT.getContentResolver(), - DEMO_MODE_ON, 0) != 0; - } - - public static boolean isDumpPermissionGranted() { - return App.CONTEXT.getPackageManager().checkPermission(Manifest.permission.DUMP, App.CONTEXT.getPackageName()) - == PackageManager.PERMISSION_GRANTED; - } - - public static boolean isWriteSecureSettingsPermissionGranted() { - return App.CONTEXT.getPackageManager() - .checkPermission(Manifest.permission.WRITE_SECURE_SETTINGS, App.CONTEXT.getPackageName()) - == PackageManager.PERMISSION_GRANTED; - } -} diff --git a/app/src/main/java/com/franco/demomode/Utils.kt b/app/src/main/java/com/franco/demomode/Utils.kt new file mode 100644 index 0000000..53523f9 --- /dev/null +++ b/app/src/main/java/com/franco/demomode/Utils.kt @@ -0,0 +1,140 @@ +package com.franco.demomode + +import android.Manifest +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Build +import android.provider.Settings +import android.provider.Settings.SettingNotFoundException +import android.view.View +import androidx.core.content.ContextCompat +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class Utils { + suspend fun enableDemoMode(context: Context) { + withContext(Dispatchers.IO) { + if (!isDemoModeAllowed(context)) { + Settings.Global.putInt(context.contentResolver, DEMO_MODE_ALLOWED, 1) + } + + Settings.Global.putInt(context.contentResolver, DEMO_MODE_ON, 1) + } + + // clock with the latest release string + val clock = Intent("com.android.systemui.demo") + clock.putExtra("command", "clock") + clock.putExtra("hhmm", "1100") + context.sendBroadcast(clock) + + // battery icon needs to be perfect + val battery = Intent("com.android.systemui.demo") + battery.putExtra("command", "battery") + battery.putExtra("level", "100") + battery.putExtra("plugged", "false") + context.sendBroadcast(battery) + + // signal icon + val data = Intent("com.android.systemui.demo") + data.putExtra("command", "network") + data.putExtra("mobile", "show") + data.putExtra("datatype", "hide") + data.putExtra("level", "4") + context.sendBroadcast(data) + + // mock sim carrier connection + val signal = Intent("com.android.systemui.demo") + signal.putExtra("command", "network") + signal.putExtra("fully", "true") + context.sendBroadcast(signal) + + // WiFi icon + val wifi = Intent("com.android.systemui.demo") + wifi.putExtra("command", "network") + wifi.putExtra("wifi", "show") + wifi.putExtra("level", "4") + context.sendBroadcast(wifi) + + // rip icons + val miscNetwork = Intent("com.android.systemui.demo") + miscNetwork.putExtra("command", "network") + miscNetwork.putExtra("airplane", "hide") + miscNetwork.putExtra("nosim", "hide") + miscNetwork.putExtra("sims", 1) + context.sendBroadcast(miscNetwork) + + + // if there's one thing I hate is cluttered statusbar with notifs + val notifs = Intent("com.android.systemui.demo") + notifs.putExtra("command", "notifications") + notifs.putExtra("visible", "false") + context.sendBroadcast(notifs) + + // goodbye more icons! + val miscIcons = Intent("com.android.systemui.demo") + miscIcons.putExtra("command", "status") + miscIcons.putExtra("bluetooth", "hide") + miscIcons.putExtra("volume", "hide") + miscIcons.putExtra("mute", "hide") + context.sendBroadcast(miscIcons) + } + + suspend fun disableDemoMode(context: Context) { + withContext(Dispatchers.IO) { + Settings.Global.putInt(context.contentResolver, DEMO_MODE_ON, 0) + } + + val enableDemoMode = Intent("com.android.systemui.demo") + enableDemoMode.putExtra("command", "exit") + context.sendBroadcast(enableDemoMode) + } + + suspend fun isDemoModeAllowed(context: Context): Boolean = withContext(Dispatchers.IO) { + try { + Settings.Global.getInt(context.contentResolver, DEMO_MODE_ALLOWED) == 1 + } catch (e: SettingNotFoundException) { + e.printStackTrace() + false + } + } + + suspend fun isDemoModeOn(context: Context): Boolean = withContext(Dispatchers.IO) { + Settings.Global.getInt(context.contentResolver, DEMO_MODE_ON, 0) != 0 + } + + suspend fun isDumpPermissionGranted(context: Context): Boolean = withContext(Dispatchers.IO) { + (context.packageManager.checkPermission(Manifest.permission.DUMP, context.packageName) + == PackageManager.PERMISSION_GRANTED) + } + + suspend fun isWriteSecureSettingsPermissionGranted(context: Context): Boolean = withContext(Dispatchers.IO) { + (context.packageManager.checkPermission( + Manifest.permission.WRITE_SECURE_SETTINGS, context.packageName) + == PackageManager.PERMISSION_GRANTED) + } + + fun setLightNavBar(view: View) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + var flags = view.systemUiVisibility + flags = flags or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR + view.systemUiVisibility = flags + (view.context as Activity).window.navigationBarColor = ContextCompat.getColor(view.context, R.color.primary) + } + } + + fun clearLightNavBar(view: View) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + var flags = view.systemUiVisibility + flags = flags and View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv() + view.systemUiVisibility = flags + } + } + + companion object { + private const val DEMO_MODE_ALLOWED = "sysui_demo_allowed" + private const val DEMO_MODE_ON = "sysui_tuner_demo_on" + const val MISSING_PERMISSION = "missing_permission" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/franco/demomode/activities/MainActivity.java b/app/src/main/java/com/franco/demomode/activities/MainActivity.java deleted file mode 100644 index 58a8365..0000000 --- a/app/src/main/java/com/franco/demomode/activities/MainActivity.java +++ /dev/null @@ -1,154 +0,0 @@ -package com.franco.demomode.activities; - -import android.app.AlertDialog; -import android.os.Bundle; -import android.os.Process; -import android.preference.Preference; -import android.preference.PreferenceFragment; -import android.support.annotation.Nullable; -import android.support.design.widget.Snackbar; -import android.support.v7.app.AppCompatActivity; -import android.view.View; -import android.widget.Toolbar; - -import com.franco.demomode.R; -import com.franco.demomode.Utils; - -import butterknife.BindView; -import butterknife.ButterKnife; - - -public class MainActivity extends AppCompatActivity { - @BindView(R.id.toolbar) - protected Toolbar toolbar; - @BindView(R.id.root_layout) - protected View rootLayout; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - ButterKnife.bind(this); - - setActionBar(toolbar); - - if (getIntent() != null) { - if (getIntent().getAction() != null) { - if (getIntent().getAction().equals(Utils.MISSING_PERMISSION)) { - final Snackbar snackbar = Snackbar.make(rootLayout, - R.string.permissions_need_to_be_granted, Snackbar.LENGTH_INDEFINITE); - snackbar.setAction(R.string.ok, view -> snackbar.dismiss()); - snackbar.show(); - } - } - } - - if (!Utils.isDemoModeAllowed()) { - new AlertDialog.Builder(this) - .setTitle(R.string.demo_mode_allowed_title) - .setMessage(R.string.demo_mode_allowed_message) - .setPositiveButton(android.R.string.ok, (dialog, which) -> dialog.dismiss()) - .show(); - } - } - - public static class SettingsFragment extends PreferenceFragment implements Preference.OnPreferenceClickListener { - private static final String KEY_DUMP = "dump_permission"; - private static final String KET_WRITE_SECURE_SETTINGS = "write_secure_settings"; - - private Preference dump; - private Preference writeSecureSettings; - - private Thread permissionsPollThread; - - private boolean isDumpPermissionGranted; - private boolean isWriteSecureSettingsPermissionGranted; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.settings); - - dump = findPreference(KEY_DUMP); - writeSecureSettings = findPreference(KET_WRITE_SECURE_SETTINGS); - - dump.setOnPreferenceClickListener(this); - writeSecureSettings.setOnPreferenceClickListener(this); - } - - @Override - public boolean onPreferenceClick(Preference preference) { - new AlertDialog.Builder(getActivity()) - .setTitle(R.string.permission_request_title) - .setMessage(preference.getKey().equals(KEY_DUMP) ? - R.string.dump_permission_msg : - R.string.write_secure_settings_permission_msg) - .show(); - - return false; - } - - @Override - public void onStart() { - super.onStart(); - - isDumpPermissionGranted = Utils.isDumpPermissionGranted(); - isWriteSecureSettingsPermissionGranted = Utils.isWriteSecureSettingsPermissionGranted(); - - dump.setSummary(isDumpPermissionGranted ? R.string.granted : R.string.not_granted); - writeSecureSettings.setSummary(isWriteSecureSettingsPermissionGranted - ? R.string.granted : R.string.not_granted); - - // there isn't a listener for when these permissions states change, so we have to poll it - // Runs in a BG thread so it's ok - permissionsPollThread = new Thread(new Runnable() { - long lastNow = System.currentTimeMillis(); - - @Override - public void run() { - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - - try { - while (permissionsPollThread != null && permissionsPollThread.isAlive()) { - if ((lastNow + 1000) < System.currentTimeMillis()) { - lastNow = System.currentTimeMillis(); - - if (isDumpPermissionGranted != Utils.isDumpPermissionGranted()) { - isDumpPermissionGranted = Utils.isDumpPermissionGranted(); - - getActivity().runOnUiThread(() -> - dump.setSummary(isDumpPermissionGranted ? - R.string.granted : R.string.not_granted)); - } - - if (isWriteSecureSettingsPermissionGranted != Utils.isWriteSecureSettingsPermissionGranted()) { - isWriteSecureSettingsPermissionGranted = Utils.isWriteSecureSettingsPermissionGranted(); - - getActivity().runOnUiThread(() -> - writeSecureSettings.setSummary(isWriteSecureSettingsPermissionGranted - ? R.string.granted : R.string.not_granted)); - } - } - } - } catch (Exception ignored) { - // it's ok to catch the exception here and not treat it properly. - // not doing anything important and the thread object might be null even after the null check - // so fail gracefully and in peace - } - } - }); - - permissionsPollThread.start(); - } - - @Override - public void onStop() { - super.onStop(); - - if (permissionsPollThread != null && !permissionsPollThread.isInterrupted()) { - permissionsPollThread.interrupt(); - permissionsPollThread = null; - } - } - } -} diff --git a/app/src/main/java/com/franco/demomode/activities/MainActivity.kt b/app/src/main/java/com/franco/demomode/activities/MainActivity.kt new file mode 100644 index 0000000..4bc9f68 --- /dev/null +++ b/app/src/main/java/com/franco/demomode/activities/MainActivity.kt @@ -0,0 +1,51 @@ +package com.franco.demomode.activities + +import android.content.DialogInterface +import android.os.Bundle +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.lifecycleScope +import com.franco.demomode.R +import com.franco.demomode.Utils +import com.franco.demomode.databinding.ActivityMainBinding +import com.google.android.material.snackbar.Snackbar +import kotlinx.coroutines.launch + +class MainActivity : AppCompatActivity() { + private lateinit var binding: ActivityMainBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityMainBinding.inflate(layoutInflater) + + setContentView(binding.root) + setSupportActionBar(binding.toolbar) + supportActionBar!!.setDisplayShowTitleEnabled(false) + + val action = intent?.action ?: "" + if (action == Utils.MISSING_PERMISSION) { + Snackbar.make(binding.root, R.string.permissions_need_to_be_granted, + Snackbar.LENGTH_INDEFINITE).apply { + setAction(R.string.ok) { dismiss() } + show() + } + } + + lifecycleScope.launch { + val isDemoModeAllowed = Utils().isDemoModeAllowed(this@MainActivity) + + if (!isDemoModeAllowed) { + AlertDialog.Builder(this@MainActivity) + .setTitle(R.string.demo_mode_allowed_title) + .setMessage(R.string.demo_mode_allowed_message) + .setPositiveButton(android.R.string.ok) { dialog: DialogInterface, _: Int -> dialog.dismiss() } + .show() + } + } + } + + override fun onResume() { + super.onResume() + Utils().setLightNavBar(binding.root) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/franco/demomode/application/App.java b/app/src/main/java/com/franco/demomode/application/App.java deleted file mode 100644 index 2fbe6cc..0000000 --- a/app/src/main/java/com/franco/demomode/application/App.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.franco.demomode.application; - -import android.app.Application; -import android.content.Context; - -public class App extends Application { - public static Context CONTEXT; - - @Override - public void onCreate() { - super.onCreate(); - CONTEXT = this; - } -} diff --git a/app/src/main/java/com/franco/demomode/fragments/SettingsFragment.kt b/app/src/main/java/com/franco/demomode/fragments/SettingsFragment.kt new file mode 100644 index 0000000..86fd6f4 --- /dev/null +++ b/app/src/main/java/com/franco/demomode/fragments/SettingsFragment.kt @@ -0,0 +1,71 @@ +package com.franco.demomode.fragments + +import android.os.Bundle +import android.view.View +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.viewModels +import androidx.preference.Preference +import androidx.preference.PreferenceFragmentCompat +import com.franco.demomode.R +import kotlin.time.ExperimentalTime + +@ExperimentalTime +class SettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferenceClickListener { + private lateinit var dumpPermissionPref: Preference + private lateinit var writeSecureSettingsPref: Preference + + private val viewModel by viewModels() + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {} + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + addPreferencesFromResource(R.xml.settings) + + dumpPermissionPref = findPreference(KEY_DUMP)!! + writeSecureSettingsPref = findPreference(KET_WRITE_SECURE_SETTINGS)!! + + dumpPermissionPref.onPreferenceClickListener = this + writeSecureSettingsPref.onPreferenceClickListener = this + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + viewModel.isDumpPermissionUpdates(requireContext()).observe(viewLifecycleOwner, ::renderDump) + viewModel.isWriteSecureSettingsPermissionUpdates(requireContext()) + .observe(viewLifecycleOwner, ::renderWriteSecureSettings) + } + + private fun renderDump(isGranted: Boolean) { + dumpPermissionPref.summary = when (isGranted) { + true -> getString(R.string.granted) + false -> getString(R.string.not_granted) + } + } + + private fun renderWriteSecureSettings(isGranted: Boolean) { + writeSecureSettingsPref.summary = when (isGranted) { + true -> getString(R.string.granted) + false -> getString(R.string.not_granted) + } + } + + override fun onPreferenceClick(preference: Preference): Boolean { + AlertDialog.Builder(requireActivity()) + .setTitle(R.string.permission_request_title) + .setMessage(when (preference.key) { + KEY_DUMP -> R.string.dump_permission_msg + KET_WRITE_SECURE_SETTINGS -> R.string.write_secure_settings_permission_msg + else -> throw IllegalArgumentException("well, this shouldn't ever happen") + }) + .show() + return false + } + + companion object { + private const val KEY_DUMP = "dump_permission" + private const val KET_WRITE_SECURE_SETTINGS = "write_secure_settings" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/franco/demomode/fragments/SettingsViewModel.kt b/app/src/main/java/com/franco/demomode/fragments/SettingsViewModel.kt new file mode 100644 index 0000000..0da85de --- /dev/null +++ b/app/src/main/java/com/franco/demomode/fragments/SettingsViewModel.kt @@ -0,0 +1,32 @@ +package com.franco.demomode.fragments + +import android.content.Context +import androidx.lifecycle.ViewModel +import androidx.lifecycle.asLiveData +import com.franco.demomode.Utils +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.flow +import kotlin.random.Random +import kotlin.time.ExperimentalTime +import kotlin.time.seconds + +@ExperimentalTime +class SettingsViewModel : ViewModel() { + + fun isDumpPermissionUpdates(context: Context) = flow { + while (true) { + emit(Utils().isDumpPermissionGranted(context)) + delay(Random.nextLong(1.seconds.toLongMilliseconds(), + 2.seconds.toLongMilliseconds())) + } + }.asLiveData(Dispatchers.IO) + + fun isWriteSecureSettingsPermissionUpdates(context: Context) = flow { + while (true) { + emit(Utils().isWriteSecureSettingsPermissionGranted(context)) + delay(Random.nextLong(1.seconds.toLongMilliseconds(), + 2.seconds.toLongMilliseconds())) + } + }.asLiveData(Dispatchers.IO) +} \ No newline at end of file diff --git a/app/src/main/java/com/franco/demomode/tiles/DemoModeTile.java b/app/src/main/java/com/franco/demomode/tiles/DemoModeTile.java deleted file mode 100644 index b4b5a6a..0000000 --- a/app/src/main/java/com/franco/demomode/tiles/DemoModeTile.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.franco.demomode.tiles; - -import android.content.Intent; -import android.graphics.drawable.Icon; -import android.service.quicksettings.Tile; -import android.service.quicksettings.TileService; - -import com.franco.demomode.R; -import com.franco.demomode.Utils; -import com.franco.demomode.activities.MainActivity; - -public class DemoModeTile extends TileService { - @Override - public void onCreate() { - super.onCreate(); - } - - @Override - public void onStartListening() { - super.onStartListening(); - - getQsTile().setState(Utils.isDemoModeOn() ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE); - getQsTile().setIcon(Utils.isDemoModeOn() ? - Icon.createWithResource(getApplicationContext(), R.drawable.ic_on) : - Icon.createWithResource(getApplicationContext(), R.drawable.ic_off)); - getQsTile().updateTile(); - } - - @Override - public void onClick() { - super.onClick(); - - if (!Utils.isDumpPermissionGranted() || !Utils.isWriteSecureSettingsPermissionGranted()) { - Intent mainActivity = new Intent(getApplicationContext(), MainActivity.class); - mainActivity.setAction(Utils.MISSING_PERMISSION); - mainActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - startActivity(mainActivity); - } else { - if (getQsTile().getState() == Tile.STATE_ACTIVE) { - getQsTile().setState(Tile.STATE_INACTIVE); - getQsTile().setIcon(Icon.createWithResource(getApplicationContext(), R.drawable.ic_off)); - getQsTile().updateTile(); - - Utils.disableDemoMode(); - } else { - getQsTile().setState(Tile.STATE_ACTIVE); - getQsTile().setIcon(Icon.createWithResource(getApplicationContext(), R.drawable.ic_on)); - getQsTile().updateTile(); - - Utils.enableDemoMode(); - } - } - } -} diff --git a/app/src/main/java/com/franco/demomode/tiles/DemoModeTile.kt b/app/src/main/java/com/franco/demomode/tiles/DemoModeTile.kt new file mode 100644 index 0000000..6fdb8c0 --- /dev/null +++ b/app/src/main/java/com/franco/demomode/tiles/DemoModeTile.kt @@ -0,0 +1,70 @@ +package com.franco.demomode.tiles + +import android.content.Intent +import android.graphics.drawable.Icon +import android.service.quicksettings.Tile +import android.service.quicksettings.TileService +import com.franco.demomode.R +import com.franco.demomode.Utils +import com.franco.demomode.activities.MainActivity +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch + +class DemoModeTile : TileService() { + override fun onStartListening() { + super.onStartListening() + + GlobalScope.launch { + val isDemoModeAllowed = Utils().isDemoModeAllowed(applicationContext) + val isDemoMode = Utils().isDemoModeOn(applicationContext) + + qsTile?.state = when { + !isDemoModeAllowed -> Tile.STATE_UNAVAILABLE + isDemoMode -> Tile.STATE_ACTIVE + else -> Tile.STATE_INACTIVE + } + + qsTile?.icon = when { + isDemoMode -> Icon.createWithResource(applicationContext, R.drawable.ic_on) + else -> Icon.createWithResource(applicationContext, R.drawable.ic_off) + } + + qsTile?.updateTile() + } + } + + override fun onClick() { + super.onClick() + + GlobalScope.launch { + val hasDumpPermission = Utils().isDumpPermissionGranted(applicationContext) + val hasWriteSecurePermission = Utils().isWriteSecureSettingsPermissionGranted(applicationContext) + + when { + !hasDumpPermission || !hasWriteSecurePermission -> { + Intent(applicationContext, MainActivity::class.java).apply { + action = Utils.MISSING_PERMISSION + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + startActivity(this) + } + } + + else -> { + qsTile?.apply { + if (state == Tile.STATE_ACTIVE) { + state = Tile.STATE_INACTIVE + icon = Icon.createWithResource(applicationContext, R.drawable.ic_off) + Utils().disableDemoMode(applicationContext) + } else { + state = Tile.STATE_ACTIVE + icon = Icon.createWithResource(applicationContext, R.drawable.ic_on) + Utils().enableDemoMode(applicationContext) + } + + updateTile() + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable-xhdpi/ic_demo_off.png b/app/src/main/res/drawable-xhdpi/ic_demo_off.png deleted file mode 100644 index 1e54e21..0000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_demo_off.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_demo_on.png b/app/src/main/res/drawable-xhdpi/ic_demo_on.png deleted file mode 100644 index b04ce48..0000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_demo_on.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_demo_off.png b/app/src/main/res/drawable-xxhdpi/ic_demo_off.png deleted file mode 100644 index ccbca1a..0000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_demo_off.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_demo_on.png b/app/src/main/res/drawable-xxhdpi/ic_demo_on.png deleted file mode 100644 index b55c563..0000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_demo_on.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_demo_off.png b/app/src/main/res/drawable-xxxhdpi/ic_demo_off.png deleted file mode 100644 index 3c8545e..0000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_demo_off.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_demo_on.png b/app/src/main/res/drawable-xxxhdpi/ic_demo_on.png deleted file mode 100644 index 0c2a5d2..0000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_demo_on.png and /dev/null differ diff --git a/app/src/main/res/drawable/bug.xml b/app/src/main/res/drawable/bug.xml new file mode 100644 index 0000000..e6bcb4e --- /dev/null +++ b/app/src/main/res/drawable/bug.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/code_braces.xml b/app/src/main/res/drawable/code_braces.xml new file mode 100644 index 0000000..50f01a8 --- /dev/null +++ b/app/src/main/res/drawable/code_braces.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_settings_black_24dp.xml b/app/src/main/res/drawable/ic_settings_black_24dp.xml new file mode 100644 index 0000000..24a5623 --- /dev/null +++ b/app/src/main/res/drawable/ic_settings_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/twitter.xml b/app/src/main/res/drawable/twitter.xml new file mode 100644 index 0000000..ae08ca6 --- /dev/null +++ b/app/src/main/res/drawable/twitter.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 5caafb8..056a9b7 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,20 +1,27 @@ - - + android:layout_height="?attr/actionBarSize" + android:gravity="center"> - + + + \ No newline at end of file diff --git a/app/src/main/res/values-fr/string.xml b/app/src/main/res/values-fr/string.xml deleted file mode 100644 index 9d050cf..0000000 --- a/app/src/main/res/values-fr/string.xml +++ /dev/null @@ -1,17 +0,0 @@ - - Demo Mode tile - Demo mode - Autorisations - Pour que cette application fonctionne, elle a besoin de l\'autorisation \"DUMP\". Malheureusement, cela n\'est possible qu\'à partir d\'un \"shell adb\" depuis votre ordinateur :\n\nadb -d shell pm grant com.franco.demomode android.permission.DUMP\n\nPour plus de transparence, le code source de cette application est disponible. - Pour que cette application fonctionne, elle a besoin de l\'autorisation \"WRITE_SECURE_SETTINGS\". Malheureusement, cela n\'est possible qu\'à partir d\'un \"shell adb\" depuis votre ordinateur :\n\nadb -d shell pm grant com.franco.demomode android.permission.WRITE_SECURE_SETTINGS\n\nPour plus de transparence, le code source de cette application est disponible. - Non - OK - Autorisation \"DUMP\" - Autorisation \"WRITE_SECURE_SETTINGS\" - Non accordée - Autorisations - Accordée - Suivez-moi - Code source - Les deux autorisations doivent être accordées pour que la tuile puisse fonctionner - diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml deleted file mode 100644 index 1b32e1b..0000000 --- a/app/src/main/res/values-it/strings.xml +++ /dev/null @@ -1,17 +0,0 @@ - - Demo Mode tile - Demo mode - Demo mode tile richesta dei permessi - Perchè l\'applicazione funzioni è necessario dare il consenso al permesso DUMP. Sfortunatamente questo è possibile solo tramite adb shell dal tuo computer:\n\nadb -d shell pm grant com.franco.demomode android.permission.DUMP\n\nPer trasparenza, il codice sorgente di questa applicazione è disponibile. - Perchè l\'applicazione funzioni è necessario dare il consenso al permesso WRITE_SECURE_SETTINGS. Sfortunatamente questo è possibile solo tramite adb shell dal tuo computer:\n\nadb -d shell pm grant com.franco.demomode android.permission.WRITE_SECURE_SETTINGS\n\nPer trasparenza, il codice sorgente di questa applicazione è disponibile. - No - OK - android.permission.DUMP - android.permission.WRITE_SECURE_SETTINGS - Negato - Permessi - Consentito - Seguimi - Codice sorgente - Entrambi i permessi sono necessari perchè il tile funzioni correttamente - diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml deleted file mode 100644 index aeb9789..0000000 --- a/app/src/main/res/values-tr/strings.xml +++ /dev/null @@ -1,17 +0,0 @@ - - Demo Mode Tile - Demo Modu - Demo modu blok izni isteği - Bu uygulamanın çalışması için DUMP izninin verilmesi gerekiyor. Ne yazık ki, bu yalnızca bilgisayarınızdaki ADB Shell ile mümkündür:\n\nadb -d shell pm grant com.franco.demomode android.permission.DUMP\n\nŞeffaflık için bu uygulamanın kaynak kodu mevcuttur. - Bu uygulamanın çalışması için WRITE_SECURE_SETTINGS izni verilmesi gerekiyor. Ne yazık ki, bu yalnızca bilgisayarınızdaki ADB Shell ile mümkündür:\n\nadb -d shell pm grant com.franco.demomode android.permission.WRITE_SECURE_SETTINGS\n\nŞeffaflık için bu uygulamanın kaynak kodu mevcuttur. - Hayır - EVET - android.permission.DUMP - android.permission.WRITE_SECURE_SETTINGS - Verilmemiş - İzinler - Verildi - Beni takip et - Kaynak kodu - Bloğun çalışması için her iki izinin de verilmesi gerekir - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1194ad3..b636baa 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -4,7 +4,6 @@ Demo mode tile permission request For this app to work it needs DUMP permission to be granted. Unfortunately that is only possible through adb shell from your computer:\n\nadb -d shell pm grant com.franco.demomode android.permission.DUMP\n\nFor transparency this app\'s source code is available. For this app to work it needs WRITE_SECURE_SETTINGS permission to be granted. Unfortunately that is only possible through adb shell from your computer:\n\nadb -d shell pm grant com.franco.demomode android.permission.WRITE_SECURE_SETTINGS\n\nFor transparency this app\'s source code is available. - No OK DUMP WRITE_SECURE_SETTINGS diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index fb14d31..7a6748f 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,6 +1,6 @@ -