Getting Google Cast v3 to work on unsupported devices - android

The Cast V3 framework has features which try to make it possible to run on devices without the Google Play Services required for it to work, but I ran into some issues when testing.
On the Kindle the Google API returns SERVICE_INVALID with a isUserResolvable() true.
On devices with the onActivityResult returning ConnectionResult.SUCCESS after upgrade, the CastContext.getSharedInstance() can throw RuntimeError.
As a side-effect of 2), the XML inflate of items containing MiniControllerFragment will fail.
Some errors I found were
java.lang.RuntimeException: Unable to start activity ComponentInfo{##########.MainActivity}: android.view.InflateException: Binary XML file line #42: Error inflating class fragment
Caused by: java.lang.RuntimeException:
com.google.android.gms.dynamite.DynamiteModule$zzc: Remote load failed. No local fallback found.
at com.google.android.gms.internal.zzauj.zzan(Unknown Source)
at com.google.android.gms.internal.zzauj.zza(Unknown Source)
at com.google.android.gms.cast.framework.CastContext.<init>(Unknown Source)
at com.google.android.gms.cast.framework.CastContext.getSharedInstance(Unknown Source)
at com.google.android.gms.cast.framework.media.uicontroller.UIMediaController.<init>(Unknown Source)
at com.google.android.gms.cast.framework.media.widget.MiniControllerFragment.onCreateView(Unknown Source)
This was caused by the inflation of the MiniControllerFragment, on a device where the CastController code wasn't installed. This is similar to the question asked SO : Cast v3 is crashing on devices below 5.0. The answer provided by Kamil Ślesiński helped in my investigation.
and
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=123, result=0, data=null} to activity #####
When I had implemented my ViewStub, I was still crashing in the pre-release test machines, as they were returning SUCCESS, but didn't have the CastContext available. To fix this, I needed another test to check if the CastContext was creatable.

