How to update volume bar in MinimalMediaRouteProvider - chromecast

I am using registerMediaRouteProvider and it gives you a volume bar to update the tv's volume. I implemented MediaRouteAdapter and when I scrub the volume bar, the volume changes, but the volume bar's ui always resets back to 0. How do I update the ui of the volume bar when the volume changes?
#Override
public void onCreate(Bundle savedInstanceState) {
mCastContext = new CastContext(getApplicationContext());
MediaRouteHelper.registerMinimalMediaRouteProvider(mCastContext, this);
mMediaRouter = MediaRouter.getInstance(getApplicationContext());
mMediaRouteSelector = MediaRouteHelper.buildMediaRouteSelector(MediaRouteHelper.CATEGORY_CAST);
mMetaData = new ContentMetadata();
mMediaRouterCallback = new MyMediaRouterCallback();
mMediaRouteButton.setRouteSelector(mMediaRouteSelector);
}
private class MyMediaRouterCallback extends MediaRouter.Callback {
#Override
public void onRouteSelected(MediaRouter router, RouteInfo route) {
MediaRouteHelper.requestCastDeviceForRoute(route);
}
#Override
public void onRouteUnselected(MediaRouter router, RouteInfo route) {
try {
if (mSession != null) {
Log.e(TAG, "Ending session and stopping application");
mSession.setStopApplicationWhenEnding(true);
mSession.endSession();
} else {
Log.e(TAG, "onRouteUnselected: mSession is null");
}
} catch (IllegalStateException e) {
Log.e(TAG, "onRouteUnselected:");
e.printStackTrace();
} catch (IOException e) {
Log.e(TAG, "onRouteUnselected:");
e.printStackTrace();
}
mSelectedDevice = null;
}
}
#Override
public void onDeviceAvailable(CastDevice device, String arg1, MediaRouteStateChangeListener listener) {
mSelectedDevice = device;
openSession();
}
#Override
public void onSetVolume(double volume) {
try {
if (mMessageStream != null) {
mMessageStream.setVolume(volume);
}
} catch (IllegalStateException e) {
Log.e(TAG, "Problem sending Set Volume", e);
} catch (IOException e) {
Log.e(TAG, "Problem sending Set Volume", e);
}
}
#Override
public void onUpdateVolume(double volumeChange) {
try {
if (mCurrentRoute != null) {
mCurrentRoute.requestUpdateVolume((int) (volumeChange * 20));
}
} catch (IllegalStateException e) {
Log.e(TAG, "Problem sending Update Volume", e);
}
}
EDIT - added where I initialize mMessageStream
private void openSession() {
mSession = new ApplicationSession(mCastContext, mSelectedDevice);
int flags = 0;
flags |= ApplicationSession.FLAG_DISABLE_NOTIFICATION;
flags |= ApplicationSession.FLAG_DISABLE_LOCK_SCREEN_REMOTE_CONTROL;
mSession.setApplicationOptions(flags);
Log.d(TAG, "Beginning session with context: " + mCastContext);
Log.d(TAG, "The session to begin: " + mSession);
mSession.setListener(new com.google.cast.ApplicationSession.Listener() {
#Override
public void onSessionStarted(ApplicationMetadata appMetadata) {
Log.d(TAG, "Getting channel after session start");
ApplicationChannel channel = mSession.getChannel();
if (channel == null) {
Log.e(TAG, "channel = null");
return;
}
Log.d(TAG, "Creating and attaching Message Stream");
mMessageStream = new MediaProtocolMessageStream();
channel.attachMessageStream(mMessageStream);
if (mMessageStream.getPlayerState() == null) {
if (vastVideoView.getPlayingPlaylistItem() != null) {
loadMedia();
}
} else {
Log.e(TAG, "Found player already running; updating status");
}
}
#Override
public void onSessionStartFailed(SessionError error) {
Log.e(TAG, "onStartFailed " + error + " code " + error.getCode());
nowifi.setVisibility(View.GONE);
}
#Override
public void onSessionEnded(SessionError error) {
Log.i(TAG, "onEnded " + error);
controller.removeChromeCastListener();
controller.setChromeCast(false);
nowifi.setVisibility(View.GONE);
}
});
try {
Log.e(TAG, "Starting session with app name " + getString(R.string.app_id));
mSession.startSession(getString(R.string.app_id));
vastVideoView.stopPlayback();
controller = vastVideoView.getMediaController();
controller.setChromeCast(true);
controller.setPausePlayListener(pausePlayListener);
seekBar = controller.getSeekBar();
seekBar.setProgress(0);
mPlayButtonShowsPlay = true;
} catch (IOException e) {
Log.e(TAG, "Failed to open session", e);
controller.removeChromeCastListener();
controller.setChromeCast(false);
nowifi.setVisibility(View.GONE);
}
}

