Browse files

It crashes when a stream goes away, but at least I've realized now th…

…at what I was doing before was bogus (I'd be editing a fragment that wasn't shown). I have to learn how to actually work with actionbar tabs... for now though, volume controls pop up.
  • Loading branch information...
1 parent 50fab67 commit 2a782a841234fdadeb916f748822eb6a734ba806 @hchapman committed May 19, 2012
View
6 jni/context.c
@@ -175,7 +175,7 @@ Java_com_harrcharr_reverb_pulse_PulseContext_setConnectionReadyCallback(
set_state_cbs_ptr(jenv, jcontext, cbs);
pa_context_set_state_callback(c, context_state_cb, cbs);
}
- if (cbs->ready_cbo != NULL) {
+ if (cbs != NULL && cbs->ready_cbo != NULL) {
del_cb_globalref(jenv, cbs->ready_cbo);
}
if (runnable != NULL) {
@@ -194,7 +194,7 @@ Java_com_harrcharr_reverb_pulse_PulseContext_setConnectionFailedCallback(
set_state_cbs_ptr(jenv, jcontext, cbs);
pa_context_set_state_callback(c, context_state_cb, cbs);
}
- if (cbs->failed_cbo != NULL) {
+ if (cbs != NULL && cbs->failed_cbo != NULL) {
del_cb_globalref(jenv, cbs->failed_cbo);
}
if (runnable != NULL) {
@@ -214,7 +214,7 @@ Java_com_harrcharr_reverb_pulse_PulseContext_JNISubscribeSinkInput(
set_event_cbs_ptr(jenv, jcontext, cbs);
pa_context_set_subscribe_callback(c, context_subscription_cb, cbs);
}
- if (cbs->sink_input_cbo != NULL) {
+ if (cbs != NULL && cbs->sink_input_cbo != NULL) {
del_cb_globalref(jenv, cbs->sink_input_cbo);
cbs->sink_input_cbo = NULL;
}
View
3 jni/context_util.h
@@ -79,6 +79,9 @@ jni_pa_cb_info_t *new_cbinfo(JNIEnv *jenv, jobject jcontext, jobject jcb,
pa_context *get_context_ptr(JNIEnv *jenv, jobject jcontext);
pa_threaded_mainloop *get_mainloop_ptr(JNIEnv *jenv, jobject jcontext);
+jni_pa_event_cbs_t *new_event_cbs();
+jni_pa_state_cbs_t *new_state_cbs();
+
jni_pa_event_cbs_t *get_event_cbs_ptr(JNIEnv *jenv, jobject jcontext);
jni_pa_state_cbs_t *get_state_cbs_ptr(JNIEnv *jenv, jobject jcontext);
View
14 res/layout/main.xml
@@ -3,14 +3,10 @@
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
-
- <fragment
- android:id="@+id/siFrag"
- android:name="com.harrcharr.reverb.SinkInputFragment"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent" >
-
- <!-- Preview: layout=@layout/sink_input_fragment -->
- </fragment>
+ <android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/pager" />
</LinearLayout>
View
6 res/layout/sink_input_fragment.xml
@@ -4,6 +4,12 @@
android:layout_height="match_parent"
android:orientation="vertical" >
+ <TextView
+ android:id="@+id/textView1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="SinkInput Fragment" />
+
<ScrollView
android:id="@+id/scroll"
android:layout_width="match_parent"
View
130 src/com/harrcharr/reverb/ActionBarTabsPager.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2011 Jake Wharton
+ *
+ * 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.
+ */
+package com.harrcharr.reverb;
+
+import java.util.ArrayList;
+import java.util.TreeMap;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentPagerAdapter;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v4.view.ViewPager;
+
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.ActionBar.Tab;
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+
+/**
+ * Demonstrates combining the action bar with a ViewPager to implement a tab UI
+ * that switches between tabs and also allows the user to perform horizontal
+ * flicks to move between the tabs.
+ */
+public class ActionBarTabsPager extends SherlockFragmentActivity {
+ ViewPager mViewPager;
+ TabsAdapter mTabsAdapter;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt("index", getSupportActionBar().getSelectedNavigationIndex());
+ }
+
+ /**
+ * This is a helper class that implements the management of tabs and all
+ * details of connecting a ViewPager with associated TabHost. It relies on a
+ * trick. Normally a tab host has a simple API for supplying a View or
+ * Intent that each tab will show. This is not sufficient for switching
+ * between pages. So instead we make the content part of the tab host
+ * 0dp high (it is not shown) and the TabsAdapter supplies its own dummy
+ * view to show as the tab content. It listens to changes in tabs, and takes
+ * care of switch to the correct paged in the ViewPager whenever the selected
+ * tab changes.
+ */
+ public static class TabsAdapter extends FragmentPagerAdapter
+ implements ViewPager.OnPageChangeListener, ActionBar.TabListener {
+ private final Context mContext;
+ private final ActionBar mActionBar;
+ private final ViewPager mViewPager;
+ private final ArrayList<String> mTabs = new ArrayList<String>();
+ private final TreeMap<String, Fragment> mFragments =
+ new TreeMap<String, Fragment>();
+
+ public TabsAdapter(FragmentActivity activity, ActionBar actionBar, ViewPager pager) {
+ super(activity.getSupportFragmentManager());
+ mContext = activity;
+ mActionBar = actionBar;
+ mViewPager = pager;
+ mViewPager.setAdapter(this);
+ mViewPager.setOnPageChangeListener(this);
+ }
+
+ public void addTab(ActionBar.Tab tab, Class<?> clss) {
+ mTabs.add(clss.getName());
+ mActionBar.addTab(tab.setTabListener(this));
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public int getCount() {
+ return mTabs.size();
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ if (mFragments.get(mTabs.get(position)) == null) {
+ mFragments.put(mTabs.get(position),
+ Fragment.instantiate(mContext, mTabs.get(position), null));
+ }
+ return mFragments.get(mTabs.get(position));
+ }
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ mActionBar.setSelectedNavigationItem(position);
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ }
+
+ @Override
+ public void onTabSelected(Tab tab, FragmentTransaction ft) {
+ mViewPager.setCurrentItem(tab.getPosition());
+ }
+
+ @Override
+ public void onTabReselected(Tab tab, FragmentTransaction ft) {
+ }
+
+ @Override
+ public void onTabUnselected(Tab tab, FragmentTransaction ft) {
+ }
+ }
+}
View
43 src/com/harrcharr/reverb/ReverbActivity.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.os.Bundle;
+import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
@@ -34,6 +35,7 @@
import android.widget.Toast;
import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.ActionBar.Tab;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
@@ -42,7 +44,7 @@
import com.harrcharr.reverb.pulse.PulseContext;
import com.harrcharr.reverb.pulse.SinkInput;
-public class ReverbActivity extends SherlockFragmentActivity {
+public class ReverbActivity extends ActionBarTabsPager {
protected final String DEFAULT_SERVER = "192.168.1.104";
protected Mainloop m;
@@ -52,6 +54,8 @@
protected ArrayList<SinkInput> sinkInputs;
protected ActionBar mActionBar;
+
+ protected SinkInputFragment mSiFrag;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -61,10 +65,19 @@ public void onCreate(Bundle savedInstanceState) {
mActionBar = getSupportActionBar();
+ Tab sinkInputTab = mActionBar.newTab().setText("Sink Inputs");
+
+ mViewPager = (ViewPager)findViewById(R.id.pager);
+
+ mTabsAdapter = new TabsAdapter(this, getSupportActionBar(), mViewPager);
+ mTabsAdapter.addTab(sinkInputTab, SinkInputFragment.class);
+
+ mSiFrag = (SinkInputFragment)mTabsAdapter.getItem(sinkInputTab.getPosition());
+
mActionBar.setCustomView(R.layout.server_actionbar);
mActionBar.setDisplayShowTitleEnabled(false);
mActionBar.setDisplayShowCustomEnabled(true);
- mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
+ mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
((EditText)mActionBar.getCustomView()
.findViewById(R.id.serverUrl)).setText(DEFAULT_SERVER);
@@ -79,51 +92,44 @@ public void onClick(View v) {
});
sinkInputs = new ArrayList<SinkInput>();
-
- final SinkInputFragment siFrag = (SinkInputFragment)getSupportFragmentManager()
- .findFragmentById(R.id.siFrag);
m = new Mainloop();
-
+
connect(DEFAULT_SERVER);
}
public synchronized void connect(final String server) {
- final SinkInputFragment siFrag = (SinkInputFragment)getSupportFragmentManager()
- .findFragmentById(R.id.siFrag);
Log.d("Reverb", server);
if(mPulse != null && mPulse.isConnected()) {
mPulse.close();
- mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
+
}
mPulse = new PulseContext(m);
mPulse.setConnectionReadyCallback(new NotifyCallback() {
@Override
public void run() {
-
- siFrag.setPulseContext(mPulse);
-
- mPulse.getSinkInputInfoList(siFrag.getInfoCallback());
- mPulse.subscribeSinkInput(siFrag.getSubscriptionCallback());
-
final Context context = getApplicationContext();
final CharSequence text = "Successfully connected to "+server;
final int duration = Toast.LENGTH_SHORT;
runOnUiThread(new Runnable() {
public void run() {
- mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+
Toast toast = Toast.makeText(context, text, duration);
toast.show();
+
+ mSiFrag.setPulseContext(mPulse);
+
}
});
}
});
+
mPulse.setConnectionFailedCallback(new NotifyCallback() {
@Override
public void run() {
@@ -144,7 +150,6 @@ public void run() {
try {
mPulse.connect(server);
} catch (Exception e) {
- // TODO Auto-generated catch block
Log.e("Reverb", "weird");
e.printStackTrace();
}
@@ -157,4 +162,8 @@ public boolean onCreateOptionsMenu(Menu menu) {
inflater.inflate(R.menu.main_menu, menu);
return true;
}
+
+ public PulseContext getPulseContext() {
+ return mPulse;
+ }
}
View
162 src/com/harrcharr/reverb/SinkInputFragment.java
@@ -22,12 +22,16 @@
package com.harrcharr.reverb;
import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map.Entry;
+import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.TextView;
import com.actionbarsherlock.app.SherlockFragment;
import com.harrcharr.reverb.pulse.InfoCallback;
@@ -37,64 +41,116 @@
import com.harrcharr.reverb.pulse.SubscriptionCallback;
public class SinkInputFragment extends SherlockFragment {
- protected HashMap<Integer, StreamNodeView<SinkInput>> mNodes;
+ protected HashMap<Integer, SinkInput> mNodes;
protected PulseContext mPulse;
protected InfoCallback<SinkInput> mInfoCallback;
protected SubscriptionCallback mSubscriptionCallback;
+ protected ViewGroup mNodeHolder;
+
public SinkInputFragment() {
super();
- mNodes = new HashMap<Integer, StreamNodeView<SinkInput>>();
-
mInfoCallback = new SinkInputCallback();
mSubscriptionCallback = new SinkInputSubscriptionCallback();
}
- public void setPulseContext(PulseContext pulse) {
+ public synchronized void setPulseContext(PulseContext pulse) {
mPulse = pulse;
// Now that we've changed our PulseContext, we have to reinstantiate.
- mNodes = new HashMap<Integer, StreamNodeView<SinkInput>>();
- getActivity().runOnUiThread(new Runnable() {
- public void run() {
- getViewGroup().removeAllViews();
- Log.d("Reverb", "Removed stale nodes");
- }
- });
+ mNodes = new HashMap<Integer, SinkInput>();
+
+ Log.e("Reverb", "Context set, activity is "+getActivity()+"Added? "+isAdded());
+ Log.e("Reverb", getActivity()+" is our activity");
+
+ mPulse.subscribeSinkInput(getSubscriptionCallback());
+ mPulse.getSinkInputInfoList(getInfoCallback());
+
+ if (isVisible()) {
+ getActivity().runOnUiThread(new Runnable() {
+ public void run() {
+ try {
+ getViewGroup().removeAllViews();
+ Log.d("Reverb", "Removed stale nodes");
+ } catch (Exception e) {
+
+ }
+
+ }
+ });
+ }
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- return inflater.inflate(R.layout.sink_input_fragment, container, false);
+ View v = inflater.inflate(R.layout.sink_input_fragment, container, false);
+
+ mNodeHolder = (ViewGroup)v.findViewById(R.id.nodeHolder);
+ Iterator<Entry<Integer, SinkInput>> nodeIterator =
+ mNodes.entrySet().iterator();
+
+ while (nodeIterator.hasNext()) {
+ Log.e("Reverb", "We're adding something");
+ final StreamNodeView<SinkInput> nodeView =
+ new StreamNodeView<SinkInput>(getActivity());
+ nodeView.setNode(nodeIterator.next().getValue());
+
+ mNodeHolder.addView(nodeView);
+ }
+
+ TextView w = new TextView(getActivity());
+ w.setText("Frig frig frig");
+ mNodeHolder.addView(w);
+
+ Log.e("Reverb", "mNodeHolder is "+mNodeHolder);
+
+ return v;
}
protected void addNode(SinkInput node) {
- final StreamNodeView<SinkInput>v = new StreamNodeView<SinkInput>(getActivity());
- v.setNode(node);
- mNodes.put(new Integer(node.getIndex()), v);
+ mNodes.put(new Integer(node.getIndex()), node);
+
+ if(getViewGroup() != null) {
+ final StreamNodeView<SinkInput> nodeView =
+ new StreamNodeView<SinkInput>(getActivity());
+ nodeView.setNode(node);
- getActivity().runOnUiThread(new Runnable(){
- public void run() {
- getViewGroup().addView(v);
- }
- });
+ Log.e("Reverb", "We want to add a node");
+ getActivity().runOnUiThread(new Runnable(){
+ public void run() {
+ mNodeHolder.addView(nodeView);
+ }
+ });
+ }
}
protected void removeNode(int index) {
- final View v = mNodes.remove(index);
+ final SinkInput node = mNodes.remove(index);
- getActivity().runOnUiThread(new Runnable(){
- public void run() {
- getViewGroup().removeView(v);
- }
- });
+ if(getViewGroup() != null) {
+ getActivity().runOnUiThread(new Runnable(){
+ public void run() {
+ mNodeHolder.removeView(mNodeHolder.findViewById(node.getIndex()));
+ }
+ });
+ }
}
protected ViewGroup getViewGroup() {
+ if (getView() == null)
+ return null;
+
return (ViewGroup)getView().findViewById(R.id.nodeHolder);
}
+ protected StreamNodeView<SinkInput> getStreamNodeViewByIndex(int idx) {
+ if (getViewGroup() == null)
+ return null;
+
+ return (StreamNodeView<SinkInput>)getViewGroup().findViewById(idx);
+ }
+
protected PulseContext getPulseContext() {
return mPulse;
}
@@ -105,41 +161,55 @@ protected PulseContext getPulseContext() {
protected SubscriptionCallback getSubscriptionCallback() {
return mSubscriptionCallback;
}
+
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ }
+
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.e("Reverb", "Created. mPulse is "+mPulse+" activity is "+getActivity());
+ Log.e("Reverb", "Context set, activity is "+getActivity()+"Added? "+isAdded());
+
+ setPulseContext(((ReverbActivity)getActivity()).getPulseContext());
+ }
private class SinkInputCallback extends SinkInputInfoCallback {
public void run(final SinkInput si) {
int idx = si.getIndex();
Log.d("Reverb [adapter]", "We're in a SinkInputCallback run().");
- Log.d("Reverb", "Update index "+idx);
- final StreamNodeView<SinkInput> v = SinkInputFragment.this.mNodes.get(si.getIndex());
- Log.d("Reverb", "Update node is "+v+" and index "+idx);
- if (v != null) {
- getActivity().runOnUiThread(new Runnable() {
- public void run() {
- v.setNode(si);
- }
- });
+ Log.d("Reverb", "Update index "+idx+"view group"+getViewGroup());
+ Log.e("Reverb", "mNodeHolder is "+SinkInputFragment.this.mNodeHolder);
+
+ if (getViewGroup() != null) {
+ final StreamNodeView<SinkInput> v = getStreamNodeViewByIndex(si.getIndex());
- Log.d("Reverb", "put with idx "+idx);
- }
- else {
- Log.d("Reverb", "put with idx "+idx);
- SinkInputFragment.this.addNode(si);
+ Log.d("Reverb", "Update node is "+v+" and index "+idx);
+ if (v != null) {
+ getActivity().runOnUiThread(new Runnable() {
+ public void run() {
+ v.setNode(si);
+ }
+ });
+
+ Log.d("Reverb", "put with idx "+idx);
+ return;
+ }
}
+
+ Log.d("Reverb", "put with idx "+idx);
+ SinkInputFragment.this.addNode(si);
}
}
private class SinkInputSubscriptionCallback extends SubscriptionCallback {
- public SinkInputSubscriptionCallback() {
- }
-
public void run(int type, int index) {
Log.d("Reverb", type + " " + index);
if (type == EVENT_REMOVE) {
- SinkInputFragment.this.removeNode(index);
+ removeNode(index);
} else {
- SinkInputFragment.this.getPulseContext()
- .getSinkInputInfo(index, SinkInputFragment.this.getInfoCallback());
+ Log.w("Reverb", ""+getPulseContext());
+ getPulseContext().getSinkInputInfo(index, getInfoCallback());
}
}
}
View
1 src/com/harrcharr/reverb/StreamNodeView.java
@@ -75,6 +75,7 @@ protected void reload() {
public void setNode(Node node) {
mNode = node;
+ setId(node.getIndex());
reload();
}
View
4 src/com/harrcharr/reverb/pulse/JniCallback.java
@@ -13,6 +13,10 @@ public void storeGlobal(PulseContext pulse, long ref) {
mPulse.holdCallback(this);
}
+ public PulseContext getPulseContext() {
+ return mPulse;
+ }
+
public long getGlobal() {
return mGlobalRef;
}
View
1 src/com/harrcharr/reverb/pulse/PulseContext.java
@@ -147,6 +147,7 @@ public synchronized void close() {
Set<JniCallback> removalSet = new TreeSet<JniCallback>();
// Free all possible remaining callbacks
+ // Possibly re-implement this using iterators and .remove()
for (JniCallback callback : mCallbacks) {
callback.freeGlobal(false);
removalSet.add(callback);

0 comments on commit 2a782a8

Please sign in to comment.