summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikitas Stamatopoulos <nikitas.stamatopoulos@gmail.com>2013-02-17 15:28:48 -0500
committerNikitas Stamatopoulos <nikitas.stamatopoulos@gmail.com>2013-02-17 20:08:58 -0500
commit004afab027fe500cb6ec91f63314e203cde02899 (patch)
treea3f83b0173cb404503bce5d7a33b666fe5ea5476
parent91f6d4666d16d0a1c65e1eaafdb22da47eab1ee0 (diff)
Added the AndroidRemote Extension
Added the Android App source code
-rw-r--r--Extensions.sln6
-rw-r--r--build/m4/extensions/androidremote.m411
-rw-r--r--configure.ac3
-rw-r--r--po/banshee-community-extensions.pot2
-rw-r--r--src/AndroidRemote/AndroidApp/.classpath8
-rw-r--r--src/AndroidRemote/AndroidApp/.project33
-rw-r--r--src/AndroidRemote/AndroidApp/AndroidManifest.xml61
-rw-r--r--src/AndroidRemote/AndroidApp/BansheeRemote.apkbin0 -> 38135 bytes
-rw-r--r--src/AndroidRemote/AndroidApp/gen/org/nstamato/bansheeremote/BuildConfig.java6
-rw-r--r--src/AndroidRemote/AndroidApp/gen/org/nstamato/bansheeremote/R.java62
-rw-r--r--src/AndroidRemote/AndroidApp/project.properties14
-rw-r--r--src/AndroidRemote/AndroidApp/res/drawable/album.pngbin0 -> 4834 bytes
-rw-r--r--src/AndroidRemote/AndroidApp/res/drawable/artist.pngbin0 -> 4468 bytes
-rw-r--r--src/AndroidRemote/AndroidApp/res/drawable/banshee.pngbin0 -> 2791 bytes
-rw-r--r--src/AndroidRemote/AndroidApp/res/drawable/ic_menu_refresh.pngbin0 -> 2450 bytes
-rw-r--r--src/AndroidRemote/AndroidApp/res/drawable/no_cover_art.jpgbin0 -> 4165 bytes
-rw-r--r--src/AndroidRemote/AndroidApp/res/drawable/seek_background.xml61
-rw-r--r--src/AndroidRemote/AndroidApp/res/drawable/songs.pngbin0 -> 4538 bytes
-rw-r--r--src/AndroidRemote/AndroidApp/res/layout-land/main.xml219
-rw-r--r--src/AndroidRemote/AndroidApp/res/layout/album_browse.xml63
-rw-r--r--src/AndroidRemote/AndroidApp/res/layout/album_row_view.xml40
-rw-r--r--src/AndroidRemote/AndroidApp/res/layout/browse.xml63
-rw-r--r--src/AndroidRemote/AndroidApp/res/layout/init.xml91
-rw-r--r--src/AndroidRemote/AndroidApp/res/layout/list_item.xml33
-rw-r--r--src/AndroidRemote/AndroidApp/res/layout/main.xml236
-rw-r--r--src/AndroidRemote/AndroidApp/res/layout/song_browse.xml63
-rw-r--r--src/AndroidRemote/AndroidApp/res/layout/song_row_view.xml40
-rw-r--r--src/AndroidRemote/AndroidApp/res/values/strings.xml5
-rw-r--r--src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/AlbumBaseAdapter.java83
-rw-r--r--src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/AlbumBrowse.java232
-rw-r--r--src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/AlbumListItem.java55
-rw-r--r--src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/ArtistBrowse.java122
-rw-r--r--src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/BansheeInstance.java50
-rw-r--r--src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/BansheeRemote.java159
-rw-r--r--src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/NewServer.java198
-rw-r--r--src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/Settings.java107
-rw-r--r--src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/SongBaseAdapter.java83
-rw-r--r--src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/SongBrowse.java216
-rw-r--r--src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/SongListItem.java64
-rw-r--r--src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/Sync.java222
-rw-r--r--src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/main.java727
-rw-r--r--src/AndroidRemote/AndroidRemote.addin.xml27
-rw-r--r--src/AndroidRemote/AndroidRemote.csproj103
-rw-r--r--src/AndroidRemote/Banshee.AndroidRemote/AndroidRemoteSource.cs422
-rw-r--r--src/AndroidRemote/Makefile.am8
-rw-r--r--src/Makefile.am1
46 files changed, 3998 insertions, 1 deletions
diff --git a/Extensions.sln b/Extensions.sln
index 319fd61..d56b513 100644
--- a/Extensions.sln
+++ b/Extensions.sln
@@ -1,6 +1,8 @@

Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AndroidRemote", "src\AndroidRemote\AndroidRemote.csproj", "{ED1C6ABE-4BA3-4771-9419-CAA77D1BBFEF}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CueSheets", "src\CueSheets\CueSheets.csproj", "{}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DuplicateSongDetector", "src\DuplicateSongDetector\DuplicateSongDetector.csproj", "{1725FB8A-DF47-4CAF-A322-59EE97AAB5DE}"
@@ -90,6 +92,10 @@ Global
Windows|Any CPU = Windows|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {ED1C6ABE-4BA3-4771-9419-CAA77D1BBFEF}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {ED1C6ABE-4BA3-4771-9419-CAA77D1BBFEF}.Debug|x86.Build.0 = Debug|Any CPU
+ {ED1C6ABE-4BA3-4771-9419-CAA77D1BBFEF}.Release|x86.ActiveCfg = Release|Any CPU
+ {ED1C6ABE-4BA3-4771-9419-CAA77D1BBFEF}.Release|x86.Build.0 = Release|Any CPU
{}.Debug|x86.ActiveCfg = Debug|Any CPU
{}.Debug|x86.Build.0 = Debug|Any CPU
{}.Release|x86.ActiveCfg = Release|Any CPU
diff --git a/build/m4/extensions/androidremote.m4 b/build/m4/extensions/androidremote.m4
new file mode 100644
index 0000000..266d64d
--- /dev/null
+++ b/build/m4/extensions/androidremote.m4
@@ -0,0 +1,11 @@
+AC_DEFUN([BCE_ANDROIDREMOTE],
+[
+ BCE_ARG_DISABLE([AndroidRemote], [yes])
+
+ if test "x$enable_AndroidRemote" = "xyes"; then
+ AM_CONDITIONAL(ENABLE_ANDROIDREMOTE, true)
+ else
+ AM_CONDITIONAL(ENABLE_ANDROIDREMOTE, false)
+ fi
+])
+
diff --git a/configure.ac b/configure.ac
index 2f5d99c..2282745 100644
--- a/configure.ac
+++ b/configure.ac
@@ -79,6 +79,7 @@ dnl Extensions
BCE_ALARMCLOCK
BCE_ALBUMARTWRITER
BCE_AMPACHE
+BCE_ANDROIDREMOTE
BCE_APPINDICATOR
BCE_AWN
BCE_CLUTTERFLOW
@@ -140,6 +141,7 @@ src/AssemblyInfo.cs
src/AlarmClock/Makefile
src/AlbumArtWriter/Makefile
src/Ampache/Makefile
+src/AndroidRemote/Makefile
src/AppIndicator/Makefile
src/Awn/Makefile
src/ClutterFlow/Makefile
@@ -207,6 +209,7 @@ ${PACKAGE}-${VERSION}
Alarm Clock: ${enable_AlarmClock}
AlbumArtWriter: ${enable_AlbumArtWriter}
Ampache: ${enable_Ampache}
+ AndroidRemote: ${enable_AndroidRemote}
AppIndicator: ${enable_AppIndicator}
Awn: ${enable_Awn}
ClutterFlow: ${enable_ClutterFlow}
diff --git a/po/banshee-community-extensions.pot b/po/banshee-community-extensions.pot
index aa716a0..e466fdf 100644
--- a/po/banshee-community-extensions.pot
+++ b/po/banshee-community-extensions.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-01-13 10:20+0100\n"
+"POT-Creation-Date: 2013-02-17 15:05-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
diff --git a/src/AndroidRemote/AndroidApp/.classpath b/src/AndroidRemote/AndroidApp/.classpath
new file mode 100644
index 0000000..a4763d1
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="gen"/>
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+ <classpathentry kind="output" path="bin/classes"/>
+</classpath>
diff --git a/src/AndroidRemote/AndroidApp/.project b/src/AndroidRemote/AndroidApp/.project
new file mode 100644
index 0000000..66744a5
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/.project
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>BansheeRemote</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.ApkBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/src/AndroidRemote/AndroidApp/AndroidManifest.xml b/src/AndroidRemote/AndroidApp/AndroidManifest.xml
new file mode 100644
index 0000000..43fc6a4
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/AndroidManifest.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ BansheeRemote
+
+ Copyright (C) 2009 Nikitas Stamatopoulos
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.nstamato.bansheeremote"
+ android:versionCode="14"
+ android:versionName="4.2">
+ <uses-sdk android:minSdkVersion="4" />
+ <application android:icon="@drawable/banshee" android:label="Banshee Remote">
+ <activity android:name=".BansheeRemote" android:label="Banshee Remote" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".Settings" android:label="Banshee Remote" android:configChanges="orientation|keyboardHidden">
+ </activity>
+ <activity android:name=".ArtistBrowse" android:label="Artists" android:configChanges = "orientation|keyboardHidden" android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
+ </activity>
+ <activity android:name=".AlbumBrowse" android:label="Artists" android:configChanges = "orientation|keyboardHidden" android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
+ </activity>
+ <activity android:name=".SongBrowse" android:label="Artists" android:configChanges = "orientation|keyboardHidden" android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
+ </activity>
+ <activity android:name=".Sync" android:label="Sync" android:configChanges = "orientation|keyboardHidden" android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
+ </activity>
+ <activity android:name=".NewServer" android:label="Banshee Remote" android:configChanges="orientation|keyboardHidden">
+ </activity>
+ <activity android:name=".main" android:label="Banshee Remote" android:launchMode="singleTask" android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
+ </activity>
+ </application>
+
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+
+<uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>
+</manifest> \ No newline at end of file
diff --git a/src/AndroidRemote/AndroidApp/BansheeRemote.apk b/src/AndroidRemote/AndroidApp/BansheeRemote.apk
new file mode 100644
index 0000000..e301c2b
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/BansheeRemote.apk
Binary files differ
diff --git a/src/AndroidRemote/AndroidApp/gen/org/nstamato/bansheeremote/BuildConfig.java b/src/AndroidRemote/AndroidApp/gen/org/nstamato/bansheeremote/BuildConfig.java
new file mode 100644
index 0000000..75abf2f
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/gen/org/nstamato/bansheeremote/BuildConfig.java
@@ -0,0 +1,6 @@
+/** Automatically generated file. DO NOT MODIFY */
+package org.nstamato.bansheeremote;
+
+public final class BuildConfig {
+ public final static boolean DEBUG = true;
+} \ No newline at end of file
diff --git a/src/AndroidRemote/AndroidApp/gen/org/nstamato/bansheeremote/R.java b/src/AndroidRemote/AndroidApp/gen/org/nstamato/bansheeremote/R.java
new file mode 100644
index 0000000..ee844f6
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/gen/org/nstamato/bansheeremote/R.java
@@ -0,0 +1,62 @@
+/* AUTO-GENERATED FILE. DO NOT MODIFY.
+ *
+ * This class was automatically generated by the
+ * aapt tool from the resource data it found. It
+ * should not be modified by hand.
+ */
+
+package org.nstamato.bansheeremote;
+
+public final class R {
+ public static final class attr {
+ }
+ public static final class drawable {
+ public static final int album=0x7f020000;
+ public static final int artist=0x7f020001;
+ public static final int banshee=0x7f020002;
+ public static final int ic_menu_refresh=0x7f020003;
+ public static final int no_cover_art=0x7f020004;
+ public static final int seek_background=0x7f020005;
+ public static final int songs=0x7f020006;
+ }
+ public static final class id {
+ public static final int album=0x7f05000a;
+ public static final int albumArtist=0x7f050004;
+ public static final int albumBrowse=0x7f050002;
+ public static final int albumName=0x7f050003;
+ public static final int albums=0x7f05000d;
+ public static final int artist=0x7f050009;
+ public static final int artists=0x7f05000c;
+ public static final int cover=0x7f05000b;
+ public static final int icon=0x7f050000;
+ public static final int ip=0x7f050005;
+ public static final int next=0x7f050011;
+ public static final int playpause=0x7f050010;
+ public static final int port=0x7f050006;
+ public static final int prev=0x7f05000f;
+ public static final int seek_position=0x7f050013;
+ public static final int seek_total=0x7f050014;
+ public static final int seekbar=0x7f050012;
+ public static final int songArtist=0x7f050017;
+ public static final int songBrowse=0x7f050015;
+ public static final int songName=0x7f050016;
+ public static final int songs=0x7f05000e;
+ public static final int submit=0x7f050007;
+ public static final int title=0x7f050001;
+ public static final int track=0x7f050008;
+ }
+ public static final class layout {
+ public static final int album_browse=0x7f030000;
+ public static final int album_row_view=0x7f030001;
+ public static final int browse=0x7f030002;
+ public static final int init=0x7f030003;
+ public static final int list_item=0x7f030004;
+ public static final int main=0x7f030005;
+ public static final int song_browse=0x7f030006;
+ public static final int song_row_view=0x7f030007;
+ }
+ public static final class string {
+ public static final int app_name=0x7f040001;
+ public static final int hello=0x7f040000;
+ }
+}
diff --git a/src/AndroidRemote/AndroidApp/project.properties b/src/AndroidRemote/AndroidApp/project.properties
new file mode 100644
index 0000000..2e98c5f
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+
+# Indicates whether an apk should be generated for each density.
+split.density=false
+# Project target.
+target=android-10
+apk-configurations=
diff --git a/src/AndroidRemote/AndroidApp/res/drawable/album.png b/src/AndroidRemote/AndroidApp/res/drawable/album.png
new file mode 100644
index 0000000..647e059
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/res/drawable/album.png
Binary files differ
diff --git a/src/AndroidRemote/AndroidApp/res/drawable/artist.png b/src/AndroidRemote/AndroidApp/res/drawable/artist.png
new file mode 100644
index 0000000..492247c
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/res/drawable/artist.png
Binary files differ
diff --git a/src/AndroidRemote/AndroidApp/res/drawable/banshee.png b/src/AndroidRemote/AndroidApp/res/drawable/banshee.png
new file mode 100644
index 0000000..af4fbf4
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/res/drawable/banshee.png
Binary files differ
diff --git a/src/AndroidRemote/AndroidApp/res/drawable/ic_menu_refresh.png b/src/AndroidRemote/AndroidApp/res/drawable/ic_menu_refresh.png
new file mode 100644
index 0000000..77d70dd
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/res/drawable/ic_menu_refresh.png
Binary files differ
diff --git a/src/AndroidRemote/AndroidApp/res/drawable/no_cover_art.jpg b/src/AndroidRemote/AndroidApp/res/drawable/no_cover_art.jpg
new file mode 100644
index 0000000..4d2f2cd
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/res/drawable/no_cover_art.jpg
Binary files differ
diff --git a/src/AndroidRemote/AndroidApp/res/drawable/seek_background.xml b/src/AndroidRemote/AndroidApp/res/drawable/seek_background.xml
new file mode 100644
index 0000000..3195f3f
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/res/drawable/seek_background.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+ 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.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:id="@android:id/background">
+ <shape>
+ <corners android:radius="5dip" />
+ <gradient
+ android:startColor="#ff9d9e9d"
+ android:centerColor="#ff5a5d5a"
+ android:centerY="0.75"
+ android:endColor="#ff747674"
+ android:angle="270"
+ />
+ </shape>
+ </item>
+
+ <item android:id="@android:id/secondaryProgress">
+ <clip>
+ <shape>
+ <corners android:radius="5dip" />
+ <gradient
+ android:startColor="#80ffd300"
+ android:centerColor="#80ffb600"
+ android:centerY="0.75"
+ android:endColor="#a0ffcb00"
+ android:angle="270"
+ />
+ </shape>
+ </clip>
+ </item>
+
+ <item android:id="@android:id/progress">
+ <clip>
+ <shape android:shape="rectangle">
+ <gradient android:startColor="#FFFFAA00"
+android:endColor="#FFFF0000"
+ android:angle="0"/>
+ <stroke android:width="1dp" android:color="#AAFFFFFF" />
+ <padding android:left="7dp" android:top="7dp"
+ android:right="7dp" android:bottom="7dp" />
+ <corners android:radius="4dp" />
+ </shape>
+ </clip>
+ </item>
+
+</layer-list> \ No newline at end of file
diff --git a/src/AndroidRemote/AndroidApp/res/drawable/songs.png b/src/AndroidRemote/AndroidApp/res/drawable/songs.png
new file mode 100644
index 0000000..c2c250b
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/res/drawable/songs.png
Binary files differ
diff --git a/src/AndroidRemote/AndroidApp/res/layout-land/main.xml b/src/AndroidRemote/AndroidApp/res/layout-land/main.xml
new file mode 100644
index 0000000..208ef8f
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/res/layout-land/main.xml
@@ -0,0 +1,219 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ BansheeRemote
+
+ Copyright (C) 2009 Nikitas Stamatopoulos
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:gravity="center_horizontal"
+ >
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingLeft="0dip"
+ android:paddingTop="20px"
+ >
+
+
+<ImageView
+ android:id="@+id/cover"
+ android:layout_width="200dip"
+ android:layout_height="200dip"
+ android:paddingRight="0dip"
+/>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="0dip"
+ android:paddingTop="0px"
+ >
+
+<TextView
+ android:id="@+id/track"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Track name"
+ android:paddingTop="5dip"
+ android:paddingLeft="15dip"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ />
+
+<TextView
+ android:id="@+id/artist"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Artist"
+ android:paddingLeft="15dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ />
+
+ <TextView
+ android:id="@+id/album"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Album"
+ android:paddingLeft="15dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ />
+
+ <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingRight="35dip"
+ android:paddingTop="20px"
+ >
+
+ <ImageButton
+ android:id="@+id/next"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="18dip" android:paddingRight="10dip"
+ android:paddingTop="0dip" android:paddingBottom="0dip"
+ android:src="@android:drawable/ic_media_next"
+ android:layout_alignParentRight="true"
+ android:background="@null"
+ android:tint="#00000000"
+ />
+
+ <ImageButton
+ android:id="@+id/playpause"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="18dip" android:paddingRight="18dip"
+ android:paddingTop="0dip" android:paddingBottom="0dip"
+ android:src="@android:drawable/ic_media_play"
+ android:layout_toLeftOf="@id/next"
+ android:background="@null"
+ android:tint="#00000000"
+ />
+
+ <ImageButton
+ android:id="@+id/prev"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="0dip" android:paddingRight="18dip"
+ android:paddingTop="0dip" android:paddingBottom="0dip"
+ android:src="@android:drawable/ic_media_previous"
+ android:layout_toLeftOf="@id/playpause"
+ android:background="@null"
+ android:tint="#00000000"
+ />
+ <!-- android:background="@null" -->
+ <ImageButton
+ android:id="@+id/songs"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="5dip" android:paddingRight="0dip"
+ android:paddingTop="20dip" android:paddingBottom="0dip"
+ android:src="@drawable/songs"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/next"
+ android:background="@null"
+ android:tint="#00000000"
+ />
+
+ <ImageButton
+ android:id="@+id/albums"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="5dip" android:paddingRight="5dip"
+ android:paddingTop="20dip" android:paddingBottom="0dip"
+ android:src="@drawable/album"
+ android:layout_toLeftOf="@id/songs"
+ android:layout_below="@id/playpause"
+ android:background="@null"
+ android:tint="#00000000"
+ />
+
+ <ImageButton
+ android:id="@+id/artists"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="0dip" android:paddingRight="5dip"
+ android:paddingTop="20dip" android:paddingBottom="0dip"
+ android:src="@drawable/artist"
+ android:layout_toLeftOf="@id/albums"
+ android:layout_below="@id/prev"
+ android:background="@null"
+ android:tint="#00000000"
+ />
+
+
+</RelativeLayout>
+</LinearLayout>
+</LinearLayout>
+
+<RelativeLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:padding="10dip"
+ >
+
+
+
+ <SeekBar
+ android:id="@+id/seekbar"
+ android:max="100"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="5dip"
+ android:progressDrawable="@drawable/seek_background"
+ />
+
+ <TextView
+ android:id="@+id/seek_position"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="0:00"
+ android:layout_below="@id/seekbar"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ />
+
+ <TextView
+ android:id="@+id/seek_total"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="0:00"
+ android:layout_below="@id/seekbar"
+ android:layout_alignParentRight="true"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ />
+</RelativeLayout>
+
+</LinearLayout>
+</ScrollView>
diff --git a/src/AndroidRemote/AndroidApp/res/layout/album_browse.xml b/src/AndroidRemote/AndroidApp/res/layout/album_browse.xml
new file mode 100644
index 0000000..cdaf100
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/res/layout/album_browse.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ BansheeRemote
+
+ Copyright (C) 2009 Nikitas Stamatopoulos
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+
+<ImageView
+android:id="@+id/icon"
+android:layout_height="wrap_content"
+android:layout_width="wrap_content"
+android:src="@drawable/album"/>
+
+<TextView
+android:id="@+id/title"
+android:paddingTop="15dip"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:textSize="26sp"
+android:text="Albums"/>
+/>
+
+</LinearLayout>
+
+ <ListView android:id="@+id/albumBrowse"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"/>
+
+
+ </LinearLayout> \ No newline at end of file
diff --git a/src/AndroidRemote/AndroidApp/res/layout/album_row_view.xml b/src/AndroidRemote/AndroidApp/res/layout/album_row_view.xml
new file mode 100644
index 0000000..52515e3
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/res/layout/album_row_view.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ BansheeRemote
+
+ Copyright (C) 2009 Nikitas Stamatopoulos
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <TextView android:id="@+id/albumName"
+ android:textSize="14sp"
+ android:textStyle="bold"
+ android:textColor="#FFFFFF"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ <TextView android:id="@+id/albumArtist"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+</LinearLayout> \ No newline at end of file
diff --git a/src/AndroidRemote/AndroidApp/res/layout/browse.xml b/src/AndroidRemote/AndroidApp/res/layout/browse.xml
new file mode 100644
index 0000000..0d2527a
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/res/layout/browse.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ BansheeRemote
+
+ Copyright (C) 2009 Nikitas Stamatopoulos
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+
+<ImageView
+android:id="@+id/icon"
+android:layout_height="wrap_content"
+android:layout_width="wrap_content"
+android:src="@drawable/artist"/>
+
+<TextView
+android:id="@+id/title"
+android:paddingTop="15dip"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:textSize="26sp"
+android:text="Artists"/>
+/>
+
+</LinearLayout>
+
+ <ListView android:id="@android:id/list"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"/>
+
+
+ </LinearLayout>
diff --git a/src/AndroidRemote/AndroidApp/res/layout/init.xml b/src/AndroidRemote/AndroidApp/res/layout/init.xml
new file mode 100644
index 0000000..b59330a
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/res/layout/init.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ BansheeRemote
+
+ Copyright (C) 2009 Nikitas Stamatopoulos
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="15dip"
+ android:text="Banshee Server Settings"
+ />
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="25dip"
+ android:text="IP Address"
+ />
+ <EditText
+ android:id="@+id/ip"
+ android:text=""
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ />
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="15dip"
+ android:text="Port"
+ />
+ <EditText
+ android:id="@+id/port"
+ android:text="8484"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="15dip"
+ />
+ <RelativeLayout
+ android:paddingTop="10dip"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ >
+ <Button
+ android:id="@+id/submit"
+ android:layout_width="70px"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:text="OK"
+ />
+ </RelativeLayout>
+ <!-- <ListView
+ android:id="@+id/savedServers"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ />-->
+ </LinearLayout>
+</ScrollView> \ No newline at end of file
diff --git a/src/AndroidRemote/AndroidApp/res/layout/list_item.xml b/src/AndroidRemote/AndroidApp/res/layout/list_item.xml
new file mode 100644
index 0000000..2cfa01d
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/res/layout/list_item.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ BansheeRemote
+
+ Copyright (C) 2009 Nikitas Stamatopoulos
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:padding="10dp"
+ android:textSize="16sp"
+ android:textColor="#FFFFFF" >
+</TextView>
diff --git a/src/AndroidRemote/AndroidApp/res/layout/main.xml b/src/AndroidRemote/AndroidApp/res/layout/main.xml
new file mode 100644
index 0000000..09b8e57
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/res/layout/main.xml
@@ -0,0 +1,236 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ BansheeRemote
+
+ Copyright (C) 2009 Nikitas Stamatopoulos
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:gravity="center_horizontal"
+ >
+
+
+
+<LinearLayout
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="10dip"
+ >
+
+ <TextView
+ android:id="@+id/track"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Track name"
+ android:paddingTop="5dip"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ />
+
+ <TextView
+ android:id="@+id/artist"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Artist"
+ android:layout_below="@id/track"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ />
+
+ <TextView
+ android:id="@+id/album"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Album"
+ android:layout_below="@id/artist"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ />
+
+</LinearLayout>
+
+
+<RelativeLayout
+ android:layout_width="300dip"
+ android:layout_height="wrap_content"
+ android:paddingLeft="10dip"
+ android:paddingRight="10dip"
+ >
+
+
+ <ImageView
+ android:id="@+id/cover"
+ android:layout_width="200dip"
+ android:layout_height="200dip"
+ android:paddingRight="0dip"
+ />
+
+ <ImageButton
+ android:id="@+id/artists"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="0dip" android:paddingBottom="10dip"
+ android:src="@drawable/artist"
+ android:layout_alignParentRight="true"
+ android:background="@null"
+ android:tint="#00000000"
+ />
+
+ <ImageButton
+ android:id="@+id/albums"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/album"
+ android:paddingTop="10dip" android:paddingBottom="10dip"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/artists"
+ android:background="@null"
+ android:tint="#00000000"
+ />
+
+ <ImageButton
+ android:id="@+id/songs"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="10dip" android:paddingBottom="0dip"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/albums"
+ android:src="@drawable/songs"
+ android:background="@null"
+ android:tint="#00000000"
+ />
+
+
+
+ <!--
+ <ImageButton
+ android:id="@+id/mute"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="0dip" android:paddingRight="0dip"
+ android:paddingTop="0dip" android:paddingBottom="0dip"
+ android:src="@android:drawable/ic_lock_silent_mode"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/volumedown"
+ android:background="@null"
+ />
+ -->
+
+</RelativeLayout>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:gravity="center_horizontal"
+ >
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="40dip"
+ >
+
+ <ImageButton
+ android:id="@+id/prev"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="0dip" android:paddingRight="15dip"
+ android:src="@android:drawable/ic_media_previous"
+ android:background="@null"
+ android:tint="#00000000"
+ />
+
+ <ImageButton
+ android:id="@+id/playpause"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="15dip" android:paddingRight="15dip"
+ android:src="@android:drawable/ic_media_play"
+ android:background="@null"
+ android:tint="#00000000"
+ />
+
+ <ImageButton
+ android:id="@+id/next"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="15dip" android:paddingRight="0dip"
+ android:src="@android:drawable/ic_media_next"
+ android:background="@null"
+ android:tint="#00000000"
+ />
+
+</LinearLayout>
+
+<RelativeLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:padding="10dip"
+ >
+
+ <SeekBar
+ android:id="@+id/seekbar"
+ android:max="100"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="5dip"
+ android:progressDrawable="@drawable/seek_background"
+ />
+
+ <TextView
+ android:id="@+id/seek_position"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="0:00"
+ android:layout_below="@id/seekbar"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ />
+
+ <TextView
+ android:id="@+id/seek_total"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="0:00"
+ android:layout_below="@id/seekbar"
+ android:layout_alignParentRight="true"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ />
+
+</RelativeLayout>
+
+
+</LinearLayout>
+
+
+</LinearLayout>
+</ScrollView>
diff --git a/src/AndroidRemote/AndroidApp/res/layout/song_browse.xml b/src/AndroidRemote/AndroidApp/res/layout/song_browse.xml
new file mode 100644
index 0000000..475c384
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/res/layout/song_browse.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ BansheeRemote
+
+ Copyright (C) 2009 Nikitas Stamatopoulos
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+
+<ImageView
+android:id="@+id/icon"
+android:layout_height="wrap_content"
+android:layout_width="wrap_content"
+android:src="@drawable/album"/>
+
+<TextView
+android:id="@+id/title"
+android:paddingTop="15dip"
+android:layout_width="wrap_content"
+android:layout_height="wrap_content"
+android:textSize="26sp"
+android:text="Songs"/>
+/>
+
+</LinearLayout>
+
+ <ListView android:id="@+id/songBrowse"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"/>
+
+
+ </LinearLayout> \ No newline at end of file
diff --git a/src/AndroidRemote/AndroidApp/res/layout/song_row_view.xml b/src/AndroidRemote/AndroidApp/res/layout/song_row_view.xml
new file mode 100644
index 0000000..655bb4f
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/res/layout/song_row_view.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ BansheeRemote
+
+ Copyright (C) 2009 Nikitas Stamatopoulos
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <TextView android:id="@+id/songName"
+ android:textSize="14sp"
+ android:textStyle="bold"
+ android:textColor="#FFFFFF"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ <TextView android:id="@+id/songArtist"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+</LinearLayout> \ No newline at end of file
diff --git a/src/AndroidRemote/AndroidApp/res/values/strings.xml b/src/AndroidRemote/AndroidApp/res/values/strings.xml
new file mode 100644
index 0000000..33bcd04
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/res/values/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="hello">Hello World, BansheeRemote</string>
+ <string name="app_name">BansheeRemote</string>
+</resources>
diff --git a/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/AlbumBaseAdapter.java b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/AlbumBaseAdapter.java
new file mode 100644
index 0000000..fd43e51
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/AlbumBaseAdapter.java
@@ -0,0 +1,83 @@
+package org.nstamato.bansheeremote;
+/*
+BansheeRemote
+
+Copyright (C) 2011 Nikitas Stamatopoulos
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+import java.util.ArrayList;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+public class AlbumBaseAdapter extends BaseAdapter {
+ private static ArrayList<AlbumListItem> albumList;
+
+ private LayoutInflater mInflater;
+
+ public AlbumBaseAdapter(Context context, ArrayList<AlbumListItem> albums) {
+ albumList = albums;
+ mInflater = LayoutInflater.from(context);
+ }
+
+ public int getCount() {
+ return albumList.size();
+ }
+
+ public Object getItem(int position) {
+ return albumList.get(position);
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ViewHolder holder;
+
+ //Log.i("banshee","Entered getView method");
+ if (convertView == null) {
+ //Log.i("banshee","convertView is null");
+ convertView = mInflater.inflate(R.layout.album_row_view, null);
+ holder = new ViewHolder();
+ holder.albumName = (TextView) convertView.findViewById(R.id.albumName);
+ holder.artist = (TextView) convertView.findViewById(R.id.albumArtist);
+
+ convertView.setTag(holder);
+ } else {
+ holder = (ViewHolder) convertView.getTag();
+ }
+
+ holder.albumName.setText(albumList.get(position).getAlbumName());
+ holder.artist.setText(albumList.get(position).getArtist());
+
+ return convertView;
+ }
+
+ public class ViewHolder {
+ TextView albumName;
+ TextView artist;
+ }
+}
diff --git a/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/AlbumBrowse.java b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/AlbumBrowse.java
new file mode 100644
index 0000000..544db4c
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/AlbumBrowse.java
@@ -0,0 +1,232 @@
+package org.nstamato.bansheeremote;
+
+/*
+BansheeRemote
+
+Copyright (C) 2011 Nikitas Stamatopoulos
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+import java.io.File;
+import java.util.ArrayList;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
+import android.os.Bundle;
+import android.os.Environment;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.widget.AdapterView.OnItemClickListener;
+
+public class AlbumBrowse extends Activity{
+ ArrayList<AlbumListItem> albumList;
+ AlbumBaseAdapter albumListAdapter;
+ //ArrayAdapter<AlbumListItem> albumListAdapter;
+ final String filenameDB = "banshee.db";
+ SQLiteDatabase bansheeDB;
+ public ImageView icon;
+ public TextView title;
+ public String titleText="Albums";
+ public boolean addedNull=false;
+ //public ListView l;
+
+ /*private OnItemClickListener clickListener = new OnItemClickListener(){
+ public void onItemClick(AdapterView<?> a, View v, int position, long id) {
+ Object o = l.getItemAtPosition(position);
+ AlbumListItem album = (AlbumListItem)o;
+ Intent i = new Intent(AlbumBrowse.this,SongBrowse.class);
+ i.putExtra("Album",album.getAlbumName());
+ i.putExtra("AlbumID",album.getAlbumId());
+ startActivityForResult(i,2);
+ }
+ };*/
+
+ protected void onActivityResult(int requestCode, int resultCode, Intent data){
+ super.onActivityResult(requestCode, resultCode, data);
+ setResult(resultCode,data);
+ if(resultCode==RESULT_OK)
+ finish();
+ }
+
+ public void onCreate(Bundle savedInstanceState) {
+ //this.setFastScrollEnabled(true);
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.album_browse);
+ Intent i=getIntent();
+ Bundle extras=null;
+ if(i!=null)
+ extras = i.getExtras();
+ if(extras!=null)
+ this.titleText = extras.getString("Artist");
+ this.icon = (ImageView)this.findViewById(R.id.icon);
+ this.title = (TextView)this.findViewById(R.id.title);
+ this.title.setText(titleText);
+ this.icon.setImageResource(R.drawable.album);
+ this.albumList = new ArrayList<AlbumListItem>();
+ this.albumListAdapter = new AlbumBaseAdapter(this,albumList);
+ //this.albumListAdapter = new ArrayAdapter<AlbumListItem>(this,R.layout.album_row_view,albumList);
+
+ //this.header = new ArrayList<String>();
+ //this.header.add("title");
+ //this.headerAdapter = new ArrayAdapter<String>(this,R.layout.list_item,header);
+ //setListAdapter(headerAdapter);
+
+ //setListAdapter(albumListAdapter);
+ ListView l = (ListView) findViewById(R.id.albumBrowse);
+ l.setAdapter(albumListAdapter);
+ //ListView l = getListView();
+ l.setTextFilterEnabled(true);
+ l.setFastScrollEnabled(true);
+ //l.setTextFilterEnabled(true);
+ //l.setFastScrollEnabled(true);
+ //l.setOnItemClickListener(clickListener);
+ File db = null;
+ try{
+ //db = getFileStreamPath(filenameDB);
+ db = Environment.getExternalStorageDirectory();
+ String path = db.getAbsolutePath()+'/'+filenameDB;
+ bansheeDB = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.NO_LOCALIZED_COLLATORS);
+ String query = "SELECT Title, TitleLowered, ArtistName, AlbumID FROM CoreAlbums";
+ String[] params = null;
+ if(!titleText.equals("Albums")){
+ query+=" WHERE ArtistName=?";
+ params = new String[]{titleText};
+ }
+ query+=" ORDER BY Title";
+
+ Cursor c = bansheeDB.rawQuery(query,params);
+ //int columnCount = c.getColumnCount();
+ int name = c.getColumnIndex("Title");
+ int lowered = c.getColumnIndex("TitleLowered");
+ int artistColumnIndex = c.getColumnIndex("ArtistName");
+ int albumIdColumnIndex = c.getColumnIndex("AlbumID");
+ //int count = c.getCount();
+ c.moveToFirst();
+ while(c.isAfterLast()==false){
+ AlbumListItem albumItem = new AlbumListItem();
+ String label = c.getString(name);
+ String artist = c.getString(artistColumnIndex);
+ int albumID = c.getInt(albumIdColumnIndex);
+ if(label!=null){
+ albumItem.setAlbumName(label);
+ albumItem.setAlbumId(albumID);
+ albumItem.setArtist("");
+ if(artist!=null)
+ albumItem.setArtist(artist);
+ albumList.add(albumItem);
+ }
+ else if(!addedNull){
+ albumItem.setAlbumName(c.getString(lowered));
+ albumItem.setAlbumId(albumID);
+ albumItem.setArtist(("Unknown Artist"));
+ albumList.add(albumItem);
+ addedNull=true;
+ }
+ c.moveToNext();
+ //artistList.add("hello");
+ }
+ c.close();
+ //artistList.add("hello");
+ //Toast.makeText(this,result,Toast.LENGTH_SHORT).show();
+ //String result = c.getString(name);
+ }catch(SQLiteException e){
+ //Toast.makeText(this,e.getMessage(),Toast.LENGTH_SHORT).show();
+ Toast.makeText(this,"Something went wrong. Make sure the banshee database file is on your sd-card.",Toast.LENGTH_LONG).show();
+ }
+ catch(Exception e){
+ //Toast.makeText(this,e.getMessage(),Toast.LENGTH_SHORT).show();
+ Toast.makeText(this,"Something went wrong. Make sure the banshee database file is on your sd-card.",Toast.LENGTH_LONG).show();
+ }
+
+ //l.setOnItemClickListener(clickListener);
+ l.setOnItemClickListener(new OnItemClickListener(){
+ public void onItemClick(AdapterView<?> a, View v, int position, long id) {
+ ListView l = (ListView) findViewById(R.id.albumBrowse);
+ Object o = l.getItemAtPosition(position);
+ AlbumListItem album = (AlbumListItem)o;
+ Intent i = new Intent(AlbumBrowse.this,SongBrowse.class);
+ i.putExtra("Album",album.getAlbumName());
+ i.putExtra("AlbumID",album.getAlbumId());
+ startActivityForResult(i,2);
+ }
+ });
+
+
+ }
+ /*public void onListItemClick(ListView l, View v, int position, long id){
+ super.onListItemClick(l, v, position, id);
+ Object o = l.getItemAtPosition(position);
+ AlbumListItem album = (AlbumListItem)o;
+ Intent i = new Intent(AlbumBrowse.this,SongBrowse.class);
+ i.putExtra("Album",album.getAlbumName());
+ i.putExtra("AlbumID",album.getAlbumId());
+ startActivityForResult(i,2);
+ }*/
+
+ /*public void onListItemClick(ListView l, View v, int position, long id) {
+ super.onListItemClick(l, v, position, id);
+ String selected = this.albumListAdapter.getItem(position);
+ String query=null;
+ String[] params=null;
+ if(!selected.equals("unknown album")){
+ query = "SELECT AlbumID FROM CoreAlbums WHERE Title=?";
+ params = new String[]{selected};
+ if(!titleText.equals("Albums")){
+ query+=" AND ArtistName=?";
+ params = new String[]{selected,titleText};
+ }
+ }
+ else{
+ query = "SELECT AlbumID FROM CoreAlbums WHERE TitleLowered=?";
+ params = new String[]{selected};
+ if(!titleText.equals("Albums")){
+ query+=" AND ArtistName=?";
+ params = new String[]{selected,titleText};
+ }
+ }
+ Cursor c = bansheeDB.rawQuery(query,params);
+ //int columnCount = c.getColumnCount();
+ int albumIDColumn = c.getColumnIndex("AlbumID");
+ //int count = c.getCount();
+ c.moveToFirst();
+ int albumID=0;
+ try{
+ albumID = c.getInt(albumIDColumn);
+ }
+ catch(Exception e){
+
+ }
+
+ //Toast.makeText(this,selected,Toast.LENGTH_SHORT).show();
+ Intent i = new Intent(this,SongBrowse.class);
+ i.putExtra("Album",selected);
+ i.putExtra("AlbumID",albumID);
+ startActivityForResult(i,2);
+ }*/
+
+}
diff --git a/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/AlbumListItem.java b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/AlbumListItem.java
new file mode 100644
index 0000000..2293811
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/AlbumListItem.java
@@ -0,0 +1,55 @@
+package org.nstamato.bansheeremote;
+/*
+BansheeRemote
+
+Copyright (C) 2011 Nikitas Stamatopoulos
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+public class AlbumListItem {
+ private String albumName = "";
+ private String artist = "";
+ private int albumId=0;
+
+ public void setAlbumName(String name) {
+ this.albumName = name;
+ }
+
+ public String getAlbumName() {
+ return albumName;
+ }
+
+
+ public void setArtist(String artist) {
+ this.artist = artist;
+ }
+
+ public String getArtist() {
+ return artist;
+ }
+
+ public void setAlbumId(int albumId){
+ this.albumId = albumId;
+ }
+
+ public int getAlbumId(){
+ return albumId;
+ }
+}
diff --git a/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/ArtistBrowse.java b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/ArtistBrowse.java
new file mode 100644
index 0000000..19e7ca5
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/ArtistBrowse.java
@@ -0,0 +1,122 @@
+package org.nstamato.bansheeremote;
+
+/*
+BansheeRemote
+
+Copyright (C) 2011 Nikitas Stamatopoulos
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+import java.io.File;
+import java.util.ArrayList;
+
+
+import android.app.ListActivity;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
+import android.os.Bundle;
+import android.os.Environment;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class ArtistBrowse extends ListActivity{
+ ArrayList<String> artistList;
+ ArrayAdapter<String> artistListAdapter;
+ final String filenameDB = "banshee.db";
+ SQLiteDatabase bansheeDB;
+ public ImageView icon;
+ public TextView title;
+
+ protected void onActivityResult(int requestCode, int resultCode, Intent data){
+ super.onActivityResult(requestCode, resultCode, data);
+ setResult(resultCode,data);
+ if(resultCode==RESULT_OK)
+ finish();
+ }
+
+ public void onCreate(Bundle savedInstanceState) {
+ //this.setFastScrollEnabled(true);
+ //Log.i("test","test");
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.browse);
+ this.icon = (ImageView)this.findViewById(R.id.icon);
+ this.title = (TextView)this.findViewById(R.id.title);
+ this.title.setText("Artists");
+ this.icon.setImageResource(R.drawable.artist);
+ this.artistList = new ArrayList<String>();
+ this.artistListAdapter = new ArrayAdapter<String>(this,R.layout.list_item,artistList);
+ //this.header = new ArrayList<String>();
+ //this.header.add("title");
+ //this.headerAdapter = new ArrayAdapter<String>(this,R.layout.list_item,header);
+ //setListAdapter(headerAdapter);n
+ setListAdapter(artistListAdapter);
+ File db = null;
+ try{
+ //db = getFileStreamPath(filenameDB);
+ db = Environment.getExternalStorageDirectory();
+ String path = db.getAbsolutePath()+'/'+filenameDB;
+ bansheeDB = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.NO_LOCALIZED_COLLATORS);
+ String query = "SELECT Name FROM CoreArtists ORDER BY Name";
+ Cursor c = bansheeDB.rawQuery(query,null);
+ //int columnCount = c.getColumnCount();
+ int name = c.getColumnIndex("Name");
+ //int count = c.getCount();
+ c.moveToFirst();
+ while(c.isAfterLast()==false){
+ String artist = c.getString(name);
+ if(artist!=null)
+ artistList.add(c.getString(name));
+ c.moveToNext();
+ //artistList.add("hello");
+ }
+ c.close();
+ //artistList.add("hello");
+ //Toast.makeText(this,result,Toast.LENGTH_SHORT).show();
+ //String result = c.getString(name);
+ }catch(SQLiteException e){
+ //Toast.makeText(this,e.getMessage(),Toast.LENGTH_SHORT).show();
+ Toast.makeText(this,"Something went wrong. Make sure the banshee database file is on your sd-card.",Toast.LENGTH_LONG).show();
+ }
+ catch(Exception e){
+ //Toast.makeText(this,e.getMessage(),Toast.LENGTH_SHORT).show();
+ Toast.makeText(this,"Something went wrong. Make sure the banshee database file is on your sd-card.",Toast.LENGTH_LONG).show();
+ }
+ ListView l = getListView();
+ l.setTextFilterEnabled(true);
+ l.setFastScrollEnabled(true);
+ }
+
+ public void onListItemClick(ListView l, View v, int position, long id) {
+ super.onListItemClick(l, v, position, id);
+ String selected = this.artistListAdapter.getItem(position);
+ //Toast.makeText(this,selected,Toast.LENGTH_SHORT).show();
+ Intent i = new Intent(this,AlbumBrowse.class);
+ i.putExtra("Artist",selected);
+ startActivityForResult(i,2);
+ }
+
+}
diff --git a/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/BansheeInstance.java b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/BansheeInstance.java
new file mode 100644
index 0000000..85518f3
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/BansheeInstance.java
@@ -0,0 +1,50 @@
+package org.nstamato.bansheeremote;
+
+/*
+BansheeRemote
+
+Copyright (C) 2011 Nikitas Stamatopoulos
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+import android.graphics.Bitmap;
+
+public class BansheeInstance {
+ public String status;
+ public Bitmap cover;
+ public String track, artist, album;
+ public int iseekposition, iseektotal;
+ public boolean isCover;
+ public String ip;
+ public int port;
+
+ public BansheeInstance(String status, Bitmap cover, String track, String artist, String album, int position, int total, boolean isCover, String ip, int port){
+ this.status=status;
+ this.cover = cover;
+ this.track=track;
+ this.artist=artist;
+ this.album=album;
+ this.iseekposition=position;
+ this.iseektotal=total;
+ this.ip=ip;
+ this.port=port;
+ }
+}
diff --git a/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/BansheeRemote.java b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/BansheeRemote.java
new file mode 100644
index 0000000..385557b
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/BansheeRemote.java
@@ -0,0 +1,159 @@
+package org.nstamato.bansheeremote;
+
+/*
+BansheeRemote
+
+Copyright (C) 2011 Nikitas Stamatopoulos
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.util.ArrayList;
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.Toast;
+
+
+public class BansheeRemote extends ListActivity {
+ String serverData = null;
+ String savedServers[];
+ boolean serversExist=false;
+ ArrayList<String> savedServersListArray;
+ ArrayAdapter<String> savedServersListAdapter;
+ final String filename = "bansheeServers.dat";
+
+
+ public String readSettings(Context context, String filename){
+ FileInputStream fIn = null;
+ InputStreamReader isr = null;
+
+ char[] inputBuffer = new char[255];
+ String data = null;
+
+ try{
+ fIn = openFileInput(filename);
+ isr = new InputStreamReader(fIn);
+ isr.read(inputBuffer);
+ data = new String(inputBuffer);
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+ finally {
+ try {
+ isr.close();
+ fIn.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return data;
+ }
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data){
+ super.onActivityResult(requestCode, resultCode, data);
+ finish();
+ }
+
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setup();
+ }
+ public void setup(){
+ this.savedServersListArray = new ArrayList<String>();
+ this.savedServersListAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,savedServersListArray);
+ setListAdapter(savedServersListAdapter);
+ serverData = readSettings(BansheeRemote.this,filename);
+ if(serverData!=null){
+ serversExist=true;
+ savedServers = serverData.split(";");
+ for(int i=savedServers.length-2;i>=0;i--){
+ savedServersListArray.add(savedServers[i]);
+ }
+ }
+ savedServersListArray.add("Add new Server");
+ //File root = Environment.getDataDirectory();
+ //Toast.makeText(BansheeRemote.this,root.getAbsolutePath(),Toast.LENGTH_LONG).show();
+ }
+
+ public void onListItemClick(ListView l, View v, int position, long id) {
+ super.onListItemClick(l, v, position, id);
+ String selected = this.savedServersListAdapter.getItem(position);
+ if(selected.equals("Add new Server")){
+ Intent response = new Intent(BansheeRemote.this,NewServer.class);
+ startActivityForResult(response,1);
+ }
+ else{
+ boolean canConnect = false;
+ boolean isReachable=false;
+ String server[] = selected.split(":");
+ String ip = server[0];
+ int port = Integer.parseInt(server[1]);
+ String command = "test/";
+ Socket s;
+ OutputStream os;
+ try{
+ InetAddress address = InetAddress.getByName(ip);
+ isReachable = address.isReachable(3000);
+ if(isReachable){
+ try {
+ s = new Socket(ip,port);
+ os = s.getOutputStream();
+ os.write(command.getBytes(), 0, command.length());
+ s.close();
+ os.close();
+ canConnect = true;
+ } catch(Exception e) {
+ canConnect = false;
+ Toast.makeText(BansheeRemote.this,"Can't connect to Server. Check your settings.",Toast.LENGTH_SHORT).show();
+ }
+ }
+ else{
+ Toast.makeText(BansheeRemote.this,"Unreachable host.",Toast.LENGTH_SHORT).show();
+
+ }
+ }catch(Exception e){
+ Toast.makeText(BansheeRemote.this,"Can't connect to Server. Check your settings.",Toast.LENGTH_SHORT).show();
+
+ }
+
+ if(canConnect){
+ Intent response = new Intent(BansheeRemote.this,main.class);
+ response.putExtra("ip",ip);
+ response.putExtra("port", port);
+ startActivityForResult(response,1);
+ }
+ }
+ //Toast.makeText(BansheeRemote.this, selected,Toast.LENGTH_SHORT).show();
+ }
+
+}
diff --git a/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/NewServer.java b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/NewServer.java
new file mode 100644
index 0000000..3044881
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/NewServer.java
@@ -0,0 +1,198 @@
+package org.nstamato.bansheeremote;
+
+/*
+BansheeRemote
+
+Copyright (C) 2011 Nikitas Stamatopoulos
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.net.InetAddress;
+import java.net.Socket;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Toast;
+
+
+public class NewServer extends Activity {
+ String serverData = null;
+ String savedServers[];
+ boolean serversExist=false;
+ final String filename = "bansheeServers.dat";
+
+ public void writeSettings(Context context, String filename, String data){
+ FileOutputStream fOut = null;
+ OutputStreamWriter osw = null;
+
+ try{
+ fOut = context.openFileOutput(filename,Context.MODE_APPEND);
+ osw = new OutputStreamWriter(fOut);
+ osw.write(data+';');
+ osw.flush();
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+ finally {
+ try {
+ osw.close();
+ fOut.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ public String readSettings(Context context, String filename){
+ FileInputStream fIn = null;
+ InputStreamReader isr = null;
+
+ char[] inputBuffer = new char[255];
+ String data = null;
+
+ try{
+ fIn = openFileInput(filename);
+ isr = new InputStreamReader(fIn);
+ isr.read(inputBuffer);
+ data = new String(inputBuffer);
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+ finally {
+ try {
+ isr.close();
+ fIn.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return data;
+ }
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data){
+ super.onActivityResult(requestCode, resultCode, data);
+ finish();
+ }
+
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setup();
+ }
+ public void setup(){
+ setContentView(R.layout.init);
+ Button data = (Button) findViewById(R.id.submit);
+ serverData = readSettings(NewServer.this,filename);
+ if(serverData!=null){
+ serversExist=true;
+ savedServers = serverData.split(";");
+ }
+ data.setOnClickListener(new OnClickListener(){
+ public void onClick(View v) {
+ EditText iptext = (EditText)findViewById(R.id.ip);
+ EditText porttext = (EditText)findViewById(R.id.port);
+ String ip = iptext.getText().toString();
+ Socket s;
+ OutputStream os;
+ String command = "test/";
+ boolean canConnect = false;
+ boolean isReachable = false;
+ int port=0;
+
+ try{
+ port=Integer.parseInt(porttext.getText().toString());
+ InetAddress address = InetAddress.getByName(ip);
+ isReachable = address.isReachable(3000);
+ if(isReachable){
+ try {
+ s = new Socket(ip,port);
+ os = s.getOutputStream();
+ os.write(command.getBytes(), 0, command.length());
+ s.close();
+ os.close();
+ canConnect = true;
+ } catch(Exception e) {
+ canConnect = false;
+ Toast.makeText(NewServer.this,"Can't connect to Server. Check your settings.",Toast.LENGTH_SHORT).show();
+ }
+ }
+ else{
+ Toast.makeText(NewServer.this,"Unreachable host.",Toast.LENGTH_SHORT).show();
+
+ }
+ }catch(Exception e){
+ Toast.makeText(NewServer.this,"Can't connect to Server. Check your settings.",Toast.LENGTH_SHORT).show();
+
+ }
+
+ if(canConnect){
+ Intent response = new Intent(NewServer.this,main.class);
+ response.putExtra("ip",ip);
+ response.putExtra("port", port);
+ String newServer = ip+':'+porttext.getText().toString();
+ boolean exists = false;
+ if(serversExist){
+ for(int i=0;i<savedServers.length-1;i++){
+ if(newServer.equals(savedServers[i])){
+ exists = true;
+
+ }
+ }
+
+ if(!exists){
+ int numberServersSaved = savedServers.length-1;
+ if(numberServersSaved<5)
+ writeSettings(NewServer.this,filename,newServer);
+ else{
+ deleteFile(filename);
+ for(int i=1;i<numberServersSaved;i++){
+ writeSettings(NewServer.this,filename,savedServers[i]);
+ }
+ writeSettings(NewServer.this,filename,newServer);
+ }
+ }
+ }
+ else{
+ writeSettings(NewServer.this,filename,newServer);
+ }
+ startActivityForResult(response,1);
+ //setResult(RESULT_OK,response);
+ //finish();
+ }
+ }
+
+ });
+ }
+
+}
+
diff --git a/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/Settings.java b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/Settings.java
new file mode 100644
index 0000000..8047f5c
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/Settings.java
@@ -0,0 +1,107 @@
+package org.nstamato.bansheeremote;
+
+/*
+BansheeRemote
+
+Copyright (C) 2011 Nikitas Stamatopoulos
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Toast;
+
+
+public class Settings extends Activity {
+
+ //public void onConfigurationChanged(Configuration newConfig){
+ //setup();
+ //}
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setup();
+ }
+ public void setup(){
+ setContentView(R.layout.init);
+ Button data = (Button) findViewById(R.id.submit);
+
+ data.setOnClickListener(new OnClickListener(){
+
+ public void onClick(View v) {
+ EditText iptext = (EditText)findViewById(R.id.ip);
+ EditText porttext = (EditText)findViewById(R.id.port);
+ String ip = iptext.getText().toString();
+ Socket s;
+ OutputStream os;
+ String command = "test/";
+ boolean canConnect = false;
+ boolean isReachable = false;
+ int port=Integer.parseInt(porttext.getText().toString());
+
+ try{
+ InetAddress address = InetAddress.getByName(ip);
+ isReachable = address.isReachable(3000);
+ if(isReachable){
+ try {
+ s = new Socket(ip,port);
+ os = s.getOutputStream();
+ os.write(command.getBytes(), 0, command.length());
+ s.close();
+ os.close();
+ canConnect = true;
+ } catch(Exception e) {
+ canConnect = false;
+ Toast.makeText(Settings.this,"Can't connect to Server. Check your settings.",Toast.LENGTH_SHORT).show();
+ }
+ }
+ else{
+ Toast.makeText(Settings.this,"Unreachable host.",Toast.LENGTH_SHORT).show();
+
+ }
+ }catch(Exception e){
+ Toast.makeText(Settings.this,"Can't connect to Server. Check your settings.",Toast.LENGTH_SHORT).show();
+
+ }
+
+ if(canConnect){
+ Intent response = new Intent();
+ response.putExtra("ip",ip);
+ response.putExtra("port", port);
+ setResult(RESULT_OK,response);
+ finish();
+ }
+ }
+
+ });
+ }
+
+}
diff --git a/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/SongBaseAdapter.java b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/SongBaseAdapter.java
new file mode 100644
index 0000000..0ca75dd
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/SongBaseAdapter.java
@@ -0,0 +1,83 @@
+package org.nstamato.bansheeremote;
+/*
+BansheeRemote
+
+Copyright (C) 2011 Nikitas Stamatopoulos
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+import java.util.ArrayList;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+public class SongBaseAdapter extends BaseAdapter {
+ private static ArrayList<SongListItem> songList;
+
+ private LayoutInflater mInflater;
+
+ public SongBaseAdapter(Context context, ArrayList<SongListItem> songs) {
+ songList = songs;
+ mInflater = LayoutInflater.from(context);
+ }
+
+ public int getCount() {
+ return songList.size();
+ }
+
+ public Object getItem(int position) {
+ return songList.get(position);
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ViewHolder holder;
+
+ //Log.i("banshee","Entered getView method");
+ if (convertView == null) {
+ //Log.i("banshee","convertView is null");
+ convertView = mInflater.inflate(R.layout.song_row_view, null);
+ holder = new ViewHolder();
+ holder.songName = (TextView) convertView.findViewById(R.id.songName);
+ holder.artist = (TextView) convertView.findViewById(R.id.songArtist);
+
+ convertView.setTag(holder);
+ } else {
+ holder = (ViewHolder) convertView.getTag();
+ }
+
+ holder.songName.setText(songList.get(position).getSongName());
+ holder.artist.setText(songList.get(position).getArtist());
+
+ return convertView;
+ }
+
+ public class ViewHolder {
+ TextView songName;
+ TextView artist;
+ }
+}
diff --git a/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/SongBrowse.java b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/SongBrowse.java
new file mode 100644
index 0000000..ffde3cf
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/SongBrowse.java
@@ -0,0 +1,216 @@
+package org.nstamato.bansheeremote;
+
+/*
+BansheeRemote
+
+Copyright (C) 2011 Nikitas Stamatopoulos
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+import java.io.File;
+import java.util.ArrayList;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
+import android.os.Bundle;
+import android.os.Environment;
+import android.util.Log;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.widget.AdapterView.OnItemClickListener;
+
+public class SongBrowse extends Activity{
+ ArrayList<SongListItem> songList;
+ //ArrayList<String> trackIdList;
+ SongBaseAdapter songListAdapter;
+ final String filenameDB = "banshee.db";
+ SQLiteDatabase bansheeDB;
+ public int albumID;
+ public ImageView icon;
+ public TextView title;
+ public Cursor c;
+ public String[] params = null;
+ public String query;
+
+ protected void onActivityResult(int requestCode, int resultCode, Intent data){
+ super.onActivityResult(requestCode, resultCode, data);
+ finish();
+ }
+
+ public void onCreate(Bundle savedInstanceState) {
+ //this.setFastScrollEnabled(true);
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.song_browse);
+ Bundle extras = getIntent().getExtras();
+ String titleText = extras.getString("Album");
+ this.albumID = extras.getInt("AlbumID");
+ this.icon = (ImageView)this.findViewById(R.id.icon);
+ this.title = (TextView)this.findViewById(R.id.title);
+ this.title.setText(titleText);
+ this.icon.setImageResource(R.drawable.songs);
+ this.songList = new ArrayList<SongListItem>();
+ this.songListAdapter = new SongBaseAdapter(this,songList);
+ //this.trackIdList = new ArrayList<Integer>();
+ //this.songListAdapter = new ArrayAdapter<String>(this,R.layout.list_item,songList);
+ //this.header = new ArrayList<String>();
+ //this.header.add("title");
+ //this.headerAdapter = new ArrayAdapter<String>(this,R.layout.list_item,header);
+ //setListAdapter(headerAdapter);
+ //setListAdapter(songListAdapter);
+ final ListView l = (ListView) findViewById(R.id.songBrowse);
+ l.setAdapter(songListAdapter);
+ l.setTextFilterEnabled(true);
+ l.setFastScrollEnabled(true);
+ File db = null;
+ try{
+ //db = getFileStreamPath(filenameDB);
+ db = Environment.getExternalStorageDirectory();
+ String path = db.getAbsolutePath()+'/'+filenameDB;
+ bansheeDB = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.NO_LOCALIZED_COLLATORS);
+ query = "SELECT CoreTracks.Title AS trackTitle, CoreTracks.Uri, CoreArtists.Name, CoreAlbums.Title FROM CoreTracks,CoreAlbums,CoreArtists WHERE CoreTracks.AlbumID==CoreAlbums.AlbumID AND CoreTracks.ArtistID==CoreArtists.ArtistID";
+
+ if(albumID>=0){
+ query+=" AND CoreTracks.AlbumID=?";
+ params = new String[]{String.valueOf(albumID)};
+ query+=" ORDER BY CoreTracks.TrackNumber";
+ }
+ else{
+ query+=" ORDER BY CoreTracks.Title";
+ }
+ //Thread queryDB = new Thread(new Runnable(){
+ //public void run(){
+ c = bansheeDB.rawQuery(query,params);
+ int name = c.getColumnIndex("trackTitle");
+ int artist = c.getColumnIndex("CoreArtists.Name");
+ int albumColumnIndex = c.getColumnIndex("CoreAlbums.Title");
+ int uriColumnIndex = c.getColumnIndex("CoreTracks.Uri");
+ Log.i("banshee","coretrack title index "+name+" corealbum title index "+albumColumnIndex+" number of columns "+c.getColumnCount());
+ for(int i=0;i<c.getColumnCount();i++){
+ Log.i("banshee",c.getColumnName(i));
+ }
+ //int albumColumnIndex = c.getColumnIndex("CoreAlbums.Title");
+ //int trackIdColumn = c.getColumnIndex("TrackID");
+ //int count = c.getCount();
+ c.moveToFirst();
+ while(c.isAfterLast()==false){
+ SongListItem songItem = new SongListItem();
+ String songName = c.getString(name);
+ String artistName = c.getString(artist);
+ String albumName = c.getString(albumColumnIndex);
+ String uri = c.getString(uriColumnIndex);
+ String combination = "";
+ songItem.setSongName(songName);
+ if(artistName!=null){
+ combination+=artistName;
+ if(albumName!=null)
+ combination+='/'+albumName;
+ }
+ else{
+ combination+="Unknown Artist";
+ if(albumName!=null)
+ combination+='/'+albumName;
+ }
+ songItem.setUri(uri);
+ songItem.setArtist(combination);
+ //String trackID = c.getString(trackIdColumn);
+ if(songName!=null){
+ songList.add(songItem);
+ //trackIdList.add(c.getString(trackIdColumn));
+ }
+ c.moveToNext();
+ //artistList.add("hello");
+ }
+ c.close();
+ //}
+ //});
+ //queryDB.start();
+ //int columnCount = c.getColumnCount();
+
+ //artistList.add("hello");
+ //Toast.makeText(this,result,Toast.LENGTH_SHORT).show();
+ //String result = c.getString(name);
+ }catch(SQLiteException e){
+ Toast.makeText(this,e.getMessage(),Toast.LENGTH_SHORT).show();
+ //Toast.makeText(this,"Something went wrong. Make sure the banshee database file is on your sd-card.",Toast.LENGTH_LONG).show();
+ }
+ catch(Exception e){
+ Toast.makeText(this,e.getMessage(),Toast.LENGTH_SHORT).show();
+ //Toast.makeText(this,"Something went wrong. Make sure the banshee database file is on your sd-card.",Toast.LENGTH_LONG).show();
+ }
+
+ l.setOnItemClickListener(new OnItemClickListener(){
+ public void onItemClick(AdapterView<?> a, View v, int position, long id) {
+ Object o = l.getItemAtPosition(position);
+ SongListItem song = (SongListItem)o;
+ Intent response = new Intent();
+ response.putExtra("Uri",song.getUri());
+ setResult(RESULT_OK,response);
+ finish();
+ }
+ });
+ }
+ /*public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if ((keyCode == KeyEvent.KEYCODE_BACK)) {
+ //Log.d(this.getClass().getName(), "back button pressed");
+ Intent response = new Intent();
+ setResult(RESULT_OK+1,response);
+ finish();
+ }
+ return super.onKeyDown(keyCode, event);
+ }*/
+
+ /*public void onListItemClick(ListView l, View v, int position, long id) {
+ super.onListItemClick(l, v, position, id);
+ String selected = this.songListAdapter.getItem(position);
+ //String trackID = this.trackIdList.get(position);
+ //Toast.makeText(this,selected,Toast.LENGTH_SHORT).show();
+ query = "SELECT Uri FROM CoreTracks WHERE Title=?";
+ params = new String[]{selected};
+ if(albumID>=0){
+ query+=" AND AlbumID=?";
+ params = new String[]{selected, String.valueOf(albumID)};
+ query+=" ORDER BY TrackNumber";
+ }
+ else{
+ query+=" ORDER BY Title";
+ }
+ this.c = bansheeDB.rawQuery(query,params);
+ //int columnCount = c.getColumnCount();
+ int uriColumn = c.getColumnIndex("Uri");
+ //int count = c.getCount();
+ c.moveToFirst();
+ String Uri = c.getString(uriColumn);
+ //Toast.makeText(this,Uri,Toast.LENGTH_SHORT).show();
+ Intent response = new Intent();
+ response.putExtra("Uri",Uri);
+ setResult(RESULT_OK,response);
+ finish();
+ //startActivityForResult(i,0);
+ }*/
+
+}
diff --git a/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/SongListItem.java b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/SongListItem.java
new file mode 100644
index 0000000..f52613e
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/SongListItem.java
@@ -0,0 +1,64 @@
+package org.nstamato.bansheeremote;
+/*
+BansheeRemote
+
+Copyright (C) 2011 Nikitas Stamatopoulos
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+public class SongListItem {
+ private String songName = "";
+ private String artist = "";
+ private int albumId=0;
+ private String uri="";
+
+ public void setSongName(String name) {
+ this.songName = name;
+ }
+
+ public String getSongName() {
+ return songName;
+ }
+
+
+ public void setArtist(String artist) {
+ this.artist = artist;
+ }
+
+ public String getArtist() {
+ return artist;
+ }
+
+ public void setAlbumId(int albumId){
+ this.albumId = albumId;
+ }
+
+ public int getAlbumId(){
+ return albumId;
+ }
+
+ public void setUri(String uri){
+ this.uri = uri;
+ }
+
+ public String getUri(){
+ return uri;
+ }
+}
diff --git a/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/Sync.java b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/Sync.java
new file mode 100644
index 0000000..4b59acd
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/Sync.java
@@ -0,0 +1,222 @@
+package org.nstamato.bansheeremote;
+
+/*
+BansheeRemote
+
+Copyright (C) 2011 Nikitas Stamatopoulos
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.database.sqlite.SQLiteException;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+public class Sync extends Activity{
+public ProgressDialog pd;
+String server;
+int port;
+public final String filenameDB = "banshee.db";
+public static ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+public static byte[] byteBuff = new byte[1024];
+
+private Handler dbHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ //int progress = msg.arg1;
+ //pd.setProgress(progress);
+ //if(progress>=100)
+ pd.dismiss();
+ //tv.setText(pi_string);
+
+ }
+};
+
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Bundle extras = getIntent().getExtras();
+ server = extras.getString("ip");
+ port = extras.getInt("port");
+ sync();
+ }
+
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ sync();
+ }
+
+
+ public void sync(){
+ pd = new ProgressDialog(this);
+ pd.setCancelable(true);
+ //pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
+ pd.setTitle("Syncing with Banshee...");
+ pd.setMessage("This might take some time, depending on your internet connection and your phone.");
+ pd.show();
+
+ Thread getDB = new Thread(new Runnable (){
+ //String command="sync/";
+ public void run(){
+ try {
+ //String dbCount = getInfo("syncCount",null);
+ //int totalReceived = 0;
+ //int remainingBytes = getInfoInt("syncCount",null);
+ int chunksize=8000;//remainingBytes;
+ //int offset = 0;
+ //Log.i("banshee","bytes "+remainingBytes);
+ //Toast.makeText(main.this,"Bytes "+dbCount,Toast.LENGTH_SHORT).show();
+ byte[] dbbytes = null;
+ File root = Environment.getExternalStorageDirectory();
+ //Log.i("banshee","external directory "+root.getAbsolutePath());
+ FileOutputStream fstream = new FileOutputStream(new File(root,filenameDB));
+ //FileOutputStream fstream = openFileOutput(filenameDB,Context.MODE_PRIVATE);
+ dbbytes = new byte[chunksize];
+ Socket s = new Socket(server,port);
+ OutputStream os = s.getOutputStream();
+ InputStream is = s.getInputStream();
+ String command = "sync/";
+ os.write(command.getBytes(), 0, command.length());
+ //Log.i("banshee","sent command");
+ int len=0;
+ while ( (len = is.read(dbbytes)) > 0 ) {
+ //Log.i("banshee","inside loop");
+ //is.read(dbbytes,0,chunksize);
+ fstream.write(dbbytes,0,len);
+ //totalReceived+=len;
+ //int progress=totalReceived/remainingBytes*100;
+ //Message progressMessage = new Message();
+ //progressMessage.arg1=progress;
+ //dbHandler.sendMessage(progressMessage);
+ }
+
+ /*while(remainingBytes>0){
+ Socket s = new Socket(server,port);
+ OutputStream os = s.getOutputStream();
+ InputStream is = s.getInputStream();
+ String offsetT = Integer.toString(offset);
+ String command = "sync/"+offsetT;
+ //String command = "sync/";
+ os.write(command.getBytes(), 0, command.length());
+ //if(remainingBytes>=chunksize){
+ //dbbytes = new byte[chunksize];
+ is.read(dbbytes,0,chunksize);
+ //if(rep==0){
+ //hash = Base64.encodeToString(dbbytes, 0, chunksize, Base64.DEFAULT);
+ //os.write(hash.getBytes(),0,chunksize);
+ //}
+ remainingBytes-=chunksize;
+ offset+=chunksize;
+ //}
+ //else{
+ //dbbytes = new byte[remainingBytes];
+ //is.read(dbbytes,0,remainingBytes);
+ //remainingBytes=0;
+ //}
+ //Toast.makeText(main.this,"hello "+dbCount,Toast.LENGTH_SHORT).show();
+ fstream.write(dbbytes);
+ s.close();
+ //rep++;
+ }*/
+ fstream.close();
+ dbHandler.sendEmptyMessage(0);
+ Intent response = new Intent();
+ //response.putExtra("ip",ip);
+ //response.putExtra("port", port);
+ setResult(RESULT_OK,response);
+ finish();
+ //continueserver=true;
+ //bansheeDB = SQLiteDatabase.openDatabase(filenameDB, null, SQLiteDatabase.OPEN_READONLY);
+ }
+ catch (java.lang.OutOfMemoryError e) {
+ Log.i("banshee","ran out of memory");
+ dbHandler.sendEmptyMessage(0);
+ Intent response = new Intent();
+ setResult(RESULT_OK+2,response);
+ finish();
+ //Toast.makeText(Sync.this,"Memory Error.",Toast.LENGTH_SHORT).show();
+
+ }
+ catch(SQLiteException e){
+ dbHandler.sendEmptyMessage(0);
+ Intent response = new Intent();
+ setResult(RESULT_OK+1,response);
+ finish();
+ //Toast.makeText(main.this,"Can't create database",Toast.LENGTH_LONG).show();
+ }
+
+ catch (Exception e) {
+ dbHandler.sendEmptyMessage(0);
+ Intent response = new Intent();
+ setResult(RESULT_OK+1,response);
+ finish();
+ //Toast.makeText(Sync.this,"Can't write to database file.",Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+ getDB.start();
+
+ }
+ public String getInfo(String action, String params) throws Exception{
+ String data=null;
+ String formattedAction = action+'/'+params;
+ Socket s = new Socket(server,port);
+ OutputStream os = s.getOutputStream();
+ InputStream is = s.getInputStream();
+ os.write(formattedAction.getBytes(), 0, formattedAction.length());
+ int byteCount = -1;
+ byteArrayOutputStream.reset();
+ while ((byteCount = is.read(byteBuff, 0, byteBuff.length)) != -1) {
+ byteArrayOutputStream.write(byteBuff, 0, byteCount);
+ }
+ data = byteArrayOutputStream.toString();
+ return data;
+ }
+ public int getInfoInt(String action,String params){
+ int n=-1;
+ boolean done=false;
+ while(!done){
+ try{
+ n = Integer.parseInt(getInfo(action,params));
+ done=true;
+ }
+ catch(NumberFormatException e){
+ done=false;
+ }
+ catch(Exception e){
+
+ }
+ }
+ return n;
+ }
+}
diff --git a/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/main.java b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/main.java
new file mode 100644
index 0000000..f5f95c7
--- /dev/null
+++ b/src/AndroidRemote/AndroidApp/src/org/nstamato/bansheeremote/main.java
@@ -0,0 +1,727 @@
+
+package org.nstamato.bansheeremote;
+
+/*
+BansheeRemote
+
+Copyright (C) 2011 Nikitas Stamatopoulos
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+import java.io.*;
+import java.net.*;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.database.sqlite.SQLiteDatabase;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
+
+public class main extends Activity{
+
+ public ImageButton prev, playpause, next, mute, artists, albums, songs;
+ public PhoneStateListener phoneListener;
+ public TelephonyManager tm;
+ public TextView track, artist, album, seekposition, seektotal;
+ public SeekBar seekbar;
+ public ImageView cover;
+
+
+ public static String server="";
+ public static int port=-1;
+ public static int interval = 1;
+
+ public final static int PARTIAL_UPDATE = 1, FULL_UPDATE = 2;
+ public final String filenameDB = "banshee.db";
+
+ public String strack, sartist, salbum;
+ public int iseekposition, iseektotal;
+ public String istatus;
+ public Bitmap bcover = null;
+ public Socket s;
+ public OutputStream os;
+ public InputStream is;
+ public String command;
+ public boolean isCover;
+ public Bitmap no_cover;//=BitmapFactory.decodeResource(getResources(),R.drawable.no_cover_art);
+ //public boolean connected;
+ public ProgressDialog pd;
+ public Thread getDB;
+ public SQLiteDatabase bansheeDB = null;
+ public boolean serverThreadDone = false;
+ public final Handler update = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+
+ if(msg.what == FULL_UPDATE) {
+ playpause.setImageResource((istatus.contains("playing")) ? android.R.drawable.ic_media_pause : android.R.drawable.ic_media_play);
+ track.setText(strack);
+ artist.setText(sartist);
+ album.setText(salbum);
+
+ seektotal.setText(formatTime(iseektotal));
+ seekbar.setMax(iseektotal);
+ cover.setImageBitmap(bcover);
+ }
+
+ seekbar.setProgress(iseekposition);
+ seekposition.setText(formatTime(iseekposition));
+
+
+ }
+ };
+
+ public static String formatTime(int seconds) {
+ String leading = (seconds % 60 < 10) ? "0" : "";
+ if(seconds >= 60)
+ return (seconds / 60) + ":" + leading + (seconds % 60);
+ else
+ return "0:" + leading + seconds;
+ }
+
+
+ public boolean continueserver = true;
+
+ public Thread serverpoke = new Thread(new Runnable() {
+ public void run() {
+ while(true){
+ while(continueserver) {
+ serverThreadDone = false;
+ if(!istatus.equals("idle")){
+ try {
+ int oldseektotal = iseektotal;
+ String oldstatus = istatus;
+ getAllInfo();
+ if(isCover && (bcover==null)){
+ bcover = getCover(server,port);
+ }
+ if(!istatus.equals(oldstatus)){
+ update.sendEmptyMessage(FULL_UPDATE);
+ }
+ if(iseektotal != oldseektotal) {
+ bcover=no_cover;
+ if(isCover)
+ bcover = getCover(server,port);
+ update.sendEmptyMessage(FULL_UPDATE);
+ }
+ else {
+ update.sendEmptyMessage(PARTIAL_UPDATE);
+ }
+
+ }catch(Exception e) {
+ }
+ }
+ else{
+ try {
+ istatus = getInfo("status",null);
+ } catch (Exception e) {
+
+ e.printStackTrace();
+ }
+ }
+
+ // then sleep until next round
+ try {
+ Thread.sleep(1000 * interval);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ serverThreadDone=true;
+ }
+ try{
+ Thread.sleep(1000 * interval);
+ }catch(Exception e){
+
+ }
+ }
+
+ }
+ });
+
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ MenuItem item1 = menu.add(0,0,0,"Settings");
+ item1.setIcon(android.R.drawable.ic_menu_edit);
+ MenuItem item5 = menu.add(0,4,4,"Sync");
+ item5.setIcon(R.drawable.ic_menu_refresh);
+ MenuItem item2 = menu.add(0,1,1,"Shuffle");
+ item2.setIcon(android.R.drawable.ic_menu_directions);
+ MenuItem item3 = menu.add(0,2,2,"Repeat");
+ item3.setIcon(android.R.drawable.ic_menu_revert);
+ MenuItem item4 = menu.add(0,3,3,"Exit");
+ item4.setIcon(android.R.drawable.ic_menu_close_clear_cancel);
+
+ return true;
+ }
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item){
+ if(item.getItemId()==3){
+ Intent response = new Intent();
+ setResult(RESULT_OK,response);
+ finish();
+ }
+ else if(item.getItemId()==0){
+ displaySettings();
+ }
+
+ else if(item.getItemId()==4){
+ //File dir = Environment.getExternalStorageDirectory();
+ //Toast.makeText(main.this,"Shuffle mode by "+dir.getAbsolutePath(),Toast.LENGTH_SHORT).show();
+
+ continueserver=false;
+ while(true){
+ if(serverThreadDone){
+ syncLibrary();
+ break;
+ }
+ }
+
+
+ }
+
+ else if(item.getItemId()==1){
+ try{
+ String shuffleText = getInfo("shuffle",null);
+ if(shuffleText.equals("off"))
+ Toast.makeText(main.this,"Shuffle mode off",Toast.LENGTH_SHORT).show();
+ else if(shuffleText.equals("song") || shuffleText.equals("Artist") || shuffleText.equals("Album") || shuffleText.equals("Score") || shuffleText.equals("Rating"))
+ Toast.makeText(main.this,"Shuffle mode by "+shuffleText,Toast.LENGTH_SHORT).show();
+
+ }catch(Exception e){
+ Toast.makeText(main.this,"Can't connect to Server. Check your settings.",Toast.LENGTH_SHORT).show();
+ }
+ serverpoke.interrupt();
+ }
+ else if(item.getItemId()==2){
+ try{
+ String repeatText = getInfo("repeat",null);
+ if(repeatText.equals("off")||repeatText.equals("single")||repeatText.equals("all")){
+ Toast.makeText(main.this,"Repeat mode "+repeatText,Toast.LENGTH_SHORT).show();
+ }
+ }catch(Exception e){
+ Toast.makeText(main.this,"Can't connect to Server. Check your settings.",Toast.LENGTH_SHORT).show();
+ }
+ serverpoke.interrupt();
+ }
+
+ return true;
+ }
+
+ public void sendCommand(String action, String params) throws Exception{
+ String formattedAction = action+'/'+params;
+
+ this.s = new Socket(server,port);
+ this.os = this.s.getOutputStream();
+ this.os.write(formattedAction.getBytes(), 0, formattedAction.length());
+
+ }
+
+ public String getInfo(String action, String params) throws Exception{
+ String data=null;
+ String formattedAction = action+'/'+params;
+ this.s = new Socket(server,port);
+ this.os = this.s.getOutputStream();
+ this.is = this.s.getInputStream();
+ this.os.write(formattedAction.getBytes(), 0, formattedAction.length());
+ int byteCount = -1;
+ byteArrayOutputStream.reset();
+ while ((byteCount = this.is.read(byteBuff, 0, byteBuff.length)) != -1) {
+ byteArrayOutputStream.write(byteBuff, 0, byteCount);
+ }
+ data = byteArrayOutputStream.toString();
+ return data;
+ }
+ public static Bitmap getCover(String server, int port){
+ Bitmap decoded = null;
+ String command="coverImage/";
+ try {
+ Socket s = new Socket(server,port);
+ OutputStream os = s.getOutputStream();
+ InputStream is = s.getInputStream();
+ os.write(command.getBytes(), 0, command.length());
+ decoded = BitmapFactory.decodeStream(is);
+ } catch (IOException e) {
+ }
+ return decoded;
+ }
+
+
+
+public void syncLibrary(){
+ Intent i = new Intent(main.this, Sync.class);
+ i.putExtra("ip", server);
+ i.putExtra("port",port);
+ startActivityForResult(i,1);
+}
+
+
+
+
+ public boolean coverExists() throws Exception{
+ String data=null;
+ String action="coverExists/";
+ boolean done=false;
+ while(!done){
+ this.s = new Socket(server,port);
+ this.os = this.s.getOutputStream();
+ this.is = this.s.getInputStream();
+ this.os.write(action.getBytes(), 0, action.length());
+ int byteCount = -1;
+ byteArrayOutputStream.reset();
+ while ((byteCount = this.is.read(byteBuff, 0, byteBuff.length)) != -1) {
+ byteArrayOutputStream.write(byteBuff, 0, byteCount);
+ }
+ data = byteArrayOutputStream.toString();
+ if(data.equals("true")||data.equals("false")){
+ done=true;
+ }
+ }
+ return Boolean.parseBoolean(data);
+ }
+
+ public int getInfoInt(String action,String params){
+ int n=-1;
+ boolean done=false;
+ while(!done){
+ try{
+ n = Integer.parseInt(getInfo(action,params));
+ done=true;
+ }
+ catch(NumberFormatException e){
+ done=false;
+ }
+ catch(Exception e){
+
+ }
+ }
+ return n;
+ }
+ public void getAllInfo() throws Exception{
+ //Toast.makeText(main.this,"Inside",Toast.LENGTH_SHORT).show();
+ String everything = getInfo("all",null);
+ boolean done=false;
+ while(!done){
+ if(everything!=null){
+ String resultArray[] = new String[7];
+ resultArray = everything.split("/");
+ istatus = resultArray[0];
+ salbum = resultArray[1];
+ sartist = resultArray[2];
+ strack = resultArray[3];
+ try {
+ iseekposition = Integer.parseInt(resultArray[4]);
+ iseektotal = Integer.parseInt(resultArray[5]);
+ isCover = Boolean.parseBoolean(resultArray[6]);
+ if(resultArray[6].equals("false") || resultArray[6].equals("true")){
+ done=true;
+ }
+ }
+ catch(NumberFormatException e){
+ done=false;
+ }
+ catch(Exception e) {
+ Toast.makeText(main.this,"Can't connect to Server. Check your settings.",Toast.LENGTH_SHORT).show();
+ }
+
+ }
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ continueserver = false;
+ this.serverpoke.interrupt();
+ //if(this.getDB.isAlive())
+ //this.getDB.interrupt();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data){
+ super.onActivityResult(requestCode, resultCode, data);
+ if(requestCode==0){
+ if(resultCode==RESULT_OK){
+ Bundle extras = data.getExtras();
+ server = extras.getString("ip");
+ port = extras.getInt("port");
+ }
+ try {
+ istatus = getInfo("status",null);
+ } catch (Exception e) {
+ }
+
+ if(!istatus.contains("idle")){
+ try{
+ getAllInfo();
+ }catch(Exception e){
+ Toast.makeText(main.this,"Can't connect to Server. Check your settings.",Toast.LENGTH_SHORT).show();
+ }
+ bcover=no_cover;
+ if(isCover)
+ bcover = getCover(server,port);
+ update.sendEmptyMessage(FULL_UPDATE);
+ }
+ }
+ else if(requestCode==1){
+ if(resultCode==RESULT_OK+1){
+ Toast.makeText(main.this,"Something went wrong, please try resynching.",Toast.LENGTH_LONG).show();
+ }
+ else if(resultCode==RESULT_OK+2){
+ Toast.makeText(main.this,"Out of device memory. Please copy the banshee.db file manually.",Toast.LENGTH_LONG).show();
+ }
+ }
+ else if(requestCode==2){
+ if(resultCode==RESULT_OK){
+ Bundle extras = data.getExtras();
+ String Uri = extras.getString("Uri");
+ String safeUri = Uri.replace('/','*');
+ try {
+ sendCommand("play",safeUri);
+ } catch (Exception e) {
+ Toast.makeText(main.this,"Something went wrong enqueuing.",Toast.LENGTH_LONG).show();
+
+ }
+ }
+ }
+
+ continueserver = true;
+ //connected = true;
+ if(!this.serverpoke.isAlive())
+ this.serverpoke.start();
+
+ }
+ public void displaySettings(){
+ Intent i = new Intent(main.this,Settings.class);
+ //Intent i = new Intent(main.this,Browse.class);
+ startActivityForResult(i,0);
+ }
+
+
+
+ @Override
+ public Object onRetainNonConfigurationInstance(){
+ BansheeInstance data = new BansheeInstance(istatus, bcover, strack, sartist, salbum, iseekposition, iseektotal, isCover, server, port);
+ return data;
+ }
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ int action = event.getAction();
+ int keyCode = event.getKeyCode();
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_VOLUME_UP:
+ if (action == KeyEvent.ACTION_UP) {
+ try{
+ sendCommand("volumeUp",null);
+ }
+ catch(Exception e){
+ Toast.makeText(main.this,"Can't connect to Server. Check your settings.",Toast.LENGTH_SHORT).show();
+
+ }
+ }
+ return true;
+ case KeyEvent.KEYCODE_VOLUME_DOWN:
+ if (action == KeyEvent.ACTION_UP) {
+ try{
+ sendCommand("volumeDown",null);
+ }
+ catch(Exception e){
+ Toast.makeText(main.this,"Can't connect to Server. Check your settings.",Toast.LENGTH_SHORT).show();
+
+ }
+ }
+ return true;
+ default:
+ return super.dispatchKeyEvent(event);
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setup(R.layout.main);
+ Bundle extras = getIntent().getExtras();
+ BansheeInstance data = (BansheeInstance)getLastNonConfigurationInstance();
+ if(data==null){
+ server = extras.getString("ip");
+ port = extras.getInt("port");
+
+ try {
+ istatus = getInfo("status",null);
+ } catch (Exception e) {
+ Toast.makeText(main.this,"Can't connect to Server. Check your settings.",Toast.LENGTH_SHORT).show();
+ }
+ if(istatus!=null){
+ if(!istatus.contains("idle")){
+ try{
+ getAllInfo();
+ }catch(Exception e){
+ Toast.makeText(main.this,"Can't connect to Server. Check your settings.",Toast.LENGTH_SHORT).show();
+ }
+ bcover=no_cover;
+ if(isCover)
+ bcover = getCover(server,port);
+ }
+ }
+ }
+ else{
+ server = data.ip;
+ port = data.port;
+ istatus=data.status;
+ bcover = data.cover;
+ strack=data.track;
+ sartist=data.artist;
+ salbum=data.album;
+ iseekposition=data.iseekposition;
+ iseektotal=data.iseektotal;
+ }
+ update.sendEmptyMessage(FULL_UPDATE);
+ continueserver = true;
+ if(!this.serverpoke.isAlive())
+ this.serverpoke.start();
+ }
+ private void setup(int content){
+ setContentView(content);
+ this.prev = (ImageButton)this.findViewById(R.id.prev);
+ this.playpause = (ImageButton)this.findViewById(R.id.playpause);
+ this.next = (ImageButton)this.findViewById(R.id.next);
+ //this.mute = (ImageButton)this.findViewById(R.id.mute);
+ this.track = (TextView)this.findViewById(R.id.track);
+ this.artist = (TextView)this.findViewById(R.id.artist);
+ this.album = (TextView)this.findViewById(R.id.album);
+ this.seekposition = (TextView)this.findViewById(R.id.seek_position);
+ this.seektotal = (TextView)this.findViewById(R.id.seek_total);
+ this.artists = (ImageButton)this.findViewById(R.id.artists);
+ this.albums = (ImageButton)this.findViewById(R.id.albums);
+ this.songs = (ImageButton)this.findViewById(R.id.songs);
+ this.seekbar = (SeekBar)this.findViewById(R.id.seekbar);
+ this.cover = (ImageView)this.findViewById(R.id.cover);
+ this.tm = (TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE);
+ this.no_cover=BitmapFactory.decodeResource(getResources(),R.drawable.no_cover_art);
+ //this.tm.listen(this.phoneListener,PhoneStateListener.LISTEN_CALL_STATE);
+ this.phoneListener = new PhoneStateListener(){
+ public void onCallStateChanged(int state, String incomingNumber){
+ if(state==TelephonyManager.CALL_STATE_RINGING){
+ if(istatus.equals("playing")){
+ try{
+ sendCommand("playPause",null);
+ }catch(Exception e){
+ Toast.makeText(main.this,"Could not pause for incoming call",Toast.LENGTH_SHORT).show();
+
+ }
+ }
+ }
+ }
+ };
+ this.tm.listen(this.phoneListener,PhoneStateListener.LISTEN_CALL_STATE);
+ this.prev.setOnTouchListener(new OnTouchListener() {
+ public boolean onTouch(View v,MotionEvent me) {
+ try{
+ if (me.getAction() == MotionEvent.ACTION_DOWN) {
+ prev.setColorFilter(Color.CYAN, PorterDuff.Mode.SRC_ATOP);
+ return false;
+ } else if (me.getAction() == MotionEvent.ACTION_UP) {
+ prev.setColorFilter(Color.parseColor("#00000000"), PorterDuff.Mode.SRC_ATOP);
+ sendCommand("prev",null);
+ //update.sendEmptyMessage(FULL_UPDATE);
+ return true;
+ }
+ }
+ catch(Exception e){
+ Toast.makeText(main.this,"Can't connect to Server. Check your settings.",Toast.LENGTH_SHORT).show();
+ }
+ return false;
+ }
+ });
+
+
+
+
+
+ this.playpause.setOnTouchListener(new OnTouchListener() {
+ public boolean onTouch(View arg0, MotionEvent me) {
+ if (me.getAction() == MotionEvent.ACTION_DOWN) {
+ playpause.setColorFilter(Color.CYAN, PorterDuff.Mode.SRC_ATOP);
+ return false;
+ } else if (me.getAction() == MotionEvent.ACTION_UP) {
+ playpause.setColorFilter(Color.parseColor("#00000000"), PorterDuff.Mode.SRC_ATOP);
+ try{
+ sendCommand("playPause",null);
+ update.sendEmptyMessage(FULL_UPDATE);
+ }
+ catch(Exception e){
+ Toast.makeText(main.this,"Can't connect to Server. Check your settings.",Toast.LENGTH_SHORT).show();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ });
+
+ this.next.setOnTouchListener(new OnTouchListener() {
+ public boolean onTouch(View v,MotionEvent me) {
+ try{
+ if (me.getAction() == MotionEvent.ACTION_DOWN) {
+ next.setColorFilter(Color.CYAN, PorterDuff.Mode.SRC_ATOP);
+ return false;
+ } else if (me.getAction() == MotionEvent.ACTION_UP) {
+ next.setColorFilter(Color.parseColor("#00000000"), PorterDuff.Mode.SRC_ATOP);
+ sendCommand("next",null);
+ update.sendEmptyMessage(FULL_UPDATE);
+ return true;
+ }
+ }
+ catch(Exception e){
+ Toast.makeText(main.this,"Can't connect to Server. Check your settings.",Toast.LENGTH_SHORT).show();
+ }
+ return false;
+ //sendCommand("prev",null);
+ /*
+ getAllInfo();
+ bcover=null;
+ if(isCover){
+ bcover = getCover(server,port);
+ }
+ update.sendEmptyMessage(FULL_UPDATE);
+ serverpoke.interrupt();
+ */
+ }
+ });
+
+
+ this.artists.setOnTouchListener(new OnTouchListener() {
+ public boolean onTouch(View v,MotionEvent me) {
+ try{
+ if (me.getAction() == MotionEvent.ACTION_DOWN) {
+ artists.setColorFilter(Color.CYAN, PorterDuff.Mode.SRC_ATOP);
+ return false;
+ } else if (me.getAction() == MotionEvent.ACTION_UP) {
+ artists.setColorFilter(Color.parseColor("#00000000"), PorterDuff.Mode.SRC_ATOP);
+ Intent i = new Intent(main.this,ArtistBrowse.class);
+ startActivityForResult(i,2);
+ return true;
+ }
+ }
+ catch(Exception e){
+ Toast.makeText(main.this,"Can't connect to Server. Check your settings.",Toast.LENGTH_SHORT).show();
+ }
+ return false;
+ }
+ });
+
+ this.albums.setOnTouchListener(new OnTouchListener() {
+ public boolean onTouch(View v,MotionEvent me) {
+ try{
+ if (me.getAction() == MotionEvent.ACTION_DOWN) {
+ albums.setColorFilter(Color.CYAN, PorterDuff.Mode.SRC_ATOP);
+ return false;
+ } else if (me.getAction() == MotionEvent.ACTION_UP) {
+ albums.setColorFilter(Color.parseColor("#00000000"), PorterDuff.Mode.SRC_ATOP);
+ Intent i = new Intent(main.this,AlbumBrowse.class);
+ i.putExtra("Artist", "Albums");
+ startActivityForResult(i,2);
+ return true;
+ }
+ }
+ catch(Exception e){
+ Toast.makeText(main.this,"Can't connect to Server. Check your settings.",Toast.LENGTH_SHORT).show();
+ }
+ return false;
+ }
+ });
+
+ this.songs.setOnTouchListener(new OnTouchListener() {
+ public boolean onTouch(View v,MotionEvent me) {
+ try{
+ if (me.getAction() == MotionEvent.ACTION_DOWN) {
+ songs.setColorFilter(Color.CYAN, PorterDuff.Mode.SRC_ATOP);
+ return false;
+ } else if (me.getAction() == MotionEvent.ACTION_UP) {
+ songs.setColorFilter(Color.parseColor("#00000000"), PorterDuff.Mode.SRC_ATOP);
+ Intent i = new Intent(main.this,SongBrowse.class);
+ i.putExtra("Album", "Songs");
+ i.putExtra("AlbumID", -1);
+ startActivityForResult(i,2);
+ return true;
+ }
+ }
+ catch(Exception e){
+ Toast.makeText(main.this,"Can't connect to Server. Check your settings.",Toast.LENGTH_SHORT).show();
+ }
+ return false;
+ }
+ });
+
+
+
+ this.seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
+ if(fromTouch){
+ try{
+ sendCommand("seek",Integer.toString(progress));
+ iseekposition=progress;
+ update.sendEmptyMessage(PARTIAL_UPDATE);
+ }catch(Exception e){
+ //Toast.makeText(BansheeRemote.this,"Can't connect to Server. Check your settings.",Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+
+
+
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+ });
+
+ }
+
+ public static ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ public static byte[] byteBuff = new byte[1024];
+
+
+} \ No newline at end of file
diff --git a/src/AndroidRemote/AndroidRemote.addin.xml b/src/AndroidRemote/AndroidRemote.addin.xml
new file mode 100644
index 0000000..3167df7
--- /dev/null
+++ b/src/AndroidRemote/AndroidRemote.addin.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Addin
+ id="Banshee.AndroidRemote"
+ version="1.0"
+ compatVersion="1.0"
+ copyright="Copyright © 2013 Nikitas Stamatopoulos. Licensed under the MIT X11 license."
+ name="Android Remote"
+ category="Community Extensions"
+ description="Control Banshee from an Android device over the internet."
+ author="Nikitas Stamatopoulos/Kristopher Dick"
+ url="http://gitorious.org/banshee-community-extension/"
+ defaultEnabled="false">
+
+ <Localizer type="Gettext" catalog="banshee-community-extensions" location="../../../share/locale"/>
+
+ <Dependencies>
+ <Addin id="Banshee.Services" version="1.0"/>
+ <Addin id="Banshee.ThickClient" version="1.0"/>
+ </Dependencies>
+
+
+
+ <Extension path="/Banshee/ServiceManager/Service">
+ <Service class="Banshee.AndroidRemote.AndroidRemoteService"/>
+ </Extension>
+
+</Addin>
diff --git a/src/AndroidRemote/AndroidRemote.csproj b/src/AndroidRemote/AndroidRemote.csproj
new file mode 100644
index 0000000..2b75e88
--- /dev/null
+++ b/src/AndroidRemote/AndroidRemote.csproj
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>8.0.50727</ProductVersion>
+ <ProjectGuid>{696EF9D2-3F3E-4FF9-9D8C-E26C74DD8563}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <UseParentDirectoryAsNamespace>true</UseParentDirectoryAsNamespace>
+ <AssemblyName>Banshee.AndroidRemote</AssemblyName>
+ <SchemaVersion>2.0</SchemaVersion>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <Optimize>true</Optimize>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
+ <RootNamespace>Foo</RootNamespace>
+ <AssemblyOriginatorKeyFile>.</AssemblyOriginatorKeyFile>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <OutputPath>..\..\..\bin</OutputPath>
+ <CustomCommands>
+ <CustomCommands>
+ <Command type="Execute" command="make run" workingdir="${SolutionDir}" />
+ </CustomCommands>
+ </CustomCommands>
+ <WarningLevel>4</WarningLevel>
+ <Optimize>false</Optimize>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Windows|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <OutputPath>..\..\..\bin</OutputPath>
+ <PlatformTarget>x86</PlatformTarget>
+ <WarningLevel>4</WarningLevel>
+ <Optimize>false</Optimize>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
+ <Package>gtk-sharp-2.0</Package>
+ </Reference>
+ <Reference Include="Mono.Posix" />
+ <Reference Include="gdk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
+ <Package>gtk-sharp-2.0</Package>
+ </Reference>
+ <Reference Include="Mono.Cairo">
+ <SpecificVersion>False</SpecificVersion>
+ </Reference>
+ <Reference Include="Mono.Addins">
+ <SpecificVersion>False</SpecificVersion>
+ <Package>mono-addins</Package>
+ </Reference>
+ <Reference Include="Banshee.Core, Version=1.7.0.0, Culture=neutral">
+ <Package>banshee-core</Package>
+ </Reference>
+ <Reference Include="Banshee.Services, Version=1.7.0.0, Culture=neutral">
+ <Package>banshee-services</Package>
+ </Reference>
+ <Reference Include="Banshee.ThickClient, Version=1.7.0.0, Culture=neutral">
+ <Package>banshee-thickclient</Package>
+ </Reference>
+ <Reference Include="Banshee.Widgets, Version=1.7.0.0, Culture=neutral">
+ <Package>banshee-thickclient</Package>
+ </Reference>
+ <Reference Include="Hyena, Version=1.7.0.0, Culture=neutral">
+ <Package>banshee-hyena</Package>
+ </Reference>
+ <Reference Include="Hyena.Data.Sqlite, Version=1.7.0.0, Culture=neutral">
+ <Package>banshee-hyena-data-sqlite</Package>
+ </Reference>
+ <Reference Include="Hyena.Gui, Version=1.7.0.0, Culture=neutral">
+ <Package>banshee-hyena-gui</Package>
+ </Reference>
+ <Reference Include="System.Core" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="AndroidRemote.addin.xml">
+ <LogicalName>AndroidRemote.addin.xml</LogicalName>
+ </EmbeddedResource>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Banshee.AndroidRemote\AndroidRemoteSource.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <ProjectExtensions>
+ <MonoDevelop>
+ <Properties>
+ <MonoDevelop.Autotools.MakefileInfo IntegrationEnabled="true" RelativeMakefileName="Makefile.am">
+ <BuildFilesVar Sync="true" Name="SOURCES" />
+ <DeployFilesVar />
+ <ResourcesVar Sync="true" Name="RESOURCES" />
+ <OthersVar />
+ <GacRefVar />
+ <AsmRefVar />
+ <ProjectRefVar />
+ </MonoDevelop.Autotools.MakefileInfo>
+ </Properties>
+ </MonoDevelop>
+ </ProjectExtensions>
+</Project>
diff --git a/src/AndroidRemote/Banshee.AndroidRemote/AndroidRemoteSource.cs b/src/AndroidRemote/Banshee.AndroidRemote/AndroidRemoteSource.cs
new file mode 100644
index 0000000..06eace6
--- /dev/null
+++ b/src/AndroidRemote/Banshee.AndroidRemote/AndroidRemoteSource.cs
@@ -0,0 +1,422 @@
+//
+// AndroidRemoteSource.cs
+//
+// Authors:
+// Nikitas Stamatopoulos / Kristopher Dick
+//
+// Copyright (C) 2013 Nikitas Stamatopoulos
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.IO;
+
+using Mono.Unix;
+using Mono.Addins;
+
+using Banshee.Collection;
+using Banshee.Sources;
+using Banshee.PlaybackController;
+using Banshee.ServiceStack;
+using Banshee.Preferences;
+using Banshee.Configuration;
+
+using Hyena;
+
+namespace Banshee.AndroidRemote
+{
+
+ public class AndroidRemoteService : IExtensionService, IDisposable
+ {
+ Socket bansheeServerConn;
+ ushort volume = ServiceManager.PlayerEngine.Volume;
+ byte[] socketBuffer = new byte[5000];
+
+ private PreferenceBase port_pref;
+ PreferenceService bansheePrefs;
+ int port;
+
+ string IService.ServiceName
+ {
+ get { return "AndroidRemote"; }
+ }
+
+ void IExtensionService.Initialize()
+ {
+ InstallPreferences();
+ Console.WriteLine("AndroidRemote: Preferences installed");
+
+ bansheePrefs["RemoteControl"]["AndroidRemote"]["remote_control_port"].ValueChanged += delegate {
+ listen();
+ };
+
+ listen();
+ }
+
+ void IDisposable.Dispose()
+ {
+ UninstallPreferences();
+ bansheeServerConn.Close();
+ }
+
+ public void listen ()
+ {
+ if (bansheeServerConn != null)
+ {
+ bansheeServerConn.Disconnect(false);
+ }
+
+ port = (int) bansheePrefs["RemoteControl"]["AndroidRemote"]["remote_control_port"].BoxedValue;
+ Console.WriteLine("AndroidRemote will listen on port " + port.ToString());
+ IPEndPoint bansheeServer_ep = new IPEndPoint (IPAddress.Any, port);
+
+ bansheeServerConn = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+ bansheeServerConn.Bind(bansheeServer_ep);
+ bansheeServerConn.Listen(1);
+ bansheeServerConn.BeginAccept(new AsyncCallback(OnIncomingConnection), bansheeServerConn);
+ }
+
+ void OnIncomingConnection (IAsyncResult ar)
+ {
+ try
+ {
+ Socket client = ((Socket)ar.AsyncState).EndAccept(ar);
+ client.BeginReceive(socketBuffer, 0, socketBuffer.Length, SocketFlags.None, OnSocketReceive,client);
+ bansheeServerConn.BeginAccept(new AsyncCallback(OnIncomingConnection), bansheeServerConn);
+ }
+ catch (Exception)
+ {
+ }
+ }
+
+ void OnSocketReceive (IAsyncResult ar) {
+ Socket client = null;
+ int bytes = 0;
+ try
+ {
+ client = (Socket) ar.AsyncState;
+ bytes = client.EndReceive(ar);
+
+ client.BeginReceive(socketBuffer, 0, socketBuffer.Length, SocketFlags.None, OnSocketReceive, client);
+
+
+ string text = Encoding.UTF8.GetString(socketBuffer,0,bytes);
+ string sep = "/";
+ string[] remoteMessage = text.Split('/');
+ string action = remoteMessage[0];
+ string variable = remoteMessage[1];
+ if (action.Equals("play"))
+ {
+ variable = variable.Replace('*','/');
+ }
+
+ Banshee.Collection.TrackInfo currTrack = ServiceManager.PlayerEngine.CurrentTrack;
+ string replyText = "";
+ ushort currVol;
+ ushort volStep = 10;
+ bool replyReq = false;
+ string home = Environment.GetEnvironmentVariable("HOME");
+ string coverPath="";
+ string dbPath = home + "/.config/banshee-1/banshee.db";
+ if(currTrack != null && currTrack.ArtworkId != null){
+ coverPath = home + "/.cache/media-art/" + currTrack.ArtworkId.ToString() +".jpg";
+ }
+
+ switch (action)
+ {
+ case "coverImage":
+ byte[] coverImage = File.ReadAllBytes(coverPath);
+ client.Send(coverImage);
+ replyReq = true;
+ break;
+
+ case "syncCount":
+ int count = System.IO.File.ReadAllBytes(dbPath).Length;
+ client.Send(System.Text.Encoding.UTF8.GetBytes(count.ToString()));
+ replyReq=true;
+ break;
+
+ case "sync":
+ byte[] db = File.ReadAllBytes(dbPath);
+ client.Send(db);
+ replyReq = true;
+ break;
+
+ case "coverExists":
+ replyText = coverExists(coverPath);
+ replyReq = true;
+ break;
+
+ case "playPause":
+ ServiceManager.PlayerEngine.TogglePlaying();
+ replyReq = true;
+ break;
+
+ case "next":
+ ServiceManager.PlaybackController.Next();
+ replyReq = true;
+ break;
+
+ case "prev":
+ ServiceManager.PlaybackController.Previous();
+ replyReq = true;
+ break;
+
+ case "play":
+ var source = ServiceManager.SourceManager.MusicLibrary as DatabaseSource;
+ source.FilterQuery="";
+ if(source!=null)
+ {
+ var countSongs = source.Count;
+
+ UnknownTrackInfo track = new UnknownTrackInfo(new SafeUri(variable));
+ TrackInfo trackTemp = null;
+ for(int i=0; i<countSongs; i++)
+ {
+ trackTemp = source.TrackModel [i];
+ if(trackTemp.TrackEqual(track))
+ {
+ break;
+ }
+ }
+ if(trackTemp != null)
+ ServiceManager.PlayerEngine.OpenPlay (trackTemp);
+ }
+ replyReq = true;
+ break;
+
+ case "volumeDown":
+ currVol = ServiceManager.PlayerEngine.Volume;
+ if (currVol < 10)
+ {
+ ServiceManager.PlayerEngine.Volume = 0;
+ }
+ else
+ {
+ ServiceManager.PlayerEngine.Volume = (ushort) (currVol - volStep);
+ }
+ replyReq = true;
+ break;
+
+ case "volumeUp":
+ currVol = ServiceManager.PlayerEngine.Volume;
+ if (currVol > 90)
+ {
+ ServiceManager.PlayerEngine.Volume = 100;
+ }
+ else
+ {
+ ServiceManager.PlayerEngine.Volume = (ushort) (currVol + volStep);
+ }
+ replyReq = true;
+ break;
+
+ case "mute":
+ currVol = ServiceManager.PlayerEngine.Volume;
+ if (currVol > 0)
+ {
+ volume = currVol;
+ ServiceManager.PlayerEngine.Volume = 0;
+ }
+ else
+ {
+ ServiceManager.PlayerEngine.Volume = volume;
+ }
+ replyReq = true;
+ break;
+ case "status":
+ replyText = ServiceManager.PlayerEngine.CurrentState.ToString().ToLower();
+ replyReq = true;
+ break;
+
+ case "album":
+ replyText = currTrack.DisplayAlbumTitle;
+ replyReq = true;
+ break;
+
+ case "artist":
+ replyText = currTrack.DisplayArtistName;
+ replyReq = true;
+ break;
+
+ case "title":
+ replyText = currTrack.DisplayTrackTitle;
+ replyReq = true;
+ break;
+ case "trackCurrentTime":
+ replyText = (ServiceManager.PlayerEngine.Position/1000).ToString();
+ replyReq = true;
+ break;
+
+ case "trackTotalTime":
+ replyText = currTrack.Duration.ToString();
+ replyReq = true;
+ break;
+
+ case "seek":
+ ServiceManager.PlayerEngine.Position = UInt32.Parse(variable)*1000;
+ replyReq = true;
+ break;
+
+ case "shuffle":
+ if (ServiceManager.PlaybackController.ShuffleMode.ToString() == "off")
+ {
+ ServiceManager.PlaybackController.ShuffleMode = "song";
+ replyText = "song";
+ }
+ else if(ServiceManager.PlaybackController.ShuffleMode.ToString() == "song")
+ {
+ ServiceManager.PlaybackController.ShuffleMode = "artist";
+ replyText = "Artist";
+ }
+ else if(ServiceManager.PlaybackController.ShuffleMode.ToString() == "artist")
+ {
+ ServiceManager.PlaybackController.ShuffleMode = "album";
+ replyText = "Album";
+ }
+ else if(ServiceManager.PlaybackController.ShuffleMode.ToString() == "album")
+ {
+ ServiceManager.PlaybackController.ShuffleMode = "rating";
+ replyText = "Rating";
+ }
+ else if(ServiceManager.PlaybackController.ShuffleMode.ToString() == "rating")
+ {
+ ServiceManager.PlaybackController.ShuffleMode = "score";
+ replyText = "Score";
+ }
+ else
+ {
+ ServiceManager.PlaybackController.ShuffleMode = "off";
+ replyText = "off";
+ }
+ replyReq = true;
+ break;
+
+ case "repeat":
+ if (ServiceManager.PlaybackController.RepeatMode == PlaybackRepeatMode.None)
+ {
+ ServiceManager.PlaybackController.RepeatMode = PlaybackRepeatMode.RepeatAll;
+ replyText = "all";
+ }
+ else if (ServiceManager.PlaybackController.RepeatMode == PlaybackRepeatMode.RepeatAll)
+ {
+ ServiceManager.PlaybackController.RepeatMode = PlaybackRepeatMode.RepeatSingle;
+ replyText = "single";
+ }
+ else
+ {
+ ServiceManager.PlaybackController.RepeatMode = PlaybackRepeatMode.None;
+ replyText = "off";
+ }
+ replyReq = true;
+ break;
+
+ case "all":
+ replyText = ServiceManager.PlayerEngine.CurrentState.ToString().ToLower() + sep;
+ replyText += currTrack.DisplayAlbumTitle.Replace('/','\\') + sep;
+ replyText += currTrack.DisplayArtistName.Replace('/','\\') + sep;
+ replyText += currTrack.DisplayTrackTitle.Replace('/','\\') + sep;
+ replyText += ((uint) (ServiceManager.PlayerEngine.Position/1000)).ToString() + sep;
+ replyText += ((uint) (currTrack.Duration.TotalSeconds)).ToString() + sep;
+ replyText += coverExists(coverPath);
+ replyReq = true;
+ break;
+
+ case "test":
+ replyText = "";
+ replyReq = true;
+ break;
+
+ default:
+ replyText = "";
+ replyReq = false;
+ break;
+ }
+
+ byte[] messageByte = System.Text.Encoding.UTF8.GetBytes(replyText);
+
+ if (replyReq)
+ {
+ reply(client, messageByte);
+ }
+ client.Close();
+ }
+ catch(Exception)
+ {
+ }
+ }
+
+ void reply (Socket remoteClient, byte[] reply)
+ {
+ remoteClient.Send(reply);
+ }
+
+ string coverExists(string coverPath)
+ {
+ string retVal = "false";
+ if (System.IO.File.Exists(coverPath))
+ {
+ if (System.IO.File.ReadAllBytes(coverPath).Length < 110000)
+ {
+ retVal = "true";
+ }
+ }
+ return retVal;
+ }
+
+ private void InstallPreferences()
+ {
+ bansheePrefs = ServiceManager.Get<PreferenceService>();
+
+ if (bansheePrefs == null)
+ {
+ return;
+ }
+ Page remoteControlPage = new Page("RemoteControl", "Remote Control", 3);
+ bansheePrefs.FindOrAdd(remoteControlPage);
+
+ Section BansheeRemotePrefs = remoteControlPage.FindOrAdd(new Section("AndroidRemote", "Android Remote", 0));
+
+ port_pref = BansheeRemotePrefs.Add (new SchemaPreference<int>(
+ RemotePortSchema,
+ Catalog.GetString("Android Remote port"),
+ Catalog.GetString("Banshee will listen for the Android Banshee Remote app on this port")));
+ }
+
+ private void UninstallPreferences()
+ {
+ Console.WriteLine("AndroidRemote: UninstallPreferences() called");
+ bansheePrefs["RemoteControl"]["AndroidRemote"].Remove(port_pref);
+ bansheePrefs["RemoteControl"].Remove(bansheePrefs["RemoteControl"].FindById("AndroidRemote"));
+ bansheePrefs.Remove(bansheePrefs.FindById("RemoteControl"));
+ }
+
+ public static readonly SchemaEntry<int> RemotePortSchema = new SchemaEntry<int> (
+ "remote_control", "remote_control_port",
+ 8484,1024,49151,
+ "Android Remote Port",
+ "Android Remote will listen for the Android Banshee Remote app on this port"
+ );
+ }
+}
diff --git a/src/AndroidRemote/Makefile.am b/src/AndroidRemote/Makefile.am
new file mode 100644
index 0000000..2faedaf
--- /dev/null
+++ b/src/AndroidRemote/Makefile.am
@@ -0,0 +1,8 @@
+ASSEMBLY = Banshee.AndroidRemote
+LINK = $(BANSHEE_LIBS)
+
+SOURCES = Banshee.AndroidRemote/AndroidRemoteSource.cs
+
+RESOURCES = AndroidRemote.addin.xml
+
+include $(top_srcdir)/build/build.mk
diff --git a/src/Makefile.am b/src/Makefile.am
index e80fff0..d18e853 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -2,6 +2,7 @@ SUBDIRS = \
AlarmClock \
AlbumArtWriter \
Ampache \
+ AndroidRemote \
AppIndicator \
Awn \
ClutterFlow \