diff options
author | Thomas Guillem <thomas@gllm.fr> | 2014-10-18 19:02:23 +0200 |
---|---|---|
committer | Max Kellermann <max@musicpd.org> | 2018-08-20 00:07:18 +0200 |
commit | f37ab5482b187d1f18427239ad055da4e751bcb4 (patch) | |
tree | d52e23f5f5084bc0924bc880bddcae0545a96573 | |
parent | ef38dbe5bfe2dd4bca4a392dc4322a2d5855c2c6 (diff) |
android: improve Settings UI and run mpd on boot
add 2 preferences to:
- enable Wakelock when MPD is running (prevent suspend)
- run MPD on boot
and display MPD logs
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | android/AndroidManifest.xml | 8 | ||||
-rw-r--r-- | android/res/layout/log_item.xml | 5 | ||||
-rw-r--r-- | android/res/layout/settings.xml | 37 | ||||
-rw-r--r-- | android/res/values/strings.xml | 4 | ||||
-rw-r--r-- | android/src/Receiver.java | 41 | ||||
-rw-r--r-- | android/src/Settings.java | 189 |
7 files changed, 244 insertions, 41 deletions
@@ -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 @@ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.INTERNET"/> + <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <application android:allowBackup="true" android:icon="@drawable/icon" @@ -21,7 +22,12 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> - <service android:name=".Main" /> + <receiver android:name=".Receiver"> + <intent-filter> + <action android:name="android.intent.action.BOOT_COMPLETED" /> + </intent-filter> + </receiver> + <service android:name=".Main" android:process=":main"/> </application> </manifest> 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:typeface="monospace" /> 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" > + + <ToggleButton + android:id="@+id/run" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textOn="@string/toggle_button_run_on" + android:textOff="@string/toggle_button_run_off" /> + + <CheckBox + android:id="@+id/run_on_boot" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/checkbox_run_on_boot" /> + + <CheckBox + android:id="@+id/wakelock" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/checkbox_wakelock" /> + + <TextView + android:id="@+id/status" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <ListView + android:id="@+id/log_list" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="10dip" /> + +</LinearLayout> 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 @@ <string name="app_name">MPD</string> <string name="notification_title_mpd_running">Music Player Daemon is running</string> <string name="notification_text_mpd_running">Touch for MPD options.</string> + <string name="toggle_button_run_on">MPD is running</string> + <string name="toggle_button_run_off">MPD is not running</string> + <string name="checkbox_run_on_boot">Run MPD automatically on boot</string> + <string name="checkbox_wakelock">Prevent suspend when MPD is running (Wakelock)</string> </resources> 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<String> mLogListArray = new LinkedList<String>(); + private ListView mLogListView; + private ArrayAdapter<String> 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; - private Handler mHandler = new Handler(new Handler.Callback() { + 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); + + mTextStatus = (TextView) findViewById(R.id.status); - mButton = new ToggleButton(this); - mButton.setOnCheckedChangeListener(mOnCheckedChangeListener); + mLogListAdapter = new ArrayAdapter<String>(this, R.layout.log_item, mLogListArray); - mLayout = new LinearLayout(this); - mLayout.setOrientation(LinearLayout.VERTICAL); - mLayout.addView(mButton); - mLayout.addView(mTextView); + mLogListView = (ListView) findViewById(R.id.log_list); + mLogListView.setAdapter(mLogListAdapter); + mLogListView.setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL); - setContentView(mLayout); + 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(); } |