You need a singleton / code in the Application something like below....
boolean gCastable = false;
boolean gCastTested = false;
public boolean isCastAvailable(Activity act, int resultCode ){
if( gCastTested == true ){
return gCastable;
}
GoogleApiAvailability castApi = GoogleApiAvailability.getInstance();
int castResult = castApi.isGooglePlayServicesAvailable(act);
switch( castResult ) {
case ConnectionResult.SUCCESS:
gCastable = true;
gCastTested = true;
return true;
/* This code is needed, so that the user doesn't get a
*
* your device is incompatible "OK"
*
* message, it isn't really "user actionable"
*/
case ConnectionResult.SERVICE_INVALID: // Result from Amazon kindle - perhaps check if kindle first??
gCastable = false;
gCastTested = true;
return false;
////////////////////////////////////////////////////////////////
default:
if (castApi.isUserResolvableError(castResult)) {
castApi.getErrorDialog(act, castResult, resultCode, new DialogInterface.OnCancelListener() {
#Override
public void onCancel(DialogInterface dialog) {
gCastable = false;
gCastTested = false;
return;
}
}).show();
} else {
gCastTested = true;
gCastable = false;
return false;
}
}
return gCastable;
}
public void setCastOK(Activity mainActivity, boolean result ) {
gCastTested = true;
gCastable = result;
}
and a helper function to check if we know the state of the cast.
public boolean isCastAvailableKnown() {
return gCastable;
}
However to cope with devices which return SUCCESS, I also needed the following code in the App / singleton.
When the Activity receives the cast result, we create a CastContext. The "hope" is, if the Application can create the CastContext, then the framework will succeed in the same way (the cause of the crash).
public boolean onCastResultReceived( Activity act, int result ) {
boolean wasOk = false;
if( result == ConnectionResult.SUCCESS ){
try {
CastContext ctx = CastContext.getSharedInstance(act );
wasOk = true;
} catch ( RuntimeException e ){
wasOk = false;
}
}
if( wasOk ) {
setCastOK(act, true);
return true;
}else {
setCastOK(act, false );
return false;
}
}
The inflation of the mini controller is disabled by using a ViewStub and a fragment...
Fragment mini_controller_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<fragment
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/cast_mini_controller"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:visibility="gone"
app:castShowImageThumbnail="true"
class="com.google.android.gms.cast.framework.media.widget.MiniControllerFragment" />
With usage something like this....
<ViewStub
android:id="#+id/cast_mini_controller"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout="#layout/mini_controller_fragment"
/>
Activity
An Activity's interaction with the cast components looks something like this...
/* called when we have found out that cast is compatible. */
private void onCastAvailable() {
ViewStub miniControllerStub = (ViewStub) findViewById(R.id.cast_mini_controller);
miniControllerStub.inflate(); // only inflated if Cast is compatible.
mCastStateListener = new CastStateListener() {
#Override
public void onCastStateChanged(int newState) {
if (newState != CastState.NO_DEVICES_AVAILABLE) {
showIntroductoryOverlay();
}
if (mQueueMenuItem != null) {
mQueueMenuItem.setVisible(
(mCastSession != null) && mCastSession.isConnected());
}
}
};
mCastContext = CastContext.getSharedInstance(this);
if (mCastSession == null) {
mCastSession = mCastContext.getSessionManager()
.getCurrentCastSession();
}
if (mQueueMenuItem != null) {
mQueueMenuItem.setVisible(
(mCastSession != null) && mCastSession.isConnected());
}
}
private void showIntroductoryOverlay() {
if (mOverlay != null) {
mOverlay.remove();
}
if ((mediaRouteMenuItem != null) && mediaRouteMenuItem.isVisible()) {
new Handler().post(new Runnable() {
#Override
public void run() {
mOverlay = new IntroductoryOverlay.Builder(
MainActivity.this, mediaRouteMenuItem)
.setTitleText(getString(R.string.introducing_cast))
.setOverlayColor(R.color.primary)
.setSingleTime()
.setOnOverlayDismissedListener(
new IntroductoryOverlay.OnOverlayDismissedListener() {
#Override
public void onOverlayDismissed() {
mOverlay = null;
}
})
.build();
mOverlay.show();
}
});
}
}
onCreate modified as below...
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mApp = (MyApplication)getApplication();
if( mApp.isCastAvailable( (Activity)this, GPS_RESULT )) {
onCastAvailable();
}
...
}
onActivityResult needs to cope with the result from the Google Play Services upgrade...
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if( requestCode == GPS_RESULT ) {
if(mApp.onCastResultReceived( this, resultCode ) ){
onCastAvailable();
}
onResume
protected void onResume() {
if( mCastContext != null && mCastStateListener != null ) {
mCastContext.addCastStateListener(mCastStateListener);
mCastContext.getSessionManager().addSessionManagerListener(
mSessionManagerListener, CastSession.class);
if (mCastSession == null) {
mCastSession = CastContext.getSharedInstance(this).getSessionManager()
.getCurrentCastSession();
}
if (mQueueMenuItem != null) {
mQueueMenuItem.setVisible(
(mCastSession != null) && mCastSession.isConnected());
}
}
super.onResume();
}
onPause
protected void onPause() {
super.onPause();
if( mCastContext != null && mCastStateListener != null ) {
mCastContext.removeCastStateListener(mCastStateListener);
mCastContext.getSessionManager().removeSessionManagerListener(
mSessionManagerListener, CastSession.class);
}
}
The session Manager listener in the class...
private final SessionManagerListener<CastSession> mSessionManagerListener =
new MySessionManagerListener();
private class MySessionManagerListener implements SessionManagerListener<CastSession> {
#Override
public void onSessionEnded(CastSession session, int error) {
if (session == mCastSession) {
mCastSession = null;
}
invalidateOptionsMenu();
}
#Override
public void onSessionResumed(CastSession session, boolean wasSuspended) {
mCastSession = session;
invalidateOptionsMenu();
}
#Override
public void onSessionStarted(CastSession session, String sessionId) {
mCastSession = session;
invalidateOptionsMenu();
}
#Override
public void onSessionStarting(CastSession session) {
}
#Override
public void onSessionStartFailed(CastSession session, int error) {
}
#Override
public void onSessionEnding(CastSession session) {
}
#Override
public void onSessionResuming(CastSession session, String sessionId) {
}
#Override
public void onSessionResumeFailed(CastSession session, int error) {
}
#Override
public void onSessionSuspended(CastSession session, int reason) {
}
}
UI interaction
Finally I could change the UI when cast was available by calling the "known" function in my Application...
int visibility = View.GONE;
if( mApplication.isCastAvailableKnown( ) ) {
CastSession castSession = CastContext.getSharedInstance(mApplication).getSessionManager()
.getCurrentCastSession();
if( castSession != null && castSession.isConnected() ){
visibility = View.VISIBLE;
}
}
viewHolder.mMenu.setVisibility( visibility);

Related

Android ZoomSDK - Meeting Service Listener

I'm trying to catch onMeetingStatusChanged event. But for my case, the onMeetingStatusChanged is sometimes invoked, not all the time. Below is my implemented code:
#Override
protected void onCreate(Bundle savedInstanceState) {
registerListener();
InitAuthSDKHelper.getInstance().initSDK(this, new InitAuthSDKCallback() {
#Override
public void onZoomSDKInitializeResult(int i, int i1) {
}
#Override
public void onZoomAuthIdentityExpired() {
}
});
}
private void registerListener() {
ZoomSDK zoomSDK = ZoomSDK.getInstance();
MeetingService meetingService = zoomSDK.getMeetingService();
if (meetingService != null) {
meetingService.addListener(this);
}
}
#Override
public void onMeetingStatusChanged(MeetingStatus meetingStatus,
int errorCode,
int internalErrorCode) {
LogD.d(TAG, String.valueOf(meetingStatus));
if (meetingStatus == MeetingStatus.MEETING_STATUS_IDLE) {
layout_zoom_loading.setVisibility(View.VISIBLE);
} else {
layout_zoom_loading.setVisibility(View.GONE);
}
if(meetingStatus == MeetingStatus.MEETING_STATUS_FAILED
&& errorCode == MeetingError.MEETING_ERROR_CLIENT_INCOMPATIBLE) {
Toast.makeText(this, "Version of ZoomSDK is too low!", Toast.LENGTH_LONG).show();
}
}
public void joinMeeting(String meetingNo, String meetingPassword) {
ZoomSDK zoomSDK = ZoomSDK.getInstance();
if (!zoomSDK.isInitialized()) {
Toast.makeText(this, getString(R.string.msg_zoom_init_fail), Toast.LENGTH_LONG).show();
return;
}
JoinMeetingHelper.getInstance().joinMeetingWithNumber(this, meetingNo, meetingPassword);
}
I see the cause of this problem. We need to separate the initSDK method to BaseActivity class. So when user forward into the next Activity which runs Zoom meeting, onMeetingStatusChanged always be invoked.

How do I dismiss a dialog when working with on a fragment?

I have an alert dialog reference that I want to dismiss. I have an issue is that I can't just dismiss the reference it to from my dialog, here is the code from my Fragment so you can understand why -
#OnClick(R.id.verification_button_got_it)
void onBtnGotItClicked(View view) {
if (!checkBoxAge.isChecked()) {
checkBoxAge.setTextColor(ContextCompat.getColor(checkBoxAge.getContext(), R.color.accent_red));
return;
}
showProgressDialog();
if (getContext() instanceof VerificationPageListener) {
((VerificationPageListener) getContext()).onPageAgreed();
}
}
private void showProgressDialog(){
if (mBuilder == null) {
mBuilder = new AlertDialog.Builder(App.getAppContext());
}
mBuilder.setCancelable(false);
mBuilder.setView(R.layout.custom_proggress_dialog);
mDialog = mBuilder.create();
mDialog.show();
}
At the point that you see "onPageAgreed()" is the point where the data is being sent to the server to verify the device IMEI for verification purpose, so if I dismiss the dialog at that point than the dialog will not show at all because it will be immediate.
As I said, this is a Fragment that sits on top of activity, so the activity handles the entire checking through the DB thing. Here is the code of the activity handling the database checking and moving the results to the presenter -
#Override
public void onPageAgreed() {
// current page is accepted, move to next
int cur = viewPager.getCurrentItem();
if (cur == adapter.getCount() - 1) {
// ask for permission
requestPhoneState();
} else {
// move to next
viewPager.setCurrentItem(cur + 1, true);
}
}
private void requestPhoneState() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_PHONE_STATE}, RC_PERMISSION_READ_STATE);
} else {
// retrieve IMEI
accessDeviceIdAndVerifyDB(this);
}
}
#Override
protected void onRequestPermissionsResultPostResume(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResultPostResume(requestCode, permissions, grantResults);
if (requestCode == RC_PERMISSION_READ_STATE
&& permissions.length > 0 && StringUtils.equals(permissions[0], Manifest.permission.READ_PHONE_STATE)
&& grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// granted, read IMEI
accessDeviceIdAndVerifyDB(this);
} else {
// failed
onErrorDeviceIdNotGrantedPermission();
}
}
private void accessDeviceIdAndVerifyDB(Activity activity) {
if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
TelephonyManager telephonyManager = (TelephonyManager) activity.getSystemService(Context.TELEPHONY_SERVICE);
if (telephonyManager != null) {
#SuppressLint("HardwareIds") String deviceId = telephonyManager.getDeviceId();
if (StringUtils.isNotBlank(deviceId)) {
checkDeviceIdWithDB(deviceId);
}
}
}
}
private void checkDeviceIdWithDB(String deviceId) {
presenter.onDeviceIdReceived(deviceId);
}
It seems like I am stuck in a loop where I have to move a reference of my dialog all over a few classes just in order to cancel it, which seems really broken. How can I cancel my dialog more easily?
edit -
I have added this code before the point where the activity is being finished -
#Override
public void sendDeviceIdResult(String deviceId, boolean isAlreadyExist) {
int currentItem = viewPager.getCurrentItem();
Fragment item = adapter.getItem(currentItem);
if (item instanceof PhoneStateAndAgeVerificationFragment) {
Dialog dialog = ((PhoneStateAndAgeVerificationFragment) item).getProgressDialog();
if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
}
}
Intent intent = new Intent();
intent.putExtra(KEY_DEVICE_ID, deviceId);
intent.putExtra(KEY_IS_SUCCESS, !isAlreadyExist);
setResult(RESULT_OK, intent);
finish();
}
but at this point my dialog is always null and I can't figure out if at this point it will be always null and the check is redundant or I am doing something total wrong?
You can close your dialog that is in your frament from your activity using interfaces.
In activity create an interface like following.
public class YourActivity extends AppCompactActivity{
public interface onSomeEventListener {
public void closeDialog();
}
onSomeEventListener someEventListener;
//.............
#Override
public void onPageAgreed() {
// current page is accepted, move to next
int cur = viewPager.getCurrentItem();
if (cur == adapter.getCount() - 1) {
// ask for permission
requestPhoneState();
} else {
// move to next
viewPager.setCurrentItem(cur + 1, true);
}
//you can add below line in your activity from where you can close your dialog.
someEventListener.closeDialog();
}
In your fragment you have to implement that interface like below
public class YourFragment extends Fragment implements onSomeEventListener{
#Override
public void closeDialog() {
// here you can close your dialog
if (mDialog != null && mDialog.isShowing()) {
mDialog.dismiss();
}
}
}
UPDATE
In your sendDeviceIdResult() you can simplify the code using the above interface like below.
#Override
public void sendDeviceIdResult(String deviceId, boolean isAlreadyExist) {
int currentItem = viewPager.getCurrentItem();
Fragment item = adapter.getItem(currentItem);
if (item instanceof PhoneStateAndAgeVerificationFragment) {
someEventListener = item; // initialize your interface here instead of onCreate()
someEventListener.closeDialog();
}
Intent intent = new Intent();
intent.putExtra(KEY_DEVICE_ID, deviceId);
intent.putExtra(KEY_IS_SUCCESS, !isAlreadyExist);
setResult(RESULT_OK, intent);
finish();
}

FlexboxLayout - Show tag or Chip or label count at the end of second line android

I am using FlexboxLayout for showing chips in android but stuck at UI Where I want to show number of chips count at the end of second line as per below screenshot
As per above design I want to show chips until second line and the chips that not fit in second line should increase chip count at the end of chips
What I have check is
1- FlexboxLayout setMaxLine(2); But in this case UI only two line draw and second line is strech
2- FlexboxLayout getFlexLineInternel() - this API gives wrong result. Count of line is increases after 1 item added in the line then it gives getFlexLineInternel().size() == 2 it means view is already added in third line but I want to restrict only on 2 lines.
3- Try with Material Chip and ChipGroup - It didn't given me number of lines draw in chipGroup
4- Try with Flowlayout similar as ChipGroup. Didn't given me number of lines draw by layout so that I can show count at end of second line
Please give me suggestion what is helpful in my situation
Here is my code
activity_search_label.xml
<com.google.android.flexbox.FlexboxLayout
android:id="#+id/assetLabelsContainer"
android:layout_width="match_parent"
android:layout_marginLeft="#dimen/size_16"
android:layout_marginRight="#dimen/size_16"
android:layout_height="wrap_content"
app:flexWrap="wrap"/>
LabelSearchActivity.java
public class LabelSearchActivity extends SearchActivity {
#BindView(R.id.assetLabelsContainer)
public FlexboxLayout assetLabelsContainer;
private int COUNTER_LABEL_ID = -1;
private ArrayList<LabelModelParcelableItem> selectedLabels;
private ArrayList<LabelModelParcelableItem> visibleSelectedList;
private CompositeDisposable disposables;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ButterKnife.bind(this);
clearLabelList();
showSelectedLabel();
}
public void hideSearchTitleCountView() {
if (null != searchTitle) {
searchTitle.setVisibility(View.GONE);
}
}
private void clearLabelList() {
selectedLabels = new ArrayList<>();
visibleSelectedList = new ArrayList<>();
assetLabelsContainer.getFlexLinesInternal().clear();
assetLabelsContainer.removeAllViews();
}
private void showSelectedLabel() {
if (getIntent() != null) {
Bundle bundle = getIntent().getExtras();
if (bundle.getParcelableArrayList(SELECTED_LABEL_ITEMS) != null) {
ArrayList<LabelModelParcelableItem> selectedLabels = bundle
.getParcelableArrayList(SELECTED_LABEL_ITEMS);
showLabelList(selectedLabels);
}
}
}
public void addViewToFlex(final LabelModelParcelableItem labelModelParcelableItem) {
if (!selectedLabels.contains(labelModelParcelableItem)) {
selectedLabels.add(labelModelParcelableItem);
final View view = getChipView(labelModelParcelableItem);
if (shouldShowCount()) {
View countView = getCounterView();
if (null != countView) {
LabelModelParcelableItem tag = (LabelModelParcelableItem) countView.getTag();
LabelModelParcelableItem updatedTag =
new LabelModelParcelableItem(
tag.getLabelId(), String.valueOf(getInvisibleItemCount()), tag.getAssetCount());
TextView textView = (TextView) countView.findViewById(R.id.labelNameText);
textView.setText(" + " + updatedTag.getLabelName());
countView.setTag(updatedTag);
assetLabelsContainer.requestLayout();
} else {
addCounterView();
}
} else {
visibleSelectedList.add(labelModelParcelableItem);
assetLabelsContainer.addView(view);
}
}
}
public int getInvisibleItemCount() {
ArrayList<LabelModelParcelableItem> itemList = new ArrayList<>(selectedLabels);
itemList.removeAll(visibleSelectedList);
return itemList.size();
}
private void addCounterView() {
final View view =
LayoutInflater.from(LabelSearchActivity.this)
.inflate(R.layout.label, assetLabelsContainer, false);
LabelModelParcelableItem item =
new LabelModelParcelableItem(
COUNTER_LABEL_ID, String.valueOf(getInvisibleItemCount()), COUNTER_LABEL_ID);
view.setTag(item);
TextView textView = (TextView) view.findViewById(R.id.labelNameText);
textView.setText(" + " + item.getLabelName());
view.setOnClickListener(v -> showLabelContainerScreen(getSelectedLabels()));
if (getInvisibleItemCount() > 0) {
assetLabelsContainer.addView(view);
}
}
public View getChipView(final LabelModelParcelableItem item) {
final View view =
LayoutInflater.from(LabelSearchActivity.this)
.inflate(R.layout.label, assetLabelsContainer, false);
TextView textView = (TextView) view.findViewById(R.id.labelNameText);
textView.setText(item.getLabelName());
view.setOnClickListener(v -> removeViewFormFlex(view, item));
return view;
}
private boolean shouldShowCount() {
if (assetLabelsContainer.getFlexLinesInternal().size() > 2) {
return true;
}
return false;
}
private void updatedLabelAfterRemove() {
if (null != getCounterView()) {
if (getInvisibleItemCount() > 0) {
ArrayList<LabelModelParcelableItem> itemList = new ArrayList<>(selectedLabels);
itemList.removeAll(visibleSelectedList);
Collections.sort(itemList, (o1, o2) -> o2.getLabelName().compareTo(o1.getLabelName()));
if (!itemList.isEmpty()) {
addViewAfterRemove(itemList.get(0));
}
} else {
assetLabelsContainer.removeView(getCounterView());
}
}
}
private void addViewAfterRemove(LabelModelParcelableItem item) {
final View labelView = getChipView(item);
View countView = getCounterView();
if (countView != null) {
assetLabelsContainer.removeView(countView);
visibleSelectedList.add(item);
assetLabelsContainer.addView(labelView);
addCounterView();
} else {
visibleSelectedList.add(item);
assetLabelsContainer.addView(labelView);
}
}
private View getCounterView() {
View countView =
assetLabelsContainer.getFlexItemAt(assetLabelsContainer.getFlexItemCount() - 1);
if (null != countView) {
LabelModelParcelableItem item = (LabelModelParcelableItem) countView.getTag();
if (item != null && item.getLabelId() == -1) {
return countView;
}
}
return null;
}
private void showLabelList(ArrayList<LabelModelParcelableItem> selectedLabels) {
for (LabelModelParcelableItem item : selectedLabels) {
addViewToFlex(item);
assetLabelsContainer.post(() -> assetLabelsContainer.requestLayout());
}
}
public void addSelectedLabel(Parcelable label) {
addViewToFlex((LabelModelParcelableItem) label);
}
public ArrayList<LabelModelParcelableItem> getSelectedLabels() {
return selectedLabels;
}
public List<Parcelable> getUpdatedLabelList(List<? extends Parcelable> newParcelableList) {
if (null != newParcelableList && !newParcelableList.isEmpty()) {
newParcelableList.removeAll(getSelectedLabels());
return new ArrayList<>(newParcelableList);
} else {
return new ArrayList<>();
}
}
public void labelUpdateList(List dataList) {
getSearchAdapter().clearAndUpdateList(dataList);
}
#Override
public void onBackButtonPressed() {
setActivityResult(getSelectedLabels());
}
public void setActivityResult(ArrayList<? extends Parcelable> searchableItem) {
setResult(RESULT_OK, new Intent().putParcelableArrayListExtra(SEARCH_RESULT, searchableItem));
super.onBackButtonPressed();
}
public void addDisposable(Disposable disposable) {
if (null == disposables) {
disposables = new CompositeDisposable();
}
disposables.add(disposable);
}
#Override
public void onDestroy() {
if (disposables != null && !disposables.isDisposed()) {
disposables.clear();
}
super.onDestroy();
}
private void showLabelContainerScreen(List<? extends Parcelable> labels) {
Intent intent = new Intent(this, ViewLabelsActivity.class);
Bundle bundle = new Bundle();
bundle.putParcelableArrayList(
LabelSelectionActivity.ARGUMENT_KEY, (ArrayList<? extends Parcelable>) labels);
intent.putExtras(bundle);
startActivityForResult(intent, LabelSelectionActivity.VIEW_ALL_LABEL_CODE);
}
#Override
public void onBackPressed() {
if (!getSelectedLabels().isEmpty()) {
Intent intent = new Intent();
intent.putParcelableArrayListExtra(
SELECTED_LABEL_ITEMS, (ArrayList<? extends Parcelable>) getSelectedLabels());
setResult(Activity.RESULT_OK, intent);
} else {
this.finish();
}
super.onBackPressed();
}
#OnClick(R.id.navBarBack)
public void onBackButton() {
onBackPressed();
}
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode != LabelSelectionActivity.VIEW_ALL_LABEL_CODE) {
return;
}
if (data == null) {
return;
}
if (resultCode != RESULT_OK) {
return;
}
if (!data.hasExtra(SELECTED_LABEL_ITEMS)) {
return;
}
clearLabelList();
ArrayList<LabelModelParcelableItem> selectedLabels =
data.getParcelableArrayListExtra(SELECTED_LABEL_ITEMS);
showLabelList(selectedLabels);
updateAfterViewAllLabels(selectedLabels);
}
private void updateAfterViewAllLabels(ArrayList<LabelModelParcelableItem> labelsAfterRemoved) {
ArrayList<LabelModelParcelableItem> listOfRemovedLabels = new ArrayList<>(getSelectedLabels());
listOfRemovedLabels.removeAll(labelsAfterRemoved);
for (LabelModelParcelableItem item : labelsAfterRemoved) {
//removeViewFormFlex(item);
}
}
public void removeViewFormFlex(View view, LabelModelParcelableItem item) {
if (selectedLabels.remove(item)) {
if (selectedLabels.isEmpty()) {
assetLabelsContainer.removeAllViews();
} else {
visibleSelectedList.remove(item);
assetLabelsContainer.removeView(view);
}
updatedLabelAfterRemove();
Comparator<LabelModelParcelableItem> compareByName =
(o1, o2) -> o1.getLabelName().compareTo(o2.getLabelName());
getSearchAdapter().addLabelItemToList(item, compareByName);
}
}
}
As per above code it look like below image
try to measure the height of FlexboxLayout after each flexboxLayout.addView(view) and if the width increased that is mean you are in second line.

