I created an app. Within this app, you have objects which contains 2 strings (name and age) and a bitmap (avatar). Everything is saved to a sqlite database.
Now I want these objects to be accessible on my smart watch. So I want to achieve that you can go to start, start the application and scroll to the left and right to see these objects.
This means I have to retrieve the objects from the phone and get them at the watch.
I am currently wondering if I did everything right, or that I should do stuff differently. Whenever you start the application on your watch, I am sending a request to the phone that I want the objects.
private void sendMessage() {
if(mGoogleApiClient.isConnected()) {
new Thread( new Runnable() {
#Override
public void run() {
NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes( mGoogleApiClient ).await();
for(Node node : nodes.getNodes()) {
Wearable.MessageApi.sendMessage(mGoogleApiClient, node.getId(), REQUEST_PET_RETRIEVAL_PATH, null).await();
}
}
}).start();
}
}
On the phone, I am receiving this message and sending a message back with an object.
public void onMessageReceived(MessageEvent messageEvent) {
super.onMessageReceived(messageEvent);
if (messageEvent.getPath().equals(REQUEST_PET_RETRIEVAL_PATH)) {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
#Override
public void onConnected(Bundle connectionHint) {
final PutDataMapRequest putRequest = PutDataMapRequest.create("/send-pets");
final DataMap map = putRequest.getDataMap();
File imgFile = new File(obj.getAvatar());
Bitmap avatar;
if(imgFile.exists()) {
avatar = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
} else {
avatar = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
}
Asset asset = createAssetFromBitmap(avatar);
map.putAsset("avatar", asset);
map.putString("name", obj.getName());
map.putString("age", obj.getDateOfBirth());
Wearable.DataApi.putDataItem(mGoogleApiClient, putRequest.asPutDataRequest());
}
#Override
public void onConnectionSuspended(int cause) {
}
})
.addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
#Override
public void onConnectionFailed(ConnectionResult result) {
}
})
.addApi(Wearable.API)
.build();
mGoogleApiClient.connect();
}
On the watch, I am then retrieving the object.
public void onDataChanged(DataEventBuffer dataEvents) {
final List<DataEvent> events = FreezableUtils.freezeIterable(dataEvents);
for(DataEvent event : events) {
final Uri uri = event.getDataItem().getUri();
final String path = uri!=null ? uri.getPath() : null;
if("/send-pets".equals(path)) {
final DataMap map = DataMapItem.fromDataItem(event.getDataItem()).getDataMap();
String name = map.getString("name");
String age = map.getString("age");
Asset avatar = map.getAsset("avatar");
Bitmap bitmap = loadBitmapFromAsset(avatar);
}
}
}
Now I am stuck with 2 questions:
1) Is this the way to go or should I solve it differently?
2) Is it possible to sent multiple objects at once or do I just have to put a loop around the part in the "onConnected" method and sent each object separatly?
Yes, this approach is good and correct one.
Yes it is possible to send multiple but you should be aware that they are not "send" they are more something like shared or synchronized between phone and Wear, and can be modified in any further point in time (however I would recommend to save them to SharedPreferences on Wear to be able to access them in offline mode.
So Message API sends objects (fast, and simple), and DataItem API is more complex but is used for bigger data and to share things between watch and phone.
Related
I want to create a modular class that I can use anytime I want to scan a barcode. Is this possible using Firebase ML Kit?
This is what I've got so far:
public List<FirebaseVisionBarcode> ScanBarcode(int... barcodeFormats)
{
//region Init, config and execution of the barcode scanning
final FirebaseVisionBarcodeDetectorOptions.Builder BUILDER =
new FirebaseVisionBarcodeDetectorOptions.Builder();
//Set barcode formats based on arguments
for (int formats : barcodeFormats)
{
BUILDER.setBarcodeFormats(formats);
}
final FirebaseVisionBarcodeDetectorOptions OPTIONS = BUILDER.build();
final FirebaseVisionImage IMAGE = FirebaseVisionImage.fromBitmap(bitmap);
final FirebaseVisionBarcodeDetector DETECTOR = FirebaseVision.getInstance()
.getVisionBarcodeDetector(OPTIONS);
DETECTOR.detectInImage(IMAGE)
.addOnSuccessListener(new OnSuccessListener<List<FirebaseVisionBarcode>>()
{
#Override
public void onSuccess(List<FirebaseVisionBarcode> _barcodes)
{
barcodes = _barcodes;
}
})
.addOnFailureListener(new OnFailureListener()
{
#Override
public void onFailure(#NonNull Exception e)
{
barcodes = new ArrayList<>();
}
});
//endregion
return barcodes;
Mostly taken from https://firebase.google.com/docs/ml-kit/
The problem here is that the listener, for the DETECTOR, is some kind of async call. Which means that it will return before onSuccess is called.
The barcode scanning part works, but I'm having a hard time wrapping it in a class of it's own.
Thanks.
I figured it out. Not sure if it's the best approach class structure wise, but it works quite nice.
public Task<List<FirebaseVisionBarcode>> ScanBarcode(int... barcodeFormats)
{
//region Init, config and execution of the barcode scanning. Mostly taken from https://firebase.google.com/docs/ml-kit/
final FirebaseVisionBarcodeDetectorOptions.Builder BUILDER =
new FirebaseVisionBarcodeDetectorOptions.Builder();
//Set barcode formats based on arguments
for (int formats : barcodeFormats)
{
BUILDER.setBarcodeFormats(formats);
}
final FirebaseVisionBarcodeDetectorOptions OPTIONS = BUILDER.build();
final FirebaseVisionImage IMAGE = FirebaseVisionImage.fromBitmap(bitmap);
final FirebaseVisionBarcodeDetector DETECTOR = FirebaseVision.getInstance()
.getVisionBarcodeDetector(OPTIONS);
final Task<List<FirebaseVisionBarcode>> DETECT_IMG_TASK = DETECTOR.detectInImage(IMAGE)
.addOnSuccessListener(new OnSuccessListener<List<FirebaseVisionBarcode>>()
{
#Override
public void onSuccess(List<FirebaseVisionBarcode> _barcodes)
{
barcodes = _barcodes;
}
})
.addOnFailureListener(new OnFailureListener()
{
#Override
public void onFailure(#NonNull Exception e)
{
barcodes = new ArrayList<>();
}
});
return DETECT_IMG_TASK;
//endregion
}
public List<FirebaseVisionBarcode> GetBarcodes()
{
return barcodes;
}
detectInImage returns a Task. What I did was return this task. Once this task completes, you can call GetBarcodes to obtain the parsed data. I hate to force calling additional methods to get the final results, but it was the only way I could get it to work.
I have an android app that is connected to an API through retrofit, ive succesfully logged in, if i press back button to return back to the login activity again, if i try re-logging in again, the app crashes and give me a NullPointerException.
here's connection code
private void loginUser(String email, String password) {
UnifyAuthenticationApiInterface service = this.client.create(UnifyAuthenticationApiInterface.class);
Call<UnifyAuthenticationApiResponse> call = service.staffLogin(email, password);
call.enqueue(new Callback<UnifyAuthenticationApiResponse>() {
#Override
public void onResponse(Call<UnifyAuthenticationApiResponse> call,
Response<UnifyAuthenticationApiResponse> response) {
UnifyAuthenticationApiResponse result = response.body();
School school = new School();
com.peterstev.unify.login.Data data = result.getData();
mySchoolsList = new ArrayList<School>();
mySchoolsList = data.getSchools();
staff = data.getStaff();
gotoHomeActivity();
}
#Override
public void onFailure(Call<UnifyAuthenticationApiResponse> call, Throwable t) {
progressDialog.dismiss();
Toast.makeText(MainActivity.this, "Login Failed # onFailure", Toast.LENGTH_SHORT).show();
}
});
}
and the goToHomeActivity() is
private void gotoHomeActivity() {
progressDialog.dismiss();
if (mySchoolsList.size() > 1) {
schoolsListView = new ListView(MainActivity.this);
schoolsArrayAdapter = new SchoolListAdapter(MainActivity.this, android.R.layout.simple_list_item_1, mySchoolsList);
schoolsListView.setAdapter(schoolsArrayAdapter);
dialog = new Dialog(MainActivity.this);
dialog.setContentView(schoolsListView);
dialog.setTitle("Welcome " + staff.getFullName());
dialog.show();
} else {
Intent intent = new Intent(MainActivity.this, NavMainActivity.class);
startActivity(intent);
}
}
the NullPointerException gets thrown at
com.peterstev.unify.login.Data data = result.getData();
at first, it gets the data n succesfully logs in, but when i use the back button n try to log in again it crashes.
Debugger is your answer - check if you aren't loosing any data when going back - maybe you're storing login params somewhere in activity class but you're not saving instance state properly and second request is triggered without necessary data. Check state of variables just before calling your request first and second time.
In situation like that always best bet to place breakpoint and trigger your work step by step. You cannot be good developer without debugger skills.
I think for some reason, the data object wasn't receiving the result when i used the back button to navigate to the parent activity. so i used and if condition to make it get the required data.
private void loginUser(String email, String password) {
UnifyAuthenticationApiInterface service = this.client.create(UnifyAuthenticationApiInterface.class);
Call<UnifyAuthenticationApiResponse> call = service.staffLogin(email, password);
call.enqueue(new Callback<UnifyAuthenticationApiResponse>() {
#Override
public void onResponse(Call<UnifyAuthenticationApiResponse> call,
Response<UnifyAuthenticationApiResponse> response) {
if(response.isSuccessful()) {
UnifyAuthenticationApiResponse result = response.body();
School school = new School();
data = result.getData();
if(data == null) {
try{
this.onResponse(call, response);
}catch(NullPointerException NPE){
Log.d("NPE", NPE.getMessage());
}
}
mySchoolsList = new ArrayList<School>();
mySchoolsList = data.getSchools();
staff = data.getStaff();
gotoHomeActivity();
}
}
#Override
public void onFailure(Call<UnifyAuthenticationApiResponse> call, Throwable t) {
progressDialog.dismiss();
Toast.makeText(MainActivity.this, "Login Failed # onFailure", Toast.LENGTH_SHORT).show();
}
});
}
I have a single incident where a complete duplicate of a entry was made into the database (the same user comment appeared twice). They had different object IDs but were otherwise the exact same. It was slower than usual to finish the posting and only happened once out of dozens of comments, so I want to say it was a Parse issue during the saveInBackground call. Even so, I expect a service like Parse to be a little more robust. As my first time working with Android though, I also can't be sure nothing is wrong on my end. Any help? Also just any criticisms? This is the method called when the user hits a comment submission button:
private void submitComment() {
String text = commentText.getText().toString().trim();
Intent intent = getIntent();
String ID = intent.getStringExtra("imageID");
String parentID = intent.getStringExtra("parent");
// Set up a progress dialog
final ProgressDialog loadingDialog = new ProgressDialog(CommentSubmitActivity.this);
loadingDialog.setMessage(getString(R.string.publishing_comment));
loadingDialog.show();
Comment comment = new Comment();
comment.setText(text);
comment.setUser((ParseUser.getCurrentUser()));
if (ID.equals("#child")) {
comment.setParent(parentID);
comment.setImage("#child");
ParseQuery<ParseObject> query = ParseQuery.getQuery("Comment");
query.getInBackground(parentID, new GetCallback<ParseObject>() {
public void done(ParseObject parentComment, ParseException e) {
if (e == null) {
int numChild = parentComment.getInt("numChild");
parentComment.put("numChild", ++numChild);
parentComment.saveInBackground();
} else {
Log.d("numChild: ", "error");
}
}
});
} else {
comment.setImage(ID);
comment.put("numChild", 0);
ParseQuery<ParseObject> query = ParseQuery.getQuery("ImageUpload");
query.getInBackground(ID, new GetCallback<ParseObject>() {
public void done(ParseObject image, ParseException e) {
if (e == null) {
int numComments = image.getInt("numComments");
image.put("numComments", ++numComments);
image.saveInBackground();
} else {
Log.d("numComments: ", "error");
}
}
});
}
comment.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
loadingDialog.dismiss();
finish();
}
}
});
}
I encountered similar problem like yours.
I created an app where user can create account and add photo to it and list of objects (friends in my case).
Once when I was testing it user was created twice.
I went through my code and my my suspicions are connected with async calls.
I see that you use asynchronous parse api in you application so no fragment of code is waiting for response and blocking the rest of operations.
You cannot control when parse server will response.
What I did I just put all synchronous requests in my custom async code (AsyncTask in Android).
Hope that my answer somehow meeets your expectations.
This is my first post so if I didn't follow some protocol I was supposed to, apologies.
I am trying to populate a ListView with some information from my Firebase database. I think the problem I am having is that the query to the database is too slow (the thread is probably downloading pictures) and my activity loads its activity layout without waiting for the thread to finish executing. (If I step through the debugger and wait a bit, I will eventually see the information I am parsing: user names, user numbers, and user pictures) Everything I have queried suggests I should use AsyncTask to accomplish this. As opposed to using thread blocking or a semaphore b/c AsyncTask is thread safe.
To my understanding, Firebase queries are already executing asynchronously; therefore, the doInBackground method for AsyncTask I have "tried" to implement seems redundant. Also, I am a bit confused of AsyncTask's overloaded signature and the call to: new someTask.execute("some stuff in a string").
Any suggestions on how I can accomplish this? Any feedback is very much appreciated!
// Please ignore the minor indentation from pasting my code in
protected void onCreate(Bundle savedInstanceState) {
...
new getFirebaseInfoTask();
}
private class getFirebaseInfoTask extends AsyncTask {
#Override
protected Object doInBackground(Object... args) {
// Do stuff
userInfoList = GetUserInfoFromFirebase.getUserInfo();
// Unsure if I need to return here.
return userInfoList;
}
#Override
protected void onProgressUpdate(Object... args) {
// Update your UI here
populateUserInfoList();
}
}
private void populateUserInfoList() {
// Create list of items
Collections.addAll(userInfoList);
populateFriendsListView();
}
private void populateFriendsListView() {
// Build the adapter
ArrayAdapter<UserInfo> adapter = new MyListAdapter();
// Configure the list view
ListView listView = (ListView) findViewById(R.id.friends_listview);
listView.setAdapter(adapter);
registerClickCallBack();
}
... // More code
public class GetUserInfoFromFirebase {
public static ArrayList getUserInfo() {
final ArrayList<UserInfo> list = new ArrayList<UserInfo>();
Firebase firebase = new Firebase("https:......firebaseio.com");
firebase.child("users").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
HashMap<String, Object> users = (HashMap<String, Object>) snapshot.getValue();
for(Object user : users.values()) {
HashMap<String, Object> userMap = (HashMap<String, Object>) user;
String userNumber = (String) userMap.remove("number");
if(!list.contains(userNumber)) {
String name = (String) userMap.remove("username");
String pic = (String) userMap.remove("profile_picture");
UserInfo info = new UserInfo(userNumber, name, pic);
list.add(info);
}
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {}
});
return list;
}
So I figured it out. I got rid of the AsyncTask and moved the method call I wanted to execute in onProgressUpdate to outside of the for loop of my onDataChange such that the thread that actually gets access to the onDataChange method calls my populateFriendsView method.
private void populateUserInfoList() {
userInfoList = new ArrayList<UserInfo>();
firebase = new Firebase("https://....firebaseio.com");
firebase.child("users").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
HashMap<String, Object> users = (HashMap<String, Object>) snapshot.getValue();
for (Object user : users.values()) {
HashMap<String, Object> userMap = (HashMap<String, Object>) user;
String userNumber = (String) userMap.remove("number");
if (!userInfoList.contains(userNumber)) {
String name = (String) userMap.remove("username");
String pic = (String) userMap.remove("profile_picture");
UserInfo info = new UserInfo(userNumber, name, pic);
userInfoList.add(info);
}
}
// thread executing here can get info from database and make subsequent call
Collections.addAll(userInfoList);
populateFriendsListView();
}
#Override
public void onCancelled(FirebaseError firebaseError) {
String message = "Server error. Refresh page";
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
});
}
Firebase itself provide Asynchronous methods to query data , They have created a sample app to show How to backing up the Listview with firebase information through Android Chat
check this
In that example the core functionality is placed in a generic base adapter called FirebaseListAdapter...
Snippet from my working code base
Firebase firebase = new Firebase(Constants.FREEBASE_DB_URL);
Firebase childRef = firebase.child("sessions");
childRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
System.out.println(snapshot.getValue());
Map<String, Session> td = (HashMap<String, Session>) snapshot.getValue();
List<Session> valuesToMatch = new ArrayList<Session>(td.values());
apiClientCallback.onSuccess(valuesToMatch);
}
#Override
public void onCancelled(FirebaseError error) {
Toast.makeText(context, "onCancelled" + error.getMessage(), Toast.LENGTH_SHORT).show();
}
});
I have the following code for FitBit integration into Android, it is used from this library https://github.com/manishsri01/FitbitIntegration, I can get the response.getBody() to show the JSON body in the webview but I would like the application to be able to automatically update the code without having to login and grab the PIN for OAuth everytime I run the app. What can I do to fix this? I would also like to parse the JSON .getBody() into separate string variables. How can I accomplish this?
MainActivity
public class MainActivity extends Activity {
OAuthService service;
Token requestToken;
// Replace these with your own api key and secret
private String apiKey = "************************";
private String apiSecret = "*************************";
private String accessToken;
private String tokenSecret;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final WebView wvAuthorize = (WebView) findViewById(R.id.wvAuthorize);
final EditText etPIN = (EditText) findViewById(R.id.etPIN);
service = new ServiceBuilder().provider(FitbitApi.class).apiKey(apiKey)
.apiSecret(apiSecret).build();
// network operation shouldn't run on main thread
new Thread(new Runnable() {
public void run() {
requestToken = service.getRequestToken();
final String authURL = service
.getAuthorizationUrl(requestToken);
// Webview nagivation should run on main thread again...
wvAuthorize.post(new Runnable() {
#Override
public void run() {
wvAuthorize.loadUrl(authURL);
}
});
}
}).start();
}
public void btnRetrieveData(View view) {
EditText etPIN = (EditText) findViewById(R.id.etPIN);
String gotPIN = etPIN.getText().toString();
final Verifier v = new Verifier(gotPIN);
// network operation shouldn't run on main thread
new Thread(new Runnable() {
public void run() {
Token accessToken = service.getAccessToken(requestToken, v);
OAuthRequest request = new OAuthRequest(Verb.GET,
"http://api.fitbit.com/1/user/-/profile.json");
service.signRequest(accessToken, request); // the access token from step
// 4
final Response response = request.send();
final TextView tvOutput = (TextView) findViewById(R.id.tvOutput);
// Visual output should run on main thread again...
tvOutput.post(new Runnable() {
#Override
public void run() {
tvOutput.setText(response.getBody());
}
});
}
}).start();
}
}
FitBitApi
public class FitbitApi extends DefaultApi10a {
private static final String AUTHORIZE_URL = "https://www.fitbit.com/oauth/authorize?oauth_token=%s";
public String getAccessTokenEndpoint() {
return "https://api.fitbit.com/oauth/access_token";
}
public String getRequestTokenEndpoint() {
return "https://api.fitbit.com/oauth/request_token";
}
public String getAuthorizationUrl(Token token) {
return String.format(AUTHORIZE_URL, token.getToken());
}
}
It sounds like you have two separate questions here. Firstly, in regards to saving credentials, there are a number of ways you can do this, the easiest is probably by saving the user/pin details in Android's SharedPreferences. However, you'll still need to make the request for an access token. You should also save the access token (in a cache or DB) and re-use it until it is expired. You may want to read up on ways to secure these credentials if they're considered private.
Your second question regarding parsing JSON is quite common, and if your intention is to map JSON objects to Java objects, you should consider using Google's GSON or Jackson JSON Processor.
If you're intending for Fitbit's API to be a large part of your app, consider using Spring Social and make a Fitbit endpoint (there is a Spring for Android which uses Spring Social). It might be a bit overkill though, but I generally like the structure.