1
0
mirror of https://github.com/ultrasonic/ultrasonic synced 2025-02-06 20:13:21 +01:00

Inital chat support

This commit is contained in:
Joshua Bahnsen 2013-05-21 22:47:40 -07:00
parent 1c98b381e9
commit 9f2bab7e63
30 changed files with 1511 additions and 624 deletions

View File

@ -8,5 +8,5 @@
# project structure.
# Project target.
target=android-16
target=android-17
android.library=true

View File

@ -55,6 +55,9 @@
a:configChanges="orientation|keyboardHidden"
a:label="@string/playlist.label"
a:launchMode="standard" />
<activity
a:name=".activity.ChatActivity"
a:configChanges="orientation|keyboardHidden" />
<activity
a:name=".activity.DownloadActivity"
a:configChanges="keyboardHidden"

View File

@ -12,5 +12,5 @@
android.library=true
# Project target.
target=android-16
target=android-17

View File

@ -44,14 +44,14 @@ public final class R {
public static final class drawable {
public static final int default_ptr_flip = 0x7f02000c;
public static final int default_ptr_rotate = 0x7f02000d;
public static final int indicator_arrow = 0x7f020044;
public static final int indicator_bg_bottom = 0x7f020045;
public static final int indicator_bg_top = 0x7f020046;
public static final int indicator_arrow = 0x7f020048;
public static final int indicator_bg_bottom = 0x7f020049;
public static final int indicator_bg_top = 0x7f02004a;
}
public static final class id {
public static final int both = 0x7f060003;
public static final int disabled = 0x7f060000;
public static final int fl_inner = 0x7f06007e;
public static final int fl_inner = 0x7f060086;
public static final int flip = 0x7f060008;
public static final int gridview = 0x7f060009;
public static final int manualOnly = 0x7f060004;
@ -59,17 +59,17 @@ public final class R {
public static final int pullFromEnd = 0x7f060002;
public static final int pullFromStart = 0x7f060001;
public static final int pullUpFromBottom = 0x7f060006;
public static final int pull_to_refresh_image = 0x7f06007f;
public static final int pull_to_refresh_progress = 0x7f060080;
public static final int pull_to_refresh_sub_text = 0x7f060082;
public static final int pull_to_refresh_text = 0x7f060081;
public static final int pull_to_refresh_image = 0x7f060087;
public static final int pull_to_refresh_progress = 0x7f060088;
public static final int pull_to_refresh_sub_text = 0x7f06008a;
public static final int pull_to_refresh_text = 0x7f060089;
public static final int rotate = 0x7f060007;
public static final int scrollview = 0x7f06000b;
public static final int webview = 0x7f06000a;
}
public static final class layout {
public static final int pull_to_refresh_header_horizontal = 0x7f030019;
public static final int pull_to_refresh_header_vertical = 0x7f03001a;
public static final int pull_to_refresh_header_horizontal = 0x7f03001c;
public static final int pull_to_refresh_header_vertical = 0x7f03001d;
}
public static final class string {
public static final int pull_to_refresh_from_bottom_pull_label = 0x7f080003;

File diff suppressed because it is too large Load Diff

View File

@ -9,5 +9,6 @@
# Project target.
target=android-17
android.library.reference.1=android-menudrawer-master\\library
android.library=false
android.library.reference.1=android-menudrawer-master/library
android.library.reference.2=Android-PullToRefresh/library

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

50
res/layout/chat.xml Normal file
View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
a:layout_width="fill_parent"
a:layout_height="fill_parent"
a:orientation="vertical" >
<include layout="@layout/tab_progress" />
<com.handmark.pulltorefresh.library.PullToRefreshListView
a:id="@+id/chat_entries"
a:layout_width="fill_parent"
a:layout_height="0dip"
a:layout_weight="1.0"
a:textFilterEnabled="true" />
<LinearLayout
a:layout_height="4dip"
a:layout_width="fill_parent"
a:layout_marginTop="4dip"
a:background="@drawable/drop_shadow" />
<LinearLayout
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:orientation="horizontal"
a:gravity="bottom" >
<EditText
a:id="@+id/chat_edittext"
a:layout_width="0dip"
a:layout_height="40dip"
a:layout_weight="1"
a:autoLink="all"
a:hint="@string/chat.send_a_message"
a:inputType="textEmailAddress|textMultiLine"
a:linksClickable="true"
a:paddingBottom="10dip"
a:paddingTop="10dip" />
<ImageButton
a:id="@+id/chat_send"
a:layout_width="60dip"
a:layout_height="40dip"
a:src="?attr/chat_send" />
</LinearLayout>
<include layout="@layout/now_playing" />
</LinearLayout>

48
res/layout/chat_item.xml Normal file
View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:orientation="vertical" >
<TextView
a:id="@+id/chat_username"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_marginLeft="6dip"
a:layout_marginRight="6dip"
a:ellipsize="marquee"
a:singleLine="true"
a:text="User"
a:textAppearance="?android:attr/textAppearanceMedium"
a:textStyle="bold" />
<LinearLayout
a:id="@+id/chat_message_layout"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_marginTop="2dip"
a:orientation="horizontal" >
<TextView
a:id="@+id/chat_time"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_marginLeft="6dip"
a:singleLine="true"
a:text="00:00"
a:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
a:id="@+id/chat_message"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_marginLeft="6dip"
a:layout_marginRight="6dip"
a:autoLink="all"
a:linksClickable="true"
a:singleLine="false"
a:text="Message Text Goes Here"
a:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:a="http://schemas.android.com/apk/res/android"
a:layout_width="fill_parent"
a:layout_height="wrap_content"
a:orientation="vertical" >
<TextView
a:id="@+id/chat_username"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_marginRight="6dip"
a:gravity="right"
a:layout_gravity="right"
a:ellipsize="marquee"
a:singleLine="true"
a:text="User"
a:textAppearance="?android:attr/textAppearanceMedium"
a:textStyle="bold" />
<LinearLayout
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_marginTop="2dip"
a:orientation="horizontal"
a:layout_gravity="right" >
<TextView
a:id="@+id/chat_time"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_marginLeft="6dip"
a:singleLine="true"
a:gravity="right"
a:text="00:00"
a:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
a:id="@+id/chat_message"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_marginLeft="6dip"
a:layout_marginRight="6dip"
a:autoLink="all"
a:linksClickable="true"
a:singleLine="false"
a:gravity="right"
a:text="Chat message"
a:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
</LinearLayout>

View File

@ -47,6 +47,14 @@
android:layout_height="wrap_content"
android:drawableLeft="?attr/playlists"
android:text="@string/button_bar.playlists" />
<TextView
android:id="@+id/menu_chat"
style="@style/MenuDrawer.Widget.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableLeft="?attr/chat"
android:text="@string/button_bar.chat" />
<TextView
android:id="@+id/menu_now_playing"

View File

@ -22,6 +22,7 @@
<string name="button_bar.home">UltraSonic Main</string>
<string name="button_bar.browse">Media Library</string>
<string name="button_bar.search">Search</string>
<string name="button_bar.chat">Chat</string>
<string name="button_bar.playlists">Playlists</string>
<string name="button_bar.now_playing">Now Playing</string>
<string name="main.welcome_title">Welcome!</string>
@ -312,6 +313,7 @@
<string name="util.bytes_format.byte">0 B</string>
<string name="util.no_time">-:--</string>
<string name="util.zero_time">0:00</string>
<string name="chat.send_a_message">Send a message</string>
<plurals name="select_album_n_songs">
<item quantity="zero">No songs</item>

View File

@ -82,5 +82,7 @@
<attr name="media_play_small" format="reference"/>
<attr name="media_stop" format="reference"/>
<attr name="media_toggle" format="reference"/>
<attr name="chat" format="reference"/>
<attr name="chat_send" format="reference" />
</resources>

View File

@ -38,6 +38,8 @@
<item name="media_play_small">@drawable/ic_stat_play_dark</item>
<item name="media_stop">@drawable/media_stop_normal_dark</item>
<item name="media_toggle">@drawable/media_toggle_list_normal_dark</item>
<item name="chat">@drawable/ic_menu_chat_dark</item>
<item name="chat_send">@drawable/ic_menu_chat_send_dark</item>
</style>
<style name="UltraSonicTheme.Light" parent="@android:style/Theme.Holo.Light">
@ -77,6 +79,8 @@
<item name="media_play_small">@drawable/ic_stat_play_light</item>
<item name="media_stop">@drawable/media_stop_normal_light</item>
<item name="media_toggle">@drawable/media_toggle_list_normal_light</item>
<item name="chat">@drawable/ic_menu_chat_light</item>
<item name="chat_send">@drawable/ic_menu_chat_send_light</item>
</style>
</resources>

View File

@ -0,0 +1,205 @@
package com.thejoshwa.ultrasonic.androidapp.activity;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.TextView;
import com.handmark.pulltorefresh.library.PullToRefreshBase;
import com.handmark.pulltorefresh.library.PullToRefreshBase.Mode;
import com.handmark.pulltorefresh.library.PullToRefreshListView;
import com.handmark.pulltorefresh.library.PullToRefreshBase.OnRefreshListener;
import com.thejoshwa.ultrasonic.androidapp.R;
import com.thejoshwa.ultrasonic.androidapp.domain.ChatMessage;
import com.thejoshwa.ultrasonic.androidapp.service.MusicService;
import com.thejoshwa.ultrasonic.androidapp.service.MusicServiceFactory;
import com.thejoshwa.ultrasonic.androidapp.util.BackgroundTask;
import com.thejoshwa.ultrasonic.androidapp.util.Constants;
import com.thejoshwa.ultrasonic.androidapp.util.TabActivityBackgroundTask;
import com.thejoshwa.ultrasonic.androidapp.util.Util;
import com.thejoshwa.ultrasonic.androidapp.view.ChatAdapter;
/**
* @author Joshua Bahnsen
*/
public final class ChatActivity extends SubsonicTabActivity {
private PullToRefreshListView refreshChatListView;
private ListView chatListView;
private EditText messageEditText;
private ImageButton sendButton;
//private ChatAdapter chatAdapter;
private static Long lastChatMessageTime = (long) 0;
private static ArrayList<ChatMessage> messageList = new ArrayList<ChatMessage>();
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.chat);
refreshChatListView = (PullToRefreshListView) findViewById(R.id.chat_entries);
refreshChatListView.setMode(Mode.PULL_FROM_END);
messageEditText = (EditText) findViewById(R.id.chat_edittext);
sendButton = (ImageButton) findViewById(R.id.chat_send);
sendButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
sendMessage();
}
});
chatListView = refreshChatListView.getRefreshableView();
chatListView.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
chatListView.setStackFromBottom(true);
String serverName = Util.getServerName(this, Util.getActiveServer(this));
String userName = Util.getUserName(this, Util.getActiveServer(this));
String title = String.format("%s [%s@%s]", getResources().getString(R.string.button_bar_chat), userName, serverName);
getActionBar().setSubtitle(title);
messageEditText.setImeActionLabel("Send", KeyEvent.KEYCODE_ENTER);
messageEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
sendButton.setEnabled(!Util.isNullOrWhiteSpace(editable.toString()));
}
});
messageEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE || (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_DOWN)) {
sendMessage();
return true;
}
return false;
}
});
refreshChatListView.setOnRefreshListener(new OnRefreshListener<ListView>() {
@Override
public void onRefresh(PullToRefreshBase<ListView> refreshView) {
new GetDataTask().execute();
}
});
View chatMenuItem = findViewById(R.id.menu_chat);
menuDrawer.setActiveView(chatMenuItem);
load();
}
private void sendMessage() {
final String message = messageEditText.getText().toString();
if (!Util.isNullOrWhiteSpace(message)) {
messageEditText.setText("");
BackgroundTask<Void> task = new TabActivityBackgroundTask<Void>(ChatActivity.this) {
@Override
protected Void doInBackground() throws Throwable {
MusicService musicService = MusicServiceFactory.getMusicService(ChatActivity.this);
musicService.addChatMessage(message, ChatActivity.this, this);
return null;
}
@Override
protected void done(Void result) {
load();
}
};
task.execute();
}
}
@Override
protected void onResume() {
super.onResume();
if (!messageList.isEmpty()) {
ChatAdapter chatAdapter = new ChatAdapter(ChatActivity.this, messageList);
chatListView.setAdapter(chatAdapter);
}
}
private synchronized void load() {
BackgroundTask<List<ChatMessage>> task = new TabActivityBackgroundTask<List<ChatMessage>>(this) {
@Override
protected List<ChatMessage> doInBackground() throws Throwable {
MusicService musicService = MusicServiceFactory.getMusicService(ChatActivity.this);
return musicService.getChatMessages(lastChatMessageTime, ChatActivity.this, this);
}
@Override
protected void done(List<ChatMessage> result) {
if (result != null && !result.isEmpty()) {
// Reset lastChatMessageTime if we have a newer message
for (ChatMessage message : result) {
if (message.getTime() > lastChatMessageTime) {
lastChatMessageTime = message.getTime();
}
}
// Reverse results to show them on the bottom
Collections.reverse(result);
messageList.addAll(result);
ChatAdapter chatAdapter = new ChatAdapter(ChatActivity.this, messageList);
chatListView.setAdapter(chatAdapter);
}
}
};
task.execute();
}
private void refresh() {
lastChatMessageTime = (long) 0;
messageList = new ArrayList<ChatMessage>();
finish();
Intent intent = getIntent();
intent.putExtra(Constants.INTENT_EXTRA_NAME_REFRESH, true);
Util.startActivityWithoutTransition(this, intent);
}
private class GetDataTask extends AsyncTask<Void, Void, String[]> {
@Override
protected void onPostExecute(String[] result) {
refreshChatListView.onRefreshComplete();
super.onPostExecute(result);
}
@Override
protected String[] doInBackground(Void... params) {
refresh();
return null;
}
}
}

