diff --git a/NEWS b/NEWS index 5eb591817..69adaf45e 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ ver 0.20.22 (not yet released) * Android - now runs as a service - add button to start/stop MPD + - add option to auto-start on boot ver 0.20.21 (2018/08/17) * database diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 3a43a5e7d..5dddf034e 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -10,6 +10,7 @@ + - + + + + + + diff --git a/android/res/layout/log_item.xml b/android/res/layout/log_item.xml new file mode 100644 index 000000000..e6e74c913 --- /dev/null +++ b/android/res/layout/log_item.xml @@ -0,0 +1,5 @@ + + diff --git a/android/res/layout/settings.xml b/android/res/layout/settings.xml new file mode 100644 index 000000000..46e471b05 --- /dev/null +++ b/android/res/layout/settings.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml index bcc1ae0c5..fc5a15bda 100644 --- a/android/res/values/strings.xml +++ b/android/res/values/strings.xml @@ -4,4 +4,8 @@ MPD Music Player Daemon is running Touch for MPD options. + MPD is running + MPD is not running + Run MPD automatically on boot + Prevent suspend when MPD is running (Wakelock) diff --git a/android/src/Receiver.java b/android/src/Receiver.java new file mode 100644 index 000000000..e24a29fbc --- /dev/null +++ b/android/src/Receiver.java @@ -0,0 +1,41 @@ + +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.musicpd; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +public class Receiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + Log.d("Receiver", "onReceive: " + intent); + if (intent.getAction() == "android.intent.action.BOOT_COMPLETED") { + if (Settings.Preferences.getBoolean(context, + Settings.Preferences.KEY_RUN_ON_BOOT, false)) { + final boolean wakelock = Settings.Preferences.getBoolean(context, + Settings.Preferences.KEY_WAKELOCK, false); + Main.start(context, wakelock); + } + } + } +} diff --git a/android/src/Settings.java b/android/src/Settings.java index 89f96447d..69b5305e2 100644 --- a/android/src/Settings.java +++ b/android/src/Settings.java @@ -19,120 +19,229 @@ package org.musicpd; +import java.util.LinkedList; + import android.app.Activity; -import android.os.Build; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; +import android.widget.ArrayAdapter; +import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; -import android.widget.LinearLayout; +import android.widget.ListView; import android.widget.TextView; import android.widget.ToggleButton; public class Settings extends Activity { private static final String TAG = "Settings"; private Main.Client mClient; - private TextView mTextView; - private ToggleButton mButton; - private LinearLayout mLayout; + private TextView mTextStatus; + private ToggleButton mRunButton; + private boolean mFirstRun; + private LinkedList mLogListArray = new LinkedList(); + private ListView mLogListView; + private ArrayAdapter mLogListAdapter; + + private static final int MAX_LOGS = 500; private static final int MSG_ERROR = 0; private static final int MSG_STOPPED = 1; private static final int MSG_STARTED = 2; + private static final int MSG_LOG = 3; + + public static class Preferences { + public static final String KEY_RUN_ON_BOOT ="run_on_boot"; + public static final String KEY_WAKELOCK ="wakelock"; + + public static SharedPreferences get(Context context) { + return context.getSharedPreferences(TAG, MODE_PRIVATE); + } + + public static void putBoolean(Context context, String key, boolean value) { + final SharedPreferences prefs = get(context); + + if (prefs == null) + return; + final Editor editor = prefs.edit(); + editor.putBoolean(key, value); + editor.apply(); + } + + public static boolean getBoolean(Context context, String key, boolean defValue) { + final SharedPreferences prefs = get(context); + + return prefs != null ? prefs.getBoolean(key, defValue) : defValue; + } + } private Handler mHandler = new Handler(new Handler.Callback() { - @Override public boolean handleMessage(Message msg) { switch (msg.what) { case MSG_ERROR: Log.d(TAG, "onError"); - final String error = (String) msg.obj; - mTextView.setText("Failed to load the native MPD libary.\n" + - "Report this problem to us, and include the following information:\n" + - "SUPPORTED_ABIS=" + String.join(", ", Build.SUPPORTED_ABIS) + "\n" + - "PRODUCT=" + Build.PRODUCT + "\n" + - "FINGERPRINT=" + Build.FINGERPRINT + "\n" + - "error=" + error); - mButton.setChecked(false); - mButton.setEnabled(false); + + mClient.release(); + connectClient(); + + mRunButton.setEnabled(false); + mRunButton.setChecked(false); + + mTextStatus.setText((String)msg.obj); + mFirstRun = true; break; case MSG_STOPPED: Log.d(TAG, "onStopped"); - if (mButton.isEnabled()) // don't overwrite previous error message - mTextView.setText("Music Player Daemon is not running"); - mButton.setEnabled(true); - mButton.setChecked(false); + mRunButton.setEnabled(true); + if (!mFirstRun && Preferences.getBoolean(Settings.this, Preferences.KEY_RUN_ON_BOOT, false)) + mRunButton.setChecked(true); + else + mRunButton.setChecked(false); + mFirstRun = true; break; case MSG_STARTED: Log.d(TAG, "onStarted"); - mTextView.setText("Music Player Daemon is running" - + "\nCAUTION: this version is EXPERIMENTAL!"); - mButton.setChecked(true); + mRunButton.setChecked(true); + mFirstRun = true; + mTextStatus.setText("CAUTION: this version is EXPERIMENTAL!"); // XXX + break; + case MSG_LOG: + if (mLogListArray.size() > MAX_LOGS) + mLogListArray.remove(0); + String priority; + switch (msg.arg1) { + case Log.DEBUG: + priority = "D"; + break; + case Log.ERROR: + priority = "E"; + break; + case Log.INFO: + priority = "I"; + break; + case Log.VERBOSE: + priority = "V"; + break; + case Log.WARN: + priority = "W"; + break; + default: + priority = ""; + } + mLogListArray.add(priority + "/ " + (String)msg.obj); + mLogListAdapter.notifyDataSetChanged(); + break; } return true; } }); - private OnCheckedChangeListener mOnCheckedChangeListener = new OnCheckedChangeListener() { - + private final OnCheckedChangeListener mOnRunChangeListener = new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (mClient != null) { - if (isChecked) + if (isChecked) { mClient.start(); - else + if (Preferences.getBoolean(Settings.this, + Preferences.KEY_WAKELOCK, false)) + mClient.setWakelockEnabled(true); + } else { mClient.stop(); + } } } }; + private final OnCheckedChangeListener mOnRunOnBootChangeListener = new OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + Preferences.putBoolean(Settings.this, Preferences.KEY_RUN_ON_BOOT, isChecked); + if (isChecked && mClient != null && !mRunButton.isChecked()) + mRunButton.setChecked(true); + } + }; + + private final OnCheckedChangeListener mOnWakelockChangeListener = new OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + Preferences.putBoolean(Settings.this, Preferences.KEY_WAKELOCK, isChecked); + if (mClient != null && mClient.isRunning()) + mClient.setWakelockEnabled(isChecked); + } + }; + @Override protected void onCreate(Bundle savedInstanceState) { - mTextView = new TextView(this); - mTextView.setText(""); + setContentView(R.layout.settings); + mRunButton = (ToggleButton) findViewById(R.id.run); + mRunButton.setOnCheckedChangeListener(mOnRunChangeListener); - mButton = new ToggleButton(this); - mButton.setOnCheckedChangeListener(mOnCheckedChangeListener); + mTextStatus = (TextView) findViewById(R.id.status); - mLayout = new LinearLayout(this); - mLayout.setOrientation(LinearLayout.VERTICAL); - mLayout.addView(mButton); - mLayout.addView(mTextView); + mLogListAdapter = new ArrayAdapter(this, R.layout.log_item, mLogListArray); - setContentView(mLayout); + mLogListView = (ListView) findViewById(R.id.log_list); + mLogListView.setAdapter(mLogListAdapter); + mLogListView.setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL); + + CheckBox checkbox = (CheckBox) findViewById(R.id.run_on_boot); + checkbox.setOnCheckedChangeListener(mOnRunOnBootChangeListener); + if (Preferences.getBoolean(this, Preferences.KEY_RUN_ON_BOOT, false)) + checkbox.setChecked(true); + + checkbox = (CheckBox) findViewById(R.id.wakelock); + checkbox.setOnCheckedChangeListener(mOnWakelockChangeListener); + if (Preferences.getBoolean(this, Preferences.KEY_WAKELOCK, false)) + checkbox.setChecked(true); super.onCreate(savedInstanceState); } - @Override - protected void onStart() { + private void connectClient() { mClient = new Main.Client(this, new Main.Client.Callback() { + + private void removeMessages() { + /* don't remove log messages */ + mHandler.removeMessages(MSG_STOPPED); + mHandler.removeMessages(MSG_STARTED); + mHandler.removeMessages(MSG_ERROR); + } + @Override public void onStopped() { - mHandler.removeCallbacksAndMessages(null); + removeMessages(); mHandler.sendEmptyMessage(MSG_STOPPED); } @Override public void onStarted() { - mHandler.removeCallbacksAndMessages(null); + removeMessages(); mHandler.sendEmptyMessage(MSG_STARTED); } @Override public void onError(String error) { - mHandler.removeCallbacksAndMessages(null); + removeMessages(); mHandler.sendMessage(Message.obtain(mHandler, MSG_ERROR, error)); } @Override public void onLog(int priority, String msg) { + mHandler.sendMessage(Message.obtain(mHandler, MSG_LOG, priority, 0, msg)); } }); + } + + @Override + protected void onStart() { + mFirstRun = false; + connectClient(); super.onStart(); }