mirror of
https://github.com/ultrasonic/ultrasonic
synced 2025-02-07 15:28:40 +01:00
Migrated parts from SubsonicTabActivity, fixed theme changes
This commit is contained in:
parent
a395bd6feb
commit
f0917820cb
@ -101,8 +101,6 @@ public class SubsonicTabActivity extends ResultActivity
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle)
|
||||
{
|
||||
setUncaughtExceptionHandler();
|
||||
Util.applyTheme(this);
|
||||
super.onCreate(bundle);
|
||||
|
||||
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
||||
@ -418,185 +416,6 @@ public class SubsonicTabActivity extends ResultActivity
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(final int id)
|
||||
{
|
||||
if (id == DIALOG_ASK_FOR_SHARE_DETAILS)
|
||||
{
|
||||
final LayoutInflater layoutInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
|
||||
final View layout = layoutInflater.inflate(R.layout.share_details, (ViewGroup) findViewById(R.id.share_details));
|
||||
|
||||
if (layout != null)
|
||||
{
|
||||
shareDescription = (EditText) layout.findViewById(R.id.share_description);
|
||||
hideDialogCheckBox = (CheckBox) layout.findViewById(R.id.hide_dialog);
|
||||
noExpirationCheckBox = (CheckBox) layout.findViewById(R.id.timeSpanDisableCheckBox);
|
||||
saveAsDefaultsCheckBox = (CheckBox) layout.findViewById(R.id.save_as_defaults);
|
||||
timeSpanPicker = (TimeSpanPicker) layout.findViewById(R.id.date_picker);
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.share_set_share_options);
|
||||
|
||||
builder.setPositiveButton(R.string.common_save, new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(final DialogInterface dialog, final int clickId)
|
||||
{
|
||||
if (!noExpirationCheckBox.isChecked())
|
||||
{
|
||||
TimeSpan timeSpan = timeSpanPicker.getTimeSpan();
|
||||
TimeSpan now = TimeSpan.getCurrentTime();
|
||||
shareDetails.Expiration = now.add(timeSpan).getTotalMilliseconds();
|
||||
}
|
||||
|
||||
shareDetails.Description = String.valueOf(shareDescription.getText());
|
||||
|
||||
if (hideDialogCheckBox.isChecked())
|
||||
{
|
||||
Util.setShouldAskForShareDetails(SubsonicTabActivity.this, false);
|
||||
}
|
||||
|
||||
if (saveAsDefaultsCheckBox.isChecked())
|
||||
{
|
||||
String timeSpanType = timeSpanPicker.getTimeSpanType();
|
||||
int timeSpanAmount = timeSpanPicker.getTimeSpanAmount();
|
||||
Util.setDefaultShareExpiration(SubsonicTabActivity.this, !noExpirationCheckBox.isChecked() && timeSpanAmount > 0 ? String.format("%d:%s", timeSpanAmount, timeSpanType) : "");
|
||||
Util.setDefaultShareDescription(SubsonicTabActivity.this, shareDetails.Description);
|
||||
}
|
||||
|
||||
share();
|
||||
}
|
||||
});
|
||||
|
||||
builder.setNegativeButton(R.string.common_cancel, new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(final DialogInterface dialog, final int clickId)
|
||||
{
|
||||
shareDetails = null;
|
||||
dialog.cancel();
|
||||
}
|
||||
});
|
||||
builder.setView(layout);
|
||||
builder.setCancelable(true);
|
||||
|
||||
timeSpanPicker.setTimeSpanDisableText(getResources().getString(R.string.no_expiration));
|
||||
|
||||
noExpirationCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton compoundButton, boolean b)
|
||||
{
|
||||
timeSpanPicker.setEnabled(!b);
|
||||
}
|
||||
});
|
||||
|
||||
String defaultDescription = Util.getDefaultShareDescription(this);
|
||||
String timeSpan = Util.getDefaultShareExpiration(this);
|
||||
|
||||
String[] split = COMPILE.split(timeSpan);
|
||||
|
||||
if (split.length == 2)
|
||||
{
|
||||
int timeSpanAmount = Integer.parseInt(split[0]);
|
||||
String timeSpanType = split[1];
|
||||
|
||||
if (timeSpanAmount > 0)
|
||||
{
|
||||
noExpirationCheckBox.setChecked(false);
|
||||
timeSpanPicker.setEnabled(true);
|
||||
timeSpanPicker.setTimeSpanAmount(String.valueOf(timeSpanAmount));
|
||||
timeSpanPicker.setTimeSpanType(timeSpanType);
|
||||
}
|
||||
else
|
||||
{
|
||||
noExpirationCheckBox.setChecked(true);
|
||||
timeSpanPicker.setEnabled(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
noExpirationCheckBox.setChecked(true);
|
||||
timeSpanPicker.setEnabled(false);
|
||||
}
|
||||
|
||||
shareDescription.setText(defaultDescription);
|
||||
|
||||
return builder.create();
|
||||
}
|
||||
else
|
||||
{
|
||||
return super.onCreateDialog(id);
|
||||
}
|
||||
}
|
||||
|
||||
public void createShare(final List<Entry> entries)
|
||||
{
|
||||
boolean askForDetails = Util.getShouldAskForShareDetails(this);
|
||||
|
||||
shareDetails = new ShareDetails();
|
||||
shareDetails.Entries = entries;
|
||||
|
||||
if (askForDetails)
|
||||
{
|
||||
showDialog(DIALOG_ASK_FOR_SHARE_DETAILS);
|
||||
}
|
||||
else
|
||||
{
|
||||
shareDetails.Description = Util.getDefaultShareDescription(this);
|
||||
shareDetails.Expiration = TimeSpan.getCurrentTime().add(Util.getDefaultShareExpirationInMillis(this)).getTotalMilliseconds();
|
||||
share();
|
||||
}
|
||||
}
|
||||
|
||||
public void share()
|
||||
{
|
||||
BackgroundTask<Share> task = new TabActivityBackgroundTask<Share>(this, true)
|
||||
{
|
||||
@Override
|
||||
protected Share doInBackground() throws Throwable
|
||||
{
|
||||
List<String> ids = new ArrayList<String>();
|
||||
|
||||
if (shareDetails.Entries.isEmpty())
|
||||
{
|
||||
ids.add(getIntent().getStringExtra(Constants.INTENT_EXTRA_NAME_ID));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (Entry entry : shareDetails.Entries)
|
||||
{
|
||||
ids.add(entry.getId());
|
||||
}
|
||||
}
|
||||
|
||||
MusicService musicService = MusicServiceFactory.getMusicService(SubsonicTabActivity.this);
|
||||
|
||||
long timeInMillis = 0;
|
||||
|
||||
if (shareDetails.Expiration != 0)
|
||||
{
|
||||
timeInMillis = shareDetails.Expiration;
|
||||
}
|
||||
|
||||
List<Share> shares = musicService.createShare(ids, shareDetails.Description, timeInMillis, SubsonicTabActivity.this, this);
|
||||
return shares.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done(Share result)
|
||||
{
|
||||
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||
intent.setType("text/plain");
|
||||
intent.putExtra(Intent.EXTRA_TEXT, String.format("%s\n\n%s", Util.getShareGreeting(SubsonicTabActivity.this), result.getUrl()));
|
||||
startActivity(Intent.createChooser(intent, getResources().getString(R.string.share_via)));
|
||||
}
|
||||
};
|
||||
|
||||
task.execute();
|
||||
}
|
||||
|
||||
public void setOnTouchListenerOnUiThread(final View view, final OnTouchListener listener)
|
||||
{
|
||||
this.runOnUiThread(new Runnable()
|
||||
@ -682,18 +501,6 @@ public class SubsonicTabActivity extends ResultActivity
|
||||
return mediaPlayerControllerLazy.getValue();
|
||||
}
|
||||
|
||||
protected void warnIfNetworkOrStorageUnavailable()
|
||||
{
|
||||
if (!Util.isExternalStoragePresent())
|
||||
{
|
||||
Util.toast(this, R.string.select_album_no_sdcard);
|
||||
}
|
||||
else if (!ActiveServerProvider.Companion.isOffline(this) && !Util.isNetworkConnected(this))
|
||||
{
|
||||
Util.toast(this, R.string.select_album_no_network);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setActionBarDisplayHomeAsUp(boolean enabled)
|
||||
{
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
@ -704,86 +511,6 @@ public class SubsonicTabActivity extends ResultActivity
|
||||
}
|
||||
}
|
||||
|
||||
protected void setActionBarSubtitle(CharSequence title)
|
||||
{
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
|
||||
if (actionBar != null)
|
||||
{
|
||||
actionBar.setSubtitle(title);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setActionBarSubtitle(int id)
|
||||
{
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
|
||||
if (actionBar != null)
|
||||
{
|
||||
actionBar.setSubtitle(id);
|
||||
}
|
||||
}
|
||||
|
||||
private void setUncaughtExceptionHandler()
|
||||
{
|
||||
Thread.UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler();
|
||||
if (!(handler instanceof SubsonicUncaughtExceptionHandler))
|
||||
{
|
||||
Thread.setDefaultUncaughtExceptionHandler(new SubsonicUncaughtExceptionHandler(this));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the stack trace of uncaught exceptions to a file on the SD card.
|
||||
*/
|
||||
private static class SubsonicUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler
|
||||
{
|
||||
|
||||
private final Thread.UncaughtExceptionHandler defaultHandler;
|
||||
private final Context context;
|
||||
private static final String filename = "ultrasonic-stacktrace.txt";
|
||||
|
||||
private SubsonicUncaughtExceptionHandler(Context context)
|
||||
{
|
||||
this.context = context;
|
||||
defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread thread, Throwable throwable)
|
||||
{
|
||||
File file = null;
|
||||
PrintWriter printWriter = null;
|
||||
|
||||
try
|
||||
{
|
||||
file = new File(FileUtil.getUltrasonicDirectory(context), filename);
|
||||
printWriter = new PrintWriter(file);
|
||||
|
||||
String logMessage = String.format(
|
||||
"Android API level: %s\nUltrasonic version name: %s\nUltrasonic version code: %s\n\n",
|
||||
Build.VERSION.SDK_INT, Util.getVersionName(context), Util.getVersionCode(context));
|
||||
|
||||
printWriter.println(logMessage);
|
||||
throwable.printStackTrace(printWriter);
|
||||
Timber.e(throwable, "Uncaught Exception! %s", logMessage);
|
||||
Timber.i("Stack trace written to %s", file);
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
Timber.e(x, "Failed to write stack trace to %s", file);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Util.close(printWriter);
|
||||
if (defaultHandler != null)
|
||||
{
|
||||
defaultHandler.uncaughtException(thread, throwable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Bundle inState)
|
||||
{
|
||||
|
@ -4,10 +4,12 @@ import android.app.AlertDialog
|
||||
import android.app.SearchManager
|
||||
import android.content.Intent
|
||||
import android.content.res.Resources
|
||||
import android.media.AudioManager
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
import android.provider.MediaStore
|
||||
import android.provider.SearchRecentSuggestions
|
||||
import android.view.KeyEvent
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
@ -25,12 +27,14 @@ import com.google.android.material.navigation.NavigationView
|
||||
import org.koin.android.ext.android.inject
|
||||
import org.koin.android.viewmodel.ext.android.viewModel
|
||||
import org.moire.ultrasonic.R
|
||||
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.isOffline
|
||||
import org.moire.ultrasonic.provider.SearchSuggestionProvider
|
||||
import org.moire.ultrasonic.service.MediaPlayerController
|
||||
import org.moire.ultrasonic.service.MediaPlayerLifecycleSupport
|
||||
import org.moire.ultrasonic.subsonic.ImageLoaderProvider
|
||||
import org.moire.ultrasonic.util.Constants
|
||||
import org.moire.ultrasonic.util.FileUtil
|
||||
import org.moire.ultrasonic.util.SubsonicUncaughtExceptionHandler
|
||||
import org.moire.ultrasonic.util.Util
|
||||
import timber.log.Timber
|
||||
|
||||
@ -39,6 +43,11 @@ import timber.log.Timber
|
||||
* A simple activity demonstrating use of a NavHostFragment with a navigation drawer.
|
||||
*/
|
||||
class NavigationActivity : AppCompatActivity() {
|
||||
var chatMenuItem: MenuItem? = null
|
||||
var bookmarksMenuItem: MenuItem? = null
|
||||
var sharesMenuItem: MenuItem? = null
|
||||
private var theme: String? = null
|
||||
|
||||
private lateinit var appBarConfiguration : AppBarConfiguration
|
||||
private val serverSettingsModel: ServerSettingsModel by viewModel()
|
||||
private val lifecycleSupport: MediaPlayerLifecycleSupport by inject()
|
||||
@ -48,7 +57,12 @@ class NavigationActivity : AppCompatActivity() {
|
||||
private var infoDialogDisplayed = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setUncaughtExceptionHandler()
|
||||
Util.applyTheme(this)
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
volumeControlStream = AudioManager.STREAM_MUSIC
|
||||
setContentView(R.layout.navigation_activity)
|
||||
|
||||
val toolbar = findViewById<Toolbar>(R.id.toolbar)
|
||||
@ -78,6 +92,15 @@ class NavigationActivity : AppCompatActivity() {
|
||||
Integer.toString(destination.id)
|
||||
}
|
||||
Timber.d("Navigated to $dest")
|
||||
|
||||
// TODO: Maybe we can find a better place for theme change. Currently the change occures when navigating between fragments
|
||||
// but theoretically Settings could request a Navigation Activity recreate instantly when the theme setting changes
|
||||
// Make sure to update theme if it has changed
|
||||
if (theme == null) theme = Util.getTheme(this)
|
||||
else if (theme != Util.getTheme(this)) {
|
||||
theme = Util.getTheme(this)
|
||||
recreate()
|
||||
}
|
||||
}
|
||||
|
||||
// Determine first run and migrate server settings to DB as early as possible
|
||||
@ -91,6 +114,49 @@ class NavigationActivity : AppCompatActivity() {
|
||||
showInfoDialog(showWelcomeScreen)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
val visibility = !isOffline(this)
|
||||
chatMenuItem?.isVisible = visibility
|
||||
bookmarksMenuItem?.isVisible = visibility
|
||||
sharesMenuItem?.isVisible = visibility
|
||||
|
||||
Util.registerMediaButtonEventReceiver(this, false)
|
||||
// Lifecycle support's constructor registers some event receivers so it should be created early
|
||||
lifecycleSupport.onCreate()
|
||||
|
||||
// TODO: Implement NowPlaying as a Fragment
|
||||
// This must be filled here because onCreate is called before the derived objects would call setContentView
|
||||
//getNowPlayingView()
|
||||
|
||||
if (!SubsonicTabActivity.nowPlayingHidden) {
|
||||
//showNowPlaying()
|
||||
} else {
|
||||
//hideNowPlaying()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
Util.unregisterMediaButtonEventReceiver(this, false)
|
||||
super.onDestroy()
|
||||
|
||||
// TODO: Handle NowPlaying if necessary
|
||||
//nowPlayingView = null
|
||||
imageLoaderProvider.clearImageLoader()
|
||||
}
|
||||
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
val isVolumeDown = keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
|
||||
val isVolumeUp = keyCode == KeyEvent.KEYCODE_VOLUME_UP
|
||||
val isVolumeAdjust = isVolumeDown || isVolumeUp
|
||||
val isJukebox = mediaPlayerController.isJukeboxEnabled
|
||||
if (isVolumeAdjust && isJukebox) {
|
||||
mediaPlayerController.adjustJukeboxVolume(isVolumeUp)
|
||||
return true
|
||||
}
|
||||
return super.onKeyDown(keyCode, event)
|
||||
}
|
||||
|
||||
private fun setupNavigationMenu(navController: NavController) {
|
||||
val sideNavView = findViewById<NavigationView>(R.id.nav_view)
|
||||
sideNavView?.setupWithNavController(navController)
|
||||
@ -107,6 +173,10 @@ class NavigationActivity : AppCompatActivity() {
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
chatMenuItem = sideNavView.menu.findItem(R.id.menu_chat)
|
||||
bookmarksMenuItem = sideNavView.menu.findItem(R.id.menu_bookmarks)
|
||||
sharesMenuItem = sideNavView.menu.findItem(R.id.menu_shares)
|
||||
}
|
||||
|
||||
private fun setupActionBar(navController: NavController, appBarConfig: AppBarConfiguration) {
|
||||
@ -182,4 +252,11 @@ class NavigationActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUncaughtExceptionHandler() {
|
||||
val handler = Thread.getDefaultUncaughtExceptionHandler()
|
||||
if (handler !is SubsonicUncaughtExceptionHandler) {
|
||||
Thread.setDefaultUncaughtExceptionHandler(SubsonicUncaughtExceptionHandler(this))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,40 @@
|
||||
package org.moire.ultrasonic.util
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.PrintWriter
|
||||
|
||||
private const val filename = "ultrasonic-stacktrace.txt"
|
||||
|
||||
/**
|
||||
* Logs the stack trace of uncaught exceptions to a file on the SD card.
|
||||
*/
|
||||
class SubsonicUncaughtExceptionHandler (
|
||||
private val context: Context
|
||||
) : Thread.UncaughtExceptionHandler {
|
||||
private val defaultHandler: Thread.UncaughtExceptionHandler? = Thread.getDefaultUncaughtExceptionHandler()
|
||||
|
||||
override fun uncaughtException(thread: Thread, throwable: Throwable) {
|
||||
var file: File? = null
|
||||
var printWriter: PrintWriter? = null
|
||||
|
||||
try {
|
||||
file = File(FileUtil.getUltrasonicDirectory(context), filename)
|
||||
printWriter = PrintWriter(file)
|
||||
val logMessage = String.format(
|
||||
"Android API level: %s\nUltrasonic version name: %s\nUltrasonic version code: %s\n\n",
|
||||
Build.VERSION.SDK_INT, Util.getVersionName(context), Util.getVersionCode(context))
|
||||
printWriter.println(logMessage)
|
||||
throwable.printStackTrace(printWriter)
|
||||
Timber.e(throwable, "Uncaught Exception! %s", logMessage)
|
||||
Timber.i("Stack trace written to %s", file)
|
||||
} catch (x: Throwable) {
|
||||
Timber.e(x, "Failed to write stack trace to %s", file)
|
||||
} finally {
|
||||
Util.close(printWriter)
|
||||
defaultHandler?.uncaughtException(thread, throwable)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="UltrasonicTheme.Black" parent="Theme.MaterialComponents">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
<item name="color_background">@color/background_color_dark</item>
|
||||
<item name="color_selected">@color/selected_color_dark</item>
|
||||
<item name="star_hollow">@drawable/ic_star_hollow_dark</item>
|
||||
@ -59,6 +61,8 @@
|
||||
</style>
|
||||
|
||||
<style name="UltrasonicTheme" parent="Theme.AppCompat">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
<item name="color_background">@color/background_color_dark</item>
|
||||
<item name="color_selected">@color/selected_color_dark</item>
|
||||
<item name="star_hollow">@drawable/ic_star_hollow_dark</item>
|
||||
@ -117,6 +121,8 @@
|
||||
</style>
|
||||
|
||||
<style name="UltrasonicTheme.Light" parent="Theme.AppCompat.Light">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
<item name="color_background">@color/background_color_light</item>
|
||||
<item name="color_selected">@color/selected_color_light</item>
|
||||
<item name="star_hollow">@drawable/ic_star_hollow_light</item>
|
||||
|
Loading…
x
Reference in New Issue
Block a user