I've been looking for this for days and I suddenly found the solution by myself :P
First, You will need a MediaRouteStateChangeListener to handle this.
MediaRouteStateChangeListener mRouteStateListener;
Second, assign the listener in onDeviceAvailable.
#Override
public void onDeviceAvailable(CastDevice device, String arg1, MediaRouteStateChangeListener listener) {
mSelectedDevice = device;
mRouteStateListener = listener;
openSession();
}
Last, call MediaRouteStateListener.onVolumeChanged() in onSetVolume or onUpdateVolume.
#Override
public void onSetVolume(double volume) {
try {
if (mMessageStream != null) {
mMessageStream.setVolume(volume);
mRouteStateListener.onVolumeChanged(volume);
}
} catch (IllegalStateException e) {
Log.e(TAG, "Problem sending Set Volume", e);
} catch (IOException e) {
Log.e(TAG, "Problem sending Set Volume", e);
}
}
This should help your volume seekbar to act normally. :)

At the Receiver level, there are two ways to set the volume:
cast.Receiver.Platform.setVolume([0.0,1.0])
This will set the volume and show the blue bar. This is what is usually called by the onVolume message.
mediaElement.volume = [0.0, 1.0];
This will set the volume without showing the blue bar. You can use this for fade in / fade out, and for equalization.
Now on to your Java code:
I don't see how you are getting your messageStream in what you've posted, but since it's likely to be the right thing. Here's the lines from the reference docs:
public final MediaProtocolCommand setVolume (double volume)
Sets the audio volume.
Parameters
volume The volume, in the range 0.0 (0%) to 1.0 (100%).
Returns
The command object for this request
Throws
IOException If an I/O error occurs while sending the message.
IllegalStateException If this stream is not attached to a connected channel.

Google updated volume controls this summer so they are shared between connected devices and apps. Please see my solution here:
https://stackoverflow.com/a/26554007/672373

Related

Callbacks are not been called

