Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Create internal settings screen #452

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions samples/java_layout/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@
android:name=".ui.common.SimpleFragmentActivity"
android:exported="false"
android:label="@string/label_simple_fragment_activity" />
<activity
android:name=".ui.settings.InternalSettingsActivity"
android:exported="false"
android:label="@string/label_internal_settings_activity" />

<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import io.customer.android.sample.java_layout.ui.common.SimpleFragmentActivity;
import io.customer.android.sample.java_layout.ui.core.BaseActivity;
import io.customer.android.sample.java_layout.ui.login.LoginActivity;
import io.customer.android.sample.java_layout.ui.settings.InternalSettingsActivity;
import io.customer.android.sample.java_layout.ui.settings.SettingsActivity;
import io.customer.android.sample.java_layout.ui.user.AuthViewModel;
import io.customer.android.sample.java_layout.utils.Randoms;
Expand Down Expand Up @@ -109,6 +110,10 @@ private void setupViews() {
binding.settingsButton.setOnClickListener(view -> {
startActivity(new Intent(DashboardActivity.this, SettingsActivity.class));
});
binding.settingsButton.setOnLongClickListener(view -> {
startActivity(new Intent(DashboardActivity.this, InternalSettingsActivity.class));
return true;
});
binding.sendRandomEventButton.setOnClickListener(view -> {
sendRandomEvent();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import io.customer.android.sample.java_layout.databinding.ActivityLoginBinding;
import io.customer.android.sample.java_layout.ui.core.BaseActivity;
import io.customer.android.sample.java_layout.ui.dashboard.DashboardActivity;
import io.customer.android.sample.java_layout.ui.settings.InternalSettingsActivity;
import io.customer.android.sample.java_layout.ui.settings.SettingsActivity;
import io.customer.android.sample.java_layout.ui.user.AuthViewModel;
import io.customer.android.sample.java_layout.utils.Randoms;
Expand Down Expand Up @@ -57,6 +58,10 @@ private void setupViews() {
binding.settingsButton.setOnClickListener(view -> {
startActivity(new Intent(LoginActivity.this, SettingsActivity.class));
});
binding.settingsButton.setOnLongClickListener(view -> {
startActivity(new Intent(LoginActivity.this, InternalSettingsActivity.class));
return true;
});
binding.loginButton.setOnClickListener(view -> {
boolean isFormValid = true;
String displayName = ViewUtils.getText(binding.displayNameTextInput);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package io.customer.android.sample.java_layout.ui.settings;

import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.text.TextUtils;
import android.widget.Toast;

import androidx.annotation.NonNull;

import io.customer.android.sample.java_layout.R;
import io.customer.android.sample.java_layout.data.model.CustomerIOSDKConfig;
import io.customer.android.sample.java_layout.databinding.ActivityInternalSettingsBinding;
import io.customer.android.sample.java_layout.ui.core.BaseActivity;
import io.customer.android.sample.java_layout.ui.dashboard.DashboardActivity;
import io.customer.android.sample.java_layout.utils.OSUtils;
import io.customer.android.sample.java_layout.utils.ViewUtils;
import io.customer.sdk.CustomerIO;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;

public class InternalSettingsActivity extends BaseActivity<ActivityInternalSettingsBinding> {

private SettingsViewModel settingsViewModel;
private final CompositeDisposable disposables = new CompositeDisposable();

@Override
protected ActivityInternalSettingsBinding inflateViewBinding() {
return ActivityInternalSettingsBinding.inflate(getLayoutInflater());
}

@Override
protected void injectDependencies() {
settingsViewModel = viewModelProvider.get(SettingsViewModel.class);
}

@Override
protected void setupContent() {
setUpObservers();
setUpActions();
}

@Override
protected void onDestroy() {
super.onDestroy();
disposables.dispose();
}

private void setUpObservers() {
binding.settingsDeviceTokenLabel.setText(CustomerIO.instance().getRegisteredDeviceToken());
settingsViewModel.getSDKConfigObservable().observe(this, config -> {
binding.progressIndicator.hide();
updateUiWithConfig(config);
});
}

private void updateUiWithConfig(@NonNull CustomerIOSDKConfig config) {
ViewUtils.setTextWithSelectionIfFocused(binding.settingsApiHostLabel, config.getApiHost());
ViewUtils.setTextWithSelectionIfFocused(binding.settingsCdnHostLabel, config.getCdnHost());
}

private void setUpActions() {
binding.topAppBar.setNavigationOnClickListener(view -> {
// For better user experience, navigate to launcher activity on navigate up button
if (isTaskRoot()) {
startActivity(new Intent(InternalSettingsActivity.this, DashboardActivity.class));
}
onBackPressed();
});
binding.settingsDeviceTokenLayout.setEndIconOnClickListener(view -> {
String deviceToken = ViewUtils.getTextTrimmed(binding.settingsDeviceTokenLabel);
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText(getString(R.string.device_token), deviceToken);
clipboard.setPrimaryClip(clip);
Toast.makeText(this, R.string.token_copied, Toast.LENGTH_SHORT).show();
});
binding.settingsSaveButton.setOnClickListener(v -> saveSettings());
binding.settingsRestoreDefaultsButton.setOnClickListener(v -> updateUiWithConfig(CustomerIOSDKConfig.getDefaultConfigurations()));
ViewUtils.clearErrorWhenTextedEntered(binding.settingsApiHostLabel, binding.settingsApiHostLayout);
ViewUtils.clearErrorWhenTextedEntered(binding.settingsCdnHostLabel, binding.settingsCdnHostLayout);
}

private void saveSettings() {
CustomerIOSDKConfig currentSettings = settingsViewModel.getSDKConfigObservable().getValue();
if (currentSettings == null) {
Toast.makeText(this, "Error! Cannot save settings!", Toast.LENGTH_SHORT).show();
return;
}

String apiHost = ViewUtils.getTextTrimmed(binding.settingsApiHostLabel);
String cdnHost = ViewUtils.getTextTrimmed(binding.settingsCdnHostLabel);
if (!validateUiInputs(apiHost, cdnHost)) {
return;
}

CustomerIOSDKConfig newSettings = createNewSettings(currentSettings, apiHost, cdnHost);
settingsViewModel.updateConfigurations(newSettings);

binding.progressIndicator.show();
Disposable disposable = settingsViewModel
.updateConfigurations(newSettings)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(preferences -> {
binding.progressIndicator.hide();
Toast.makeText(this, R.string.settings_save_msg, Toast.LENGTH_SHORT).show();
OSUtils.restartApp();
});
disposables.add(disposable);
}

private boolean validateUiInputs(@NonNull String apiHost, @NonNull String cdnHost) {
boolean valid = true;
if (isHostURLInvalid(apiHost)) {
valid = false;
ViewUtils.setError(binding.settingsApiHostLayout, getString(R.string.error_url_input_field));
}
if (isHostURLInvalid(cdnHost)) {
valid = false;
ViewUtils.setError(binding.settingsCdnHostLayout, getString(R.string.error_url_input_field));
}
return valid;
}

@NonNull
private static CustomerIOSDKConfig createNewSettings(CustomerIOSDKConfig currentSettings, String apiHost, String cdnHost) {
return new CustomerIOSDKConfig(
currentSettings.getCdpApiKey(),
currentSettings.getSiteId(),
apiHost,
cdnHost,
currentSettings.getFlushInterval(),
currentSettings.getFlushAt(),
currentSettings.isScreenTrackingEnabled(),
currentSettings.isDeviceAttributesTrackingEnabled(),
currentSettings.isDebugModeEnabled()
);
}

private boolean isHostURLInvalid(String url) {
// Empty text is not considered valid
if (TextUtils.isEmpty(url)) {
return true;
}

try {
Uri uri = Uri.parse(url);
// Since SDK does not support custom schemes, we manually append http:// to the URL
// So the URL is considered invalid if it ends with a slash, contains a scheme, query or fragment
return url.endsWith("/")
|| !TextUtils.isEmpty(uri.getScheme())
|| !TextUtils.isEmpty(uri.getQuery())
|| !TextUtils.isEmpty(uri.getFragment());
} catch (Exception ex) {
//noinspection CallToPrintStackTrace
ex.printStackTrace();
return true;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.customer.android.sample.java_layout.utils;

import android.text.Editable;
import android.text.TextWatcher;

/**
* Convenience class to prevent classes from having to override all methods and only override the
* ones desired.
*/
public class DefaultTextWatcher implements TextWatcher {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
/* NO OP */
}

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
/* NO OP */
}

@Override
public void afterTextChanged(Editable s) {
/* NO OP */
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import androidx.appcompat.widget.Toolbar;

import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;

import java.util.Locale;
Expand Down Expand Up @@ -70,4 +71,15 @@ public static MaterialAlertDialogBuilder createAlertDialog(@NonNull Activity act
.setCancelable(true)
.setPositiveButton(android.R.string.ok, null);
}

public static void clearErrorWhenTextedEntered(@NonNull TextInputEditText editText,
@NonNull TextInputLayout textInputLayout) {
editText.addTextChangedListener(new DefaultTextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
textInputLayout.setError(null);
textInputLayout.setErrorEnabled(false);
}
});
}
}
Loading
Loading