/* * Copyright 2014-2016 Fukurou Mishiranu * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.mishiranu.dashchan.preference.fragment; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Locale; import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.preference.Preference; import android.text.format.DateFormat; import chan.content.ChanLocator; import chan.http.HttpException; import chan.http.HttpHolder; import chan.http.HttpRequest; import chan.text.GroupParser; import chan.text.ParseException; import chan.util.CommonUtils; import com.mishiranu.dashchan.C; import com.mishiranu.dashchan.R; import com.mishiranu.dashchan.content.BackupManager; import com.mishiranu.dashchan.content.LocaleManager; import com.mishiranu.dashchan.content.async.AsyncManager; import com.mishiranu.dashchan.content.async.ReadUpdateTask; import com.mishiranu.dashchan.content.model.ErrorItem; import com.mishiranu.dashchan.preference.PreferencesActivity; import com.mishiranu.dashchan.util.Log; import com.mishiranu.dashchan.util.ToastUtils; public class AboutFragment extends BasePreferenceFragment { private Preference backupDataPreference; private Preference changelogPreference; private Preference checkForUpdatesPreference; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Preference statisticsPreference = makeButton(null, R.string.preference_statistics, 0, false); backupDataPreference = makeButton(null, R.string.preference_backup_data, R.string.preference_backup_data_summary, false); changelogPreference = makeButton(null, R.string.preference_changelog, 0, false); checkForUpdatesPreference = makeButton(null, R.string.preference_check_for_updates, 0, false); Preference licensePreference = makeButton(null, R.string.preference_licenses, R.string.preference_licenses_summary, false); makeButton(null, getString(R.string.preference_version), C.BUILD_VERSION + " (" + DateFormat.getDateFormat(getActivity()).format(C.BUILD_TIMESTAMP) + ")", true); Intent intent = new Intent(getActivity(), PreferencesActivity.class); intent.putExtra(PreferencesActivity.EXTRA_SHOW_FRAGMENT, StatisticsFragment.class.getName()); intent.putExtra(PreferencesActivity.EXTRA_NO_HEADERS, true); statisticsPreference.setIntent(intent); intent = new Intent(getActivity(), PreferencesActivity.class); intent.putExtra(PreferencesActivity.EXTRA_SHOW_FRAGMENT, TextFragment.class.getName()); intent.putExtra(PreferencesActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, TextFragment.createArguments(TextFragment.TYPE_LICENSES, null)); intent.putExtra(PreferencesActivity.EXTRA_NO_HEADERS, true); licensePreference.setIntent(intent); } @Override public boolean onPreferenceClick(Preference preference) { if (preference == backupDataPreference) { new BackupDialog().show(getFragmentManager(), BackupDialog.class.getName()); return true; } else if (preference == changelogPreference) { new ReadDialog(ReadDialog.TYPE_CHANGELOG).show(getFragmentManager(), ReadDialog.class.getName()); return true; } else if (preference == checkForUpdatesPreference) { new ReadDialog(ReadDialog.TYPE_UPDATE).show(getFragmentManager(), ReadDialog.class.getName()); return true; } return super.onPreferenceClick(preference); } public static class BackupDialog extends DialogFragment implements DialogInterface.OnClickListener { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { String[] items = getResources().getStringArray(R.array.preference_backup_data_choices); return new AlertDialog.Builder(getActivity()).setItems(items, this).create(); } @Override public void onClick(DialogInterface dialog, int which) { if (which == 0) { BackupManager.makeBackup(getActivity()); } else if (which == 1) { LinkedHashMap<File, String> filesMap = BackupManager.getAvailableBackups(getActivity()); if (filesMap != null && filesMap.size() > 0) { new RestoreFragment(filesMap).show(getFragmentManager(), RestoreFragment.class.getName()); } else { ToastUtils.show(getActivity(), R.string.message_no_backups); } } } } public static class RestoreFragment extends DialogFragment implements DialogInterface.OnClickListener { private static final String EXTRA_FILES = "files"; private static final String EXTRA_NAMES = "names"; public RestoreFragment() {} public RestoreFragment(LinkedHashMap<File, String> filesMap) { Bundle args = new Bundle(); ArrayList<String> files = new ArrayList<>(filesMap.size()); ArrayList<String> names = new ArrayList<>(filesMap.size()); for (LinkedHashMap.Entry<File, String> pair : filesMap.entrySet()) { files.add(pair.getKey().getAbsolutePath()); names.add(pair.getValue()); } args.putStringArrayList(EXTRA_FILES, files); args.putStringArrayList(EXTRA_NAMES, names); setArguments(args); } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { ArrayList<String> names = getArguments().getStringArrayList(EXTRA_NAMES); String[] items = CommonUtils.toArray(names, String.class); return new AlertDialog.Builder(getActivity()).setSingleChoiceItems(items, 0, null).setNegativeButton (android.R.string.cancel, null).setPositiveButton(android.R.string.ok, this).create(); } @Override public void onClick(DialogInterface dialog, int which) { int index = ((AlertDialog) dialog).getListView().getCheckedItemPosition(); File file = new File(getArguments().getStringArrayList(EXTRA_FILES).get(index)); BackupManager.loadBackup(getActivity(), file); } } public static class ReadDialog extends DialogFragment implements AsyncManager.Callback { private static final String EXTRA_TYPE = "type"; private static final int TYPE_CHANGELOG = 0; private static final int TYPE_UPDATE = 1; private static final String TASK_READ_CHANGELOG = "read_changelog"; private static final String TASK_READ_UPDATE = "read_update"; public ReadDialog() {} public ReadDialog(int type) { Bundle args = new Bundle(); args.putInt(EXTRA_TYPE, type); setArguments(args); } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { ProgressDialog dialog = new ProgressDialog(getActivity()); dialog.setMessage(getString(R.string.message_loading)); dialog.setCanceledOnTouchOutside(false); return dialog; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); switch (getArguments().getInt(EXTRA_TYPE)) { case TYPE_CHANGELOG: { AsyncManager.get(this).startTask(TASK_READ_CHANGELOG, this, null, false); break; } case TYPE_UPDATE: { AsyncManager.get(this).startTask(TASK_READ_UPDATE, this, null, false); break; } } } @Override public void onCancel(DialogInterface dialog) { super.onCancel(dialog); switch (getArguments().getInt(EXTRA_TYPE)) { case TYPE_CHANGELOG: { AsyncManager.get(this).cancelTask(TASK_READ_CHANGELOG, this); break; } case TYPE_UPDATE: { AsyncManager.get(this).cancelTask(TASK_READ_UPDATE, this); break; } } } private static class ReadUpdateHolder extends AsyncManager.Holder implements ReadUpdateTask.Callback { @Override public void onReadUpdateComplete(ReadUpdateTask.UpdateDataMap updateDataMap, ErrorItem errorItem) { storeResult(updateDataMap, errorItem); } } @Override public AsyncManager.Holder onCreateAndExecuteTask(String name, HashMap<String, Object> extra) { switch (name) { case TASK_READ_CHANGELOG: { ReadChangelogTask task = new ReadChangelogTask(getActivity()); task.executeOnExecutor(ReadChangelogTask.THREAD_POOL_EXECUTOR); return task.getHolder(); } case TASK_READ_UPDATE: { ReadUpdateHolder holder = new ReadUpdateHolder(); ReadUpdateTask task = new ReadUpdateTask(getActivity(), holder); task.executeOnExecutor(ReadChangelogTask.THREAD_POOL_EXECUTOR); return holder.attach(task); } } return null; } @Override public void onFinishTaskExecution(String name, AsyncManager.Holder holder) { dismissAllowingStateLoss(); switch (name) { case TASK_READ_CHANGELOG: { String content = holder.nextArgument(); ErrorItem errorItem = holder.nextArgument(); if (errorItem == null) { Intent intent = new Intent(getActivity(), PreferencesActivity.class); intent.putExtra(PreferencesActivity.EXTRA_SHOW_FRAGMENT, TextFragment.class.getName()); intent.putExtra(PreferencesActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, TextFragment.createArguments(TextFragment.TYPE_CHANGELOG, content)); intent.putExtra(PreferencesActivity.EXTRA_NO_HEADERS, true); startActivity(intent); } else { ToastUtils.show(getActivity(), errorItem); } break; } case TASK_READ_UPDATE: { ReadUpdateTask.UpdateDataMap updateDataMap = holder.nextArgument(); ErrorItem errorItem = holder.nextArgument(); if (updateDataMap != null) { startActivity(UpdateFragment.createUpdateIntent(getActivity(), updateDataMap)); } else { ToastUtils.show(getActivity(), errorItem); } break; } } } @Override public void onRequestTaskCancel(String name, Object task) { switch (name) { case TASK_READ_CHANGELOG: { ((ReadChangelogTask) task).cancel(); break; } case TASK_READ_UPDATE: { ((ReadUpdateTask) task).cancel(); break; } } } } private static class ReadChangelogTask extends AsyncManager.SimpleTask<Void, Void, Boolean> { private final HttpHolder holder = new HttpHolder(); private final Context context; private String result; private ErrorItem errorItem; public ReadChangelogTask(Context context) { this.context = context.getApplicationContext(); } @Override public Boolean doInBackground(Void... params) { String page = "Changelog-EN"; for (Locale locale : LocaleManager.getInstance().list(context)) { String language = locale.getLanguage(); if ("ru".equals(language)) { page = "Changelog-RU"; break; } } Uri uri = ChanLocator.getDefault().buildPathWithHost("github.com", "Mishiranu", "Dashchan", "wiki", page); try { String result = new HttpRequest(uri, holder).read().getString(); if (result != null) { result = ChangelogGroupCallback.parse(result); } if (result == null) { errorItem = new ErrorItem(ErrorItem.TYPE_UNKNOWN); return false; } else { this.result = result; return true; } } catch (HttpException e) { errorItem = e.getErrorItemAndHandle(); holder.disconnect(); return false; } finally { holder.cleanup(); } } @Override protected void onStoreResult(AsyncManager.Holder holder, Boolean result) { holder.storeResult(this.result, errorItem); } @Override public void cancel() { cancel(true); holder.interrupt(); } } private static class ChangelogGroupCallback implements GroupParser.Callback { private String result; public static String parse(String source) { ChangelogGroupCallback callback = new ChangelogGroupCallback(); try { GroupParser.parse(source, callback); } catch (ParseException e) { Log.persistent().stack(e); } return callback.result; } @Override public boolean onStartElement(GroupParser parser, String tagName, String attrs) { return "div".equals(tagName) && "markdown-body".equals(parser.getAttr(attrs, "class")); } @Override public void onEndElement(GroupParser parser, String tagName) {} @Override public void onText(GroupParser parser, String source, int start, int end) throws ParseException {} @Override public void onGroupComplete(GroupParser parser, String text) throws ParseException { result = text; throw new ParseException(); } } }