From 6a18b9b7459d68bbe2d7af90e610838359f12057 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Tue, 9 Mar 2021 15:22:11 +0100 Subject: [PATCH] add ability to enter own phone number when enabling address book sync Signed-off-by: Marcel Hibbe --- CHANGELOG.md | 1 + .../java/com/nextcloud/talk/api/NcApi.java | 10 + .../talk/controllers/SettingsController.java | 201 +++++++++++++++--- .../talk/jobs/ContactAddressBookWorker.kt | 4 +- .../json/userprofile/UserProfileData.java | 3 + .../com/nextcloud/talk/utils/ApiUtils.java | 4 + app/src/main/res/values/strings.xml | 7 + 7 files changed, 199 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2aac0a8b5..115de5c1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Types of changes can be: Added/Changed/Deprecated/Removed/Fixed/Security ## [Unreleased] ### Added +- add ability to enter own phone number when address book sync is enabled ### Fixed - show links for deck-cards diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApi.java b/app/src/main/java/com/nextcloud/talk/api/NcApi.java index cc839685f..9ee3c6661 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -227,6 +227,16 @@ public interface NcApi { @GET Observable getUserProfile(@Header("Authorization") String authorization, @Url String url); + + @GET + Observable getUserData(@Header("Authorization") String authorization, @Url String url); + + @FormUrlEncoded + @PUT + Observable setUserData(@Header("Authorization") String authorization, @Url String url, + @Field("key") String key, @Field("value") String value); + + /* Server URL is: baseUrl + /status.php */ diff --git a/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.java b/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.java index 5151e16c8..b94f54691 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/SettingsController.java @@ -24,20 +24,37 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.app.KeyguardManager; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; +import android.content.res.ColorStateList; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.security.KeyChain; +import android.text.Editable; +import android.text.InputType; import android.text.TextUtils; +import android.text.TextWatcher; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; +import android.widget.Button; import android.widget.Checkable; +import android.widget.EditText; +import android.widget.LinearLayout; import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.core.view.ViewCompat; +import androidx.emoji.widget.EmojiTextView; +import androidx.work.OneTimeWorkRequest; +import androidx.work.WorkManager; import com.bluelinelabs.conductor.Controller; import com.bluelinelabs.conductor.RouterTransaction; @@ -48,6 +65,7 @@ import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.drawee.interfaces.DraweeController; import com.facebook.drawee.view.SimpleDraweeView; import com.google.android.material.snackbar.Snackbar; +import com.google.android.material.textfield.TextInputLayout; import com.nextcloud.talk.BuildConfig; import com.nextcloud.talk.R; import com.nextcloud.talk.api.NcApi; @@ -58,6 +76,7 @@ import com.nextcloud.talk.jobs.ContactAddressBookWorker; import com.nextcloud.talk.models.RingtoneSettings; import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.json.generic.GenericOverall; +import com.nextcloud.talk.models.json.userprofile.UserProfileOverall; import com.nextcloud.talk.utils.ApiUtils; import com.nextcloud.talk.utils.DisplayUtils; import com.nextcloud.talk.utils.DoNotDisturbUtils; @@ -90,12 +109,6 @@ import java.util.Objects; import javax.inject.Inject; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.view.ViewCompat; -import androidx.emoji.widget.EmojiTextView; -import androidx.work.OneTimeWorkRequest; -import androidx.work.WorkManager; import autodagger.AutoInjector; import butterknife.BindView; import butterknife.OnClick; @@ -160,7 +173,7 @@ public class SettingsController extends BaseController { @BindView(R.id.settings_screen_lock_timeout) MaterialChoicePreference screenLockTimeoutChoicePreference; @BindView(R.id.settings_phone_book_integration) - MaterialSwitchPreference phoneBookIntegretationPreference; + MaterialSwitchPreference phoneBookIntegrationPreference; @BindView(R.id.settings_read_privacy) MaterialSwitchPreference readPrivacyPreference; @@ -313,11 +326,11 @@ public class SettingsController extends BaseController { SwitchAccountController()).pushChangeHandler(new VerticalChangeHandler()) .popChangeHandler(new VerticalChangeHandler())); }); - + if (userUtils.getCurrentUser().isPhoneBookIntegrationAvailable()) { - phoneBookIntegretationPreference.setVisibility(View.VISIBLE); + phoneBookIntegrationPreference.setVisibility(View.VISIBLE); } else { - phoneBookIntegretationPreference.setVisibility(View.GONE); + phoneBookIntegrationPreference.setVisibility(View.GONE); } String host = null; @@ -464,7 +477,7 @@ public class SettingsController extends BaseController { } ((Checkable) linkPreviewsSwitchPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.getAreLinkPreviewsAllowed()); - ((Checkable) phoneBookIntegretationPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.isPhoneBookIntegrationEnabled()); + ((Checkable) phoneBookIntegrationPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.isPhoneBookIntegrationEnabled()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); @@ -741,20 +754,20 @@ public class SettingsController extends BaseController { @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - if (requestCode == ContactAddressBookWorker.REQUEST_PERMISSION && - grantResults.length > 0 && - grantResults[0] == PackageManager.PERMISSION_GRANTED) { - WorkManager - .getInstance() - .enqueue(new OneTimeWorkRequest.Builder(ContactAddressBookWorker.class).build()); - } else { - appPreferences.setPhoneBookIntegration(false); - ((Checkable) phoneBookIntegretationPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.isPhoneBookIntegrationEnabled()); - Snackbar.make(getView(), - context.getResources().getString(R.string.no_phone_book_integration_due_to_permissions), - Snackbar.LENGTH_LONG) - .show(); - } + if (requestCode == ContactAddressBookWorker.REQUEST_PERMISSION && + grantResults.length > 0 && + grantResults[0] == PackageManager.PERMISSION_GRANTED) { + WorkManager + .getInstance() + .enqueue(new OneTimeWorkRequest.Builder(ContactAddressBookWorker.class).build()); + checkForPhoneNumber(); + } else { + appPreferences.setPhoneBookIntegration(false); + ((Checkable) phoneBookIntegrationPreference.findViewById(R.id.mp_checkable)).setChecked(appPreferences.isPhoneBookIntegrationEnabled()); + Toast.makeText(context, context.getResources().getString( + R.string.no_phone_book_integration_due_to_permissions), + Toast.LENGTH_LONG).show(); + } } private class ScreenLockTimeoutListener implements OnPreferenceValueChangedListener { @@ -847,22 +860,150 @@ public class SettingsController extends BaseController { NextcloudTalkApplication.Companion.setAppTheme(newValue); } } - + private class PhoneBookIntegrationChangeListener implements OnPreferenceValueChangedListener { private final Controller controller; - + public PhoneBookIntegrationChangeListener(Controller controller) { this.controller = controller; } - + @Override public void onChanged(Boolean newValue) { if (newValue) { - ContactAddressBookWorker.Companion.checkPermission(controller, context); + if(ContactAddressBookWorker.Companion.checkPermission(controller, context)){ + checkForPhoneNumber(); + } } } } - + + private void checkForPhoneNumber() { + ncApi.getUserData( + ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()), + ApiUtils.getUrlForUserProfile(currentUser.getBaseUrl()) + ).subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Observer() { + + @Override + public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) { + + } + + @Override + public void onNext(@io.reactivex.annotations.NonNull UserProfileOverall userProfileOverall) { + if (userProfileOverall.getOcs().getData().getPhone().isEmpty()) { + askForPhoneNumber(); + } else { + Log.d(TAG, "phone number already set"); + } + } + + @Override + public void onError(@io.reactivex.annotations.NonNull Throwable e) { + + } + + @Override + public void onComplete() { + + } + }); + } + + private void askForPhoneNumber() { + final LinearLayout phoneNumberLayoutWrapper = new LinearLayout(getActivity()); + phoneNumberLayoutWrapper.setOrientation(LinearLayout.VERTICAL); + phoneNumberLayoutWrapper.setPadding(50, 0, 50, 0); + + final TextInputLayout phoneNumberInputLayout = new TextInputLayout(getActivity()); + final EditText phoneNumberField = new EditText(getActivity()); + + phoneNumberInputLayout.setHelperTextColor(ColorStateList.valueOf(getResources().getColor(R.color.nc_darkRed))); + phoneNumberField.setInputType(InputType.TYPE_CLASS_PHONE); + phoneNumberField.setText("+"); + phoneNumberField.addTextChangedListener(new TextWatcher() { + public void afterTextChanged(Editable s) { + } + + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + public void onTextChanged(CharSequence s, int start, int before, int count) { + phoneNumberInputLayout.setHelperText(""); + } + }); + + phoneNumberInputLayout.addView(phoneNumberField); + phoneNumberLayoutWrapper.addView(phoneNumberInputLayout); + + AlertDialog dialog = new AlertDialog.Builder(getActivity()) + .setTitle(R.string.nc_settings_phone_book_integration_phone_number_dialog_title) + .setMessage(R.string.nc_settings_phone_book_integration_phone_number_dialog_description) + .setView(phoneNumberLayoutWrapper) + .setPositiveButton(context.getResources().getString(R.string.nc_common_set), null) + .setNegativeButton(context.getResources().getString(R.string.nc_common_skip), null) + .create(); + + dialog.setOnShowListener(new DialogInterface.OnShowListener() { + @Override + public void onShow(DialogInterface dialogInterface) { + Button button = ((AlertDialog) dialog).getButton(AlertDialog.BUTTON_POSITIVE); + button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + setPhoneNumber(phoneNumberInputLayout, dialog); + } + }); + } + }); + dialog.show(); + } + + private void setPhoneNumber(TextInputLayout textInputLayout, AlertDialog dialog) { + String phoneNumber = textInputLayout.getEditText().getText().toString(); + + ncApi.setUserData(ApiUtils.getCredentials(currentUser.getUsername(), currentUser.getToken()), + ApiUtils.getUrlForUserData(currentUser.getBaseUrl(), currentUser.getUserId()), "phone", phoneNumber + ).subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Observer() { + + @Override + public void onSubscribe(@io.reactivex.annotations.NonNull Disposable d) { + + } + + @Override + public void onNext(@io.reactivex.annotations.NonNull GenericOverall genericOverall) { + int statusCode = genericOverall.ocs.meta.statusCode; + if (statusCode == 200) { + dialog.dismiss(); + Toast.makeText(context, context.getResources().getString( + R.string.nc_settings_phone_book_integration_phone_number_dialog_success), + Toast.LENGTH_LONG).show(); + } else { + textInputLayout.setHelperText(context.getResources().getString( + R.string.nc_settings_phone_book_integration_phone_number_dialog_invalid)); + Log.d(TAG, "failed to set phoneNumber. statusCode=" + statusCode); + } + + } + + @Override + public void onError(@io.reactivex.annotations.NonNull Throwable e) { + textInputLayout.setHelperText(context.getResources().getString( + R.string.nc_settings_phone_book_integration_phone_number_dialog_invalid)); + Log.e(TAG, "setPhoneNumber error", e); + } + + @Override + public void onComplete() { + } + }); + } + private class ReadPrivacyChangeListener implements OnPreferenceValueChangedListener { @Override public void onChanged(Boolean newValue) { diff --git a/app/src/main/java/com/nextcloud/talk/jobs/ContactAddressBookWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/ContactAddressBookWorker.kt index abd5c1e48..ad86c08d2 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/ContactAddressBookWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/ContactAddressBookWorker.kt @@ -366,19 +366,21 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar } } - fun checkPermission(controller: Controller, context: Context) { + fun checkPermission(controller: Controller, context: Context): Boolean { if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_CONTACTS) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) { controller.requestPermissions(arrayOf(Manifest.permission.WRITE_CONTACTS, Manifest.permission.READ_CONTACTS), REQUEST_PERMISSION) + return false } else { WorkManager .getInstance() .enqueue(OneTimeWorkRequest.Builder(ContactAddressBookWorker::class.java) .setInputData(Data.Builder().putBoolean(KEY_FORCE, true).build()) .build()) + return true } } } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileData.java b/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileData.java index fa2978617..202beb7eb 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileData.java +++ b/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileData.java @@ -37,4 +37,7 @@ public class UserProfileData { @JsonField(name = "id") String userId; + + @JsonField(name = "phone") + String phone; } diff --git a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java index 82b80f0c3..43bd11f59 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java @@ -229,6 +229,10 @@ public class ApiUtils { return baseUrl + ocsApiVersion + "/cloud/user"; } + public static String getUrlForUserData(String baseUrl, String userId) { + return baseUrl + ocsApiVersion + "/cloud/users/" + userId; + } + public static String getUrlForUserSettings(String baseUrl) { return baseUrl + ocsApiVersion + spreedApiVersion + "/settings/user"; } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 23eb125a3..0ab52c40d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -22,6 +22,8 @@ Yes No + Skip + Set Sorry, something went wrong! @@ -344,6 +346,11 @@ phone_book_integration Match contacts based on phone number to integrate Talk shortcut in phone book Phone book integration + Phone number + You can set your phone number so + other users will be able to find you + Invalid phone number + Phone number set successfully No phone book integration due to missing permissions