Detect if MediaBrowserServiceCompat is running

I'm using this example code to build a MediaPlayer Service. I have it more or less working, however if a user returns to the Activity that contains the media controls, I need to detect what state the MediaPlayer is in. The code I'm currently using throws a NullReferenceException error in onResume because getPlaybackState() is always null.
I'm new to using MediaSessionCompat and according to the documentation, I can get the current session somehow:
"Once a session is created the owner of the session may pass its session token to other processes to allow them to create a MediaControllerCompat to interact with the session."
public class MediaActivity extends AppCompatActivity {
private MediaBrowserCompat mMediaBrowserCompat;
private MediaControllerCompat mMediaControllerCompat;
private Activity mActivity;
#Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_media);
mActivity = this;
mPlayButton = (Button)findViewById(R.id.btn_play);
mMediaBrowserCompat = new MediaBrowserCompat(
getApplicationContext(),
new ComponentName(mContext, MediaPlayerService.class),
mMediaBrowserCompatConnectionCallback,
getIntent().getExtras()
);
mMediaBrowserCompat.connect();
mPlayButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if( mCurrentState == STATE_PAUSED ) {
getSupportMediaController().getTransportControls().play();
mCurrentState = STATE_PLAYING;
mPlayButton.setText("Pause");
} else {
MediaControllerCompat.getMediaController(mActivity).getTransportControls().pause();
mCurrentState = STATE_PAUSED;
mPlayButton.setText("Play");
}
}
});
#Override
public void onResume() {
super.onResume();
if (MediaControllerCompat.getMediaController(mActivity).getPlaybackState().getState() == PlaybackStateCompat.STATE_PLAYING) {
mPlayButton.setText("Pause")
}
else{
mPlayButton.setText("Play")
}
}
private MediaBrowserCompat.ConnectionCallback mMediaBrowserCompatConnectionCallback = new MediaBrowserCompat.ConnectionCallback() {
#Override
public void onConnected() {
super.onConnected();
try {
mMediaControllerCompat = new MediaControllerCompat(PodcastEpisodeActivity.this, mMediaBrowserCompat.getSessionToken());
mMediaControllerCompat.registerCallback(mMediaControllerCompatCallback);
MediaControllerCompat.setMediaController(mActivity, mMediaControllerCompat);
MediaControllerCompat.getMediaController(mActivity).getTransportControls().playFromUri(Uri.parse("http://www.url.com"), extras);
} catch( RemoteException e ) {
Log.e(mActivity.getPackageName(), e.toString());
}
}
};
private MediaControllerCompat.Callback mMediaControllerCompatCallback = new MediaControllerCompat.Callback() {
#Override
public void onPlaybackStateChanged(PlaybackStateCompat state) {
super.onPlaybackStateChanged(state);
if (state == null ) return;
switch (state.getState()) {
case PlaybackStateCompat.STATE_PLAYING: {
mCurrentState = STATE_PLAYING;
break;
}
case PlaybackStateCompat.STATE_PAUSED: {
mCurrentState = STATE_PAUSED;
break;
}
}
}
};
}
}
I couldn't figure out a way through the available API, so I'm just tracking which media is being played through local storage (sqlite) and updating the display through that. Probably not the most elegant solution but it works.

