package tw.nekomimi.nekogram.settings; import android.Manifest; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.Build; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import com.jakewharton.processphoenix.ProcessPhoenix; import org.json.JSONException; import org.json.JSONObject; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MessagesController; import org.telegram.messenger.R; import org.telegram.messenger.SendMessagesHelper; import org.telegram.messenger.browser.Browser; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; import org.telegram.ui.ActionBar.AlertDialog; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.ActionBar.ThemeDescription; import org.telegram.ui.Cells.EmptyCell; import org.telegram.ui.Cells.HeaderCell; import org.telegram.ui.Cells.NotificationsCheckCell; import org.telegram.ui.Cells.ShadowSectionCell; import org.telegram.ui.Cells.TextCell; import org.telegram.ui.Cells.TextCheckCell; import org.telegram.ui.Cells.TextDetailSettingsCell; import org.telegram.ui.Cells.TextInfoPrivacyCell; import org.telegram.ui.Cells.TextSettingsCell; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.RecyclerListView; import org.telegram.ui.DocumentSelectActivity; import org.telegram.ui.LaunchActivity; import java.io.File; import java.util.ArrayList; import java.util.Date; import java.util.Map; import java.util.function.Function; import kotlin.text.StringsKt; import tw.nekomimi.nekogram.utils.AlertUtil; import tw.nekomimi.nekogram.utils.FileUtil; import tw.nekomimi.nekogram.utils.GsonUtil; import tw.nekomimi.nekogram.utils.ShareUtil; @SuppressLint("RtlHardcoded") public class NekoSettingsActivity extends BaseFragment { private RecyclerListView listView; private ListAdapter listAdapter; private int rowCount; private int categoriesRow; private int generalRow; private int accountRow; private int chatRow; private int experimentRow; private int categories2Row; private int aboutRow; private int channelRow; private int fdroidRow; private int googlePlayRow; private int sourceCodeRow; private int translationRow; private int about2Row; @Override public boolean onFragmentCreate() { super.onFragmentCreate(); updateRows(); return true; } private static final int backup_settings = 1; private static final int import_settings = 2; @SuppressLint("NewApi") @Override public View createView(Context context) { actionBar.setBackButtonImage(R.drawable.ic_ab_back); actionBar.setTitle(LocaleController.getString("NekoSettings", R.string.NekoSettings)); ActionBarMenu menu = actionBar.createMenu(); ActionBarMenuItem otherMenu = menu.addItem(0, R.drawable.ic_ab_other); otherMenu.addSubItem(backup_settings, LocaleController.getString("BackupSettings", R.string.BackupSettings)); otherMenu.addSubItem(import_settings, LocaleController.getString("ImportSettings", R.string.ImportSettings)); if (AndroidUtilities.isTablet()) { actionBar.setOccupyStatusBar(false); } actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @Override public void onItemClick(int id) { if (id == -1) { finishFragment(); } else if (id == backup_settings) { backupSettings(); } else if (id == import_settings) { try { if (Build.VERSION.SDK_INT >= 23 && getParentActivity().checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { getParentActivity().requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 4); return; } } catch (Throwable ignore) { } DocumentSelectActivity fragment = new DocumentSelectActivity(false); fragment.setMaxSelectedFiles(1); fragment.setAllowPhoto(false); fragment.setDelegate(new DocumentSelectActivity.DocumentSelectActivityDelegate() { @Override public void didSelectFiles(DocumentSelectActivity activity, ArrayList files, String caption, boolean notify, int scheduleDate) { activity.finishFragment(); importSettings(getParentActivity(), new File(files.get(0))); } @Override public void didSelectPhotos(ArrayList photos, boolean notify, int scheduleDate) { } @Override public void startDocumentSelectActivity() { } }); presentFragment(fragment); } } }); listAdapter = new ListAdapter(context); fragmentView = new FrameLayout(context); fragmentView.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundGray)); FrameLayout frameLayout = (FrameLayout) fragmentView; listView = new RecyclerListView(context); listView.setVerticalScrollBarEnabled(false); listView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)); frameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT)); listView.setAdapter(listAdapter); listView.setOnItemClickListener((view, position, x, y) -> { if (position == chatRow) { presentFragment(new NekoChatSettingsActivity()); } else if (position == generalRow) { presentFragment(new NekoGeneralSettingsActivity()); } else if (position == accountRow) { presentFragment(new NekoAccountSettingsActivity()); } else if (position == experimentRow) { presentFragment(new NekoExperimentalSettingsActivity()); } else if (position == channelRow) { MessagesController.getInstance(currentAccount).openByUserName("NekogramX", this, 1); } else if (position == translationRow) { Browser.openUrl(getParentActivity(), "https://hosted.weblate.org/engage/nekox/"); } else if (position == fdroidRow) { Browser.openUrl(getParentActivity(), "https://f-droid.org/packages/nekox.messenger"); } else if (position == googlePlayRow) { Browser.openUrl(getParentActivity(), "https://play.google.com/store/apps/details?id=nekox.messenger"); } else if (position == sourceCodeRow) { Browser.openUrl(getParentActivity(), "https://github.com/NekoX-Dev/NekoX"); } }); return fragmentView; } private void backupSettings() { try { File cacheFile = new File(ApplicationLoader.applicationContext.getCacheDir(), new Date().toLocaleString() + ".nekox-settings.json"); FileUtil.writeUtf8String(backupSettingsJson(), cacheFile); ShareUtil.shareFile(getParentActivity(), cacheFile); } catch (JSONException e) { AlertUtil.showSimpleAlert(getParentActivity(), e); } } private String backupSettingsJson() throws JSONException { JSONObject configJson = new JSONObject(); ArrayList userconfig = new ArrayList<>(); userconfig.add("saveIncomingPhotos"); userconfig.add("passcodeHash"); userconfig.add("passcodeType"); userconfig.add("passcodeHash"); userconfig.add("autoLockIn"); userconfig.add("useFingerprint"); spToJSON("userconfing", configJson, userconfig::contains); ArrayList mainconfig = new ArrayList<>(); mainconfig.add("saveToGallery"); mainconfig.add("autoplayGifs"); mainconfig.add("autoplayVideo"); mainconfig.add("mapPreviewType"); mainconfig.add("raiseToSpeak"); mainconfig.add("customTabs"); mainconfig.add("directShare"); mainconfig.add("shuffleMusic"); mainconfig.add("playOrderReversed"); mainconfig.add("inappCamera"); mainconfig.add("repeatMode"); mainconfig.add("fontSize"); mainconfig.add("bubbleRadius"); mainconfig.add("ivFontSize"); mainconfig.add("allowBigEmoji"); mainconfig.add("streamMedia"); mainconfig.add("saveStreamMedia"); mainconfig.add("smoothKeyboard"); mainconfig.add("pauseMusicOnRecord"); mainconfig.add("streamAllVideo"); mainconfig.add("streamMkv"); mainconfig.add("suggestStickers"); mainconfig.add("sortContactsByName"); mainconfig.add("sortFilesByName"); mainconfig.add("noSoundHintShowed"); mainconfig.add("directShareHash"); mainconfig.add("useThreeLinesLayout"); mainconfig.add("archiveHidden"); mainconfig.add("distanceSystemType"); mainconfig.add("loopStickers"); mainconfig.add("keepMedia"); mainconfig.add("noStatusBar"); mainconfig.add("lastKeepMediaCheckTime"); mainconfig.add("searchMessagesAsListHintShows"); mainconfig.add("searchMessagesAsListUsed"); mainconfig.add("stickersReorderingHintUsed"); mainconfig.add("textSelectionHintShows"); mainconfig.add("scheduledOrNoSoundHintShows"); mainconfig.add("lockRecordAudioVideoHint"); mainconfig.add("disableVoiceAudioEffects"); mainconfig.add("chatSwipeAction"); mainconfig.add("theme"); mainconfig.add("selectedAutoNightType"); mainconfig.add("autoNightScheduleByLocation"); mainconfig.add("autoNightBrighnessThreshold"); mainconfig.add("autoNightDayStartTime"); mainconfig.add("autoNightDayEndTime"); mainconfig.add("autoNightSunriseTime"); mainconfig.add("autoNightCityName"); mainconfig.add("autoNightSunsetTime"); mainconfig.add("autoNightLocationLatitude3"); mainconfig.add("autoNightLocationLongitude3"); mainconfig.add("autoNightLastSunCheckDay"); mainconfig.add("lang_code"); spToJSON("mainconfig", configJson, mainconfig::contains); spToJSON("themeconfig", configJson, null); spToJSON("nkmrcfg", configJson, null); return configJson.toString(4); } private static void spToJSON(String sp, JSONObject object, Function filter) throws JSONException { SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences(sp, Activity.MODE_PRIVATE); JSONObject jsonConfig = new JSONObject(); for (Map.Entry entry : preferences.getAll().entrySet()) { String key = entry.getKey(); if (filter != null && !filter.apply(key)) continue; if (entry.getValue() instanceof Long) { key = key + "_long"; } else if (entry.getValue() instanceof Float) { key = key + "_float"; } jsonConfig.put(key, entry.getValue()); } object.put(sp, jsonConfig); } public static void importSettings(Context context, File settingsFile) { AlertUtil.showConfirm(context, LocaleController.getString("ImportSettingsAlert", R.string.ImportSettingsAlert), R.drawable.baseline_security_24, LocaleController.getString("Import", R.string.Import), true, () -> importSettingsConfirmed(context, settingsFile)); } public static void importSettingsConfirmed(Context context, File settingsFile) { try { JsonObject configJson = GsonUtil.toJsonObject(FileUtil.readUtf8String(settingsFile)); importSettings(configJson); AlertDialog restart = new AlertDialog(context, 0); restart.setTitle(LocaleController.getString("NekoX", R.string.NekoX)); restart.setMessage(LocaleController.getString("RestartAppToTakeEffect", R.string.RestartAppToTakeEffect)); restart.setPositiveButton(LocaleController.getString("OK", R.string.OK), (__, ___) -> { ProcessPhoenix.triggerRebirth(context, new Intent(context, LaunchActivity.class)); }); restart.show(); } catch (Exception e) { AlertUtil.showSimpleAlert(context, e); } } @SuppressLint("ApplySharedPref") public static void importSettings(JsonObject configJson) throws JSONException { for (Map.Entry element : configJson.entrySet()) { SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences(element.getKey(), Activity.MODE_PRIVATE); SharedPreferences.Editor editor = preferences.edit(); for (Map.Entry config : ((JsonObject) element.getValue()).entrySet()) { String key = config.getKey(); JsonPrimitive value = (JsonPrimitive) config.getValue(); if (value.isBoolean()) { editor.putBoolean(key, value.getAsBoolean()); } else if (value.isNumber()) { boolean isLong = false; boolean isFloat = false; if (key.endsWith("_long")) { key = StringsKt.substringBeforeLast(key, "_long", key); isLong = true; } else if (key.endsWith("_float")) { key = StringsKt.substringBeforeLast(key, "_float", key); isFloat = true; } if (isLong) { editor.putLong(key, value.getAsLong()); } else if (isFloat) { editor.putFloat(key, value.getAsFloat()); } else { editor.putInt(key, value.getAsInt()); } } else { editor.putString(key, value.getAsString()); } } editor.commit(); } } @Override public void onResume() { super.onResume(); if (listAdapter != null) { listAdapter.notifyDataSetChanged(); } } private void updateRows() { rowCount = 0; categoriesRow = rowCount++; generalRow = rowCount++; accountRow = rowCount++; chatRow = rowCount++; experimentRow = rowCount++; categories2Row = rowCount++; aboutRow = rowCount++; channelRow = rowCount++; fdroidRow = rowCount++; /*if (ExternalGcm.checkPlayServices()) { googlePlayRow = rowCount++; } else {*/ googlePlayRow = -1; // } sourceCodeRow = rowCount++; translationRow = rowCount++; about2Row = rowCount++; if (listAdapter != null) { listAdapter.notifyDataSetChanged(); } } @Override public ArrayList getThemeDescriptions() { ArrayList themeDescriptions = new ArrayList<>(); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CELLBACKGROUNDCOLOR, new Class[]{EmptyCell.class, TextSettingsCell.class, TextCheckCell.class, HeaderCell.class, TextDetailSettingsCell.class, NotificationsCheckCell.class}, null, null, null, Theme.key_windowBackgroundWhite)); themeDescriptions.add(new ThemeDescription(fragmentView, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundGray)); themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_avatar_backgroundActionBarBlue)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_LISTGLOWCOLOR, null, null, null, null, Theme.key_avatar_backgroundActionBarBlue)); themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_ITEMSCOLOR, null, null, null, null, Theme.key_avatar_actionBarIconBlue)); themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_TITLECOLOR, null, null, null, null, Theme.key_actionBarDefaultTitle)); themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SELECTORCOLOR, null, null, null, null, Theme.key_avatar_actionBarSelectorBlue)); themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SUBMENUBACKGROUND, null, null, null, null, Theme.key_actionBarDefaultSubmenuBackground)); themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SUBMENUITEM, null, null, null, null, Theme.key_actionBarDefaultSubmenuItem)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_SELECTOR, null, null, null, null, Theme.key_listSelector)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{View.class}, Theme.dividerPaint, null, null, Theme.key_divider)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{ShadowSectionCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextSettingsCell.class}, new String[]{"valueTextView"}, null, null, null, Theme.key_windowBackgroundWhiteValueText)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{NotificationsCheckCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{NotificationsCheckCell.class}, new String[]{"valueTextView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText2)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{NotificationsCheckCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_switchTrack)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{NotificationsCheckCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_switchTrackChecked)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextCheckCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextCheckCell.class}, new String[]{"valueTextView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText2)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextCheckCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_switchTrack)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextCheckCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_switchTrackChecked)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{HeaderCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueHeader)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextDetailSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextDetailSettingsCell.class}, new String[]{"valueTextView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText2)); return themeDescriptions; } private class ListAdapter extends RecyclerListView.SelectionAdapter { private Context mContext; public ListAdapter(Context context) { mContext = context; } @Override public int getItemCount() { return rowCount; } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { switch (holder.getItemViewType()) { case 1: { if (position == about2Row) { holder.itemView.setBackground(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); } else { holder.itemView.setBackground(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); } break; } case 2: { TextCell textCell = (TextCell) holder.itemView; if (position == chatRow) { textCell.setTextAndIcon(LocaleController.getString("Chat", R.string.Chat), R.drawable.baseline_chat_bubble_24, true); } else if (position == generalRow) { textCell.setTextAndIcon(LocaleController.getString("General", R.string.General), R.drawable.baseline_palette_24, true); } else if (position == experimentRow) { textCell.setTextAndIcon(LocaleController.getString("Experiment", R.string.Experiment), R.drawable.baseline_star_24, true); } else if (position == accountRow) { textCell.setTextAndIcon(LocaleController.getString("Account", R.string.Account), R.drawable.baseline_person_24, true); } break; } case 3: { TextSettingsCell textCell = (TextSettingsCell) holder.itemView; if (position == channelRow) { textCell.setTextAndValue(LocaleController.getString("OfficialChannel", R.string.OfficialChannel), "@NekogramX", true); } else if (position == fdroidRow) { textCell.setText(LocaleController.getString("AppLinkFDroid", R.string.AppLinkFDroid), true); } else if (position == googlePlayRow) { textCell.setText(LocaleController.getString("GooglePlay", R.string.GooglePlay), true); } else if (position == sourceCodeRow) { textCell.setText(LocaleController.getString("SourceCode", R.string.SourceCode), true); } else if (position == translationRow) { textCell.setText(LocaleController.getString("TransSite", R.string.TransSite), true); } break; } case 4: { HeaderCell headerCell = (HeaderCell) holder.itemView; if (position == categoriesRow) { headerCell.setText(LocaleController.getString("Categories", R.string.Categories)); } else if (position == aboutRow) { headerCell.setText(LocaleController.getString("About", R.string.About)); } break; } } } @Override public boolean isEnabled(RecyclerView.ViewHolder holder) { int type = holder.getItemViewType(); return type == 2 || type == 3 || type == 6; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = null; switch (viewType) { case 1: view = new ShadowSectionCell(mContext); break; case 2: view = new TextCell(mContext); view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; case 3: view = new TextSettingsCell(mContext); view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; case 4: view = new HeaderCell(mContext); view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; case 5: view = new NotificationsCheckCell(mContext); view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; case 6: view = new TextDetailSettingsCell(mContext); view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; case 7: view = new TextInfoPrivacyCell(mContext); view.setBackground(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); break; } //noinspection ConstantConditions view.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT)); return new RecyclerListView.Holder(view); } @Override public int getItemViewType(int position) { if (position == categories2Row || position == about2Row) { return 1; } else if (position == chatRow || position == accountRow || position == generalRow || position == experimentRow) { return 2; } else if (position == categoriesRow || position == aboutRow) { return 4; } return 3; } } }