I'm writing an app with Tango and getting a strange bug.
My program successfully connected to the tango service,and i have setup a callback with "mTango.connectListener(framePairs, new Tango.TangoUpdateCallback() {...});",but i can not get any tango datas because the callback have never been called.
here is my main code
private void bindTangoService() {
Log.d(TAG, "正在绑定Tango 服务");
if (mIsConnected) {
return;
}
mTango = new Tango(mContext, new Runnable() {
#Override
public void run() {
synchronized (TangoAR.this) {
try {
mConfig = setupTangoConfig(mTango, false, false);
mTango.connect(mConfig);
startupTango();
TangoSupport.initialize(mTango);
mIsConnected = true;
onRotationUpdated(Util.getDisplayRotation(mContext));
makeToast("tango 连接成功");
Log.d(TAG, "tango 连接成功");
} catch (TangoOutOfDateException e) {
makeToast("TangoOutOfDateException");
} catch (Throwable e) {
makeToast(e.getMessage());
}
}
}
});
}
private TangoConfig setupTangoConfig(Tango tango, boolean isLearningMode, boolean isLoadAdf) {
Log.d(TAG, "setupTangoConfig");
TangoConfig config = tango.getConfig(TangoConfig.CONFIG_TYPE_DEFAULT);
//在tango连接后使用相机必须使用true
config.putBoolean(TangoConfig.KEY_BOOLEAN_COLORCAMERA, false);
//motion
config.putBoolean(TangoConfig.KEY_BOOLEAN_MOTIONTRACKING, true);
config.putBoolean(TangoConfig.KEY_BOOLEAN_AUTORECOVERY, true);
config.putBoolean(TangoConfig.KEY_BOOLEAN_DRIFT_CORRECTION, true);
//depth
config.putBoolean(TangoConfig.KEY_BOOLEAN_DEPTH, true);
config.putBoolean(TangoConfig.KEY_BOOLEAN_LOWLATENCYIMUINTEGRATION, true);
config.putInt(TangoConfig.KEY_INT_DEPTH_MODE, TangoConfig.TANGO_DEPTH_MODE_POINT_CLOUD);//使用点云,不使用老版本的TANGO_DEPTH_MODE_XYZ_IJ
//area learning
if (isLearningMode) {
//区域学习需要权限授权
if (checkAndRequestTangoPermissions(Tango.PERMISSIONTYPE_ADF_LOAD_SAVE)) {
Log.d(TAG, "PERMISSIONTYPE_ADF_LOAD_SAVE 开启");
config.putBoolean(TangoConfig.KEY_BOOLEAN_LEARNINGMODE, true);
}
}
if (isLoadAdf) {
//加载ADF
ArrayList<String> fullUuidList;
fullUuidList = tango.listAreaDescriptions();
if (fullUuidList.size() > 0) {
config.putString(TangoConfig.KEY_STRING_AREADESCRIPTION,
fullUuidList.get(fullUuidList.size() - 1));
}
}
return config;
}
private void startupTango() {
Log.d(TAG, "startupTango");
ArrayList<TangoCoordinateFramePair> framePairs = new ArrayList<>();
//设置参考系,0,0,0点为tango服务启动时设备的位置,测量目标为设备的位置
Log.d(TAG, "startup");
framePairs.add(new TangoCoordinateFramePair(
TangoPoseData.COORDINATE_FRAME_START_OF_SERVICE,
TangoPoseData.COORDINATE_FRAME_DEVICE));
Log.d(TAG, "startup-listener");
mTango.connectListener(framePairs, new Tango.TangoUpdateCallback() {
#Override
public void onPoseAvailable(final TangoPoseData pose) {
//motion
Log.d(TAG, "onPoseAvailable");
//获取手机新的状态
mTangoTime = pose.timestamp;
TangoPoseData poseData = TangoSupport.getPoseAtTime(
mTangoTime,
TangoPoseData.COORDINATE_FRAME_START_OF_SERVICE,
TangoPoseData.COORDINATE_FRAME_CAMERA_COLOR,
TangoSupport.ENGINE_OPENGL,
TangoSupport.ENGINE_OPENGL,
mDisplayRotation);
if (poseData.statusCode == TangoPoseData.POSE_VALID) {
mTangoPoseData = poseData;
}
}
#Override
public void onPointCloudAvailable(TangoPointCloudData pointCloud) {
//记录当扫描到到的深度信息
mPointCloudManager.updatePointCloud(pointCloud);
Log.d(TAG, "depth size:" + pointCloud.numPoints);
}
#Override
public void onXyzIjAvailable(TangoXyzIjData xyzIj) {
Log.d(TAG, "onXyzIjAvailable");
}
#Override
public void onFrameAvailable(int cameraId) {
Log.d(TAG, "onFrameAvailable");
}
#Override
public void onTangoEvent(TangoEvent event) {
Log.d(TAG, event.eventValue);
}
});
Log.d(TAG, "startup-listener-end");
}
mTango.connect(mConfig); execute successfully and without any exception.
I have been plagued by this problem for three days.I need a hero.Thanks everyone.
Notes:The rear camera is not be used for tango because i use it elsewhere.
I had the same problem. For me the solution was to request permissions:
startActivityForResult(
Tango.getRequestPermissionIntent(Tango.PERMISSIONTYPE_MOTION_TRACKING),
Tango.TANGO_INTENT_ACTIVITYCODE);

touch button with hold and unhold events to send different data in android