View File

@ -96,12 +96,6 @@ public final class HelpActivity extends Activity implements OnClickListener {
View aboutMenuItem = findViewById(R.id.menu_about);
menuDrawer.setActiveView(aboutMenuItem);
TextView activeView = (TextView)findViewById(menuActiveViewId);
if (activeView != null) {
menuDrawer.setActiveView(activeView);
}
webView = (WebView) findViewById(R.id.help_contents);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new HelpClient());
@ -231,6 +225,9 @@ public final class HelpActivity extends Activity implements OnClickListener {
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
Util.startActivityWithoutTransition(this, intent);
break;
case R.id.menu_chat:
Util.startActivityWithoutTransition(this, ChatActivity.class);
break;
case R.id.menu_now_playing:
Util.startActivityWithoutTransition(this, DownloadActivity.class);
break;

View File

@ -623,6 +623,9 @@ public class SettingsActivity extends PreferenceActivity implements SharedPrefer
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
Util.startActivityWithoutTransition(this, intent);
break;
case R.id.menu_chat:
Util.startActivityWithoutTransition(this, ChatActivity.class);
break;
case R.id.menu_now_playing:
Util.startActivityWithoutTransition(this, DownloadActivity.class);
break;

View File

@ -49,6 +49,8 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.RemoteViews;
import android.widget.TextView;
import com.thejoshwa.ultrasonic.androidapp.R;
@ -69,7 +71,6 @@ import com.thejoshwa.ultrasonic.androidapp.util.LoadingTask;
import com.thejoshwa.ultrasonic.androidapp.util.ModalBackgroundTask;
import com.thejoshwa.ultrasonic.androidapp.util.SilentBackgroundTask;
import com.thejoshwa.ultrasonic.androidapp.util.Util;
import net.simonvt.menudrawer.MenuDrawer;
import net.simonvt.menudrawer.Position;
@ -124,6 +125,7 @@ public class SubsonicTabActivity extends Activity implements OnClickListener{
findViewById(R.id.menu_browse).setOnClickListener(this);
searchMenuItem.setOnClickListener(this);
playlistsMenuItem.setOnClickListener(this);
findViewById(R.id.menu_chat).setOnClickListener(this);
findViewById(R.id.menu_now_playing).setOnClickListener(this);
findViewById(R.id.menu_settings).setOnClickListener(this);
findViewById(R.id.menu_about).setOnClickListener(this);
@ -492,6 +494,17 @@ public class SubsonicTabActivity extends Activity implements OnClickListener{
});
}
public void setListAdapterOnListViewOnUiThread(final ListView view, final ListAdapter adapter) {
this.runOnUiThread( new Runnable() {
@Override
public void run() {
if (view != null) {
view.setAdapter(adapter);
}
}
});
}
public void setOnTouchListenerOnUiThread(final View view, final View.OnTouchListener listener) {
this.runOnUiThread( new Runnable() {
@Override
@ -945,6 +958,9 @@ public class SubsonicTabActivity extends Activity implements OnClickListener{
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
Util.startActivityWithoutTransition(SubsonicTabActivity.this, intent);
break;
case R.id.menu_chat:
Util.startActivityWithoutTransition(SubsonicTabActivity.this, ChatActivity.class);
break;
case R.id.menu_now_playing:
Util.startActivityWithoutTransition(SubsonicTabActivity.this, DownloadActivity.class);
break;

View File

@ -0,0 +1,37 @@
package com.thejoshwa.ultrasonic.androidapp.domain;
import java.io.Serializable;
public class ChatMessage implements Serializable {
/**
*
*/
private static final long serialVersionUID = 496544310289324167L;
private String username;
private Long time;
private String message;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Long getTime() {
return time;
}
public void setTime(Long time) {
this.time = time;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -0,0 +1,124 @@
package com.thejoshwa.ultrasonic.androidapp.domain;
import java.io.Serializable;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import com.thejoshwa.ultrasonic.androidapp.domain.MusicDirectory.Entry;
public class Share implements Serializable {
private static final long serialVersionUID = 1487561657691009668L;
private String id;
private String url;
private String description;
private String username;
private Date created;
private Date lastVisited;
private Date expires;
private Long visitCount;
private List<Entry> entries;
public Share() {
entries = new ArrayList<Entry>();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getCreated() {
return created;
}
public void setCreated(String created) {
if (created != null) {
try {
this.created = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH).parse(created);
} catch (ParseException e) {
this.created = null;
}
} else {
this.created = null;
}
}
public Date getLastVisited() {
return lastVisited;
}
public void setLastVisited(String lastVisited) {
if (lastVisited != null) {
try {
this.lastVisited = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH).parse(lastVisited);
} catch (ParseException e) {
this.lastVisited = null;
}
} else {
this.lastVisited = null;
}
}
public Date getExpires() {
return expires;
}
public void setExpires(String expires) {
if (expires != null) {
try {
this.expires = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH).parse(expires);
} catch (ParseException e) {
this.expires = null;
}
} else {
this.expires = null;
}
}
public Long getVisitCount() {
return visitCount;
}
public void setVisitCount(Long visitCount) {
this.visitCount = visitCount;
}
public List<Entry> getEntries() {
return this.entries;
}
public void addEntry(Entry entry) {
entries.add(entry);
}
}

View File

@ -26,15 +26,18 @@ import org.apache.http.HttpResponse;
import android.content.Context;
import android.graphics.Bitmap;
import com.thejoshwa.ultrasonic.androidapp.domain.ChatMessage;
import com.thejoshwa.ultrasonic.androidapp.domain.Genre;
import com.thejoshwa.ultrasonic.androidapp.domain.Indexes;
import com.thejoshwa.ultrasonic.androidapp.domain.JukeboxStatus;
import com.thejoshwa.ultrasonic.androidapp.domain.Lyrics;
import com.thejoshwa.ultrasonic.androidapp.domain.MusicDirectory;
import com.thejoshwa.ultrasonic.androidapp.domain.MusicDirectory.Entry;
import com.thejoshwa.ultrasonic.androidapp.domain.MusicFolder;
import com.thejoshwa.ultrasonic.androidapp.domain.Playlist;
import com.thejoshwa.ultrasonic.androidapp.domain.SearchCritera;
import com.thejoshwa.ultrasonic.androidapp.domain.SearchResult;
import com.thejoshwa.ultrasonic.androidapp.domain.Share;
import com.thejoshwa.ultrasonic.androidapp.domain.Version;
import com.thejoshwa.ultrasonic.androidapp.util.CancellableTask;
import com.thejoshwa.ultrasonic.androidapp.util.LRUCache;
@ -341,10 +344,12 @@ public class CachedMusicService implements MusicService {
public List<Genre> getGenres(Context context, ProgressListener progressListener) throws Exception {
checkSettingsChanged(context);
List<Genre> result = cachedGenres.get();
if (result == null) {
result = musicService.getGenres(context, progressListener);
cachedGenres.set(result);
}
return result;
}
@ -352,4 +357,19 @@ public class CachedMusicService implements MusicService {
public MusicDirectory getSongsByGenre(String genre, int count, int offset, Context context, ProgressListener progressListener) throws Exception {
return musicService.getSongsByGenre(genre, count, offset, context, progressListener);
}
@Override
public List<Share> getShares(Context context, ProgressListener progressListener) throws Exception {
return musicService.getShares(context, progressListener);
}
@Override
public List<ChatMessage> getChatMessages(Long since, Context context, ProgressListener progressListener) throws Exception {
return musicService.getChatMessages(since, context, progressListener);
}
@Override
public void addChatMessage(String message, Context context, ProgressListener progressListener) throws Exception {
musicService.addChatMessage(message, context, progressListener);
}
}

View File

@ -25,6 +25,7 @@ import org.apache.http.HttpResponse;
import android.content.Context;
import android.graphics.Bitmap;
import com.thejoshwa.ultrasonic.androidapp.domain.ChatMessage;
import com.thejoshwa.ultrasonic.androidapp.domain.Genre;
import com.thejoshwa.ultrasonic.androidapp.domain.Indexes;
import com.thejoshwa.ultrasonic.androidapp.domain.JukeboxStatus;
@ -35,6 +36,7 @@ import com.thejoshwa.ultrasonic.androidapp.domain.MusicFolder;
import com.thejoshwa.ultrasonic.androidapp.domain.Playlist;
import com.thejoshwa.ultrasonic.androidapp.domain.SearchCritera;
import com.thejoshwa.ultrasonic.androidapp.domain.SearchResult;
import com.thejoshwa.ultrasonic.androidapp.domain.Share;
import com.thejoshwa.ultrasonic.androidapp.domain.Version;
import com.thejoshwa.ultrasonic.androidapp.util.CancellableTask;
import com.thejoshwa.ultrasonic.androidapp.util.ProgressListener;
@ -121,4 +123,10 @@ public interface MusicService {
JukeboxStatus getJukeboxStatus(Context context, ProgressListener progressListener) throws Exception;
JukeboxStatus setJukeboxGain(float gain, Context context, ProgressListener progressListener) throws Exception;
List<Share> getShares(Context context, ProgressListener progressListener) throws Exception;
List<ChatMessage> getChatMessages(Long since, Context context, ProgressListener progressListener) throws Exception;
void addChatMessage(String message, Context context, ProgressListener progressListener) throws Exception;
}

View File

@ -70,18 +70,22 @@ import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;
import com.thejoshwa.ultrasonic.androidapp.R;
import com.thejoshwa.ultrasonic.androidapp.domain.ChatMessage;
import com.thejoshwa.ultrasonic.androidapp.domain.Genre;
import com.thejoshwa.ultrasonic.androidapp.domain.Indexes;
import com.thejoshwa.ultrasonic.androidapp.domain.JukeboxStatus;
import com.thejoshwa.ultrasonic.androidapp.domain.Lyrics;
import com.thejoshwa.ultrasonic.androidapp.domain.MusicDirectory;
import com.thejoshwa.ultrasonic.androidapp.domain.MusicDirectory.Entry;
import com.thejoshwa.ultrasonic.androidapp.domain.MusicFolder;
import com.thejoshwa.ultrasonic.androidapp.domain.Playlist;
import com.thejoshwa.ultrasonic.androidapp.domain.SearchCritera;
import com.thejoshwa.ultrasonic.androidapp.domain.SearchResult;
import com.thejoshwa.ultrasonic.androidapp.domain.ServerInfo;
import com.thejoshwa.ultrasonic.androidapp.domain.Share;
import com.thejoshwa.ultrasonic.androidapp.domain.Version;
import com.thejoshwa.ultrasonic.androidapp.service.parser.AlbumListParser;
import com.thejoshwa.ultrasonic.androidapp.service.parser.ChatMessageParser;
import com.thejoshwa.ultrasonic.androidapp.service.parser.ErrorParser;
import com.thejoshwa.ultrasonic.androidapp.service.parser.GenreParser;
import com.thejoshwa.ultrasonic.androidapp.service.parser.IndexesParser;
@ -95,6 +99,7 @@ import com.thejoshwa.ultrasonic.androidapp.service.parser.PlaylistsParser;
import com.thejoshwa.ultrasonic.androidapp.service.parser.RandomSongsParser;
import com.thejoshwa.ultrasonic.androidapp.service.parser.SearchResult2Parser;
import com.thejoshwa.ultrasonic.androidapp.service.parser.SearchResultParser;
import com.thejoshwa.ultrasonic.androidapp.service.parser.ShareParser;
import com.thejoshwa.ultrasonic.androidapp.service.parser.VersionParser;
import com.thejoshwa.ultrasonic.androidapp.service.ssl.SSLSocketFactory;
import com.thejoshwa.ultrasonic.androidapp.service.ssl.TrustSelfSignedStrategy;
@ -1143,4 +1148,61 @@ public class RESTMusicService implements MusicService {
Util.close(reader);
}
}
@Override
public List<Share> getShares(Context context, ProgressListener progressListener) throws Exception {
checkServerVersion(context, "1.6", "Shares not supported.");
Reader reader = getReader(context, progressListener, "getShares", null);
try {
return new ShareParser(context).parse(reader, progressListener);
} finally {
Util.close(reader);
}
}
@Override
public List<ChatMessage> getChatMessages(Long since, Context context, ProgressListener progressListener) throws Exception {
checkServerVersion(context, "1.2", "Chat not supported.");
HttpParams params = new BasicHttpParams();
HttpConnectionParams.setSoTimeout(params, SOCKET_READ_TIMEOUT_GET_RANDOM_SONGS);
List<String> parameterNames = new ArrayList<String>();
List<Object> parameterValues = new ArrayList<Object>();
parameterNames.add("since");
parameterValues.add(since);
Reader reader = getReader(context, progressListener, "getChatMessages", params, parameterNames, parameterValues);
try {
return new ChatMessageParser(context).parse(reader, progressListener);
} finally {
Util.close(reader);
}
}
@Override
public void addChatMessage(String message, Context context, ProgressListener progressListener) throws Exception {
checkServerVersion(context, "1.2", "Chat not supported.");
HttpParams params = new BasicHttpParams();
HttpConnectionParams.setSoTimeout(params, SOCKET_READ_TIMEOUT_GET_RANDOM_SONGS);
List<String> parameterNames = new ArrayList<String>();
List<Object> parameterValues = new ArrayList<Object>();
parameterNames.add("message");
parameterValues.add(message);
Reader reader = getReader(context, progressListener, "addChatMessage", params, parameterNames, parameterValues);
try {
new ErrorParser(context).parse(reader);
} finally {
Util.close(reader);
}
}
}

View File

@ -0,0 +1,49 @@
package com.thejoshwa.ultrasonic.androidapp.service.parser;
import android.content.Context;
import com.thejoshwa.ultrasonic.androidapp.R;
import com.thejoshwa.ultrasonic.androidapp.domain.ChatMessage;
import com.thejoshwa.ultrasonic.androidapp.util.ProgressListener;
import org.xmlpull.v1.XmlPullParser;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
/**
* @author Joshua Bahnsen
*/
public class ChatMessageParser extends AbstractParser {
public ChatMessageParser(Context context) {
super(context);
}
public List<ChatMessage> parse(Reader reader, ProgressListener progressListener) throws Exception {
updateProgress(progressListener, R.string.parser_reading);
init(reader);
List<ChatMessage> result = new ArrayList<ChatMessage>();
int eventType;
do {
eventType = nextParseEvent();
if (eventType == XmlPullParser.START_TAG) {
String name = getElementName();
if ("chatMessage".equals(name)) {
ChatMessage chatMessage = new ChatMessage();
chatMessage.setUsername(get("username"));
chatMessage.setTime(getLong("time"));
chatMessage.setMessage(get("message"));
result.add(chatMessage);
} else if ("error".equals(name)) {
handleError();
}
}
} while (eventType != XmlPullParser.END_DOCUMENT);
validate();
updateProgress(progressListener, R.string.parser_reading_done);
return result;
}
}

View File

@ -0,0 +1,59 @@
package com.thejoshwa.ultrasonic.androidapp.service.parser;
import android.content.Context;
import com.thejoshwa.ultrasonic.androidapp.R;
import com.thejoshwa.ultrasonic.androidapp.domain.Share;
import com.thejoshwa.ultrasonic.androidapp.util.ProgressListener;
import org.xmlpull.v1.XmlPullParser;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
/**
* @author Joshua Bahnsen
*/
public class ShareParser extends MusicDirectoryEntryParser {
public ShareParser(Context context) {
super(context);
}
public List<Share> parse(Reader reader, ProgressListener progressListener) throws Exception {
updateProgress(progressListener, R.string.parser_reading);
init(reader);
List<Share> dir = new ArrayList<Share>();
Share share = null;
int eventType;
do {
eventType = nextParseEvent();
if (eventType == XmlPullParser.START_TAG) {
String name = getElementName();
if ("share".equals(name)) {
share = new Share();
share.setCreated(get("created"));
share.setDescription(get("description"));
share.setExpires(get("expires"));
share.setId(get("id"));
share.setLastVisited(get("lastVisited"));
share.setUrl(get("url"));
share.setUsername(get("username"));
share.setVisitCount(getLong("visitCount"));
} else if ("entry".equals(name)) {
share.addEntry(parseEntry(null, false));
} else if ("error".equals(name)) {
handleError();
}
}
} while (eventType != XmlPullParser.END_DOCUMENT);
validate();
updateProgress(progressListener, R.string.parser_reading_done);
return dir;
}
}

View File

@ -203,6 +203,14 @@ public class Util extends DownloadActivity {
return prefs.getString(Constants.PREFERENCES_KEY_SERVER_NAME + instance, null);
}
public static String getUserName(Context context, int instance) {
if (instance == 0) {
return context.getResources().getString(R.string.main_offline);
}
SharedPreferences prefs = getPreferences(context);
return prefs.getString(Constants.PREFERENCES_KEY_USERNAME + instance, null);
}
public static boolean getServerEnabled(Context context, int instance) {
if (instance == 0) {
return true;
@ -1226,4 +1234,8 @@ public class Util extends DownloadActivity {
SharedPreferences prefs = getPreferences(context);
return prefs.getBoolean(Constants.PREFERENCES_KEY_ID3_TAGS, false);
}
public static boolean isNullOrWhiteSpace(String string) {
return string == null || string.isEmpty() || string.trim().isEmpty();
}
}

View File

@ -0,0 +1,101 @@
package com.thejoshwa.ultrasonic.androidapp.view;
import com.thejoshwa.ultrasonic.androidapp.activity.SubsonicTabActivity;
import com.thejoshwa.ultrasonic.androidapp.domain.ChatMessage;
import com.thejoshwa.ultrasonic.androidapp.util.Util;
import com.thejoshwa.ultrasonic.androidapp.R;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.regex.Pattern;
public class ChatAdapter extends ArrayAdapter<ChatMessage> {
private final SubsonicTabActivity activity;
private ArrayList<ChatMessage> messages;
private static final String phoneRegex = "1?\\W*([2-9][0-8][0-9])\\W*([2-9][0-9]{2})\\W*([0-9]{4})"; //you can just place your support phone here
private static final Pattern phoneMatcher = Pattern.compile(phoneRegex);
public ChatAdapter(SubsonicTabActivity activity, ArrayList<ChatMessage> messages) {
super(activity, R.layout.chat_item, messages);
this.activity = activity;
this.messages = messages;
}
@Override
public int getCount() {
return messages.size();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ChatMessage message = this.getItem(position);
ViewHolder holder;
int layout;
String messageUser = message.getUsername();
Date messageTime = new java.util.Date(message.getTime());
String messageText = message.getMessage();
String me = Util.getUserName(activity, Util.getActiveServer(activity));
if (messageUser.equals(me)) {
layout = R.layout.chat_item_reverse;
} else {
layout = R.layout.chat_item;
}
if (convertView == null)
{
holder = new ViewHolder();
convertView = LayoutInflater.from(activity).inflate(layout, parent, false);
TextView usernameView = (TextView) convertView.findViewById(R.id.chat_username);
TextView timeView = (TextView) convertView.findViewById(R.id.chat_time);
TextView messageView = (TextView) convertView.findViewById(R.id.chat_message);
messageView.setMovementMethod(LinkMovementMethod.getInstance());
Linkify.addLinks(messageView, Linkify.EMAIL_ADDRESSES);
Linkify.addLinks(messageView, Linkify.WEB_URLS);
Linkify.addLinks(messageView, phoneMatcher, "tel:");
holder.message = messageView;
holder.username = usernameView;
holder.time = timeView;
convertView.setTag(holder);
}
else
{
holder = (ViewHolder) convertView.getTag();
}
DateFormat timeFormat = android.text.format.DateFormat.getTimeFormat(activity);
String messageTimeFormatted = String.format("[%s]", timeFormat.format(messageTime));
holder.username.setText(messageUser);
holder.message.setText(messageText);
holder.time.setText(messageTimeFormatted);
return convertView;
}
private static class ViewHolder
{
TextView message;
TextView username;
TextView time;
}
}