() {
+ @Override
+ public void onUiSuccess(String s) {
+ try {
+ JSONObject json = new JSONObject(s);
+ diagnoseResultSB.append(getString(R.string.diagnose_im_server_ok));
+ diagnoseResultSB.append(getString(R.string.diagnose_remote_origin, json.getString("remoteOriginUrl"))).append("\n");
+ diagnoseResultSB.append(getString(R.string.diagnose_commit_message, json.getString("commitMessageShort"))).append("\n");
+ diagnoseResultSB.append(getString(R.string.diagnose_commit_time, json.getString("commitTime"))).append("\n\n");
+ updateDiagnoseResult();
+ } catch (JSONException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void onUiFailure(int code, String msg) {
+ diagnoseResultSB.append(getString(R.string.diagnose_im_server_error, code, msg)).append("\n");
+ updateDiagnoseResult();
+ }
+ });
+ }
+
+ private void tcping() {
+ if (TextUtils.isEmpty(MyApp.longLinkHost)) {
+ Toast.makeText(this, R.string.diagnose_longlink_empty, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ String host = MyApp.longLinkHost;
+ int port = ChatManager.Instance().getLongLinkPort();
+ ChatManager.Instance().getWorkHandler().post(() -> {
+ try (Socket socket = new Socket(host, port)) {
+ diagnoseResultSB.append(getString(R.string.diagnose_tcp_ping_ok)).append("\n\n");
+ ChatManager.Instance().getMainHandler().post(this::updateDiagnoseResult);
+ } catch (IOException e) {
+ diagnoseResultSB.append(getString(R.string.diagnose_tcp_ping_error, e.getMessage())).append("\n\n");
+ ChatManager.Instance().getMainHandler().post(this::updateDiagnoseResult);
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/chat/src/main/java/cn/wildfire/chat/app/misc/KeyStoreUtil.java b/chat/src/main/java/cn/wildfire/chat/app/misc/KeyStoreUtil.java
new file mode 100644
index 0000000000000000000000000000000000000000..0cc7630ec3bcc6925618d3b4a3a6cef7eaf194b1
--- /dev/null
+++ b/chat/src/main/java/cn/wildfire/chat/app/misc/KeyStoreUtil.java
@@ -0,0 +1,133 @@
+package cn.wildfire.chat.app.misc;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+
+import java.nio.charset.StandardCharsets;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Base64;
+
+import javax.crypto.Cipher;
+
+import cn.wildfire.chat.kit.Config;
+
+public class KeyStoreUtil {
+ // 密钥库类型
+ private static final String PP_KEYSTORE_TYPE = "AndroidKeyStore";
+ // 密钥库别名
+ private static final String PP_KEYSTORE_ALIAS = "pp_keystore_alias";
+ // 加密算法标准算法名称
+ private static final String PP_TRANSFORMATION = "RSA/ECB/PKCS1Padding";
+
+ /**
+ * 触发生成密钥对.
+ *
+ * 生成RSA 密钥对,包括公钥和私钥
+ *
+ * @return KeyPair 密钥对,包含公钥和私钥
+ */
+ private static KeyPair generateKey() throws Exception {
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
+ // 创建密钥生成器
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, PP_KEYSTORE_TYPE);
+ // 配置密钥生成器参数
+ KeyGenParameterSpec builder = new KeyGenParameterSpec.Builder(PP_KEYSTORE_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
+ .setDigests(KeyProperties.DIGEST_SHA256)
+ .build();
+
+ keyPairGenerator.initialize(builder);
+ // 生成密钥对
+ return keyPairGenerator.generateKeyPair();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * 获取公钥.
+ *
+ * @return 公钥
+ */
+ private static PublicKey getPublicKey() throws Exception {
+ KeyStore keyStore = KeyStore.getInstance(PP_KEYSTORE_TYPE);
+ keyStore.load(null);
+ // 判断密钥是否存在
+ if (!keyStore.containsAlias(PP_KEYSTORE_ALIAS)) {
+ return generateKey().getPublic();
+ }
+ KeyStore.Entry entry = keyStore.getEntry(PP_KEYSTORE_ALIAS, null);
+ if (!(entry instanceof KeyStore.PrivateKeyEntry)) {
+ return null;
+ }
+ return ((KeyStore.PrivateKeyEntry) entry).getCertificate().getPublicKey();
+ }
+
+ /**
+ * 获取私钥.
+ *
+ * @return 密钥
+ */
+ private static PrivateKey getPrivateKey() throws Exception {
+ KeyStore keyStore = KeyStore.getInstance(PP_KEYSTORE_TYPE);
+ keyStore.load(null);
+ // 判断密钥是否存在
+ if (!keyStore.containsAlias(PP_KEYSTORE_ALIAS)) {
+ return generateKey().getPrivate();
+ }
+ KeyStore.Entry entry = keyStore.getEntry(PP_KEYSTORE_ALIAS, null);
+ if (!(entry instanceof KeyStore.PrivateKeyEntry)) {
+ return null;
+ }
+ return ((KeyStore.PrivateKeyEntry) entry).getPrivateKey();
+ }
+
+ /**
+ * 加密保存数据
+ *
+ * @param context 上下文
+ * @param key 数据的Key
+ * @param data 数据
+ */
+ public static void saveData(Context context, String key, String data) throws Exception {
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
+ byte[] bytes = data.getBytes(StandardCharsets.UTF_8);
+ PublicKey publicKey = getPublicKey();
+ Cipher cipher = Cipher.getInstance(PP_TRANSFORMATION);
+ cipher.init(Cipher.ENCRYPT_MODE, publicKey);
+ bytes = cipher.doFinal(bytes);
+ data = Base64.getEncoder().encodeToString(bytes);
+ }
+ SharedPreferences sharedPreferences = context.getSharedPreferences(Config.SP_CONFIG_FILE_NAME, Context.MODE_PRIVATE);
+ sharedPreferences.edit().putString(key, data).commit();
+ }
+
+ /**
+ * 获取保密数据
+ *
+ * @param context 上下文
+ * @param key 数据的Key
+ * @return 解密后的数据
+ */
+ public static String getData(Context context, String key) throws Exception {
+ SharedPreferences sharedPreferences = context.getSharedPreferences(Config.SP_CONFIG_FILE_NAME, Context.MODE_PRIVATE);
+ String data = sharedPreferences.getString(key, null);
+ if (data == null) {
+ return null;
+ }
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
+ PrivateKey privateKey = getPrivateKey();
+ Cipher cipher = Cipher.getInstance(PP_TRANSFORMATION);
+ cipher.init(Cipher.DECRYPT_MODE, privateKey);
+ byte[] bytes = cipher.doFinal(Base64.getDecoder().decode(data));
+ data = new String(bytes, StandardCharsets.UTF_8);
+ }
+ return data;
+ }
+}
diff --git a/chat/src/main/java/cn/wildfire/chat/kit/setting/AboutActivity.java b/chat/src/main/java/cn/wildfire/chat/app/setting/AboutActivity.java
similarity index 36%
rename from chat/src/main/java/cn/wildfire/chat/kit/setting/AboutActivity.java
rename to chat/src/main/java/cn/wildfire/chat/app/setting/AboutActivity.java
index a09e66d487769c72c60d0865fc7fa20321bee1f1..2099eec712892e2f55d715cfa505d19f885e9731 100644
--- a/chat/src/main/java/cn/wildfire/chat/kit/setting/AboutActivity.java
+++ b/chat/src/main/java/cn/wildfire/chat/app/setting/AboutActivity.java
@@ -1,21 +1,37 @@
-package cn.wildfire.chat.kit.setting;
+/*
+ * Copyright (c) 2020 WildFireChat. All rights reserved.
+ */
+
+package cn.wildfire.chat.app.setting;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.widget.TextView;
-import butterknife.Bind;
-import butterknife.OnClick;
-import cn.wildfire.chat.app.Config;
+import cn.wildfire.chat.app.AppService;
+import cn.wildfire.chat.kit.Config;
import cn.wildfire.chat.kit.WfcBaseActivity;
import cn.wildfire.chat.kit.WfcWebViewActivity;
+import cn.wildfirechat.avenginekit.AVEngineKit;
import cn.wildfirechat.chat.R;
+import cn.wildfirechat.remote.ChatManager;
public class AboutActivity extends WfcBaseActivity {
- @Bind(R.id.infoTextView)
TextView infoTextView;
+ protected void bindEvents() {
+ super.bindEvents();
+ findViewById(R.id.introOptionItemView).setOnClickListener(v -> intro());
+ findViewById(R.id.agreementOptionItemView).setOnClickListener(v -> agreement());
+ findViewById(R.id.privacyOptionItemView).setOnClickListener(v -> privacy());
+ }
+
+ protected void bindViews() {
+ super.bindViews();
+ infoTextView = findViewById(R.id.infoTextView);
+ }
+
@Override
protected int contentLayout() {
return R.layout.activity_about;
@@ -27,10 +43,19 @@ public class AboutActivity extends WfcBaseActivity {
try {
PackageInfo packageInfo = packageManager.getPackageInfo(getPackageName(), PackageManager.GET_CONFIGURATIONS);
String info = packageInfo.packageName + "\n"
- + packageInfo.versionCode + " " + packageInfo.versionName + "\n"
- + Config.IM_SERVER_HOST + " " + Config.IM_SERVER_PORT + "\n"
- + Config.APP_SERVER_HOST + " " + Config.APP_SERVER_PORT + "\n"
- + Config.ICE_ADDRESS + " " + Config.ICE_USERNAME + " " + Config.ICE_PASSWORD + "\n";
+ + packageInfo.versionCode + " " + packageInfo.versionName + "\n"
+ + ChatManager.Instance().getProtoRevision() + "\n"
+ + Config.IM_SERVER_HOST + "\n"
+ + AppService.APP_SERVER_ADDRESS + "\n";
+
+ if (AVEngineKit.isSupportConference()) {
+ info += "高级版音视频\n";
+ } else {
+ info += "多人版版音视频\n";
+ for (String[] ice : Config.ICE_SERVERS) {
+ info += ice[0] + " " + ice[1] + " " + ice[2] + "\n";
+ }
+ }
infoTextView.setText(info);
@@ -39,18 +64,15 @@ public class AboutActivity extends WfcBaseActivity {
}
}
- @OnClick(R.id.introOptionItemView)
public void intro() {
- WfcWebViewActivity.loadUrl(this, "野火IM功能介绍", "http://docs.wildfirechat.cn/");
+ WfcWebViewActivity.loadUrl(this, getString(R.string.about_intro_title), getString(R.string.about_intro_url));
}
- @OnClick(R.id.agreementOptionItemView)
public void agreement() {
- WfcWebViewActivity.loadUrl(this, "野火IM用户协议", "http://www.wildfirechat.cn/firechat_user_agreement.html");
+ WfcWebViewActivity.loadUrl(this, getString(R.string.about_agreement_title), getString(R.string.about_agreement_url));
}
- @OnClick(R.id.privacyOptionItemView)
public void privacy() {
- WfcWebViewActivity.loadUrl(this, "野火IM个人信息保护政策", "http://www.wildfirechat.cn/firechat_user_privacy.html");
+ WfcWebViewActivity.loadUrl(this, getString(R.string.about_privacy_title), getString(R.string.about_privacy_url));
}
}
diff --git a/chat/src/main/java/cn/wildfire/chat/app/setting/AccountActivity.java b/chat/src/main/java/cn/wildfire/chat/app/setting/AccountActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..fe64d00a8ce6ed22a7f1c60e878f4e10ce5cd5b0
--- /dev/null
+++ b/chat/src/main/java/cn/wildfire/chat/app/setting/AccountActivity.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020 WildFireChat. All rights reserved.
+ */
+
+package cn.wildfire.chat.app.setting;
+
+import android.content.Intent;
+import android.view.View;
+
+import com.afollestad.materialdialogs.MaterialDialog;
+
+import cn.wildfire.chat.kit.WfcBaseActivity;
+import cn.wildfirechat.chat.R;
+
+public class AccountActivity extends WfcBaseActivity {
+
+ @Override
+ protected int contentLayout() {
+ return R.layout.account_activity;
+ }
+
+ protected void bindEvents() {
+ super.bindEvents();
+ findViewById(R.id.changePasswordOptionItemView).setOnClickListener(v -> changePassword());
+ }
+
+ void changePassword() {
+ new MaterialDialog.Builder(this).items(R.array.change_password).itemsCallback(new MaterialDialog.ListCallback() {
+ @Override
+ public void onSelection(MaterialDialog dialog, View v, int position, CharSequence text) {
+ if (position == 0) {
+ Intent intent = new Intent(AccountActivity.this, ResetPasswordActivity.class);
+ startActivity(intent);
+ } else if (position == 1) {
+ Intent intent = new Intent(AccountActivity.this, ChangePasswordActivity.class);
+ startActivity(intent);
+ }
+ }
+ }).show();
+ }
+}
diff --git a/chat/src/main/java/cn/wildfire/chat/app/setting/ChangePasswordActivity.java b/chat/src/main/java/cn/wildfire/chat/app/setting/ChangePasswordActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..5900abc30f1a36a63c399b3d8943a731862299a1
--- /dev/null
+++ b/chat/src/main/java/cn/wildfire/chat/app/setting/ChangePasswordActivity.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2020 WildFireChat. All rights reserved.
+ */
+
+package cn.wildfire.chat.app.setting;
+
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import androidx.annotation.Nullable;
+
+import com.afollestad.materialdialogs.MaterialDialog;
+
+import cn.wildfire.chat.app.AppService;
+import cn.wildfire.chat.kit.WfcBaseActivity;
+import cn.wildfire.chat.kit.net.SimpleCallback;
+import cn.wildfire.chat.kit.net.base.StatusResult;
+import cn.wildfire.chat.kit.widget.SimpleTextWatcher;
+import cn.wildfirechat.chat.R;
+
+public class ChangePasswordActivity extends WfcBaseActivity {
+ Button confirmButton;
+ EditText oldPasswordEditText;
+ EditText newPasswordEditText;
+ EditText confirmPasswordEditText;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ protected void bindEvents() {
+ super.bindEvents();
+ findViewById(R.id.confirmButton).setOnClickListener(v -> resetPassword());
+ oldPasswordEditText.addTextChangedListener(new SimpleTextWatcher() {
+ @Override
+ public void afterTextChanged(Editable s) {
+ oldPassword(s);
+ }
+ });
+ newPasswordEditText.addTextChangedListener(new SimpleTextWatcher() {
+ @Override
+ public void afterTextChanged(Editable s) {
+ newPassword(s);
+ }
+ });
+ confirmPasswordEditText.addTextChangedListener(new SimpleTextWatcher() {
+ @Override
+ public void afterTextChanged(Editable s) {
+ confirmPassword(s);
+ }
+ });
+ }
+
+ protected void bindViews() {
+ super.bindViews();
+ confirmButton = findViewById(R.id.confirmButton);
+ oldPasswordEditText = findViewById(R.id.oldPasswordEditText);
+ newPasswordEditText = findViewById(R.id.newPasswordEditText);
+ confirmPasswordEditText = findViewById(R.id.confirmPasswordEditText);
+ }
+
+ @Override
+ protected int contentLayout() {
+ return R.layout.change_password_activity;
+ }
+
+ @Override
+ protected void afterViews() {
+// setStatusBarTheme(this, false);
+// setStatusBarColor(R.color.gray14);
+ }
+
+ void oldPassword(Editable editable) {
+ if (!TextUtils.isEmpty(newPasswordEditText.getText()) && !TextUtils.isEmpty(confirmPasswordEditText.getText()) && !TextUtils.isEmpty(editable)) {
+ confirmButton.setEnabled(true);
+ } else {
+ confirmButton.setEnabled(false);
+ }
+ }
+
+ void newPassword(Editable editable) {
+ if (!TextUtils.isEmpty(oldPasswordEditText.getText()) && !TextUtils.isEmpty(confirmPasswordEditText.getText()) && !TextUtils.isEmpty(editable)) {
+ confirmButton.setEnabled(true);
+ } else {
+ confirmButton.setEnabled(false);
+ }
+ }
+
+ void confirmPassword(Editable editable) {
+ if (!TextUtils.isEmpty(oldPasswordEditText.getText()) && !TextUtils.isEmpty(newPasswordEditText.getText()) && !TextUtils.isEmpty(editable)) {
+ confirmButton.setEnabled(true);
+ } else {
+ confirmButton.setEnabled(false);
+ }
+ }
+
+ void resetPassword() {
+ String oldPassword = oldPasswordEditText.getText().toString().trim();
+ String newPassword = newPasswordEditText.getText().toString().trim();
+ String confirmPassword = confirmPasswordEditText.getText().toString().trim();
+ if (!TextUtils.equals(newPassword, confirmPassword)) {
+ Toast.makeText(this, R.string.password_not_match, Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ MaterialDialog dialog = new MaterialDialog.Builder(this)
+ .content(R.string.password_changing)
+ .progress(true, 10)
+ .cancelable(false)
+ .build();
+ dialog.show();
+
+ AppService.Instance().changePassword(oldPassword, newPassword, new SimpleCallback() {
+ @Override
+ public void onUiSuccess(StatusResult result) {
+ if (isFinishing()) {
+ return;
+ }
+ Toast.makeText(ChangePasswordActivity.this, R.string.password_change_success, Toast.LENGTH_SHORT).show();
+ dialog.dismiss();
+ finish();
+ }
+
+ @Override
+ public void onUiFailure(int code, String msg) {
+ if (isFinishing()) {
+ return;
+ }
+ dialog.dismiss();
+ Toast.makeText(ChangePasswordActivity.this, getString(R.string.password_change_failed, code, msg), Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+}
diff --git a/chat/src/main/java/cn/wildfire/chat/app/setting/ResetPasswordActivity.java b/chat/src/main/java/cn/wildfire/chat/app/setting/ResetPasswordActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..3dc88e1b01d491a12ce274a978090bb369e3b83b
--- /dev/null
+++ b/chat/src/main/java/cn/wildfire/chat/app/setting/ResetPasswordActivity.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2020 WildFireChat. All rights reserved.
+ */
+
+package cn.wildfire.chat.app.setting;
+
+import android.os.Handler;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.afollestad.materialdialogs.MaterialDialog;
+
+import cn.wildfire.chat.app.AppService;
+import cn.wildfire.chat.kit.WfcBaseActivity;
+import cn.wildfire.chat.kit.net.SimpleCallback;
+import cn.wildfire.chat.kit.net.base.StatusResult;
+import cn.wildfire.chat.kit.widget.SimpleTextWatcher;
+import cn.wildfirechat.chat.R;
+
+public class ResetPasswordActivity extends WfcBaseActivity {
+ Button confirmButton;
+
+ EditText authCodeEditText;
+ EditText newPasswordEditText;
+ EditText confirmPasswordEditText;
+
+ TextView requestAuthCodeButton;
+
+ FrameLayout authCodeFrameLayout;
+
+ private String resetCode;
+
+ protected void bindEvents() {
+ super.bindEvents();
+ requestAuthCodeButton.setOnClickListener(v -> requestAuthCode());
+ confirmButton.setOnClickListener(v -> resetPassword());
+ authCodeEditText.addTextChangedListener(new SimpleTextWatcher() {
+ @Override
+ public void afterTextChanged(Editable s) {
+ authCode(s);
+ }
+ });
+ newPasswordEditText.addTextChangedListener(new SimpleTextWatcher() {
+ @Override
+ public void afterTextChanged(Editable s) {
+ newPassword(s);
+ }
+ });
+ confirmPasswordEditText.addTextChangedListener(new SimpleTextWatcher() {
+ @Override
+ public void afterTextChanged(Editable s) {
+ confirmPassword(s);
+ }
+ });
+ }
+
+ protected void bindViews() {
+ super.bindViews();
+ confirmButton = findViewById(R.id.confirmButton);
+ authCodeEditText = findViewById(R.id.authCodeEditText);
+ newPasswordEditText = findViewById(R.id.newPasswordEditText);
+ confirmPasswordEditText = findViewById(R.id.confirmPasswordEditText);
+ requestAuthCodeButton = findViewById(R.id.requestAuthCodeButton);
+ authCodeFrameLayout = findViewById(R.id.authCodeFrameLayout);
+ }
+
+ @Override
+ protected int contentLayout() {
+ return R.layout.reset_password_activity;
+ }
+
+ @Override
+ protected void afterViews() {
+ resetCode = getIntent().getStringExtra("resetCode");
+ if (!TextUtils.isEmpty(resetCode)) {
+ authCodeFrameLayout.setVisibility(View.GONE);
+ }
+ }
+
+ void authCode(Editable editable) {
+ if (!TextUtils.isEmpty(newPasswordEditText.getText()) && !TextUtils.isEmpty(confirmPasswordEditText.getText()) && !TextUtils.isEmpty(editable)) {
+ confirmButton.setEnabled(true);
+ } else {
+ confirmButton.setEnabled(false);
+ }
+ }
+
+ void newPassword(Editable editable) {
+ if ((!TextUtils.isEmpty(authCodeEditText.getText()) || !TextUtils.isEmpty(resetCode)) && !TextUtils.isEmpty(confirmPasswordEditText.getText()) && !TextUtils.isEmpty(editable)) {
+ confirmButton.setEnabled(true);
+ } else {
+ confirmButton.setEnabled(false);
+ }
+ }
+
+ void confirmPassword(Editable editable) {
+ if ((!TextUtils.isEmpty(authCodeEditText.getText()) || !TextUtils.isEmpty(resetCode)) && !TextUtils.isEmpty(newPasswordEditText.getText()) && !TextUtils.isEmpty(editable)) {
+ confirmButton.setEnabled(true);
+ } else {
+ confirmButton.setEnabled(false);
+ }
+ }
+
+ private Handler handler = new Handler();
+
+ void requestAuthCode() {
+ requestAuthCodeButton.setEnabled(false);
+ handler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ if (!isFinishing()) {
+ requestAuthCodeButton.setEnabled(true);
+ }
+ }
+ }, 60 * 1000);
+
+ Toast.makeText(this, R.string.requesting_reset_code, Toast.LENGTH_SHORT).show();
+
+ AppService.Instance().requestResetAuthCode(null, new AppService.SendCodeCallback() {
+ @Override
+ public void onUiSuccess() {
+ Toast.makeText(ResetPasswordActivity.this, R.string.reset_code_send_success, Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onUiFailure(int code, String msg) {
+ Toast.makeText(ResetPasswordActivity.this, getString(R.string.reset_code_send_failure, code, msg), Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+
+ void resetPassword() {
+ String newPassword = newPasswordEditText.getText().toString().trim();
+ String confirmPassword = confirmPasswordEditText.getText().toString().trim();
+ if (!TextUtils.equals(newPassword, confirmPassword)) {
+ Toast.makeText(this, R.string.password_not_match, Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ MaterialDialog dialog = new MaterialDialog.Builder(this)
+ .content(R.string.reset_password_progress)
+ .progress(true, 10)
+ .cancelable(false)
+ .build();
+ dialog.show();
+
+ String code = TextUtils.isEmpty(resetCode) ? authCodeEditText.getText().toString() : resetCode;
+
+ AppService.Instance().resetPassword(null, code, newPassword, new SimpleCallback() {
+ @Override
+ public void onUiSuccess(StatusResult result) {
+ if (isFinishing()) {
+ return;
+ }
+ Toast.makeText(ResetPasswordActivity.this, R.string.reset_password_success, Toast.LENGTH_SHORT).show();
+ dialog.dismiss();
+ finish();
+ }
+
+ @Override
+ public void onUiFailure(int code, String msg) {
+ if (isFinishing()) {
+ return;
+ }
+ dialog.dismiss();
+ Toast.makeText(ResetPasswordActivity.this, getString(R.string.reset_password_failure, code, msg), Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+}
diff --git a/chat/src/main/java/cn/wildfire/chat/app/setting/SettingActivity.java b/chat/src/main/java/cn/wildfire/chat/app/setting/SettingActivity.java
new file mode 100644
index 0000000000000000000000000000000000000000..42fa5e55733fd572779169499998bd118248d2cc
--- /dev/null
+++ b/chat/src/main/java/cn/wildfire/chat/app/setting/SettingActivity.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2020 WildFireChat. All rights reserved.
+ */
+
+package cn.wildfire.chat.app.setting;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.Build;
+import android.os.PowerManager;
+import android.provider.Settings;
+import android.webkit.CookieManager;
+import android.webkit.WebStorage;
+import android.widget.Toast;
+
+import androidx.annotation.Nullable;
+
+import cn.wildfire.chat.app.AppService;
+import cn.wildfire.chat.app.main.SplashActivity;
+import cn.wildfire.chat.app.misc.DiagnoseActivity;
+import cn.wildfire.chat.kit.ChatManagerHolder;
+import cn.wildfire.chat.kit.Config;
+import cn.wildfire.chat.kit.WfcBaseActivity;
+import cn.wildfire.chat.kit.net.OKHttpHelper;
+import cn.wildfire.chat.kit.net.SimpleCallback;
+import cn.wildfire.chat.kit.settings.PrivacySettingActivity;
+import cn.wildfire.chat.kit.widget.OptionItemView;
+import cn.wildfirechat.chat.R;
+
+public class SettingActivity extends WfcBaseActivity {
+ private final int REQUEST_IGNORE_BATTERY_CODE = 100;
+ OptionItemView diagnoseOptionItemView;
+
+
+ protected void bindEvents() {
+ super.bindEvents();
+ findViewById(R.id.exitOptionItemView).setOnClickListener(v -> exit());
+ findViewById(R.id.privacySettingOptionItemView).setOnClickListener(v -> privacySetting());
+ findViewById(R.id.diagnoseOptionItemView).setOnClickListener(v -> diagnose());
+ findViewById(R.id.uploadLogOptionItemView).setOnClickListener(v -> uploadLog());
+ findViewById(R.id.batteryOptionItemView).setOnClickListener(v -> batteryOptimize());
+ findViewById(R.id.aboutOptionItemView).setOnClickListener(v -> about());
+ }
+
+ protected void bindViews() {
+ super.bindViews();
+ diagnoseOptionItemView = findViewById(R.id.diagnoseOptionItemView);
+ }
+
+ @Override
+ protected int contentLayout() {
+ return R.layout.setting_activity;
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+ switch (requestCode) {
+ case REQUEST_IGNORE_BATTERY_CODE:
+ if (resultCode == RESULT_CANCELED) {
+ Toast.makeText(this, R.string.battery_optimize_tip, Toast.LENGTH_SHORT).show();
+ }
+ break;
+ default:
+ super.onActivityResult(requestCode, resultCode, data);
+ break;
+ }
+ }
+
+ void exit() {
+ //不要清除session,这样再次登录时能够保留历史记录。如果需要清除掉本地历史记录和服务器信息这里使用true
+ ChatManagerHolder.gChatManager.disconnect(true, false);
+ SharedPreferences sp = getSharedPreferences(Config.SP_CONFIG_FILE_NAME, Context.MODE_PRIVATE);
+ sp.edit()
+ .clear()
+ .putBoolean("hasReadUserAgreement", true)
+ .apply();
+
+ sp = getSharedPreferences("moment", Context.MODE_PRIVATE);
+ sp.edit().clear().apply();
+
+ OKHttpHelper.clearCookies();
+
+ WebStorage.getInstance().deleteAllData();
+ CookieManager.getInstance().removeAllCookies(null);
+ CookieManager.getInstance().flush();
+
+ Intent intent = new Intent(this, SplashActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ finish();
+ }
+
+ void privacySetting() {
+ Intent intent = new Intent(this, PrivacySettingActivity.class);
+ startActivity(intent);
+ }
+
+ void diagnose() {
+ Intent intent = new Intent(this, DiagnoseActivity.class);
+ startActivity(intent);
+ }
+
+ void uploadLog() {
+ AppService.Instance().uploadLog(new SimpleCallback() {
+ @Override
+ public void onUiSuccess(String path) {
+ if (!isFinishing()) {
+ Toast.makeText(SettingActivity.this, getString(R.string.upload_log_success, path), Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ @Override
+ public void onUiFailure(int code, String msg) {
+ if (!isFinishing()) {
+ Toast.makeText(SettingActivity.this, getString(R.string.upload_log_failed, code, msg), Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+ }
+
+ @SuppressLint("BatteryLife")
+ void batteryOptimize() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ try {
+ Intent intent = new Intent();
+ String packageName = getPackageName();
+ PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+ if (!pm.isIgnoringBatteryOptimizations(packageName)) {
+ intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
+ intent.setData(Uri.parse("package:" + packageName));
+ startActivityForResult(intent, REQUEST_IGNORE_BATTERY_CODE);
+ } else {
+ Toast.makeText(this, R.string.battery_optimize_allowed, Toast.LENGTH_SHORT).show();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ } else {
+ Toast.makeText(this, R.string.system_version_not_support, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ void about() {
+ Intent intent = new Intent(this, AboutActivity.class);
+ startActivity(intent);
+ }
+}
diff --git a/chat/src/main/java/cn/wildfire/chat/kit/ConfigEventViewModel.java b/chat/src/main/java/cn/wildfire/chat/kit/ConfigEventViewModel.java
deleted file mode 100644
index 7883c9830bb4139385244cd274d66a99b30c18a1..0000000000000000000000000000000000000000
--- a/chat/src/main/java/cn/wildfire/chat/kit/ConfigEventViewModel.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package cn.wildfire.chat.kit;
-
-import androidx.lifecycle.MutableLiveData;
-import androidx.lifecycle.ViewModel;
-
-import cn.wildfire.chat.kit.common.AppScopeViewModel;
-
-public class ConfigEventViewModel extends ViewModel implements AppScopeViewModel {
- private MutableLiveData> showGroupAliasLiveData;
-
- public MutableLiveData> showGroupAliasLiveData() {
- if (showGroupAliasLiveData == null) {
- showGroupAliasLiveData = new MutableLiveData<>();
- }
- return showGroupAliasLiveData;
- }
-
- public void postGroupAliasEvent(String groupId, boolean show) {
- if (showGroupAliasLiveData != null) {
- showGroupAliasLiveData.setValue(new Event<>(show));
- }
- }
-}
diff --git a/chat/src/main/java/cn/wildfire/chat/kit/WfcGlideModule.java b/chat/src/main/java/cn/wildfire/chat/kit/WfcGlideModule.java
deleted file mode 100644
index 4b800134b9f6627a6326bdf9e2f1e8d57ce8ba14..0000000000000000000000000000000000000000
--- a/chat/src/main/java/cn/wildfire/chat/kit/WfcGlideModule.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package cn.wildfire.chat.kit;
-
-import com.bumptech.glide.annotation.GlideModule;
-import com.bumptech.glide.module.AppGlideModule;
-
-@GlideModule
-public final class WfcGlideModule extends AppGlideModule {
-}
-
diff --git a/chat/src/main/java/cn/wildfire/chat/kit/WfcIntent.java b/chat/src/main/java/cn/wildfire/chat/kit/WfcIntent.java
deleted file mode 100644
index d1b9044680e2715f5d7eb713ceb92f91da5d504a..0000000000000000000000000000000000000000
--- a/chat/src/main/java/cn/wildfire/chat/kit/WfcIntent.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package cn.wildfire.chat.kit;
-
-public interface WfcIntent {
- String ACTION_MAIN = "cn.wildfirechat.chat.main";
- String ACTION_CONVERSATION = "cn.wildfirechat.chat.conversation";
- String ACTION_CONTACT = "cn.wildfirechat.chat.contact";
- String ACTION_USER_INFO = "cn.wildfirechat.chat.user.info";
- String ACTION_GROUP_INFO = "cn.wildfirechat.chat.group.info";
- String ACTION_VOIP_SINGLE = "cn.wildfirechat.kit.chat.voip.single";
-}
diff --git a/chat/src/main/java/cn/wildfire/chat/kit/WfcUIKit.java b/chat/src/main/java/cn/wildfire/chat/kit/WfcUIKit.java
deleted file mode 100644
index 704b185e29bb05d4067040f54d92b1a8617e8d9b..0000000000000000000000000000000000000000
--- a/chat/src/main/java/cn/wildfire/chat/kit/WfcUIKit.java
+++ /dev/null
@@ -1,192 +0,0 @@
-package cn.wildfire.chat.kit;
-
-import android.app.Activity;
-import android.app.Application;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.media.AudioManager;
-import android.net.Uri;
-import android.text.TextUtils;
-
-import androidx.annotation.NonNull;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleObserver;
-import androidx.lifecycle.OnLifecycleEvent;
-import androidx.lifecycle.ProcessLifecycleOwner;
-import androidx.lifecycle.ViewModel;
-import androidx.lifecycle.ViewModelProvider;
-import androidx.lifecycle.ViewModelStore;
-
-import com.bumptech.glide.Glide;
-import com.bumptech.glide.load.engine.DiskCacheStrategy;
-import com.bumptech.glide.request.RequestOptions;
-import com.lqr.emoji.LQREmotionKit;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import cn.wildfire.chat.app.Config;
-import cn.wildfire.chat.kit.common.AppScopeViewModel;
-import cn.wildfire.chat.kit.voip.AsyncPlayer;
-import cn.wildfire.chat.kit.voip.SingleVoipCallActivity;
-import cn.wildfirechat.avenginekit.AVEngineKit;
-import cn.wildfirechat.chat.R;
-import cn.wildfirechat.client.NotInitializedExecption;
-import cn.wildfirechat.message.Message;
-import cn.wildfirechat.message.core.PersistFlag;
-import cn.wildfirechat.push.PushService;
-import cn.wildfirechat.remote.ChatManager;
-import cn.wildfirechat.remote.OnRecallMessageListener;
-import cn.wildfirechat.remote.OnReceiveMessageListener;
-
-
-public class WfcUIKit implements AVEngineKit.AVEngineCallback, OnReceiveMessageListener, OnRecallMessageListener {
-
- private boolean isBackground = true;
- private static Application application;
- private static ViewModelProvider viewModelProvider;
- private ViewModelStore viewModelStore;
-
- public void init(Application application) {
- WfcUIKit.application = application;
- initWFClient(application);
- //初始化表情控件
- LQREmotionKit.init(application, (context, path, imageView) -> Glide.with(context).load(path).apply(new RequestOptions().centerCrop().diskCacheStrategy(DiskCacheStrategy.RESOURCE).dontAnimate()).into(imageView));
-
- ProcessLifecycleOwner.get().getLifecycle().addObserver(new LifecycleObserver() {
- @OnLifecycleEvent(Lifecycle.Event.ON_START)
- public void onForeground() {
- PushService.clearNotification(application);
- WfcNotificationManager.getInstance().clearAllNotification(application);
- isBackground = false;
- }
-
- @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
- public void onBackground() {
- isBackground = true;
- viewModelStore.clear();
- }
- });
-
- viewModelStore = new ViewModelStore();
- ViewModelProvider.Factory factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
- viewModelProvider = new ViewModelProvider(viewModelStore, factory);
- }
-
- private void initWFClient(Application application) {
- ChatManager.init(application, Config.IM_SERVER_HOST, Config.IM_SERVER_PORT);
- try {
- ChatManagerHolder.gChatManager = ChatManager.Instance();
- ChatManagerHolder.gChatManager.startLog();
- ChatManagerHolder.gChatManager.addOnReceiveMessageListener(this);
- ChatManagerHolder.gChatManager.addRecallMessageListener(this);
- PushService.init(application);
-
- ringPlayer = new AsyncPlayer(null);
- AVEngineKit.init(application, this);
- ChatManagerHolder.gAVEngine = AVEngineKit.Instance();
- ChatManagerHolder.gAVEngine.addIceServer(Config.ICE_ADDRESS, Config.ICE_USERNAME, Config.ICE_PASSWORD);
-
- SharedPreferences sp = application.getSharedPreferences("config", Context.MODE_PRIVATE);
- String id = sp.getString("id", null);
- String token = sp.getString("token", null);
- if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(token)) {
- ChatManagerHolder.gChatManager.connect(id, token);
- }
- } catch (NotInitializedExecption notInitializedExecption) {
- notInitializedExecption.printStackTrace();
- }
- }
-
- /**
- * 当{@link androidx.lifecycle.ViewModel} 需要跨{@link android.app.Activity} 共享数据时使用
- */
- public static T getAppScopeViewModel(@NonNull Class modelClass) {
- if (!AppScopeViewModel.class.isAssignableFrom(modelClass)) {
- throw new IllegalArgumentException("the model class should be subclass of AppScopeViewModel");
- }
- return viewModelProvider.get(modelClass);
- }
-
- @Override
- public void onReceiveCall(AVEngineKit.CallSession session) {
- onCall(application, session.getClientId(), false, session.isAudioOnly());
- }
-
- private AsyncPlayer ringPlayer;
-
- @Override
- public void shouldStartRing(boolean isIncomming) {
- if (isIncomming) {
- Uri uri = Uri.parse("android.resource://" + application.getPackageName() + "/" + R.raw.incoming_call_ring);
- ringPlayer.play(application, uri, true, AudioManager.STREAM_RING);
- } else {
- Uri uri = Uri.parse("android.resource://" + application.getPackageName() + "/" + R.raw.outgoing_call_ring);
- ringPlayer.play(application, uri, true, AudioManager.STREAM_RING);
- }
- }
-
- @Override
- public void shouldSopRing() {
- ringPlayer.stop();
- }
-
- // pls refer to https://stackoverflow.com/questions/11124119/android-starting-new-activity-from-application-class
- public static void onCall(Context context, String targetId, boolean isMo, boolean isAudioOnly) {
- Intent voip = new Intent(WfcIntent.ACTION_VOIP_SINGLE);
- voip.putExtra(SingleVoipCallActivity.EXTRA_MO, isMo);
- voip.putExtra(SingleVoipCallActivity.EXTRA_TARGET, targetId);
- voip.putExtra(SingleVoipCallActivity.EXTRA_AUDIO_ONLY, isAudioOnly);
-
- if (context instanceof Activity) {
- context.startActivity(voip);
- } else {
- Intent main = new Intent(WfcIntent.ACTION_MAIN);
- voip.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- PendingIntent pendingIntent = PendingIntent.getActivities(context, 100, new Intent[]{main, voip}, 0);
- try {
- pendingIntent.send();
- } catch (PendingIntent.CanceledException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
-
- @Override
- public void onReceiveMessage(List messages, boolean hasMore) {
- if (isBackground) {
- // FIXME: 2018/5/28 只是临时方案,No_Persist消息,我觉得不应当到这儿,注册监听时,
- // 就表明自己关系哪些类型的消息, 设置哪些种类的消息
-
- if (messages == null) {
- return;
- }
-
- List msgs = new ArrayList<>(messages);
- long now = System.currentTimeMillis();
- long delta = ChatManager.Instance().getServerDeltaTime();
- Iterator iterator = msgs.iterator();
- while (iterator.hasNext()) {
- Message message = iterator.next();
- if (message.content.getPersistFlag() == PersistFlag.No_Persist
- || now - (message.serverTime - delta) > 10 * 1000) {
- iterator.remove();
- }
- }
- WfcNotificationManager.getInstance().handleReceiveMessage(application, msgs);
- } else {
- // do nothing
- }
- }
-
- @Override
- public void onRecallMessage(Message message) {
- if (isBackground) {
- WfcNotificationManager.getInstance().handleRecallMessage(application, message);
- }
- }
-}
diff --git a/chat/src/main/java/cn/wildfire/chat/kit/WfcWebViewActivity.java b/chat/src/main/java/cn/wildfire/chat/kit/WfcWebViewActivity.java
deleted file mode 100644
index fa3cd10f958ecef4afde335c37ed2e318df8ef81..0000000000000000000000000000000000000000
--- a/chat/src/main/java/cn/wildfire/chat/kit/WfcWebViewActivity.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package cn.wildfire.chat.kit;
-
-import android.content.Context;
-import android.content.Intent;
-import android.text.TextUtils;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-
-import butterknife.Bind;
-import cn.wildfirechat.chat.R;
-
-public class WfcWebViewActivity extends WfcBaseActivity {
- private String url;
-
- @Bind(R.id.webview)
- WebView webView;
-
- public static void loadUrl(Context context, String title, String url) {
- Intent intent = new Intent(context, WfcWebViewActivity.class);
- intent.putExtra("url", url);
- intent.putExtra("title", title);
- context.startActivity(intent);
- }
-
- @Override
- protected int contentLayout() {
- return R.layout.activity_webview;
- }
-
- @Override
- protected void afterViews() {
- url = getIntent().getStringExtra("url");
- webView.loadUrl(url);
- String title = getIntent().getStringExtra("title");
- if (TextUtils.isEmpty(title)) {
- webView.setWebViewClient(new WebViewClient() {
- @Override
- public void onPageFinished(WebView view, String url) {
- super.onPageFinished(view, url);
- String title = view.getTitle();
- if (!TextUtils.isEmpty(title)) {
- setTitle(title);
- }
- }
- });
- } else {
- setTitle(title);
- }
- }
-}
diff --git a/chat/src/main/java/cn/wildfire/chat/kit/contact/ContactFragment.java b/chat/src/main/java/cn/wildfire/chat/kit/contact/ContactFragment.java
deleted file mode 100644
index b544e836ece68b47f34abcefe96a51fc7f1827ea..0000000000000000000000000000000000000000
--- a/chat/src/main/java/cn/wildfire/chat/kit/contact/ContactFragment.java
+++ /dev/null
@@ -1,154 +0,0 @@
-package cn.wildfire.chat.kit.contact;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.lifecycle.Observer;
-
-import java.util.List;
-
-import cn.wildfire.chat.app.main.MainActivity;
-import cn.wildfire.chat.kit.IMServiceStatusViewModel;
-import cn.wildfire.chat.kit.WfcUIKit;
-import cn.wildfire.chat.kit.channel.ChannelListActivity;
-import cn.wildfire.chat.kit.contact.model.ContactCountFooterValue;
-import cn.wildfire.chat.kit.contact.model.FriendRequestValue;
-import cn.wildfire.chat.kit.contact.model.GroupValue;
-import cn.wildfire.chat.kit.contact.model.HeaderValue;
-import cn.wildfire.chat.kit.contact.model.UIUserInfo;
-import cn.wildfire.chat.kit.contact.newfriend.FriendRequestListActivity;
-import cn.wildfire.chat.kit.contact.viewholder.footer.ContactCountViewHolder;
-import cn.wildfire.chat.kit.contact.viewholder.header.ChannelViewHolder;
-import cn.wildfire.chat.kit.contact.viewholder.header.FriendRequestViewHolder;
-import cn.wildfire.chat.kit.contact.viewholder.header.GroupViewHolder;
-import cn.wildfire.chat.kit.group.GroupListActivity;
-import cn.wildfire.chat.kit.user.UserInfoActivity;
-import cn.wildfire.chat.kit.user.UserViewModel;
-import cn.wildfire.chat.kit.widget.QuickIndexBar;
-import cn.wildfirechat.model.UserInfo;
-
-public class ContactFragment extends BaseContactFragment implements QuickIndexBar.OnLetterUpdateListener {
- private UserViewModel userViewModel;
- private IMServiceStatusViewModel imServiceStatusViewModel;
-
- private Observer friendRequestUpdateLiveDataObserver = count -> {
- FriendRequestValue requestValue = new FriendRequestValue(count == null ? 0 : count);
- contactAdapter.updateHeader(0, requestValue);
- };
-
- private Observer