/* * Copyright 2016 Christian Basler * * 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 ch.dissem.apps.abit import android.app.Activity import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.ServiceConnection import android.os.Bundle import android.os.IBinder import android.widget.Toast import androidx.core.content.FileProvider.getUriForFile import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreferenceCompat import ch.dissem.apps.abit.service.BatchProcessorService import ch.dissem.apps.abit.service.SimpleJob import ch.dissem.apps.abit.service.Singleton import ch.dissem.apps.abit.util.Exports import ch.dissem.apps.abit.util.network import ch.dissem.apps.abit.util.preferences import ch.dissem.bitmessage.entity.Plaintext import com.mikepenz.aboutlibraries.Libs import com.mikepenz.aboutlibraries.LibsBuilder import org.jetbrains.anko.doAsync import org.jetbrains.anko.indeterminateProgressDialog import org.jetbrains.anko.startActivity import org.jetbrains.anko.uiThread import java.util.* /** * @author Christian Basler */ class SettingsFragment : PreferenceFragmentCompat() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.preferences, rootKey) findPreference("about")?.onPreferenceClickListener = aboutClickListener() findPreference("cleanup")?.let { it.onPreferenceClickListener = cleanupClickListener(it) } findPreference("export")?.onPreferenceClickListener = exportClickListener() findPreference("import")?.onPreferenceClickListener = importClickListener() findPreference("status")?.onPreferenceClickListener = statusClickListener() connectivityChangeListener().let { findPreference("wifi_only")?.onPreferenceChangeListener = it findPreference("require_charging")?.onPreferenceChangeListener = it } val emulateConversations = findPreference("emulate_conversations") as? SwitchPreferenceCompat val conversationInit = findPreference("emulate_conversations_initialize") emulateConversations?.onPreferenceChangeListener = emulateConversationChangeListener(conversationInit) conversationInit?.onPreferenceClickListener = conversationInitClickListener() conversationInit?.isEnabled = emulateConversations?.isChecked ?: false } private fun aboutClickListener() = Preference.OnPreferenceClickListener { (activity as? MainActivity)?.let { activity -> val libsBuilder = LibsBuilder() .withActivityTitle(activity.getString(R.string.about)) .withActivityStyle(Libs.ActivityStyle.LIGHT_DARK_TOOLBAR) .withAboutIconShown(true) .withAboutVersionShown(true) .withAboutDescription(getString(R.string.about_app)) if (activity.hasDetailPane) { activity.setDetailView(libsBuilder.supportFragment()) } else { libsBuilder.start(activity) } } return@OnPreferenceClickListener true } private fun cleanupClickListener(cleanup: Preference) = Preference.OnPreferenceClickListener { val ctx = activity?.applicationContext ?: throw IllegalStateException("Context not available") cleanup.isEnabled = false Toast.makeText(ctx, R.string.cleanup_notification_start, Toast.LENGTH_SHORT).show() doAsync { val bmc = Singleton.getBitmessageContext(ctx) bmc.internals.nodeRegistry.clear() bmc.cleanup() ctx.preferences.cleanupExportDirectory() uiThread { Toast.makeText( ctx, R.string.cleanup_notification_end, Toast.LENGTH_LONG ).show() cleanup.isEnabled = true } } return@OnPreferenceClickListener true } private fun exportClickListener() = Preference.OnPreferenceClickListener { val ctx = activity ?: throw IllegalStateException("No context available") ctx.indeterminateProgressDialog(R.string.export_data_summary, R.string.export_data).apply { doAsync { val exportDirectory = ctx.preferences.exportDirectory exportDirectory.mkdirs() val file = Exports.exportData(exportDirectory, ctx) val contentUri = getUriForFile(ctx, "ch.dissem.apps.abit.fileprovider", file) val intent = Intent(android.content.Intent.ACTION_SEND) intent.type = "application/zip" intent.putExtra(Intent.EXTRA_SUBJECT, "abit-export.zip") intent.putExtra(Intent.EXTRA_STREAM, contentUri) startActivityForResult(Intent.createChooser(intent, ""), WRITE_EXPORT_REQUEST_CODE) uiThread { dismiss() } } } return@OnPreferenceClickListener true } private fun importClickListener() = Preference.OnPreferenceClickListener { val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) intent.addCategory(Intent.CATEGORY_OPENABLE) intent.type = "application/zip" startActivityForResult(intent, READ_IMPORT_REQUEST_CODE) return@OnPreferenceClickListener true } private fun statusClickListener() = Preference.OnPreferenceClickListener { val activity = activity as MainActivity if (activity.hasDetailPane) { activity.setDetailView(StatusFragment()) } else { activity.startActivity() } return@OnPreferenceClickListener true } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { val ctx = activity ?: throw IllegalStateException("No context available") when (requestCode) { WRITE_EXPORT_REQUEST_CODE -> ctx.preferences.cleanupExportDirectory() READ_IMPORT_REQUEST_CODE -> { if (resultCode == Activity.RESULT_OK && data?.data != null) { ctx.indeterminateProgressDialog(R.string.import_data_summary, R.string.import_data).apply { doAsync { Exports.importData(data.data!!, ctx) uiThread { dismiss() } } } } } } } override fun onAttach(ctx: Context?) { super.onAttach(ctx) ctx?.let { if (it is MainActivity) { it.floatingActionButton?.hide() it.updateTitle(getString(R.string.settings)) } } } private val connection = object : ServiceConnection { override fun onServiceConnected(name: ComponentName, service: IBinder) { if (service is BatchProcessorService.BatchBinder) { val messageRepo = Singleton.getMessageRepository(service.service) val conversationService = Singleton.getConversationService(service.service) service.process( SimpleJob( messageRepo.count(), { messageRepo.findNextLegacyMessages(it) }, { msg -> if (msg.encoding == Plaintext.Encoding.SIMPLE) { conversationService.getSubject(listOf(msg))?.let { subject -> msg.conversationId = UUID.nameUUIDFromBytes(subject.toByteArray()) messageRepo.save(msg) Thread.yield() } } }, R.drawable.ic_notification_batch, R.string.emulate_conversations_batch ) ) } } override fun onServiceDisconnected(name: ComponentName) { } } private fun conversationInitClickListener() = Preference.OnPreferenceClickListener { val ctx = activity?.applicationContext ?: throw IllegalStateException("Context not available") ctx.bindService(Intent(ctx, BatchProcessorService::class.java), connection, Context.BIND_AUTO_CREATE) true } private fun emulateConversationChangeListener(conversationInit: Preference?) = Preference.OnPreferenceChangeListener { _, newValue -> conversationInit?.isEnabled = newValue as Boolean true } private fun connectivityChangeListener() = Preference.OnPreferenceChangeListener { _, _ -> activity?.network?.scheduleNodeStart() true } // The why-is-it-so-damn-hard-to-group-preferences section // FIXME: maybe this is once again necessary, maybe not. Test! // override fun getCallbackFragment(): Fragment = this // // override fun onPreferenceStartScreen( // preferenceFragmentCompat: PreferenceFragment, // preferenceScreen: PreferenceScreen // ): Boolean { // fragmentManager?.beginTransaction()?.let { ft -> // val fragment = SettingsFragment() // fragment.arguments = Bundle().apply { // putString(PreferenceFragment.ARG_PREFERENCE_ROOT, preferenceScreen.key) // } // ft.add(R.id.item_list, fragment, preferenceScreen.key) // ft.addToBackStack(preferenceScreen.key) // ft.commit() // } // return true // } // // override fun onViewCreated(view: View, savedInstanceState: Bundle?) { // super.onViewCreated(view, savedInstanceState) // context?.let { ctx -> view.setBackgroundColor(ContextCompat.getColor(ctx, R.color.contentBackground)) } // } // End of the why-is-it-so-damn-hard-to-group-preferences section // Afterthought: here it looks so simple: https://developer.android.com/guide/topics/ui/settings.html // Remind me, why do we need to use PreferenceFragmentCompat? companion object { const val WRITE_EXPORT_REQUEST_CODE = 1 const val READ_IMPORT_REQUEST_CODE = 2 } }