Add new contact us flow.
parent
f1f505d41c
commit
f9de131017
|
@ -29,6 +29,7 @@ import androidx.fragment.app.FragmentManager;
|
|||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import org.thoughtcrime.securesms.help.HelpFragment;
|
||||
import org.thoughtcrime.securesms.preferences.AdvancedPreferenceFragment;
|
||||
import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment;
|
||||
import org.thoughtcrime.securesms.preferences.AppearancePreferenceFragment;
|
||||
|
@ -66,6 +67,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
|
|||
private static final String PREFERENCE_CATEGORY_CHATS = "preference_category_chats";
|
||||
private static final String PREFERENCE_CATEGORY_STORAGE = "preference_category_storage";
|
||||
private static final String PREFERENCE_CATEGORY_DEVICES = "preference_category_devices";
|
||||
private static final String PREFERENCE_CATEGORY_HELP = "preference_category_help";
|
||||
private static final String PREFERENCE_CATEGORY_ADVANCED = "preference_category_advanced";
|
||||
|
||||
private final DynamicTheme dynamicTheme = new DynamicTheme();
|
||||
|
@ -154,6 +156,8 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
|
|||
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_STORAGE));
|
||||
this.findPreference(PREFERENCE_CATEGORY_DEVICES)
|
||||
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_DEVICES));
|
||||
this.findPreference(PREFERENCE_CATEGORY_HELP)
|
||||
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_HELP));
|
||||
this.findPreference(PREFERENCE_CATEGORY_ADVANCED)
|
||||
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_ADVANCED));
|
||||
|
||||
|
@ -240,6 +244,9 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
|
|||
case PREFERENCE_CATEGORY_ADVANCED:
|
||||
fragment = new AdvancedPreferenceFragment();
|
||||
break;
|
||||
case PREFERENCE_CATEGORY_HELP:
|
||||
fragment = new HelpFragment();
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,279 @@
|
|||
package org.thoughtcrime.securesms.help;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.pm.LabeledIntent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.IdRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
import com.dd.CircularProgressButton;
|
||||
|
||||
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
|
||||
import org.thoughtcrime.securesms.BuildConfig;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiImageView;
|
||||
import org.thoughtcrime.securesms.util.IntentUtils;
|
||||
import org.thoughtcrime.securesms.util.text.AfterTextChanged;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class HelpFragment extends Fragment {
|
||||
|
||||
private EditText problem;
|
||||
private CheckBox includeDebugLogs;
|
||||
private View debugLogInfo;
|
||||
private View faq;
|
||||
private CircularProgressButton next;
|
||||
private View toaster;
|
||||
private List<EmojiImageView> emoji;
|
||||
private HelpViewModel helpViewModel;
|
||||
|
||||
@Override
|
||||
public @Nullable View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.help_fragment, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
initializeViewModels();
|
||||
initializeViews(view);
|
||||
initializeListeners();
|
||||
initializeObservers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
((ApplicationPreferencesActivity) getActivity()).getSupportActionBar().setTitle(R.string.preferences__help);
|
||||
|
||||
cancelSpinning(next);
|
||||
problem.setEnabled(true);
|
||||
}
|
||||
|
||||
private void initializeViewModels() {
|
||||
helpViewModel = ViewModelProviders.of(this).get(HelpViewModel.class);
|
||||
}
|
||||
|
||||
private void initializeViews(@NonNull View view) {
|
||||
problem = view.findViewById(R.id.help_fragment_problem);
|
||||
includeDebugLogs = view.findViewById(R.id.help_fragment_debug);
|
||||
debugLogInfo = view.findViewById(R.id.help_fragment_debug_info);
|
||||
faq = view.findViewById(R.id.help_fragment_faq);
|
||||
next = view.findViewById(R.id.help_fragment_next);
|
||||
toaster = view.findViewById(R.id.help_fragment_next_toaster);
|
||||
emoji = new ArrayList<>(Feeling.values().length);
|
||||
|
||||
for (Feeling feeling : Feeling.values()) {
|
||||
EmojiImageView emojiView = view.findViewById(feeling.getViewId());
|
||||
emojiView.setImageEmoji(feeling.getEmojiCode());
|
||||
emoji.add(view.findViewById(feeling.getViewId()));
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeListeners() {
|
||||
problem.addTextChangedListener(new AfterTextChanged(e -> helpViewModel.onProblemChanged(e.toString())));
|
||||
Stream.of(emoji).forEach(view -> view.setOnClickListener(this::handleEmojiClicked));
|
||||
faq.setOnClickListener(v -> launchFaq());
|
||||
debugLogInfo.setOnClickListener(v -> launchDebugLogInfo());
|
||||
next.setOnClickListener(v -> submitForm());
|
||||
toaster.setOnClickListener(v -> Toast.makeText(requireContext(), R.string.HelpFragment__please_be_as_descriptive_as_possible, Toast.LENGTH_LONG).show());
|
||||
}
|
||||
|
||||
private void initializeObservers() {
|
||||
//noinspection CodeBlock2Expr
|
||||
helpViewModel.isFormValid().observe(getViewLifecycleOwner(), isValid -> {
|
||||
next.setEnabled(isValid);
|
||||
toaster.setVisibility(isValid ? View.GONE : View.VISIBLE);
|
||||
});
|
||||
}
|
||||
|
||||
private void handleEmojiClicked(@NonNull View clicked) {
|
||||
if (clicked.isSelected()) {
|
||||
clicked.setSelected(false);
|
||||
} else {
|
||||
Stream.of(emoji).forEach(view -> view.setSelected(false));
|
||||
clicked.setSelected(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void launchFaq() {
|
||||
Uri data = Uri.parse(getString(R.string.HelpFragment__link__faq));
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, data);
|
||||
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void launchDebugLogInfo() {
|
||||
Uri data = Uri.parse(getString(R.string.HelpFragment__link__debug_info));
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, data);
|
||||
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void submitForm() {
|
||||
setSpinning(next);
|
||||
problem.setEnabled(false);
|
||||
|
||||
helpViewModel.onSubmitClicked(includeDebugLogs.isChecked()).observe(this, result -> {
|
||||
if (result.getDebugLogUrl().isPresent()) {
|
||||
submitFormWithDebugLog(result.getDebugLogUrl().get());
|
||||
} else if (result.isError()) {
|
||||
submitFormWithDebugLog(getString(R.string.HelpFragment__could_not_upload_logs));
|
||||
} else {
|
||||
submitFormWithDebugLog(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void submitFormWithDebugLog(@Nullable String debugLog) {
|
||||
Feeling feeling = Stream.of(emoji)
|
||||
.filter(View::isSelected)
|
||||
.map(view -> Feeling.getByViewId(view.getId()))
|
||||
.findFirst().orElse(null);
|
||||
|
||||
Spanned body = getEmailBody(debugLog, feeling);
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_SENDTO);
|
||||
intent.setData(Uri.parse("mailto:"));
|
||||
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{getString(R.string.RegistrationActivity_support_email)});
|
||||
intent.putExtra(Intent.EXTRA_SUBJECT, getEmailSubject());
|
||||
intent.putExtra(Intent.EXTRA_TEXT, body.toString());
|
||||
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private String getEmailSubject() {
|
||||
return getString(R.string.HelpFragment__signal_android_support_request);
|
||||
}
|
||||
|
||||
private Spanned getEmailBody(@Nullable String debugLog, @Nullable Feeling feeling) {
|
||||
SpannableStringBuilder builder = new SpannableStringBuilder();
|
||||
|
||||
builder.append(problem.getText().toString());
|
||||
builder.append("\n\n");
|
||||
builder.append("--- ");
|
||||
builder.append(getString(R.string.HelpFragment__support_info));
|
||||
builder.append(" ---\n");
|
||||
builder.append(getString(R.string.HelpFragment__subject));
|
||||
builder.append(" ");
|
||||
builder.append(getString(R.string.HelpFragment__signal_android_support_request));
|
||||
builder.append("\n");
|
||||
builder.append(getString(R.string.HelpFragment__device_info));
|
||||
builder.append(" ");
|
||||
builder.append(getDeviceInfo());
|
||||
builder.append("\n");
|
||||
builder.append(getString(R.string.HelpFragment__android_version));
|
||||
builder.append(" ");
|
||||
builder.append(getAndroidVersion());
|
||||
builder.append("\n");
|
||||
builder.append(getString(R.string.HelpFragment__signal_version));
|
||||
builder.append(" ");
|
||||
builder.append(getSignalVersion());
|
||||
builder.append("\n");
|
||||
builder.append(getString(R.string.HelpFragment__locale));
|
||||
builder.append(" ");
|
||||
builder.append(Locale.getDefault().toString());
|
||||
|
||||
if (debugLog != null) {
|
||||
builder.append("\n");
|
||||
builder.append(getString(R.string.HelpFragment__debug_log));
|
||||
builder.append(" ");
|
||||
builder.append(debugLog);
|
||||
}
|
||||
|
||||
if (feeling != null) {
|
||||
builder.append("\n\n");
|
||||
builder.append(feeling.getEmojiCode());
|
||||
builder.append("\n");
|
||||
builder.append(getString(feeling.getStringId()));
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static CharSequence getDeviceInfo() {
|
||||
return String.format("%s %s (%s)", Build.MANUFACTURER, Build.MODEL, Build.PRODUCT);
|
||||
}
|
||||
|
||||
private static CharSequence getAndroidVersion() {
|
||||
return String.format("%s (%s, %s)", Build.VERSION.RELEASE, Build.VERSION.INCREMENTAL, Build.DISPLAY);
|
||||
}
|
||||
|
||||
private static CharSequence getSignalVersion() {
|
||||
return BuildConfig.VERSION_NAME;
|
||||
}
|
||||
|
||||
private static void setSpinning(@Nullable CircularProgressButton button) {
|
||||
if (button != null) {
|
||||
button.setClickable(false);
|
||||
button.setIndeterminateProgressMode(true);
|
||||
button.setProgress(50);
|
||||
}
|
||||
}
|
||||
|
||||
private static void cancelSpinning(@Nullable CircularProgressButton button) {
|
||||
if (button != null) {
|
||||
button.setProgress(0);
|
||||
button.setIndeterminateProgressMode(false);
|
||||
button.setClickable(true);
|
||||
}
|
||||
}
|
||||
|
||||
private enum Feeling {
|
||||
ECSTATIC(R.id.help_fragment_emoji_5, R.string.HelpFragment__emoji_5, "\ud83d\ude00"),
|
||||
HAPPY(R.id.help_fragment_emoji_4, R.string.HelpFragment__emoji_4, "\ud83d\ude42"),
|
||||
AMBIVALENT(R.id.help_fragment_emoji_3, R.string.HelpFragment__emoji_3, "\ud83d\ude10"),
|
||||
UNHAPPY(R.id.help_fragment_emoji_2, R.string.HelpFragment__emoji_2, "\ud83d\ude41"),
|
||||
ANGRY(R.id.help_fragment_emoji_1, R.string.HelpFragment__emoji_1, "\ud83d\ude20");
|
||||
|
||||
private final @IdRes int viewId;
|
||||
private final @StringRes int stringId;
|
||||
private final CharSequence emojiCode;
|
||||
|
||||
Feeling(@IdRes int viewId, @StringRes int stringId, @NonNull CharSequence emojiCode) {
|
||||
this.viewId = viewId;
|
||||
this.stringId = stringId;
|
||||
this.emojiCode = emojiCode;
|
||||
}
|
||||
|
||||
public @IdRes int getViewId() {
|
||||
return viewId;
|
||||
}
|
||||
|
||||
public @StringRes int getStringId() {
|
||||
return stringId;
|
||||
}
|
||||
|
||||
public @NonNull CharSequence getEmojiCode() {
|
||||
return emojiCode;
|
||||
}
|
||||
|
||||
static Feeling getByViewId(@IdRes int viewId) {
|
||||
for (Feeling feeling : values()) {
|
||||
if (feeling.viewId == viewId) {
|
||||
return feeling;
|
||||
}
|
||||
}
|
||||
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package org.thoughtcrime.securesms.help;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.Transformations;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import org.thoughtcrime.securesms.logsubmit.LogLine;
|
||||
import org.thoughtcrime.securesms.logsubmit.SubmitDebugLogRepository;
|
||||
import org.thoughtcrime.securesms.util.livedata.LiveDataPair;
|
||||
import org.whispersystems.libsignal.util.Pair;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class HelpViewModel extends ViewModel {
|
||||
|
||||
private static final int MINIMUM_PROBLEM_CHARS = 10;
|
||||
|
||||
private MutableLiveData<Boolean> problemMeetsLengthRequirements = new MutableLiveData<>();
|
||||
private MutableLiveData<Boolean> hasLines = new MutableLiveData<>(false);
|
||||
private LiveData<Boolean> isFormValid = Transformations.map(new LiveDataPair<>(problemMeetsLengthRequirements, hasLines), this::transformValidationData);
|
||||
|
||||
private final SubmitDebugLogRepository submitDebugLogRepository;
|
||||
|
||||
private List<LogLine> logLines;
|
||||
|
||||
public HelpViewModel() {
|
||||
submitDebugLogRepository = new SubmitDebugLogRepository();
|
||||
|
||||
submitDebugLogRepository.getLogLines(lines -> {
|
||||
logLines = lines;
|
||||
hasLines.postValue(true);
|
||||
});
|
||||
}
|
||||
|
||||
LiveData<Boolean> isFormValid() {
|
||||
return isFormValid;
|
||||
}
|
||||
|
||||
void onProblemChanged(@NonNull String problem) {
|
||||
problemMeetsLengthRequirements.setValue(problem.length() >= MINIMUM_PROBLEM_CHARS);
|
||||
}
|
||||
|
||||
LiveData<SubmitResult> onSubmitClicked(boolean includeDebugLogs) {
|
||||
MutableLiveData<SubmitResult> resultLiveData = new MutableLiveData<>();
|
||||
|
||||
if (includeDebugLogs) {
|
||||
submitDebugLogRepository.submitLog(logLines, result -> resultLiveData.postValue(new SubmitResult(result, result.isPresent())));
|
||||
} else {
|
||||
resultLiveData.postValue(new SubmitResult(Optional.absent(), false));
|
||||
}
|
||||
|
||||
return resultLiveData;
|
||||
}
|
||||
|
||||
private boolean transformValidationData(Pair<Boolean, Boolean> validationData) {
|
||||
return validationData.first() == Boolean.TRUE && validationData.second() == Boolean.TRUE;
|
||||
}
|
||||
|
||||
static class SubmitResult {
|
||||
private final Optional<String> debugLogUrl;
|
||||
private final boolean isError;
|
||||
|
||||
private SubmitResult(@NonNull Optional<String> debugLogUrl, boolean isError) {
|
||||
this.debugLogUrl = debugLogUrl;
|
||||
this.isError = isError;
|
||||
}
|
||||
|
||||
@NonNull Optional<String> getDebugLogUrl() {
|
||||
return debugLogUrl;
|
||||
}
|
||||
|
||||
boolean isError() {
|
||||
return isError;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ import com.annimon.stream.Stream;
|
|||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
interface LogLine {
|
||||
public interface LogLine {
|
||||
|
||||
long getId();
|
||||
@NonNull String getText();
|
||||
|
|
|
@ -41,7 +41,7 @@ import okhttp3.ResponseBody;
|
|||
* - Create a new {@link LogSection}.
|
||||
* - Add it to {@link #SECTIONS}. The order of the list is the order the sections are displayed.
|
||||
*/
|
||||
class SubmitDebugLogRepository {
|
||||
public class SubmitDebugLogRepository {
|
||||
|
||||
private static final String TAG = Log.tag(SubmitDebugLogRepository.class);
|
||||
|
||||
|
@ -67,16 +67,16 @@ class SubmitDebugLogRepository {
|
|||
private final Context context;
|
||||
private final ExecutorService executor;
|
||||
|
||||
SubmitDebugLogRepository() {
|
||||
public SubmitDebugLogRepository() {
|
||||
this.context = ApplicationDependencies.getApplication();
|
||||
this.executor = SignalExecutors.SERIAL;
|
||||
}
|
||||
|
||||
void getLogLines(@NonNull Callback<List<LogLine>> callback) {
|
||||
public void getLogLines(@NonNull Callback<List<LogLine>> callback) {
|
||||
executor.execute(() -> callback.onResult(getLogLinesInternal()));
|
||||
}
|
||||
|
||||
void submitLog(@NonNull List<LogLine> lines, Callback<Optional<String>> callback) {
|
||||
public void submitLog(@NonNull List<LogLine> lines, Callback<Optional<String>> callback) {
|
||||
SignalExecutors.UNBOUNDED.execute(() -> callback.onResult(submitLogInternal(lines)));
|
||||
}
|
||||
|
||||
|
@ -209,7 +209,7 @@ class SubmitDebugLogRepository {
|
|||
return out.toString();
|
||||
}
|
||||
|
||||
interface Callback<E> {
|
||||
public interface Callback<E> {
|
||||
void onResult(E result);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.LabeledIntent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -15,4 +19,21 @@ public class IntentUtils {
|
|||
return resolveInfoList != null && resolveInfoList.size() > 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* From: <a href="https://stackoverflow.com/a/12328282">https://stackoverflow.com/a/12328282</a>
|
||||
*/
|
||||
public static @Nullable LabeledIntent getLabelintent(@NonNull Context context, @NonNull Intent origIntent, int name, int drawable) {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
ComponentName launchName = origIntent.resolveActivity(pm);
|
||||
|
||||
if (launchName != null) {
|
||||
Intent resolved = new Intent();
|
||||
resolved.setComponent(launchName);
|
||||
resolved.setData(origIntent.getData());
|
||||
|
||||
return new LabeledIntent(resolved, context.getPackageName(), name, drawable);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,12 +1,19 @@
|
|||
package org.thoughtcrime.securesms.util;
|
||||
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.style.AbsoluteSizeSpan;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.text.style.ImageSpan;
|
||||
import android.text.style.RelativeSizeSpan;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.text.style.URLSpan;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class SpanUtil {
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@color/signal_primary" android:state_enabled="true" />
|
||||
<item android:color="@color/core_grey_75" />
|
||||
</selector>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@color/signal_primary" android:state_enabled="true" />
|
||||
<item android:color="@color/core_grey_25" />
|
||||
</selector>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="4dp" />
|
||||
<solid android:color="@color/core_grey_25" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_selected="true">
|
||||
<shape android:shape="oval">
|
||||
<solid android:color="@color/signal_primary" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape android:shape="oval">
|
||||
<solid android:color="@color/core_grey_75" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_selected="true">
|
||||
<shape android:shape="oval">
|
||||
<solid android:color="@color/signal_primary" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape android:shape="oval">
|
||||
<solid android:color="@color/core_grey_05" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<corners
|
||||
android:topLeftRadius="4dp"
|
||||
android:topRightRadius="4dp" />
|
||||
<padding android:bottom="1dp" />
|
||||
<solid android:color="@color/core_grey_90" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<corners
|
||||
android:topLeftRadius="4dp"
|
||||
android:topRightRadius="4dp" />
|
||||
<solid android:color="@color/core_grey_75" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<corners
|
||||
android:topLeftRadius="4dp"
|
||||
android:topRightRadius="4dp" />
|
||||
<padding android:bottom="1dp" />
|
||||
<solid android:color="@color/core_grey_25" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<corners
|
||||
android:topLeftRadius="4dp"
|
||||
android:topRightRadius="4dp" />
|
||||
<solid android:color="@color/core_grey_05" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,5 @@
|
|||
<vector android:autoMirrored="true" android:height="24dp"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="?icon_tint" android:pathData="M12,1A11,11 0,1 0,23 12,11 11,0 0,0 12,1ZM12.88,18.13a1.18,1.18 0,0 1,-0.88 0.37,1.24 1.24,0 0,1 -0.88,-2.13A1.16,1.16 0,0 1,12 16a1.18,1.18 0,0 1,0.88 0.37,1.23 1.23,0 0,1 0,1.76ZM14.3,11.73a2.67,2.67 0,0 0,-1.45 2.65v0.11L11.12,14.49v-0.11c0,-2.24 0.68,-2.93 1.8,-3.62a2.33,2.33 0,0 0,1.28 -2A1.74,1.74 0,0 0,12.34 7a1.83,1.83 0,0 0,-1.91 1.84L8.59,8.84a3.44,3.44 0,0 1,3.75 -3.36c2.22,0 3.7,1.33 3.7,3.29A3.24,3.24 0,0 1,14.3 11.73Z"/>
|
||||
</vector>
|
|
@ -0,0 +1,221 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/help_fragment_scroller"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:fadingEdge="vertical"
|
||||
android:fillViewport="true"
|
||||
android:requiresFadingEdge="vertical"
|
||||
app:layout_constraintBottom_toTopOf="@id/help_fragment_faq"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/help_fragment_contact"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@string/HelpFragment__contact_us"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
|
||||
android:textColor="?title_text_color_secondary"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/help_fragment_problem"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="?attr/help_problem_background"
|
||||
android:gravity="top"
|
||||
android:hint="@string/HelpFragment__tell_us_whats_going_on"
|
||||
android:inputType="textMultiLine"
|
||||
android:minHeight="144dp"
|
||||
android:padding="16dp"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/help_fragment_contact" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/help_fragment_debug"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:checked="true"
|
||||
android:text="@string/HelpFragment__include_debug_log"
|
||||
android:textColor="?title_text_color_secondary"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/help_fragment_debug_info"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/help_fragment_debug_info" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/help_fragment_debug_info"
|
||||
style="@style/Button.Borderless"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="2dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:text="@string/HelpFragment__whats_this"
|
||||
android:textAllCaps="false"
|
||||
android:textAppearance="@style/Signal.Text.Body"
|
||||
android:textColor="@color/signal_primary"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintStart_toEndOf="@id/help_fragment_debug"
|
||||
app:layout_constraintTop_toBottomOf="@id/help_fragment_problem" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/help_fragment_feelings"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@string/HelpFragment__how_do_you_feel"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
|
||||
android:textColor="?title_text_color_secondary"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/help_fragment_debug_info" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiImageView
|
||||
android:id="@+id/help_fragment_emoji_5"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="?attr/help_emoji_radio_background"
|
||||
android:button="@null"
|
||||
android:gravity="center"
|
||||
android:scaleType="centerInside"
|
||||
app:layout_constraintEnd_toStartOf="@id/help_fragment_emoji_4"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/help_fragment_feelings" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiImageView
|
||||
android:id="@+id/help_fragment_emoji_4"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="?attr/help_emoji_radio_background"
|
||||
android:button="@null"
|
||||
android:gravity="center"
|
||||
android:scaleType="centerInside"
|
||||
app:layout_constraintBottom_toBottomOf="@id/help_fragment_emoji_5"
|
||||
app:layout_constraintEnd_toEndOf="@id/help_fragment_emoji_3"
|
||||
app:layout_constraintStart_toEndOf="@id/help_fragment_emoji_5"
|
||||
app:layout_constraintTop_toTopOf="@id/help_fragment_emoji_5" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiImageView
|
||||
android:id="@+id/help_fragment_emoji_3"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="?attr/help_emoji_radio_background"
|
||||
android:button="@null"
|
||||
android:gravity="center"
|
||||
android:scaleType="centerInside"
|
||||
app:layout_constraintBottom_toBottomOf="@id/help_fragment_emoji_4"
|
||||
app:layout_constraintEnd_toStartOf="@id/help_fragment_emoji_2"
|
||||
app:layout_constraintStart_toEndOf="@id/help_fragment_emoji_4"
|
||||
app:layout_constraintTop_toTopOf="@id/help_fragment_emoji_4" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiImageView
|
||||
android:id="@+id/help_fragment_emoji_2"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="?attr/help_emoji_radio_background"
|
||||
android:button="@null"
|
||||
android:gravity="center"
|
||||
android:scaleType="centerInside"
|
||||
app:layout_constraintBottom_toBottomOf="@id/help_fragment_emoji_3"
|
||||
app:layout_constraintEnd_toStartOf="@id/help_fragment_emoji_1"
|
||||
app:layout_constraintStart_toEndOf="@id/help_fragment_emoji_3"
|
||||
app:layout_constraintTop_toTopOf="@id/help_fragment_emoji_3" />
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiImageView
|
||||
android:id="@+id/help_fragment_emoji_1"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="?attr/help_emoji_radio_background"
|
||||
android:button="@null"
|
||||
android:gravity="center"
|
||||
android:scaleType="centerInside"
|
||||
app:layout_constraintBottom_toBottomOf="@id/help_fragment_emoji_2"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/help_fragment_emoji_2"
|
||||
app:layout_constraintTop_toTopOf="@id/help_fragment_emoji_2" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
<Button
|
||||
android:id="@+id/help_fragment_faq"
|
||||
style="@style/Button.Borderless"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="60dp"
|
||||
android:gravity="start|center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:text="@string/HelpFragment__have_you_read_our_faq_yet"
|
||||
android:textAllCaps="false"
|
||||
android:textAppearance="@style/Signal.Text.Body"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/help_fragment_next"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<com.dd.CircularProgressButton
|
||||
android:id="@+id/help_fragment_next"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:enabled="false"
|
||||
android:textColor="@color/white"
|
||||
app:cpb_colorIndicator="@color/white"
|
||||
app:cpb_colorProgress="@color/signal_primary"
|
||||
app:cpb_cornerRadius="4dp"
|
||||
app:cpb_selectorIdle="?attr/help_next_background"
|
||||
app:cpb_textIdle="@string/HelpFragment__next"
|
||||
app:layout_constraintBottom_toBottomOf="@id/help_fragment_faq"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/help_fragment_faq"
|
||||
app:layout_constraintTop_toTopOf="@id/help_fragment_faq" />
|
||||
|
||||
<View
|
||||
android:id="@+id/help_fragment_next_toaster"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:enabled="true"
|
||||
app:layout_constraintBottom_toBottomOf="@id/help_fragment_faq"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/help_fragment_faq"
|
||||
app:layout_constraintTop_toTopOf="@id/help_fragment_faq" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -202,6 +202,11 @@
|
|||
<attr name="linked_devices_icon" format="reference" />
|
||||
<attr name="advanced_icon" format="reference" />
|
||||
<attr name="safety_number_icon" format="reference" />
|
||||
<attr name="help_icon" format="reference" />
|
||||
|
||||
<attr name="help_problem_background" format="reference" />
|
||||
<attr name="help_next_background" format="reference" />
|
||||
<attr name="help_emoji_radio_background" format="reference" />
|
||||
|
||||
<attr name="message_request_dialog_button_background" format="reference" />
|
||||
<attr name="message_request_text_color_primary" format="color" />
|
||||
|
|
|
@ -1436,6 +1436,35 @@
|
|||
<string name="MessageRequestsMegaphone__add_name">Add name</string>
|
||||
<string name="MessageRequestsMegaphone__you_can_now_choose_whether_to_accept">You can now choose whether to accept a new conversation. You’ll see options to \"Accept,\" \"Delete,\" or \"Block.\"</string>
|
||||
|
||||
<!-- HelpFragment -->
|
||||
<string name="HelpFragment__help">Help</string>
|
||||
<string name="HelpFragment__have_you_read_our_faq_yet">Have you read our FAQ yet?</string>
|
||||
<string name="HelpFragment__next">Next</string>
|
||||
<string name="HelpFragment__contact_us">Contact us</string>
|
||||
<string name="HelpFragment__tell_us_whats_going_on">Tell us what\'s going on</string>
|
||||
<string name="HelpFragment__include_debug_log">Include debug log.</string>
|
||||
<string name="HelpFragment__whats_this">What\'s this?</string>
|
||||
<string name="HelpFragment__how_do_you_feel">How do you feel? (Optional)</string>
|
||||
<string name="HelpFragment__emoji_5" translatable="false">emoji_5</string>
|
||||
<string name="HelpFragment__emoji_4" translatable="false">emoji_4</string>
|
||||
<string name="HelpFragment__emoji_3" translatable="false">emoji_3</string>
|
||||
<string name="HelpFragment__emoji_2" translatable="false">emoji_2</string>
|
||||
<string name="HelpFragment__emoji_1" translatable="false">emoji_1</string>
|
||||
<string name="HelpFragment__link__debug_info" translatable="false">https://support.signal.org/hc/en-us/articles/360007318591-Debug-Logs-and-Crash-Reports</string>
|
||||
<string name="HelpFragment__link__faq" translatable="false">https://support.signal.org</string>
|
||||
<string name="HelpFragment__support_info">Support Info</string>
|
||||
<string name="HelpFragment__subject">Subject:</string>
|
||||
<string name="HelpFragment__signal_android_support_request">Signal Android Support Request</string>
|
||||
<string name="HelpFragment__device_info">Device info:</string>
|
||||
<string name="HelpFragment__android_version">Android version:</string>
|
||||
<string name="HelpFragment__signal_version">Signal version:</string>
|
||||
<string name="HelpFragment__locale">Locale:</string>
|
||||
<string name="HelpFragment__debug_log">Debug Log:</string>
|
||||
<string name="HelpFragment__na">n/a</string>
|
||||
<string name="HelpFragment__could_not_upload_logs">Could not upload logs</string>
|
||||
<string name="HelpFragment__signal_support">Signal Support</string>
|
||||
<string name="HelpFragment__please_be_as_descriptive_as_possible">Please be as descriptive as possible to help us understand the issue.</string>
|
||||
|
||||
<!-- arrays.xml -->
|
||||
<string name="arrays__import_export">Import</string>
|
||||
<string name="arrays__use_default">Use default</string>
|
||||
|
@ -1522,6 +1551,7 @@
|
|||
<string name="preferences__fast">Fast</string>
|
||||
<string name="preferences__normal">Normal</string>
|
||||
<string name="preferences__slow">Slow</string>
|
||||
<string name="preferences__help">Help</string>
|
||||
<string name="preferences__advanced">Advanced</string>
|
||||
<string name="preferences__privacy">Privacy</string>
|
||||
<string name="preferences__mms_user_agent">MMS User Agent</string>
|
||||
|
|
|
@ -221,6 +221,10 @@
|
|||
|
||||
<item name="conversation_group_member_name">#99000000</item>
|
||||
|
||||
<item name="help_emoji_radio_background">@drawable/help_fragment_emoji_radio_background_light</item>
|
||||
<item name="help_next_background">@color/help_fragment_next_light</item>
|
||||
<item name="help_problem_background">@drawable/help_fragment_problem_background_light</item>
|
||||
|
||||
<item name="conversation_background">@color/core_white</item>
|
||||
<item name="conversation_editor_background">#22000000</item>
|
||||
<item name="conversation_editor_text_color">#ff111111</item>
|
||||
|
@ -364,6 +368,7 @@
|
|||
<item name="linked_devices_icon">@drawable/ic_linked_devices_24</item>
|
||||
<item name="advanced_icon">@drawable/ic_advanced_24</item>
|
||||
<item name="safety_number_icon">@drawable/ic_safety_number_outline_24</item>
|
||||
<item name="help_icon">@drawable/ic_help_outline_24</item>
|
||||
<item name="message_request_dialog_button_background">@drawable/message_request_button_background_light</item>
|
||||
<item name="message_request_text_color_primary">@color/core_grey_90</item>
|
||||
<item name="message_request_text_color_secondary">@color/core_grey_60</item>
|
||||
|
@ -531,6 +536,10 @@
|
|||
<item name="fab_color">@color/textsecure_primary_dark</item>
|
||||
<item name="lower_right_divet">@drawable/divet_lower_right_light</item>
|
||||
|
||||
<item name="help_emoji_radio_background">@drawable/help_fragment_emoji_radio_background_dark</item>
|
||||
<item name="help_next_background">@color/help_fragment_next_dark</item>
|
||||
<item name="help_problem_background">@drawable/help_fragment_problem_background_dark</item>
|
||||
|
||||
<item name="conversation_background">@color/core_grey_95</item>
|
||||
<item name="conversation_editor_background">#22ffffff</item>
|
||||
<item name="conversation_editor_text_color">#ffeeeeee</item>
|
||||
|
@ -631,6 +640,7 @@
|
|||
<item name="linked_devices_icon">@drawable/ic_linked_devices_24</item>
|
||||
<item name="advanced_icon">@drawable/ic_advanced_24</item>
|
||||
<item name="safety_number_icon">@drawable/ic_safety_number_solid_24</item>
|
||||
<item name="help_icon">@drawable/ic_help_solid_24</item>
|
||||
<item name="message_request_dialog_button_background">@drawable/message_request_button_background_dark</item>
|
||||
<item name="message_request_text_color_primary">@color/core_grey_05</item>
|
||||
<item name="message_request_text_color_secondary">@color/core_grey_25</item>
|
||||
|
|
|
@ -33,6 +33,10 @@
|
|||
android:title="@string/preferences__linked_devices"
|
||||
android:icon="?attr/linked_devices_icon"/>
|
||||
|
||||
<Preference android:key="preference_category_help"
|
||||
android:title="@string/preferences__help"
|
||||
android:icon="?attr/help_icon" />
|
||||
|
||||
<Preference android:key="preference_category_advanced"
|
||||
android:title="@string/preferences__advanced"
|
||||
android:icon="?attr/advanced_icon"/>
|
||||
|
|
Loading…
Reference in New Issue