Android: PullToRefresh ListView not showing

I have used Chris Banes implementation of pull to refresh list view for my app. The problem is if I set visibility for list view as gone or invisible and make it visible in java code, the list doesn't shows up. On the other hand, if I set its visibility as visible or don't set its visibility, every thing works fine. My requirement is such that I have two list views in the same activity. I have to set the visibility as one will appear first once it get data from server. The other will appear on search function. I had set the visibility for search result's listview as gone in the xml code, and making it visible only once it gets search results. Despite using setVisibility() for this listview, it never shows up screen. I had checked server response as well. It is showing search result on logcat.
I am posting my code below:
Code Snippet from Activity
//The result from this async task will populate the first list view
if(NetworkConnection.isOnline(MainCategory.this))
{
new MainMenuAsyncTask(dataUrl, MainCategory.this, listMainMenu, false).execute();
}
else
{
Log.v(TAG, "no network available");
SeattleNightLifeUtility.OpenWiFiDialog(MainCategory.this, getResources().getString(R.string.no_internet_msg));
}
loadListView();
//This will populate the list view that I have created for search results
_txtAutoSearch.setOnEditorActionListener(new TextView.OnEditorActionListener()
{
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event)
{
String term = _txtAutoSearch.getText().toString().trim();
if(! term.equals(""))
{
SeattleNightLifeUtility.hideSoftKeyboard(MainCategory.this, _txtAutoSearch);
if(NetworkConnection.isOnline(MainCategory.this))
{
search(term, false);
}
else
{
SeattleNightLifeUtility.OpenWiFiDialog(MainCategory.this, getResources().getString(R.string.no_internet_msg));
}
}
return true;
}//onEditorAction
});
listMainMenu.setOnRefreshListener(new PullToRefreshListView.OnRefreshListener()
{
#Override
public void onRefresh()
{
if(NetworkConnection.isOnline(MainCategory.this))
{
new MainMenuAsyncTask(dataUrl, MainCategory.this, listMainMenu, true).execute();
}
else
{
SeattleNightLifeUtility.OpenWiFiDialog(MainCategory.this, getResources().getString(R.string.no_internet_msg));
}
}
});
listViewSearch.setOnRefreshListener(new PullToRefreshListView.OnRefreshListener()
{
public void onRefresh()
{
if(NetworkConnection.isOnline(MainCategory.this))
{
search(_txtAutoSearch.getText().toString().trim(), true);
}
else
{
SeattleNightLifeUtility.OpenWiFiDialog(MainCategory.this, getResources().getString(R.string.no_internet_msg));
}
}
});
Search result Async Task
public class GetSearchAsyncTask extends AsyncTask<Void, Void, String>
{
Context ctx;
ProgressDialog pd;
PullToRefreshListView listViewSearch;
public static final String TAG = "GetSearchAsyncTask";
public static ArrayList<SearchDAO> searchArrayList;
private String term, callingclass;
private TextView txtNoData;
boolean flag;
public GetSearchAsyncTask(String term, Context ctx,
PullToRefreshListView listViewSearch, TextView txtNoData,
String callingclass, boolean flag)
{
this.term = term;
this.ctx = ctx;
this.listViewSearch = listViewSearch;
this.txtNoData = txtNoData;
this.callingclass = callingclass;
this.flag = flag;
}//Constructor
#Override
protected void onPreExecute()
{
if(flag == false)
{
pd = new ProgressDialog(ctx);
pd.setMessage(ctx.getResources().getString(R.string.please_wait));
pd.show();
}
}//onPreExecute
protected String doInBackground(Void... params)
{
String parsed = ServerConnection.getSearchedData(term);
try
{
if(flag == true)
{
Log.v(TAG, "doInBackground isListRefreshed is true");
Thread.sleep(2000);
}
}
catch(Exception e){}
return parsed;
}//doInBackground
#Override
protected void onPostExecute(String result)
{
searchArrayList = ParsedSearchData.getSearchedData(result);
listViewSearch.setVisibility(View.VISIBLE);
if(searchArrayList != null && searchArrayList.size() > 0)
{
Log.v(TAG, "searcharraylist not null");
for(int i = 0; i < searchArrayList.size(); i++)
{
Log.v(TAG, "Name: "+searchArrayList.get(i).getMerchant());
}
SearchAdapter mSearchAdapter = new SearchAdapter(ctx, searchArrayList);
mSearchAdapter.notifyDataSetChanged();
listViewSearch.setAdapter(mSearchAdapter);
if(callingclass.equals("EventActivity"))
{
Log.v(TAG, "callingclass EventActivity");
if(txtNoData.getVisibility() == View.VISIBLE)
{
Log.v(TAG, "txtNoData VISIBLE");
txtNoData.setVisibility(View.GONE);
}
if(((EventsActivity)ctx).txtNoEvent.getVisibility() == View.VISIBLE)
{
Log.v(TAG, "txtNoEvent VISIBLE");
((EventsActivity)ctx).txtNoEvent.setVisibility(View.GONE);
}
}
else
{
Log.v(TAG, "callingclass not EventActivity");
if(txtNoData.getVisibility() == View.VISIBLE)
{
Log.v(TAG, "else loop txtNoData VISIBLE");
txtNoData.setVisibility(View.GONE);
}
if(listViewSearch.getVisibility() == View.VISIBLE)
{
Log.v(TAG, "listViewSearch VISIBLE");
}
else
{
Log.v(TAG, "listViewSearch INVISIBLE");
}
}
}
else
{
Log.v(TAG, "searcharraylist null");
if(callingclass.equals("EventActivity"))
{
Log.v(TAG, "callingclass EventActivity");
txtNoData.setVisibility(View.VISIBLE);
listViewSearch.setVisibility(View.GONE);
if(((EventsActivity)ctx).txtNoEvent.getVisibility() == View.VISIBLE)
{
Log.v(TAG, "searcharraylist null else txtNoEvent VISIBLE");
((EventsActivity)ctx).txtNoEvent.setVisibility(View.GONE);
}
}
else
{
Log.v(TAG, "callingclass not EventActivitysearcharraylist null else txtNoEvent VISIBLE");
txtNoData.setVisibility(View.VISIBLE);
listViewSearch.setVisibility(View.GONE);
}
}
if(flag == false)
{
if(pd != null)
{
Log.v(TAG, "onPostExecute pd not null");
if(pd.isShowing())
{
Log.v(TAG, "onPostExecute pd is showing");
pd.dismiss();
}
}
}
else
{
listViewSearch.onRefreshComplete();
}
}//onPostExecute
}
Search Method
protected void search(String term, boolean result)
{
listMainMenu.setVisibility(View.GONE);
//listViewSearch.setVisibility(View.VISIBLE);
new GetSearchAsyncTask(term, MainCategory.this, listViewSearch , txtNoData, "MainCategory", result).execute();
}//search
Earlier I was setting visibility of in the XML as gone and in java code, I was making it VISIBLE. At that time, the list didn't showed up. When I removed the visibility attribute from XML layout file, and only set it in java code with setVisibility(), it worked perfect. I couldn't figured out the reason behind this. May be, I need to take a look at the implementation of library so that I find where did I went wrong. But, for the time being, this is what worked for me.

Categories

Resources