I'm struggling with Android Auto using Desktop Head Unit Emulator.
So far I create my media app in Android Auto, start it and all media albums where shown.
However, when I click on an entry, nothing happens.
It is not clear to me how to catch the click in order to further start an action.
#Nullable
#Override
public BrowserRoot onGetRoot(#NonNull String clientPackageName, int clientUid, #Nullable Bundle rootHints) {
int maximumRootChildLimit = rootHints.getInt(
MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT,
/* defaultValue= */ 4);
Bundle extras = new Bundle();
extras.putInt(
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE,
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM);
extras.putInt(
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE,
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM);
extras.putBoolean(
MediaConstants.BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED, true);
return new BrowserRoot(MY_MEDIA_ROOT_ID, extras);
}
#Override
public void onLoadChildren(#NonNull String parentId, #NonNull Result<List<MediaBrowserCompat.MediaItem>> result) {
if (MY_MEDIA_ROOT_ID.equals(parentId)) {
for (Album album : allAlbums) {
.....
MediaDescriptionCompat desc =
new MediaDescriptionCompat.Builder()
.setMediaId(mediaID)
.setTitle(title)
.setSubtitle(artist)
.setIconBitmap(bitmap(mediaID))
.setExtras(extras)
.build();
mediaItems.add(albumList);
}
}
result.sendResult(mediaItems);
}
I set the mediaID in my PlaybackStateCompat.Builder:
Bundle playbackStateExtras = new Bundle();
playbackStateExtras.putString(
MediaConstants.PLAYBACK_STATE_EXTRAS_KEY_MEDIA_ID,"mediaID");
playbackstateBuilder.setExtras(playbackStateExtras);
I set the mediaID in my MediaMetadataCompat.Builder
metadataBuilder.putString(MediaMetadata.METADATA_KEY_MEDIA_ID,"mediaID");
mMediaSessionCompat.setMetadata(metadataBuilder.build());
What have I overlooked in the documentation or didn't understand.
Thanks
Alejandro
note the following procedure, please:
FLAG_Browsable: use this flag, then the MediaID is returned in getLoadChildren.
FLAG_PLAYABLE: use this flag, then the MediaID is returned to onPlayFromMediaID. In onPlayFromMediaIDyou have to put the logic how your mediaPlayerknows what to do with the handed over mediaID
I noticed that you didn't define any tabs. In onLoadChildren several queries on the mediaID make sense. The first level defines the tabs - these are missing in the post or in your code.
e.g:
MediaDescriptionCompat desc =
new MediaDescriptionCompat.Builder()
.setMediaId(AUDIOBOOK_HEADER)
.setTitle("Library")
.build();
MediaBrowserCompat.MediaItem albumList =
new MediaBrowserCompat.MediaItem(desc,
MediaBrowserCompat.MediaItem.FLAG_BROWSABLE);
mediaItems.add(albumList);
desc =
new MediaDescriptionCompat.Builder()
.setMediaId(LATEST_HEADER)
.setTitle("Recently played")
.build();
itemList =
new MediaBrowserCompat.MediaItem(desc,
MediaBrowserCompat.MediaItem.FLAG_BROWSABLE);
mediaItems.add(itemList);
Related
I'm not able to display firebase recyclerview in released mode, but it shows all the values in debug mode. This issue is happening after i met firebase database limit. I have now upgraded plan from flame to blaze. But, the issue is still not resolved.
Query query = FirebaseDatabase.getInstance()
.getReference()
.child("quizTimings");
FirebaseRecyclerOptions<CarouselModel> options =
new FirebaseRecyclerOptions.Builder<CarouselModel>()
.setQuery(query, CarouselModel.class)
.build();
scrollView = (RecyclerView) findViewById(R.id.picker);
adapter = new FirebaseRecyclerAdapter<CarouselModel, TimeViewHolder>(options) {
#Override
public TimeViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.item_time, viewGroup, false);
return new TimeViewHolder(view);
}
#Override
protected void onBindViewHolder(TimeViewHolder timeViewHolder, int i, final CarouselModel carouselModel) {
try {
String quizTimings = carouselModel.getTime();
String timeMoney = carouselModel.getTimeMoney();
timeViewHolder.timeShow.setText(quizTimings);
timeViewHolder.timeMoney.setText(timeMoney);
Log.w("timeMoney",timeMoney);
/*final Animation myAnim = AnimationUtils.loadAnimation(MainActivity.this, R.anim.bounce);
MyBounceInterpolator interpolator = new MyBounceInterpolator(0.2, 20);
myAnim.setInterpolator(interpolator);
startBtn.startAnimation(myAnim);*/
} catch (Exception ex) {
String err = (ex.getMessage()==null)?"SD Card failed":ex.getMessage();
Log.e("sdcard-err2:",err);
}
}
};
//For setting time
/*CarouselModel carouselModel = new CarouselModel("ewt","wtt","ete","wte");
quizTiming.push().setValue(carouselModel);*/
CustomLinearLayout linearLayoutManager = new CustomLinearLayout(this,LinearLayoutManager.HORIZONTAL,false);
scrollView.addItemDecoration(new LinePagerIndicatorDecoration(MainActivity.this));
scrollView.setLayoutManager(linearLayoutManager);
scrollView.setAdapter(adapter);
Make sure you follow all the steps specified in the guide:
https://developers.google.com/identity/sign-in/android/start-integrating
. If need be, create a new OAuth Client, new configuration file (the google-services.json) file and do everything step-by-step.
Also try adding a Web Client giving the same credentials. And for Android as well as Web client give both the SHA1 for debug as well as the release keystores. In Android Studio, at extreme left you will see a tab saying "Build Variants". Select the release mode there and do everything after that.
I am trying to use the FirestorePagingAdapter to display a list of all users in my firestore database. I am using FirestorePagingAdapter instead of FirestoreRecyclerAdapter to minimize the number of reads as FirestorePagingAdapterdoesn't read the whole list of documents while FirestoreRecyclerAdapter does. I am able to display the paginated list successfully but I need to implement onClickListener onto it and on click of every item, I need to open another activity which shows a detailed description of the particular user which was clicked. For this, I need to pass the documentId of the clicked user to the next activity.
But unfortunately, FirestorePagingAdapter doesn't have getSnapshots() method so that I use getSnapshots().getSnapshot(position).getId().
On the other hand, FirestoreRecyclerAdapter has this method which makes fetching the document id a very easy task. Something like this: How to get document id or name in Android in Firestore db for passing on to another activity?
// Query to fetch documents from user collection ordered by name
Query query = FirebaseFirestore.getInstance().collection("users")
.orderBy("name");
// Setting the pagination configuration
PagedList.Config config = new PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setPrefetchDistance(10)
.setPageSize(20)
.build();
FirestorePagingOptions<User> firestorePagingOptions = new FirestorePagingOptions.Builder<User>()
.setLifecycleOwner(this)
.setQuery(query, config, User.class)
.build();
firestorePagingAdapter =
new FirestorePagingAdapter<User, UserViewHolder>(firestorePagingOptions){
#NonNull
#Override
public UserViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.single_user_layout, parent, false);
return new UserViewHolder(view);
}
#Override
protected void onBindViewHolder(#NonNull UserViewHolder holder, int position, #NonNull User user) {
holder.setUserName(user.name);
holder.setStatus(user.status);
holder.setThumbImage(user.thumb_image, UsersActivity.this);
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent userProfileIntent = new Intent(UsersActivity.this, UserProfileActivity.class);
// Need to fetch the user_id to pass it as intent extra
// String user_id = getSnapshots().getSnapshot(position).getId();
// userProfileIntent.putExtra("user_id", user_id);
startActivity(userProfileIntent);
}
});
}
};
I was able to do this by using SnapshotParser in setQuery method. Through this, I was able to modify the object which I got from the firestore. The documentSnapshot.getId() method returns the document id.
FirestorePagingOptions<User> firestorePagingOptions = new FirestorePagingOptions.Builder<User>()
.setLifecycleOwner(this)
.setQuery(query, config, new SnapshotParser<User>() {
#NonNull
#Override
public User parseSnapshot(#NonNull DocumentSnapshot snapshot) {
User user = snapshot.toObject(User.class);
user.userId = snapshot.getId();
return user;
}
})
.build();
In the User class, I just added another field "String userId" in the User class. The userId field doesn't exist in my firestore document.
In the onClickListener, I can then directly use user.userId to get the document id and send it to other activity.
While trying to access the document snapshot from itemView, I found out this
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int pos = getAdapterPosition();
if (pos != RecyclerView.NO_POSITION && listener != null) {
String docId = getItem(pos).getId();
Toast.makeText(context, "doc Id: "+docId, Toast.LENGTH_SHORT).show();
//listener.onItemClick(getSnapshots().getSnapshot(pos), pos, docId);
listener.onItemClick(getItem(pos), pos, docId);
}
}
});
As mentioned here, the getItem() returns the item's data object.
As you already noticed:
String id = getSnapshots().getSnapshot(position).getId();
Doesn't work, it works only when using FirestoreRecyclerAdapter. So to solve this, you need to store the id of the document as property of your document. If the id of the document is the id of the user that comes from the Firebase autentication, then simply store that uid. If you are not using the uid, get the id of the document and pass it to the User constructor when creating a new object like this:
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference usersRef = rootRef.collection("users");
String id = eventsRef.document().getId();
User user = new User(id, name, status, thumb_image);
usersRef.document(id).set(user);
I spent a day trying to get the id of the document as I am now using the FirestorePagingAdapter. For Kotlin this is what worked for me
override fun onBindViewHolder(viewHolder: LyricViewHolder, position: Int, song: Lyric) {
// Bind to ViewHolder
viewHolder.bind(song)
viewHolder.itemView.setOnClickListener { view ->
val id = getItem(position)?.id
var bundle = bundleOf("id" to id)
view.findNavController().navigate(R.id.songDetailFragment, bundle)
}
}
Hope this helps someone else in the near future. Can post full code and fully explain if anyone is confused. Happy coding!
Try using this
getSnapshots().getSnapshot(position).getId()
I have a RecyclerView that utilizes the FireaseUI and orders all objects from the "Polls" node by the "timestamp" field (sequentially).
New Fragment - .onViewCreated()
Query queryStore = FirebaseFirestore.getInstance()
.collection(POLLS_LABEL)
.orderBy("timestamp", Query.Direction.ASCENDING);
//Cloud Firestore does not have any ordering; must implement a timestampe to order sequentially
FirestoreRecyclerOptions<Poll> storeOptions = new FirestoreRecyclerOptions.Builder<Poll>()
.setQuery(queryStore, Poll.class)
.build();
mFirestoreAdaper = new FirestoreRecyclerAdapter<Poll, PollHolder>(storeOptions) {
#Override
protected void onBindViewHolder(#NonNull final PollHolder holder, final int position, #NonNull Poll model) {
holder.mPollQuestion.setText(model.getQuestion());
String voteCount = String.valueOf(model.getVote_count());
//TODO: Investigate formatting of vote count for thousands
holder.mVoteCount.setText(voteCount);
Picasso.get()
.load(model.getImage_URL())
.fit()
.into(holder.mPollImage);
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent toClickedPoll = new Intent(getActivity(), PollHostActivity.class);
String recyclerPosition = getSnapshots().getSnapshot(position).getId();
Log.v("Firestore ID", recyclerPosition);
toClickedPoll.putExtra("POLL_ID", recyclerPosition);
startActivity(toClickedPoll);
}
});
}
I have another layout in my app that subscribes to this same node, but instead queries by "followers" and then by "timestamp.:
Following Fragment - .onViewCreated()
Query queryStore = FirebaseFirestore.getInstance()
.collection(POLLS_LABEL)
.whereArrayContains("followers", mUserId)
.orderBy("timestamp", Query.Direction.ASCENDING);
FirestoreRecyclerOptions<Poll> storeOptions = new FirestoreRecyclerOptions.Builder<Poll>()
.setQuery(queryStore, Poll.class)
.build();
mFirestoreAdaper = new FirestoreRecyclerAdapter<Poll, PollHolder>(storeOptions) {
#Override
protected void onBindViewHolder(#NonNull final PollHolder holder, final int position, #NonNull Poll model) {
holder.mPollQuestion.setText(model.getQuestion());
String voteCount = String.valueOf(model.getVote_count());
//TODO: Investigate formatting of vote count for thousands
holder.mVoteCount.setText(voteCount);
Picasso.get()
.load(model.getImage_URL())
.fit()
.into(holder.mPollImage);
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent toClickedPoll = new Intent(getActivity(), PollHostActivity.class);
String recyclerPosition = getSnapshots().getSnapshot(position).getId();
Log.v("Firestore ID", recyclerPosition);
toClickedPoll.putExtra("POLL_ID", recyclerPosition);
startActivity(toClickedPoll);
}
});
}
In the first scenario, UI items populate, in real-time, into my RecyclerView as they are added to Firebase. However, when I query by ".whereArrayContains," I do not get this same behavior, and I was curious as to why. The items only reappear when I restart the application:
Edit:
I commented out the below line:
// .whereArrayContains("followers", mUserId)
and the behavior performed as expected, therefore I can isolate the issue to the .whereArrayContains() query. It is the only difference between each Fragment.
This is happening because when you are using whereArrayContains() and orderBy() methods in the same query, an index is required. To use one, go to your Firebase Console and create it manually or if you are using Android Studio, you'll find in your logcat a message that sounds like this:
W/Firestore: (0.6.6-dev) [Firestore]: Listen for Query(products where array array_contains YourItem order by timestamp) failed: Status{code=FAILED_PRECONDITION, description=The query requires an index. You can create it here: ...
You can simply click on that link or copy and paste the url into a web broswer and you index will be created automatically.
Why is this index needed?
As you probably noticed, queries in Cloud Firestore are very fast and this is because Firestore automatically creates an indexes for any fields you have in your document. When you need to order your items, a particular index is required that should be created as explained above. However, if you intend to create the index manually, please also select from the dropdown the corresponding Array contains option, as in the below image:
So Azure spit the following code for me to insert into an activity (Android Studio is what I'm using)
Add the following line to the top of the .java file containing your launcher activity:
import com.microsoft.windowsazure.mobileservices.*;
Inside your activity, add a private variable
private MobileServiceClient mClient;
Add the following code the onCreate method of the activity:
mClient = new MobileServiceClient("https://pbbingo.azurewebsites.net", this);
Add a sample item class to your project::
public class ToDoItem{ public String id; public String Text;}
In the same activity where you defined mClient, add the following code:
ToDoItem item = new ToDoItem();
item.Text = "Don't text and drive";
mClient.getTable(ToDoItem.class).insert(item, new TableOperationCallback<item>(){
public void onCompleted(ToDoItem entity, Exception exception, ServiceFilter response)
{
if(exception == null){
//Insert Succeeded
} else {
//Insert Failed
}
}});
My goal is to create a login page. I understand that the above was probably offered up more with a ToList in mind. I just want to get the syntax correct today. The problem I think, is my basic class structure. I have created an OnClick Listener within my on create that gets the ID from a button in my layout. I don't need it checking for anything in the database until the button has been actually clicked to either login or register.
public class LoginClass extends AppCompatActivity{
public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.MyLoginLayout);
MobileServiceClient mClient = null;
try {
mClient = new MobileServiceClient ("myAzureWebsite", "AzureKey", this);
} catch (MalformedURLException e) {
e.printStackTrace();
}
Button Attempt = (Button) findViewById (R.id.mySubmitButton);
final MobileServiceClient finalMClient = mClient; // finalized so I can use it later.
Attempt.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick (View v) {
final View thisView = v;
final MyToDoItemClass item = new MyToDoItemClass();
In MyToDoItemClass I have two variables (Both String) Just left over from
the example of a ToDoList (they are String ID and String Text)
item.Text = "Filler";
item.ID = "Fill";
finalMClient.getTable(MyToDoItemClass.class).insert(new Table OperationCallback<item>() { //<--- I'm getting an error that the variable, item
is from an unknown class...
public void onCompleted (Item entity, Exception exception, ServiceFilterResponse response){
if(exception == null) {
Intent i = new Intent (LoginClass.this, MainActivity.class);
startActivity(i);
}else{
Toast.makeText(thisView.getContext(), "Failed", Toast.LENGTH_LONG).show();
}}
});
}
});
}}
The problem is with that the TableOperationCallback is saying that the item from MyToDoItemClass class is from an unknown class.
There are many issues in your code, as below.
According to the javadoc for class MobileServiceClient, there is not a method insert(TableOperationCallback<E> callback), so the code finalMClient.getTable(MyToDoItemClass.class).insert(new Table OperationCallback<item>() {...} is invalid.
The generics E in Table OperationCallback<E> means that you need to write a POJO class name instead of E, not an object variable name like item, so the correct code should be new Table OperationCallback<MyToDoItemClass>, please see the Oracle tutorial for Generics to know more details.
The figure below shows all methods insert of class MobileServiceClient. The bold word Deprecated under the method name means that you should not use it for developing on new project, it‘s only compatible for old project on the new version of Java SDK.
Please follow the offical tutorial to develop your app. Any concern, please feel free to let me know.
In a service I'm populating an ArrayList, which I then return to the calling activity:
In service (here, resultArrayList contains items and is of class ArrayList<MyObjs>):
public class DataFetchService extends BaseService {
#Override
protected void onHandleIntent(final Intent intent) {
super.onHandleIntent(intent);
// Do some work here that populates resultArrayList...
final Bundle bundle = new Bundle();
bundle.putSerializable(BaseService.RESULT_OBJ, resultArrayList);
message.setData(bundle);
try {
final Messenger messenger = startIntent.getParcelableExtra(BaseService.PARAM_MESSENGER);
messenger.send(message);
} catch (RemoteException e) {
L.p("Help!");
}
From BaseService:
public class BaseService extends IntentService {
protected ArrayList<MyObjs> resultArrayList = new ArrayList<>();
// Yada yada...
In the activity's handleMessage():
#Override
public boolean handleMessage(final Message msg) {
final Bundle bundle = msg.getData();
#SuppressWarnings("unchecked")
final ArrayList<MyObjs> nodes = (ArrayList<MyObjs>) bundle.getSerializable(BaseService.RESULT_OBJ);
if (nodes == null) {
//App never enters this
return
}
if (nodes.size() == 0) {
// Always enters here!
// If I set a breakpoint here, the IDE tells me nodes size is 1
}
The weird thing is that if I set a breakpoint inside the if (nodes.size == 0) { code, the IDE shows that nodes does contain items (says size = 1 and I can expand it and see the variables), even though it enters that.
Any idea what could be the issue? Could this be a race condition between other services sending data back to handleMessage()?
I'm uncertain about the causes of this error, but creating a new ArrayList<> before sending from the service seems to fix it.
So the following makes it work:
final Bundle bundle = new Bundle();
// Created this new ArrayList
final ArrayList<MyObjs> newArrayList = new ArrayList<>();
// Add items to the new ArrayList
newArrayList.addAll(resultArrayList);
// Send the new ArrayList and NOT the other one.
bundle.putSerializable(BaseService.RESULT_OBJ, newArrayList);
message.setData(bundle);
try {
final Messenger messenger = startIntent.getParcelableExtra(BaseService.PARAM_MESSENGER);
messenger.send(message);
} catch (RemoteException e) {
L.p("Help!");
}