Skip to content

Commit

Permalink
custom data path
Browse files Browse the repository at this point in the history
  • Loading branch information
levinli303 committed Apr 23, 2020
1 parent 4cca801 commit bfd0449
Show file tree
Hide file tree
Showing 10 changed files with 327 additions and 10 deletions.
2 changes: 2 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,6 @@ dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.2'
}
2 changes: 2 additions & 0 deletions app/src/main/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,8 @@ list(APPEND CELESTIA_SOURCES
${CELESTIA_SRC_DIR}/celengine/dsoname.h
${CELESTIA_SRC_DIR}/celengine/dsooctree.cpp
${CELESTIA_SRC_DIR}/celengine/dsooctree.h
${CELESTIA_SRC_DIR}/celengine/dsorenderer.cpp
${CELESTIA_SRC_DIR}/celengine/dsorenderer.h
${CELESTIA_SRC_DIR}/celengine/frame.cpp
${CELESTIA_SRC_DIR}/celengine/frame.h
${CELESTIA_SRC_DIR}/celengine/framebuffer.cpp
Expand Down
109 changes: 103 additions & 6 deletions app/src/main/java/space/celestia/mobilecelestia/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import android.app.TimePickerDialog
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.DocumentsContract
import android.util.Log
import android.view.View
import android.view.ViewGroup
Expand Down Expand Up @@ -83,7 +84,8 @@ class MainActivity : AppCompatActivity(),
DatePickerDialog.OnDateSetListener,
AboutFragment.Listener,
AppStatusReporter.Listener,
CelestiaFragment.Listener {
CelestiaFragment.Listener,
SettingsDataLocationFragment.Listener {

private val preferenceManager by lazy { PreferenceManager(this, "celestia") }
private val settingManager by lazy { PreferenceManager(this, "celestia_setting") }
Expand All @@ -96,14 +98,30 @@ class MainActivity : AppCompatActivity(),
private var readyForInteraction = false
private var scriptOrURLPath: String? = null

private val celestiaConfigFilePath: String
get() {
val custom = customConfigFilePath
if (custom != null)
return custom
return "$celestiaParentPath/$CELESTIA_DATA_FOLDER_NAME/$CELESTIA_CFG_NAME"
}

private val celestiaDataDirPath: String
get() {
val custom = customDataDirPath
if (custom != null)
return custom
return "$celestiaParentPath/$CELESTIA_DATA_FOLDER_NAME"
}

@SuppressLint("CheckResult")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

AppCenter.start(
application, "d1108985-aa25-4fb5-9269-31a70a87d28e",
Analytics::class.java, Crashes::class.java
)
// AppCenter.start(
// application, "d1108985-aa25-4fb5-9269-31a70a87d28e",
// Analytics::class.java, Crashes::class.java
// )

Crashes.getMinidumpDirectory().thenAccept { path ->
if (path != null) {
Expand Down Expand Up @@ -181,6 +199,27 @@ class MainActivity : AppCompatActivity(),

override fun celestiaLoadingFailed() {
AppStatusReporter.shared().updateStatus("Loading Celestia failed...")
if (customDataDirPath != null || customConfigFilePath != null) {
// Fallback to default
setConfigFilePath(null)
setDataDirectoryPath(null)
runOnUiThread {
removeCelestiaFragment()
showAlert(CelestiaString("Error loading data, fallback to original configuration.", "")) {
loadLibrarySuccess()
}
}
} else {
runOnUiThread {
removeCelestiaFragment()
}
}
}

private fun removeCelestiaFragment() {
supportFragmentManager.findFragmentById(R.id.celestia_fragment_container)?.let {
supportFragmentManager.beginTransaction().remove(it).commitAllowingStateLoss()
}
}

private fun createCopyAssetObservable(): Observable<String> {
Expand Down Expand Up @@ -208,8 +247,14 @@ class MainActivity : AppCompatActivity(),
private fun createLoadLibraryObservable(): Observable<String> {
return Observable.create {
it.onNext("Loading library...")

System.loadLibrary("celestia")
CelestiaAppCore.initGL()

// Also read custom paths here
customConfigFilePath = preferenceManager[PreferenceManager.PredefinedKey.ConfigFilePath]
customDataDirPath = preferenceManager[PreferenceManager.PredefinedKey.DataDirPath]

it.onComplete()
}
}
Expand Down Expand Up @@ -434,7 +479,7 @@ class MainActivity : AppCompatActivity(),

private fun loadLibrarySuccess() {
// Add gl fragment
val celestiaFragment = CelestiaFragment.newInstance("$celestiaParentPath/$CELESTIA_DATA_FOLDER_NAME", "$celestiaParentPath/$CELESTIA_DATA_FOLDER_NAME/$CELESTIA_CFG_NAME", addonPath)
val celestiaFragment = CelestiaFragment.newInstance(celestiaDataDirPath, celestiaConfigFilePath, addonPath)
supportFragmentManager
.beginTransaction()
.add(R.id.celestia_fragment_container, celestiaFragment)
Expand Down Expand Up @@ -704,6 +749,52 @@ class MainActivity : AppCompatActivity(),
}
}

override fun onDataLocationNeedReset() {
setConfigFilePath(null)
setDataDirectoryPath(null)
reloadSettings()
}

override fun onDataLocationRequested(dataType: DataType) {
if (!RxPermissions(this).isGranted(android.Manifest.permission.READ_EXTERNAL_STORAGE)) {
// No permission to read
return
}
when (dataType) {
DataType.Config -> {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.type = "*/*"
startActivityForResult(intent, CONFIG_FILE_REQUEST)
}
DataType.DataDirectory -> {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
startActivityForResult(intent, DATA_DIR_REQUEST)
}
}
}

private fun setDataDirectoryPath(path: String?) {
preferenceManager[PreferenceManager.PredefinedKey.DataDirPath] = path
customDataDirPath = path
}

private fun setConfigFilePath(path: String?) {
preferenceManager[PreferenceManager.PredefinedKey.ConfigFilePath] = path
customConfigFilePath = null
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
val uri = data?.data
if (uri == null) { return }
if (requestCode == CONFIG_FILE_REQUEST) {
reloadSettings()
} else if (requestCode == DATA_DIR_REQUEST) {
val docUri = DocumentsContract.buildDocumentUriUsingTree(uri, DocumentsContract.getDocumentId(uri))
reloadSettings()
}
}

override fun celestiaFragmentDidRequestActionMenu() {
showToolbar()
}
Expand Down Expand Up @@ -838,10 +929,16 @@ class MainActivity : AppCompatActivity(),
private const val CELESTIA_CFG_NAME = "celestia.cfg"
private const val CELESTIA_EXTRA_FOLDER_NAME = "CelestiaResources/extras"

private const val DATA_DIR_REQUEST = 1
private const val CONFIG_FILE_REQUEST = 2

private const val TAG = "MainActivity"

private var firstInstance = true

var customDataDirPath: String? = null
var customConfigFilePath: String? = null

init {
System.loadLibrary("nativecrashhandler")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* SettingsDataLocationFragment.kt
*
* Copyright (C) 2001-2020, the Celestia Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*/

package space.celestia.mobilecelestia.settings

import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import space.celestia.mobilecelestia.MainActivity
import space.celestia.mobilecelestia.R
import space.celestia.mobilecelestia.utils.CelestiaString

class SettingsDataLocationFragment : SettingsBaseFragment() {

private var listener: Listener? = null

private val listAdapter by lazy { SettingsDataLocationRecyclerViewAdapter(listener) }

override val title: String
get() = CelestiaString("Data Location", "")

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view =
inflater.inflate(R.layout.fragment_settings_current_time_list, container, false)

// Set the adapter
if (view is RecyclerView) {
with(view) {
layoutManager = LinearLayoutManager(context)
listAdapter.update(customConfig, customDataDir)
adapter = listAdapter
}
}
return view
}

override fun onAttach(context: Context) {
super.onAttach(context)
if (context is Listener) {
listener = context
} else {
throw RuntimeException("$context must implement SettingsDataLocationFragment.Listener")
}
}

override fun onDetach() {
super.onDetach()
listener = null
}

override fun reload() {
listAdapter.update(customConfig, customDataDir)
listAdapter.notifyDataSetChanged()
}

interface Listener {
fun onDataLocationNeedReset()
fun onDataLocationRequested(dataType: DataType)
}

companion object {
val customConfig: Boolean
get() = MainActivity.customConfigFilePath != null
val customDataDir: Boolean
get() = MainActivity.customDataDirPath != null

@JvmStatic
fun newInstance() =
SettingsDataLocationFragment()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* SettingsDataLocationRecyclerViewAdapter.kt
*
* Copyright (C) 2001-2020, the Celestia Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*/

package space.celestia.mobilecelestia.settings

import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import space.celestia.mobilecelestia.common.CommonSectionV2
import space.celestia.mobilecelestia.common.CommonTextViewHolder
import space.celestia.mobilecelestia.common.RecyclerViewItem
import space.celestia.mobilecelestia.common.SeparatorHeaderRecyclerViewAdapter
import space.celestia.mobilecelestia.core.CelestiaAppCore
import space.celestia.mobilecelestia.utils.CelestiaString

enum class DataType {
Config, DataDirectory;
}

interface DataLocationItem : RecyclerViewItem {
val title: String
val subtitle: String?
}

interface DataLocationDisplayItem : DataLocationItem {
val custom: Boolean
val type: DataType

override val title: String
get() = if (type == DataType.Config) CelestiaString("Config File", "") else CelestiaString("Data Directory", "")

override val subtitle: String?
get() = if (custom) CelestiaString("Custom", "") else CelestiaString("Default", "")
}

class SpecificDataLocationItem(override val custom: Boolean, override val type: DataType) : DataLocationDisplayItem

class ResetDataLocationItem : DataLocationItem {
override val title: String
get() = CelestiaString("Reset to Default", "")
override val subtitle: String?
get() = null
}

class SettingsDataLocationRecyclerViewAdapter(
private val listener: SettingsDataLocationFragment.Listener?
) : SeparatorHeaderRecyclerViewAdapter(listOf()) {

override fun onItemSelected(item: RecyclerViewItem) {
if (item is ResetDataLocationItem) {
listener?.onDataLocationNeedReset()
} else if (item is DataLocationDisplayItem) {
listener?.onDataLocationRequested(item.type)
}
}

override fun itemViewType(item: RecyclerViewItem): Int {
if (item is DataLocationItem)
return SETTING_ITEM
return super.itemViewType(item)
}

override fun createVH(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
if (viewType == SETTING_ITEM) {
return CommonTextViewHolder(parent)
}
return super.createVH(parent, viewType)
}

override fun bindVH(holder: RecyclerView.ViewHolder, item: RecyclerViewItem) {
if (item is DataLocationItem && holder is CommonTextViewHolder) {
val core = CelestiaAppCore.shared()
holder.title.text = item.title
holder.detail.visibility = View.VISIBLE
holder.detail.text = item.subtitle
return
}
super.bindVH(holder, item)
}

fun update(customConfig: Boolean, customDataDir: Boolean) {
val sections = ArrayList<CommonSectionV2>()
sections.add(CommonSectionV2(listOf(SpecificDataLocationItem(customConfig, DataType.Config),
SpecificDataLocationItem(customDataDir, DataType.DataDirectory)
), "", CelestiaString("Configuration will take effect after a restart.", "")))
sections.add(CommonSectionV2(listOf(ResetDataLocationItem()), null))
updateSectionsWithHeader(sections)
}

private companion object {
const val SETTING_ITEM = 0
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ class SettingsFragment : Fragment() {
is SettingsAboutItem -> {
push(AboutFragment.newInstance(), item.name)
}
is SettingsDataLocationItem -> {
push(SettingsDataLocationFragment.newInstance(), item.name)
}
}
}

Expand Down
Loading

0 comments on commit bfd0449

Please sign in to comment.