Skip to content

Commit

Permalink
Merge pull request #21467 from wordpress-mobile/feat/relocate-experim…
Browse files Browse the repository at this point in the history
…ental-features-settings

feat: Relocate experimental features settings
  • Loading branch information
dcalhoun authored Nov 15, 2024
2 parents 74cd8f0 + f1d467b commit b7f44fb
Show file tree
Hide file tree
Showing 11 changed files with 242 additions and 72 deletions.
5 changes: 5 additions & 0 deletions WordPress/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,11 @@
android:configChanges="locale|orientation|screenSize"
android:label="@string/me_btn_app_settings"
android:theme="@style/WordPress.NoActionBar" />
<activity
android:name=".ui.prefs.ExperimentalFeaturesActivity"
android:configChanges="locale|orientation|screenSize"
android:label="@string/me_btn_experimental_features"
android:theme="@style/WordPress.NoActionBar" />
<activity
android:name=".designsystem.DesignSystemActivity"
android:configChanges="locale|orientation|screenSize"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
import org.wordpress.android.ui.prefs.AccountSettingsActivity;
import org.wordpress.android.ui.prefs.AppSettingsActivity;
import org.wordpress.android.ui.prefs.BlogPreferencesActivity;
import org.wordpress.android.ui.prefs.ExperimentalFeaturesActivity;
import org.wordpress.android.ui.prefs.MyProfileActivity;
import org.wordpress.android.ui.prefs.categories.detail.CategoryDetailActivity;
import org.wordpress.android.ui.prefs.categories.list.CategoriesListActivity;
Expand Down Expand Up @@ -1271,6 +1272,12 @@ public static void viewAppSettingsForResult(Activity activity) {
activity.startActivityForResult(intent, RequestCodes.APP_SETTINGS);
}

public static void viewExperimentalFeatures(@NonNull Context context) {
Intent intent = new Intent(context, ExperimentalFeaturesActivity.class);
AnalyticsTracker.track(AnalyticsTracker.Stat.EXPERIMENTAL_FEATURES_OPENED);
context.startActivity(intent);
}

