feat(settings): implement settings import
This commit is contained in:
parent
09a7da2952
commit
eea78302ab
|
@ -25,6 +25,7 @@ import com.google.gson.ToNumberPolicy;
|
||||||
|
|
||||||
import org.joinmastodon.android.BuildConfig;
|
import org.joinmastodon.android.BuildConfig;
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
|
import org.joinmastodon.android.MastodonApp;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.MastodonAPIController;
|
import org.joinmastodon.android.api.MastodonAPIController;
|
||||||
import org.joinmastodon.android.api.session.AccountSession;
|
import org.joinmastodon.android.api.session.AccountSession;
|
||||||
|
@ -61,6 +62,7 @@ import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class SettingsAboutAppFragment extends BaseSettingsFragment<Void> implements HasAccountID{
|
public class SettingsAboutAppFragment extends BaseSettingsFragment<Void> implements HasAccountID{
|
||||||
private static final String TAG="SettingsAboutAppFragment";
|
private static final String TAG="SettingsAboutAppFragment";
|
||||||
|
private static final int IMPORT_RESULT=314;
|
||||||
private ListItem<Void> mediaCacheItem, copyCrashLogItem;
|
private ListItem<Void> mediaCacheItem, copyCrashLogItem;
|
||||||
private CheckableListItem<Void> enablePreReleasesItem;
|
private CheckableListItem<Void> enablePreReleasesItem;
|
||||||
private AccountSession session;
|
private AccountSession session;
|
||||||
|
@ -68,7 +70,7 @@ public class SettingsAboutAppFragment extends BaseSettingsFragment<Void> impleme
|
||||||
private File crashLogFile=new File(MastodonApp.context.getFilesDir(), "crash.log");
|
private File crashLogFile=new File(MastodonApp.context.getFilesDir(), "crash.log");
|
||||||
|
|
||||||
// MOSHIDON
|
// MOSHIDON
|
||||||
private ListItem<Void> clearRecentEmojisItem, exportItem;
|
private ListItem<Void> clearRecentEmojisItem, exportItem, importItem;
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
@ -84,6 +86,7 @@ public class SettingsAboutAppFragment extends BaseSettingsFragment<Void> impleme
|
||||||
new ListItem<>(R.string.settings_tos, 0, R.drawable.ic_fluent_open_24_regular, i->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/terms")),
|
new ListItem<>(R.string.settings_tos, 0, R.drawable.ic_fluent_open_24_regular, i->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/terms")),
|
||||||
new ListItem<>(R.string.settings_privacy_policy, 0, R.drawable.ic_fluent_open_24_regular, i->UiUtils.launchWebBrowser(getActivity(), getString(R.string.privacy_policy_url)), 0, true),
|
new ListItem<>(R.string.settings_privacy_policy, 0, R.drawable.ic_fluent_open_24_regular, i->UiUtils.launchWebBrowser(getActivity(), getString(R.string.privacy_policy_url)), 0, true),
|
||||||
exportItem=new ListItem<>(R.string.export_settings_title, R.string.export_settings_summary, R.drawable.ic_fluent_arrow_export_24_filled, this::onExportClick),
|
exportItem=new ListItem<>(R.string.export_settings_title, R.string.export_settings_summary, R.drawable.ic_fluent_arrow_export_24_filled, this::onExportClick),
|
||||||
|
importItem=new ListItem<>(R.string.import_settings_title, R.string.import_settings_summary, R.drawable.ic_fluent_arrow_import_24_filled, this::onImportClick, 0, true),
|
||||||
clearRecentEmojisItem=new ListItem<>(R.string.mo_clear_recent_emoji, 0, this::onClearRecentEmojisClick),
|
clearRecentEmojisItem=new ListItem<>(R.string.mo_clear_recent_emoji, 0, this::onClearRecentEmojisClick),
|
||||||
mediaCacheItem=new ListItem<>(R.string.settings_clear_cache, 0, this::onClearMediaCacheClick),
|
mediaCacheItem=new ListItem<>(R.string.settings_clear_cache, 0, this::onClearMediaCacheClick),
|
||||||
new ListItem<>(getString(R.string.sk_settings_clear_timeline_cache), session.domain, this::onClearTimelineCacheClick),
|
new ListItem<>(getString(R.string.sk_settings_clear_timeline_cache), session.domain, this::onClearTimelineCacheClick),
|
||||||
|
@ -194,6 +197,113 @@ public class SettingsAboutAppFragment extends BaseSettingsFragment<Void> impleme
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onImportClick(ListItem<?> item){
|
||||||
|
new M3AlertDialogBuilder(getContext())
|
||||||
|
.setTitle(R.string.import_settings_confirm)
|
||||||
|
.setIcon(R.drawable.ic_fluent_warning_24_regular)
|
||||||
|
.setMessage(R.string.import_settings_confirm_body)
|
||||||
|
.setPositiveButton(R.string.ok, (dialogInterface, i) -> {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
||||||
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
|
intent.setType("application/json");
|
||||||
|
startActivityForResult(intent, IMPORT_RESULT);
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent data){
|
||||||
|
if(requestCode==IMPORT_RESULT && resultCode==Activity.RESULT_OK){
|
||||||
|
Uri uri=data.getData();
|
||||||
|
if(uri==null){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try{
|
||||||
|
InputStream inputStream=getContext().getContentResolver().openInputStream(uri);
|
||||||
|
if(inputStream==null)
|
||||||
|
return;
|
||||||
|
BufferedReader reader=new BufferedReader(new InputStreamReader(inputStream));
|
||||||
|
StringBuilder stringBuilder=new StringBuilder();
|
||||||
|
String line;
|
||||||
|
while((line=reader.readLine())!=null){
|
||||||
|
stringBuilder.append(line);
|
||||||
|
}
|
||||||
|
inputStream.close();
|
||||||
|
String jsonString=stringBuilder.toString();
|
||||||
|
|
||||||
|
Gson gson=new GsonBuilder().setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE).create();
|
||||||
|
JsonObject jsonObject=JsonParser.parseString(jsonString).getAsJsonObject();
|
||||||
|
|
||||||
|
//check if json has required attributes
|
||||||
|
if(!(jsonObject.has("versionName") && jsonObject.has("versionCode") && jsonObject.has("GlobalUserPreferences"))){
|
||||||
|
Toast.makeText(getContext(), getContext().getString(R.string.import_settings_failed), Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String versionName=jsonObject.get("versionName").getAsString();
|
||||||
|
int versionCode=jsonObject.get("versionCode").getAsInt();
|
||||||
|
Log.i(TAG, "onActivityResult: Reading exported settings ("+versionName+" "+versionCode+")");
|
||||||
|
|
||||||
|
// retrieve GlobalUserPreferences
|
||||||
|
Map<String, ?> jsonGlobalPrefs=gson.fromJson(jsonObject.getAsJsonObject("GlobalUserPreferences"), Map.class);
|
||||||
|
SharedPreferences.Editor globalPrefsEditor=GlobalUserPreferences.getPrefs().edit();
|
||||||
|
for(String key : jsonGlobalPrefs.keySet()){
|
||||||
|
Object value=jsonGlobalPrefs.get(key);
|
||||||
|
if(value==null)
|
||||||
|
continue;
|
||||||
|
Log.e(TAG, "onActivityResult: " + key + ":" + value);
|
||||||
|
savePrefValue(globalPrefsEditor, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// retrieve LocalPreferences for all logged in accounts
|
||||||
|
//TODO: maybe show a dialog for which accounts to import?
|
||||||
|
for(AccountSession accountSession : AccountSessionManager.getInstance().getLoggedInAccounts()){
|
||||||
|
if(!jsonObject.has(accountSession.self.id))
|
||||||
|
continue;
|
||||||
|
Map<String, ?> prefs=gson.fromJson(jsonObject.getAsJsonObject(accountSession.self.id), Map.class);
|
||||||
|
|
||||||
|
SharedPreferences.Editor prefEditor=accountSession.getRawLocalPreferences().edit();
|
||||||
|
for(String key : prefs.keySet()){
|
||||||
|
Object value=prefs.get(key);
|
||||||
|
if(value==null)
|
||||||
|
continue;
|
||||||
|
savePrefValue(prefEditor, key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// restart app to apply new preferences
|
||||||
|
// https://stackoverflow.com/a/46848226
|
||||||
|
PackageManager packageManager=getContext().getPackageManager();
|
||||||
|
Intent intent=packageManager.getLaunchIntentForPackage(getContext().getPackageName());
|
||||||
|
ComponentName componentName=intent.getComponent();
|
||||||
|
Intent mainIntent=Intent.makeRestartActivityTask(componentName);
|
||||||
|
// Required for API 34 and later
|
||||||
|
// Ref: https://developer.android.com/about/versions/14/behavior-changes-14#safer-intents
|
||||||
|
mainIntent.setPackage(getContext().getPackageName());
|
||||||
|
getContext().startActivity(mainIntent);
|
||||||
|
Runtime.getRuntime().exit(0);
|
||||||
|
}catch(IOException e){
|
||||||
|
Log.w(TAG, e);
|
||||||
|
Toast.makeText(getContext(), getContext().getString(R.string.import_settings_failed), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void savePrefValue(SharedPreferences.Editor editor, String key, Object value) {
|
||||||
|
if(value.getClass().equals(Boolean.class))
|
||||||
|
editor.putBoolean(key, (Boolean) value);
|
||||||
|
// gson parses all numbers either long (for int) or double (the rest)
|
||||||
|
else if(value.getClass().equals(Long.class))
|
||||||
|
editor.putInt(key, ((Long) value).intValue());
|
||||||
|
else if(value.getClass().equals(Double.class))
|
||||||
|
editor.putFloat(key, ((Double) value).floatValue());
|
||||||
|
else
|
||||||
|
editor.putString(key, String.valueOf(value));
|
||||||
|
//explicitly immediately since the app will restarted soon after
|
||||||
|
// and it may not have the time to write the values in the background
|
||||||
|
editor.commit();
|
||||||
|
}
|
||||||
|
|
||||||
private void updateMediaCacheItem(){
|
private void updateMediaCacheItem(){
|
||||||
long size=ImageCache.getInstance(getActivity()).getDiskCache().size();
|
long size=ImageCache.getInstance(getActivity()).getDiskCache().size();
|
||||||
mediaCacheItem.subtitle=UiUtils.formatFileSize(getActivity(), size, false);
|
mediaCacheItem.subtitle=UiUtils.formatFileSize(getActivity(), size, false);
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M21.25,4.5a0.75,0.75 0,0 1,0.743 0.648L22,5.25v13.5a0.75,0.75 0,0 1,-1.493 0.102l-0.007,-0.102L20.5,5.25a0.75,0.75 0,0 1,0.75 -0.75ZM12.21,6.387 L12.293,6.293a1,1 0,0 1,1.32 -0.083l0.094,0.083 4.997,4.998a1,1 0,0 1,0.083 1.32l-0.083,0.093 -4.996,5.004a1,1 0,0 1,-1.499 -1.32l0.083,-0.094L15.581,13L3,13a1,1 0,0 1,-0.993 -0.883L2,12a1,1 0,0 1,0.883 -0.993L3,11h12.584l-3.291,-3.293a1,1 0,0 1,-0.083 -1.32l0.083,-0.094 -0.083,0.094Z"
|
||||||
|
android:fillColor="#212121"/>
|
||||||
|
</vector>
|
|
@ -120,8 +120,13 @@
|
||||||
<string name="mo_blocked_accounts">Blocked accounts</string>
|
<string name="mo_blocked_accounts">Blocked accounts</string>
|
||||||
<!-- <string name="mo_blocks">Blocks</string>-->
|
<!-- <string name="mo_blocks">Blocks</string>-->
|
||||||
<string name="mo_mute_notifications">Hide notifications from this user?</string>
|
<string name="mo_mute_notifications">Hide notifications from this user?</string>
|
||||||
|
<string name="import_settings_confirm">Confirm to import settings?</string>
|
||||||
|
<string name="import_settings_confirm_body">All current settings and timelines will be overwritten! This action cannot be undone.</string>
|
||||||
|
<string name="import_settings_failed">Failed to import settings</string>
|
||||||
<string name="export_settings_share">Export Settings</string>
|
<string name="export_settings_share">Export Settings</string>
|
||||||
<string name="export_settings_fail">Failed to export settings</string>
|
<string name="export_settings_fail">Failed to export settings</string>
|
||||||
<string name="export_settings_title">Export settings</string>
|
<string name="export_settings_title">Export settings</string>
|
||||||
<string name="export_settings_summary">Export all logged-in accounts\' settings and timelines</string>
|
<string name="export_settings_summary">Export all logged-in accounts\' settings and timelines</string>
|
||||||
|
<string name="import_settings_title">Import settings</string>
|
||||||
|
<string name="import_settings_summary">Import previously exported settings and timelines</string>
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in New Issue