i want a button that will send a data to a Bluetooth device when the button get touched once. and after removing the touch the button should send another data once. it's like when i touch the button and hold it, it'll only send a string like 'A' and when the touch removed it'll send another string like 'C'. i am a beginner. here is my code with two button but now i want only one button to control.
public class MainActivity extends Activity {
private static final String TAG = "bluetooth1";
Button btnOn, btnOff;
private BluetoothAdapter btAdapter = null;
private BluetoothSocket btSocket = null;
private OutputStream outStream = null;
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private static String address = "98:D3:31:30:27:BD";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnOn = (Button) findViewById(R.id.btnOn);
btnOff = (Button) findViewById(R.id.btnOff);
btAdapter = BluetoothAdapter.getDefaultAdapter();
checkBTState();
// i want one button here for touch event
btnOn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
sendData("A");
Toast.makeText(getBaseContext(), "Turn on LED", Toast.LENGTH_SHORT).show();
}
});
btnOff.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
sendData("C");
Toast.makeText(getBaseContext(), "Turn off LED", Toast.LENGTH_SHORT).show();
}
});
}
private BluetoothSocket createBluetoothSocket(BluetoothDevice device) throws IOException {
if(Build.VERSION.SDK_INT >= 10){
try {
final Method m = device.getClass().getMethod("createInsecureRfcommSocketToServiceRecord", new Class[] { UUID.class });
return (BluetoothSocket) m.invoke(device, MY_UUID);
} catch (Exception e) {
Log.e(TAG, "Could not create Insecure RFComm Connection",e);
}
}
return device.createRfcommSocketToServiceRecord(MY_UUID);
}
#Override
public void onResume() {
super.onResume();
Log.d(TAG, "...onResume - try connect...");
BluetoothDevice device = btAdapter.getRemoteDevice(address);
try {
btSocket = createBluetoothSocket(device);
} catch (IOException e1) {
errorExit("Fatal Error", "In onResume() and socket create failed: " + e1.getMessage() + ".");
}
btAdapter.cancelDiscovery();
Log.d(TAG, "...Connecting...");
try {
btSocket.connect();
Log.d(TAG, "...Connection ok...");
} catch (IOException e) {
try {
btSocket.close();
} catch (IOException e2) {
errorExit("Fatal Error", "In onResume() and unable to close socket during connection failure" + e2.getMessage() + ".");
}
}
Log.d(TAG, "...Create Socket...");
try {
outStream = btSocket.getOutputStream();
} catch (IOException e) {
errorExit("Fatal Error", "In onResume() and output stream creation failed:" + e.getMessage() + ".");
}
}
#Override
public void onPause() {
super.onPause();
Log.d(TAG, "...In onPause()...");
if (outStream != null) {
try {
outStream.flush();
} catch (IOException e) {
errorExit("Fatal Error", "In onPause() and failed to flush output stream: " + e.getMessage() + ".");
}
}
try {
btSocket.close();
} catch (IOException e2) {
errorExit("Fatal Error", "In onPause() and failed to close socket." + e2.getMessage() + ".");
}
}
private void checkBTState() {
if(btAdapter==null) {
errorExit("Fatal Error", "Bluetooth not support");
} else {
if (btAdapter.isEnabled()) {
Log.d(TAG, "...Bluetooth ON...");
} else {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 1);
}
}
}
private void errorExit(String title, String message){
Toast.makeText(getBaseContext(), title + " - " + message, Toast.LENGTH_LONG).show();
finish();
}
private void sendData(String message) {
byte[] msgBuffer = message.getBytes();
Log.d(TAG, "...Send data: " + message + "...");
try {
outStream.write(msgBuffer);
} catch (IOException e) {
String msg = "In onResume() and an exception occurred during write: " + e.getMessage();
if (address.equals("00:00:00:00:00:00"))
msg = msg + ".\n\nUpdate your server address from 00:00:00:00:00:00 to the correct address on line 35 in the java code";
msg = msg + ".\n\nCheck that the SPP UUID: " + MY_UUID.toString() + " exists on server.\n\n";
errorExit("Fatal Error", msg);
}
}
}
i solve the following by this and it's works perfect
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN ) {
sendData("A");
return true;
}
if (event.getAction() == MotionEvent.ACTION_UP) {
sendData("C");
return true;
}
return false;
}
});

The info about new Tango()

I have downloaded the demo "java_quick_start_example".
1. I run the app directly, but it is failed. The log is described as
TangoErrorType TangoService_initialize (JNIEnv*, jobject): Done initializing, error code = -1
2. I modify the code as below
mTango = new Tango(this, new Runnable() {
#Override
public void run() {
}
});
It is failed, too. The log is described as "Error - TangoService_connectOnPoseAvailable: -1"
some example pull from Tango Example :
mTango = new Tango(MotionTrackingActivity.this, new Runnable() {
// Pass in a Runnable to be called from UI thread when Tango is ready,
// this Runnable will be running on a new thread.
// When Tango is ready, we can call Tango functions safely here only
// when there is no UI thread changes involved.
#Override
public void run() {
mConfig = setupTangoConfig(mTango);
try {
setTangoListeners();
} catch (TangoErrorException e) {
Log.e(TAG, getString(R.string.exception_tango_error), e);
} catch (SecurityException e) {
Log.e(TAG, getString(R.string.permission_motion_tracking), e);
}
try {
mTango.connect(mConfig);
} catch (TangoOutOfDateException e) {
Log.e(TAG, getString(R.string.exception_out_of_date), e);
} catch (TangoErrorException e) {
Log.e(TAG, getString(R.string.exception_tango_error), e);
}
}
});
}