public static void viewNotificationsSettings(Activity activity) {
Intent intent = new Intent(activity, NotificationsSettingsActivity.class);
activity.startActivity(intent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,10 @@ class MeFragment : Fragment(R.layout.me_fragment), OnScrollToTopListener {

if (BuildConfig.IS_JETPACK_APP) meAboutIcon.setImageResource(R.drawable.ic_jetpack_logo_white_24dp)

rowExperimentalFeaturesSettings.setOnClickListener {
context?.let { context -> ActivityLauncher.viewExperimentalFeatures(context) }
}

if (BuildConfig.DEBUG && BuildConfig.ENABLE_DEBUG_SETTINGS) {
rowDebugSettings.isVisible = true
debugSettingsDivider.isVisible = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,6 @@ public class AppSettingsFragment extends PreferenceFragment
private WPSwitchPreference mStripImageLocation;
private WPSwitchPreference mReportCrashPref;
private WPSwitchPreference mOpenWebLinksWithJetpack;
@Nullable private PreferenceScreen mExperimentalFeaturesSettings;
@Nullable private WPSwitchPreference mExperimentalBlockEditorPref;
@Nullable private WPSwitchPreference mExperimentalBlockEditorStylesPref;

private Preference mWhatsNew;

Expand Down Expand Up @@ -180,18 +177,12 @@ public boolean onPreferenceChange(Preference preference, Object newValue) {
.getPrefAndSetChangeListener(this, R.string.pref_key_site_video_encoder_bitrate, this);
mPrivacySettings = (PreferenceScreen) WPPrefUtils
.getPrefAndSetClickListener(this, R.string.pref_key_privacy_settings, this);
mExperimentalFeaturesSettings = (PreferenceScreen) WPPrefUtils
.getPrefAndSetClickListener(this, R.string.pref_key_experimental_features_settings, this);

mStripImageLocation =
(WPSwitchPreference) WPPrefUtils
.getPrefAndSetChangeListener(this, R.string.pref_key_strip_image_location, this);
mReportCrashPref = (WPSwitchPreference) WPPrefUtils
.getPrefAndSetChangeListener(this, R.string.pref_key_send_crash, this);
mExperimentalBlockEditorPref = (WPSwitchPreference) WPPrefUtils
.getPrefAndSetChangeListener(this, R.string.pref_key_experimental_block_editor, this);
mExperimentalBlockEditorStylesPref = (WPSwitchPreference) WPPrefUtils
.getPrefAndSetChangeListener(this, R.string.pref_key_experimental_block_editor_theme_styles, this);

mOpenWebLinksWithJetpack =
(WPSwitchPreference) WPPrefUtils
Expand Down Expand Up @@ -269,7 +260,6 @@ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Override public void onViewStateRestored(Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
addPrivacyToolbar();
addExperimentalFeaturesToolbar();
}

private void addJetpackBadgeAsFooterIfEnabled(LayoutInflater inflater, ListView listView) {
Expand Down Expand Up @@ -415,8 +405,6 @@ public boolean onPreferenceClick(Preference preference) {
return handleDevicePreferenceClick();
} else if (preference == mPrivacySettings) {
return handlePrivacyClick();
} else if (preference == mExperimentalFeaturesSettings) {
return handleExperimentalFeaturesClick();
} else if (preference == mWhatsNew) {
return handleFeatureAnnouncementClick();
} else if (preference == mLanguagePreference) {
Expand Down Expand Up @@ -485,12 +473,6 @@ public boolean onPreferenceChange(Preference preference, Object newValue) {
} else if (preference == mReportCrashPref) {
AnalyticsTracker.track(Stat.PRIVACY_SETTINGS_REPORT_CRASHES_TOGGLED, Collections
.singletonMap(TRACK_ENABLED, newValue));
} else if (preference == mExperimentalBlockEditorPref) {
AnalyticsTracker.track(Stat.EXPERIMENTAL_BLOCK_EDITOR_TOGGLED, Collections
.singletonMap(TRACK_ENABLED, newValue));
} else if (preference == mExperimentalBlockEditorStylesPref) {
AnalyticsTracker.track(Stat.EXPERIMENTAL_BLOCK_EDITOR_THEME_STYLES_TOGGLED, Collections
.singletonMap(TRACK_ENABLED, newValue));
} else if (preference == mOpenWebLinksWithJetpack) {
handleOpenLinksInJetpack((Boolean) newValue);
}
Expand Down Expand Up @@ -635,32 +617,6 @@ private boolean addPrivacyToolbar() {
return true;
}

private boolean handleExperimentalFeaturesClick() {
AnalyticsTracker.track(Stat.APP_SETTINGS_EXPERIMENTAL_FEATURES_TAPPED);

boolean isToolbarAdded = addExperimentalFeaturesToolbar();

if (!isToolbarAdded) {
return false;
}

AnalyticsTracker.track(Stat.EXPERIMENTAL_FEATURES_SETTINGS_OPENED);
return true;
}

private boolean addExperimentalFeaturesToolbar() {
if (mExperimentalFeaturesSettings == null || !isAdded()) {
return false;
}

String title = getString(R.string.preference_experimental_features_settings);
Dialog dialog = mExperimentalFeaturesSettings.getDialog();
if (dialog != null) {
WPActivityUtils.addToolbarToDialog(this, dialog, title);
}
return true;
}

private boolean handleFeatureAnnouncementClick() {
if (getActivity() instanceof AppCompatActivity) {
AnalyticsTracker.track(Stat.FEATURE_ANNOUNCEMENT_SHOWN_FROM_APP_SETTINGS);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
package org.wordpress.android.ui.prefs

import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.ViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import org.wordpress.android.R
import org.wordpress.android.ui.compose.theme.AppThemeM3
import org.wordpress.android.ui.compose.unit.Margin
import org.wordpress.android.util.extensions.setContent

val experimentalFeatures = listOf(
Feature(key = "experimental_block_editor"),
Feature(key = "experimental_block_editor_theme_styles")
)

data class Feature(
val enabled: Boolean = false,
val key: String,
)

class FeatureViewModel : ViewModel() {
private val _switchStates = MutableStateFlow<Map<String, Feature>>(emptyMap())
val switchStates: StateFlow<Map<String, Feature>> = _switchStates.asStateFlow()

init {
val initialStates = experimentalFeatures.associate { item ->
item.key to Feature(AppPrefs.getManualFeatureConfig(item.key), item.key)
}
_switchStates.value = initialStates
}

fun toggleFeature(key: String, enabled: Boolean) {
_switchStates.update { currentStates ->
currentStates.toMutableMap().apply {
this[key] = Feature(enabled, key)
AppPrefs.setManualFeatureConfig(enabled, key)
}
}
}
}

@AndroidEntryPoint
class ExperimentalFeaturesActivity : AppCompatActivity() {
private val viewModel: FeatureViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setContent {
AppThemeM3 {
val features by viewModel.switchStates.collectAsStateWithLifecycle()

ExperimentalFeaturesScreen(
features = features,
onFeatureToggled = viewModel::toggleFeature,
onNavigateBack = onBackPressedDispatcher::onBackPressed
)
}
}
}
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ExperimentalFeaturesScreen(
features: Map<String, Feature>,
onFeatureToggled: (key: String, enabled: Boolean) -> Unit,
onNavigateBack: () -> Unit
) {
Scaffold(
topBar = {
TopAppBar(
title = {
Text(text = stringResource(R.string.experimental_features_screen_title))
},
navigationIcon = {
IconButton(onClick = onNavigateBack) {
Icon(
Icons.AutoMirrored.Filled.ArrowBack,
stringResource(R.string.back)
)
}
},
)
},
) { innerPadding ->
Box(modifier = Modifier.padding(innerPadding)) {
Column {
features.forEach { (key, feature) ->
val context = LocalContext.current
val label = remember(key) {
context.getStringResourceByName(key)
}

FeatureToggle(
key = key,
label = label,
enabled = feature.enabled,
onChange = onFeatureToggled,
)
}
}
}
}
}

@SuppressLint("DiscouragedApi")
fun Context.getStringResourceByName(name: String): String {
val resourceId = resources.getIdentifier(name, "string", packageName)
return if (resourceId != 0) getString(resourceId) else name
}

@Composable
fun FeatureToggle(
key: String,
label: String,
enabled: Boolean,
onChange: (String, Boolean) -> Unit,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clickable { onChange(key, !enabled) }
.padding(horizontal = Margin.ExtraLarge.value, vertical = Margin.MediumLarge.value)
) {
Spacer(modifier = Modifier.width(Margin.ExtraLarge.value))
Text(
text = label,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurface,
)
Spacer(modifier = Modifier.weight(1f))
Switch(
checked = enabled,
onCheckedChange = { newValue ->
onChange(key, newValue)
},
)
}
}

@Preview
@Preview(uiMode = UI_MODE_NIGHT_YES)
@Composable
fun ExperimentalFeaturesScreenPreview() {
AppThemeM3 {
val featuresStatusAlternated = remember {
experimentalFeatures.mapIndexed { index, feature ->
feature.key to feature.copy(enabled = index % 2 == 0)
}.toMap()
}

ExperimentalFeaturesScreen(
features = featuresStatusAlternated,
onFeatureToggled = { _, _ -> },
onNavigateBack = {}
)
}
}
5 changes: 5 additions & 0 deletions WordPress/src/main/res/drawable/ic_science_24.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">

<path android:fillColor="@android:color/white" android:pathData="M19.8,18.4L14,10.67V6.5l1.35,-1.69C15.61,4.48 15.38,4 14.96,4H9.04C8.62,4 8.39,4.48 8.65,4.81L10,6.5v4.17L4.2,18.4C3.71,19.06 4.18,20 5,20h14C19.82,20 20.29,19.06 19.8,18.4z"/>

</vector>
19 changes: 19 additions & 0 deletions WordPress/src/main/res/layout/me_fragment.xml
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,25 @@

<View style="@style/MeListSectionDividerView" />

<LinearLayout
android:id="@+id/row_experimental_features_settings"
style="@style/MeListRowLayout">

<ImageView
android:id="@+id/me_experimental_features_icon"
style="@style/MeListRowIcon"
android:contentDescription="@null"
android:src="@drawable/ic_science_24" />

<org.wordpress.android.widgets.WPTextView
android:id="@+id/me_open_experimental_features"
style="@style/MeListRowTextView"
android:text="@string/experimental_features_screen_title" />

</LinearLayout>

<View style="@style/MeListSectionDividerView" />

<LinearLayout
android:id="@+id/row_debug_settings"
android:visibility="gone"
Expand Down
3 changes: 0 additions & 3 deletions WordPress/src/main/res/values/key_strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@
<string name="pref_key_privacy_settings" translatable="false">wp_pref_privacy_settings</string>
<string name="pref_key_send_usage" translatable="false">wp_pref_send_usage_stats</string>
<string name="pref_key_send_crash" translatable="false">wp_pref_send_crash_stats</string>
<string name="pref_key_experimental_features_settings" translatable="false">wp_pref_experimental_features_settings</string>
<string name="pref_key_experimental_block_editor" translatable="false">MANUAL_FEATURE_CONFIGexperimental_block_editor</string>
<string name="pref_key_experimental_block_editor_theme_styles" translatable="false">MANUAL_FEATURE_CONFIGexperimental_block_editor_theme_styles</string>
<string name="pref_key_device_settings" translatable="false">wp_pref_device_settings</string>
<string name="pref_key_experimental_section" translatable="false">wp_pref_app_experimental_section</string>
<string name="pref_key_language" translatable="false">wp_pref_language</string>
Expand Down
Loading

0 comments on commit b7f44fb

Please sign in to comment.