"Cannot save a ParseUser that is not authenticated"

I am trying to save data in authData field android, But I am getting exception: Cannot save a ParseUser that is not authenticated" after only reading the user
ParseQuery<ParseUser> query = ParseUser.getQuery();
query.whereEqualTo("username", email);
query.findInBackground(new FindCallback<ParseUser>() {
#SuppressWarnings("deprecation")
public void done(List<ParseUser> scoreList, ParseException e) {
if (e == null) {
if(scoreList.size()>0){
facebook_user = (ParseUser) scoreList.get(0);
facebook_user.isAuthenticated()
JSONObject obj = new JSONObject();
try {
obj.put("name", "xyz");
} catch (JSONException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
facebook_user.put("authdata", obj);
facebook_user.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
// exception in this section //
//Cannot save a ParseUser that is not authenticated" after only reading the user//
});
}else{
}
} else {
Log.d("score", "Error: " + e.getMessage());
}
}
});
It looks like you are attempting to modify that user with the .put() before saving:
facebook_user.put("authdata", obj);
facebook_user.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
// exception in this section //
});
If you want to change a user, you need to be logged in as the user, execute the change on Cloud Code using the masterKey, or change the user objects' ACL. Here's a SO post with a bit more info.
Let me know if you have any more questions.

Adobe CQ : Regarding Session in Event Listener

I have a question regarding event listener. We have a event listener which listen to delete node event and perform some activity say "send email".
While code review i found this, although this code is working fine i am not convinced with the session being handled here :
#Activate
protected void activate(ComponentContext context) {
try{
final String path="/content/dam/";
Session session = repository.loginAdministrative(repository.getDefaultWorkspace());
observationManager = session.getWorkspace().getObservationManager();
observationManager.addEventListener(this, Event.PROPERTY_REMOVED, path, true, null, null, true);
checkOutProperty = OsgiUtil.toString(context.getProperties()
.get(ASSET_LOCK_PROPNAME_UPDATE), ASSET_LOCK_PROPNAME_DEFAULT);
if (session != null && session.isLive()) {
session.save();
}
} catch (RepositoryException e) {
if(LOG.isErrorEnabled()){
LOG.error("Error Occured in activate method of Property Removed Listener class:" + e.getMessage());
}
}catch (Exception e) {
if(LOG.isErrorEnabled()){
LOG.error("Error Occured in activate method of Property Removed Listener class:"+e.getMessage());
}
}
}
#Deactivate
protected void deactivate(ComponentContext componentContext) {
try {
if (observationManager != null) {
observationManager.removeEventListener(this);
}
} catch (RepositoryException e) {
if(LOG.isErrorEnabled()){
LOG.error("Error Occured " + e);
}
} catch (Exception e) {
if(LOG.isErrorEnabled()){
LOG.error(e.getMessage());
}
}
}
Questions:
Best practice would be to create session object private to this class and should be logout in deactivate method?
Once an event is added in Observation Manager, do we really need session object? I was expecting if we should logout from session there.
EventListener are a bit cumbersome here. I fought many battles with JCR Sessions and Sling ResourceResolvers within them. The problem is, you need to keep the Session active as long as the Event Listener is active. So the only thing missing in your code is a logout on deactivate.
I created an AbstractEventListener which takes care of this and provides the following two methods and has two private members:
private Session session;
private ObservationManager observationManager;
protected void addEventListener(final EventListener eventListener,
final int eventTypes, final String path, final String[] nodeTypes) {
try {
session = getRepositorySession();
observationManager = session.getWorkspace().getObservationManager();
observationManager.addEventListener(eventListener, eventTypes,
path, true, null, nodeTypes, true);
} catch (RepositoryException e) {
LOGGER.error("Repository error while registering observation: ", e);
}
}
protected void removeEventListener(final EventListener eventListener) {
if (observationManager != null) {
try {
observationManager.removeEventListener(eventListener);
} catch (RepositoryException e) {
LOGGER.error(
"Repository error while unregistering observation: ", e);
} finally {
logoutSession(session);
}
}
}
And then in the actual EventListener I just call them:
protected void activate(ComponentContext context) {
addEventListener(this, Event.PROPERTY_ADDED| Event.PROPERTY_CHANGED, "/content/mysite", null);
}
}
protected void deactivate(ComponentContext componentContext) {
removeEventListener(this);
}

Resources