How to fix API authentication problem in Kotlin Android app? - java

I developed an API in Laravel for reading some data with authentication. Now in my app I need to send the token in my header for API to respond. Every time i login with app, I'm getting token without problem, but i can't provide token in header and it's returning a null token response. Now every time i reopen my app everything is fine but problem appear only when i login.
Please read my code and say if you found the bug.
This is my (AppServer.kt). I use this for adding the token to my header
class AppServer private constructor(private val client: INetworkClient) : IAppServer {
private val gson = Gson()
companion object {
private var instance: AppServer? = null
fun getInstance(): AppServer {
val token = AppLoggedInUser.getInstance()?.userProfile?.userToken ?: ""
if (instance == null)
instance = AppServer(
MixedNetworkClient(
mHeaders = mutableMapOf(
Headers.CONTENT_TYPE to "application/json",
"X-Requested-With" to "XMLHttpRequest",
"Authorization" to "Bearer $token"
),
mBasePath = "https://myWebsiteURL.com/"
)
)
return instance!!
}
}
override fun getPurchasedItems(): Observable<Pair<ResponseState, List<OrderInfo>?>> {
return Observable.create<Pair<ResponseState, List<OrderInfo>?>> { emitter ->
val (items, statusCode, msg, error) =
client.get(
"api/users/${AppLoggedInUser.getInstance().userProfile.userId}/orders",
{ responseStr ->
try {
val orders = mutableListOf<OrderInfo>()
val jArray = JSONArray(responseStr)
for (i in 0 until jArray.length()) {
val jObject = jArray.getJSONObject(i)
val order =
gson.fromJson<OrderInfo>(jObject.toString(), OrderInfo::class.java)
if (order.isPaymentSuccessful)
orders.add(order)
}
if (orders.isEmpty())
emitter.onNext(pair(empty(), null))
else
emitter.onNext(pair(success(), orders))
} catch (t: Throwable) {
Timber.e(t)
emitter.onNext(pair(ResponseState.internalError(t), null))
}
})
}.attachSchedulers()
}
override fun getDiscountDetailsById(id: Int): Observable<Pair<ResponseState, DiscountDetails?>> {
return Observable.create<Pair<ResponseState, DiscountDetails?>> { emitter ->
try {
client.post("api/post", listOf("post_id" to id.toString())) { responseStr ->
val jObject = JSONObject(responseStr)
var discount: DiscountDetails? = null
if (jObject.has("ID"))
discount =
gson.fromJson<DiscountDetails>(jObject.toString(), DiscountDetails::class.java)
if (discount != null)
emitter.onNext(pair(success(), discount))
else
emitter.onNext(pair(notFound404(), null))
}
} catch (t: Throwable) {
Timber.e(t)
emitter.onNext(pair(ResponseState.internalError(t), null))
}
}.attachSchedulers()
}
}
and this is my (AppLoggedInUser.java) I use it for getting my current user token.
package com.fartaak.gilantakhfif.utilities;
import android.content.Context;
import com.fartaak.gilantakhfif.backend.server.ParsingGSON;
import com.fartaak.gilantakhfif.model.UserProfile;
public class AppLoggedInUser extends AppPreferences {
public static final String NAM_LOGIN_SdPs = "login";
public static final String KEY_USER_TOKEN = "tokenPor";
private static AppLoggedInUser mInstance;
private final String KEY_LOGIN = "prefP";
private boolean isCompletelyRegistered;
public static void init(Context context) {
if (mInstance == null) {
mInstance = new AppLoggedInUser(context);
}
}
public static AppLoggedInUser getInstance() {
if (mInstance != null) {
return mInstance;
} else {
throw new IllegalStateException(
"you should call init to initialize only once per app launch before calling getInstance");
}
}
private AppLoggedInUser(Context context) {
super(NAM_LOGIN_SdPs, context);
}
public String getUserToken() {
return getField(KEY_USER_TOKEN);
}
public void clearUserProfile() {
removeField(KEY_LOGIN);
}
public UserProfile getUserProfile() {
String data = getField(KEY_LOGIN);
if (data == null) {
return null;
}
//return new Gson().fromJson(data, UserProfile.class);
return ParsingGSON.getInstance().getParsingJSONObject(data, UserProfile.class, null);
}
public boolean isRegisterCompleted() {
if (!isCompletelyRegistered) {
UserProfile userProfile = getUserProfile();
if (userProfile.getUserName() == null) {
return false;
} else {
isCompletelyRegistered = true;
}
}
return true;
}
public boolean isUserProfile() {
return isField(KEY_LOGIN);
}
public void setUserProfile(UserProfile userProfile) {
String json = ParsingGSON.getInstance().toJSONObject(userProfile, UserProfile.class, null);
setField(KEY_LOGIN, json);
}
}
I think the problem is with my second class.

Related

OneSignal customKey get error: missing return statement report

I use codeEditor to make appybuilder extensions. I tried to get the customkey value that was obtained when the application got push notification from OneSignal.
here is the full code that I tried to make:
import android.content.Context;
import android.util.Log;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.annotations.*;
import com.google.appinventor.components.runtime.*;
import com.google.appinventor.components.annotations.DesignerComponent;
import com.google.appinventor.components.annotations.DesignerProperty;
import com.google.appinventor.components.annotations.PropertyCategory;
import com.google.appinventor.components.annotations.SimpleObject;
import com.google.appinventor.components.annotations.SimpleProperty;
import com.google.appinventor.components.annotations.UsesLibraries;
import com.google.appinventor.components.annotations.UsesPermissions;
import com.onesignal.OSNotification;
import com.onesignal.OSNotificationAction.ActionType;
import com.onesignal.OSNotificationOpenResult;
import com.onesignal.OSPermissionSubscriptionState;
import com.onesignal.OneSignal;
import com.onesignal.OneSignal.LOG_LEVEL;
import com.onesignal.OneSignal.NotificationOpenedHandler;
import com.onesignal.OneSignal.NotificationReceivedHandler;
import com.onesignal.OneSignal.OSInFocusDisplayOption;
import org.json.JSONObject;
#DesignerComponent(version = 1, description = "This Extension was created with the AppyBuilder Code Editor.<br>" +
"Create your own here:<br><a href='https://editor.appybuilder.com' target='_blank'>https://editor.appybuilder.com</a><br>",
category = ComponentCategory.EXTENSION,
nonVisible = true, iconName = "http://appyBuilder.com/extensions/icons/extension.png")
#SimpleObject(external = true)
public class OneSignalPlus extends AndroidNonvisibleComponent {
private ComponentContainer container;
private boolean soundEnabled = true;
private boolean subscriptionEnabled = true;
private boolean vibrateEnabled = true;
private class ExampleNotificationReceivedHandler implements NotificationReceivedHandler {
private ExampleNotificationReceivedHandler() {
}
public void notificationReceived(OSNotification notification) {
JSONObject data = notification.payload.additionalData;
String notificationID = notification.payload.notificationID;
String title = notification.payload.title;
String body = notification.payload.body;
String smallIcon = notification.payload.smallIcon;
String largeIcon = notification.payload.largeIcon;
String bigPicture = notification.payload.bigPicture;
String smallIconAccentColor = notification.payload.smallIconAccentColor;
String sound = notification.payload.sound;
String ledColor = notification.payload.ledColor;
int lockScreenVisibility = notification.payload.lockScreenVisibility;
String groupKey = notification.payload.groupKey;
String groupMessage = notification.payload.groupMessage;
String fromProjectNumber = notification.payload.fromProjectNumber;
String rawPayload = notification.payload.rawPayload;
Log.d("OneSignalPush", "NotificationID received: " + notificationID);
if (data != null) {
String customKey = data.optString("customkey", null);
if (customKey != null) {
Log.d("OneSignalPush", "customkey set with value: " + customKey);
}
}
}
}
class NotificationOpenHandler implements NotificationOpenedHandler {
NotificationOpenHandler() {
}
public void notificationOpened(OSNotificationOpenResult osNotificationOpenResult) {
ActionType actionType = osNotificationOpenResult.action.type;
JSONObject data = osNotificationOpenResult.notification.payload.additionalData;
Log.d("OneSignalPush", "NotificationID received: " + data);
if (data != null) {
String customKey = data.optString("customkey", null);
if (customKey != null) {
Log.i("OneSignalExample", "customkey set with value: " + customKey);
} else {
Log.i("OneSignalExample", "No data");
}
}
}
}
public OneSignalPlus(ComponentContainer container) {
super(container.$form());
this.container = container;
OneSignal.setLogLevel(LOG_LEVEL.DEBUG, LOG_LEVEL.NONE);
OneSignal.startInit(container.$context()).autoPromptLocation(false).setNotificationReceivedHandler(new ExampleNotificationReceivedHandler()).inFocusDisplaying(OSInFocusDisplayOption.Notification).init();
}
/**
* Get Custom Key
*/
#SimpleProperty(category = PropertyCategory.BEHAVIOR, description = "Get custom Key. If ther is no customkey it will return '-1'.")
public final String GetCustomKey(OSNotificationOpenResult osNotificationOpenResult) {
try {
ActionType actionType = osNotificationOpenResult.action.type;
JSONObject data = osNotificationOpenResult.notification.payload.additionalData;
Log.d("OneSignalPush", "NotificationID received: " + data);
if (data != null) {
String customKey = data.optString("customkey", null);
if (customKey != null) {
return customKey;
} else {
return "-1";
}
}
} catch (NullPointerException e) {
return false;
}
}
/**
* Showing Log
*/
#SimpleProperty(description = "If you want to enable the log then set it to true.")
public final void EnableLog(boolean z) {
PushNotifications pushNotifications = this;
if (z) {
LOG_LEVEL log_level = LOG_LEVEL.DEBUG;
OneSignal.setLogLevel(log_level, log_level);
return;
}
OneSignal.setLogLevel(LOG_LEVEL.DEBUG, LOG_LEVEL.NONE);
}
#SimpleProperty(category = PropertyCategory.BEHAVIOR, description = "Get the subscription Status")
public final boolean GetSubscriptionStatus() {
try {
return OneSignal.getPermissionSubscriptionState().getSubscriptionStatus().getSubscribed();
} catch (NullPointerException e) {
return false;
}
}
}
but the code when compiled gives a report of the error "missing return statement". you can try to build this code here codeEditor
edited:
i have tried to add return statement after if like this
public final String GetCustomKey(OSNotificationOpenResult osNotificationOpenResult) {
ActionType actionType = osNotificationOpenResult.action.type;
JSONObject data = osNotificationOpenResult.notification.payload.additionalData;
String customKey;
if (data != null) {
customKey = data.optString("customkey", null);
if (customKey != null) {
return customKey;
} else {
return "-1";
}
}
return "-1";
}
or
public final String GetCustomKey(OSNotificationOpenResult osNotificationOpenResult) {
ActionType actionType = osNotificationOpenResult.action.type;
JSONObject data = osNotificationOpenResult.notification.payload.additionalData;
String customKey;
if (data != null) {
customKey = data.optString("customkey", null);
if (customKey != null) {
return customKey;
}
}
return "-1";
}
but it give me more warning and error report
can you give me a clue what is wrong or less from the code
You need to remove the return "-1"; out of the inner if statement.
public final String GetCustomKey() {
if (data != null) {
customKey = data.optString("customkey", null);
if (customKey != null) {
return customKey;
}
}
return "-1";
}
For bonus points, you can clean the method up a little such as
public final String GetCustomKey(OSNotificationOpenResult osNotificationOpenResult) {
ActionType actionType = osNotificationOpenResult.action.type;
JSONObject data = osNotificationOpenResult.notification.payload.additionalData;
return data.containsKey("customkey") ? data.get("customkey").toString() : "-1";
}

How to implement servlet response.redirect in jersey?

While i am try to implement response.redirect in jersey by using Response.temporaryRedirect(location) and Response.seeOther(location), But both are not working as like Servlet response.sendRedirect.
As i tried,
Java script :-
function loginFunction(){
var password=$("#password").val();
var username=$("#emailId").val();
function make_base_auth(user, password) {
var tok = user + ':' + password;
var hash = btoa(tok);
return "Basic " + hash;
}
$.ajax({
url:'/test/login/filter',
type:'GET',
crossDomain: true,
beforeSend : function(req) {
req.setRequestHeader('Authorization', make_base_auth(username, password));
},
success:function(res,status,xhr){
userToken=xhr.getResponseHeader("Authorization");
localStorage.setItem("userToken", userToken);
if(userToken != undefined || userToken != null){
window.location.href = "./index.html";
}else{
$('.errmsg').html('please provide a valid credentials.')
$(this).load();
}
},
error : function(jqXHR, textStatus, errorThrown) {
console.log("textStatus is:"+textStatus);
}
});
}
Jersey Code:-
#Path("/login")
public class UserLogin {
private static Map<String ,Token> tokTable=new HashMap<String ,Token>();
#GET
#Path("/filter")
public void filter(#Context ContainerRequestContext context){
String authentication = filterContext.getHeaderString(HttpHeaders.AUTHORIZATION);
if (authentication == null) {
throw new AuthenticationException("Authentication credentials are required");
}
if (!authentication.startsWith("Basic ")) {
return null;
}
authentication = authentication.substring("Basic ".length());
String[] values = new String(DatatypeConverter.parseBase64Binary(authentication), Charset.forName("ASCII")).split(":");
String username=null,givenPass=null;
try{
username = values[0];
givenPass=org.glassfish.jersey.internal.util.Base64.encodeAsString( values[1]);
}catch(Exception e){
throw new AuthenticationException("User name and password can't be empty\r\n");
}
}
User user = AuthLookUpTables.userTable.get(username);
// Validate the extracted credentials
if ( user == null ) {
logger.info("USER NOT AUTHENTICATED");
throw new AuthenticationException("Invalid username");
}
String password=org.glassfish.jersey.internal.util.Base64.encodeAsString(givenPass.concat(user.getSalt()));
if ((username == null) || (password == null)) {
throw new WebApplicationException(400);
}
String dbPass=org.glassfish.jersey.internal.util.Base64.encodeAsString(user.getHashedPassword().concat(user.getSalt()));
if ( dbPass.equals(password) ) {
logger.info("USER AUTHENTICATED");
//SOme code using for main sever.
} else {
logger.info("USER NOT AUTHENTICATED");
throw new AuthenticationException("Invalid username or password\r\n");
}
return user;
}
POJO (USER):-
public class User {
public String username;
public String roles;
public String salt;
public String hashedPassword;
//and below getter and setter methods
}
Custom Exception:-
public class AuthenticationException extends RuntimeException {
public AuthenticationException() {
super();
}
public AuthenticationException(String message) {
super(message);
}
}
Exception Mapper:-
#Provider
public class AuthenticationExceptionMapper implements ExceptionMapper<AuthenticationException> {
private Logger logger=Logger.getLogger(AuthenticationExceptionMapper.class);
#Context
UriInfo uriInfo;
public Response toResponse(AuthenticationException e) {
UriBuilder builder=null;
if (e.getMessage() != null) {
builder=UriBuilder.fromPath(uriInfo.getBaseUriBuilder().toString()).path("..");
return Response.temporaryRedirect(builder.build()).build();
} else {
return Response
.status(Status.UNAUTHORIZED)
.type("text/plain")
.entity(e.getMessage())
.build();
}
}
}
this is what i get in developer tool,
But, i am expecting server redirect to login page, how do i do that, kindly same one help me to do this.
thank you

Json array and retrofit

I've this kind of json response:
{
error: false
stats: {
punti: 150
punti2: 200
}-
}
I created a StatsReceiver class:
public class StatsReceiver {
Boolean error;
Stats stat;
public Boolean isError() {
if (error == null)
return true;
else
return error;
}
public int getPunti() {
if (stat == null)
return -1;
else
return stat.getPunti();
}
private class Stats {
private int punti = 0;
public int getPunti() {
return punti;
}
public void setPunti(int punti) {
this.punti = punti;
}
public int getPunti2() {
return punti2;
}
public void setPunti2(int punti2) {
this.punti2 = punti2;
}
private int punti2 = 0;
public Stats(int punti, int punti2) {
this.punti = punti;
this.punti2 = punti2;
}
}
}
Next:
#GET("/v1/stats")
void stats(#Header("Auth") String code,
Callback<StatsReceiver> object);
Now when I do:
apiServiceUsers.stats(apiKey, new Callback<StatsReceiver>() {
#Override
public void success(StatsReceiver statsReceiver, Response response) {
if (statsReceiver.isError()) {
Toast.makeText(this, "Error", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(this, statsReceiver.getPunti(), Toast.LENGTH_LONG).show();
}
}
Error is false as expected, but getPunti() return always -1, so stat object is always null.
What's wrong?
P.S in the Log console, there is:
{"error":false,"stats":{"punti":150,"punti2":200}}
In your JSON example the key is stats; but in your Java class the member variable is called stat. For Gson to work, these must be either exactly the same, or you must use #SerializedName to tell Gson which JSON key corresponds to which variable:
#SerializedName("stats")
Stats statOrSomethingElseEntirely;

How do I combine two separate parsed jsonObjects into a single arraylist?

I would like to combine two separate parsed jsonObjects into a single arraylist, then display the results as Strings?
I would like to store summaryJsonObject & segment in storylineData. When I step through the code using the debugger summaryJsonObject & segment both hold the raw json. The raw json data also shows in the logcat but storylineData remains null & unavailable throughout.
Here is the parsing code.
public class StorylineData {
private static String date;
private ArrayList<SummaryData> summary;
private ArrayList<SegmentData> segments;
private String caloriesIdle;
private String lastUpdate;
public String getDate() {
return date;
}
public ArrayList<SummaryData> getSummary() {
return summary;
}
public ArrayList<SegmentData> getSegments() {
return segments;
}
public String getCaloriesIdle() {
return caloriesIdle;
}
public String getLastUpdate() {
return lastUpdate;
}
public void setDate(String date) {
this.date = date;
}
public void setSummary(ArrayList<SummaryData> summary) {
this.summary = summary;
}
public void setSegments(ArrayList<SegmentData> segments) {
this.segments = segments;
}
public void setCaloriesIdle(String caloriesIdle) {
this.caloriesIdle = caloriesIdle;
}
public void setLastUpdate(String lastUpdate) {
this.lastUpdate = lastUpdate;
}
public static StorylineData parse(JSONObject jsonObject) {
if (jsonObject != null) {
StorylineData storylineData = new StorylineData();
storylineData.date = jsonObject.optString("date");
storylineData.caloriesIdle = jsonObject.optString("caloriesIdle");
storylineData.lastUpdate = jsonObject.optString("lastUpdate");
storylineData.summary = new ArrayList<SummaryData>();
storylineData.segments = new ArrayList<SegmentData>();
JSONArray summariesJsonArray= jsonObject.optJSONArray("summary");
if (summariesJsonArray != null) {
for (int i = 0; i < summariesJsonArray.length(); i++) {
JSONObject summaryJsonObject = summariesJsonArray.optJSONObject(i);
if (summaryJsonObject != null) {
storylineData.summary.add(SummaryData.parse(summaryJsonObject));
Log.d("storylineHandler", summaryJsonObject.toString());
}
}
}
JSONArray segmentsJsonArray = jsonObject.optJSONArray("segments");
if (segmentsJsonArray != null) {
for (int i = 0; i < segmentsJsonArray.length(); i++) {
JSONObject segment = segmentsJsonArray.optJSONObject(i);
if (segment != null) {
storylineData.segments.add(SegmentData.parse(segment));
Log.d("storylineHandler", segment.toString());
}
}
}
return storylineData;
}
return null;
}
}
The MainActivity looks like this:
MainActivity
public class MainActivity extends FragmentActivity implements OnClickListener{
..other variables here..
List<StorylineData> storylineData;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...other ui elements here...
mEditTextResponse = (TextView) findViewById(R.id.editResponse);
storylineData = new StorylineData();
MovesAPI.init(this, CLIENT_ID, CLIENT_SECRET, CLIENT_SCOPES.....
#Override
public void onClick(View v) {
toggleProgress(true);
switch (mSpinnerAPI.getSelectedItemPosition()) {
... other cases here...
break;
...other cases here...
case 4: // Get Summary Day
MovesAPI.getSummary_SingleDay(summaryHandler, "20150418", null);//Date changed to "20150117"
break;
Other cases here..
case 10: // Get Storyline Day
MovesAPI.getStoryline_SingleDay(storylineHandler, "20150418", null, false);//Date changed to "20150418"
break;
...Other cases here..
}
}
... Other MovesHandlers here...
private JSONObject summaryJsonObject;
private List<StorylineData> storylineList;
private JSONObject summariesJsonArray;
private MovesHandler<ArrayList<StorylineData>> storylineHandler = new MovesHandler<ArrayList<StorylineData>>() {
#Override
public void onSuccess(ArrayList<StorylineData> result) {
toggleProgress(false);
storylineList = (List<StorylineData>) StorylineData.parse(summaryJsonObject);
updateResponse( + storylineData.toString() + "\n" //displays true to layout view
result.add(StorylineData.parse(summariesJsonArray))+ "\n"
+Log.d("call result", result.toString()) + "\n" //displays 60 in layout view & com.protogeo.moves.demos.apps.storyline.StorylineData#52824f88, null]
+ Log.d("Log.d storylineHandler", storylineHandler.toString()) + "\n" ); //returns 78 in layout view & com.protogeo.moves.demos.apps.Mainactivity#234234 to log cat
onFailure code here..
}
};
public void toggleProgress(final boolean isProgrressing) {
togglePregress code here..
}
public void updateResponse(final String message) {
runOnUiThread(new Runnable() {
public List<StorylineData> storylineList;
#Override
public void run() {
mEditTextResponse.setText(message);
if (storylineData!= null) {
for (StorylineData storylineData : storylineList) {
mEditTextResponse.append(("storylineData" + storylineData.toString()));
}
}
}
});
}
}
HttpClass
public static void getDailyStorylineList(final MovesHandler<JSONArray> handler,
final String specificSummary,
final String from,
final String to,
final String pastDays,
final String updatedSince,
final boolean needTrackPoints) {
new Thread(new Runnable() {
#Override
public void run() {
try {
/* Refresh access token if only AuthData.MOVES_REFRESHBEFORE days are there to expire current token */
AuthData.refreshAccessTokenIfNeeded();
/* Exchange the authorization code we obtained after login to get access token */
HashMap<String, String> nameValuePairs = new HashMap<String, String>();
nameValuePairs.put("access_token", AuthData.getAuthData().getAccessToken());
// if (specificSummary != null && specificSummary.length() > 0) nameValuePairs.put("specificSummary", specificSummary);//att
if (from != null && from.length() > 0) nameValuePairs.put("from", from);
if (to != null && to.length() > 0) nameValuePairs.put("to", to);
if (pastDays != null && pastDays.length() > 0) nameValuePairs.put("pastDays", pastDays);
if (updatedSince != null && updatedSince.length() > 0) nameValuePairs.put("updatedSince", updatedSince);
if (needTrackPoints) nameValuePairs.put("trackPoints", "true");
URL url = new URL(MovesAPI.API_BASE + MovesAPI.API_PATH_STORYLINE + (specificSummary != null ? specificSummary : "") + "?" + Utilities.encodeUrl(nameValuePairs));
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setDoInput(true);
urlConnection.connect();
if (urlConnection.getResponseCode() != 200) {
/* All other HTTP errors from Moves will fall here */
handler.onFailure(getErrorStatus(Utilities.readStream(urlConnection.getErrorStream()), urlConnection.getResponseCode()), "Server not responded with success ("+ urlConnection.getResponseCode() +")");
return;
}
String response = Utilities.readStream(urlConnection.getInputStream());
Object object = new JSONTokener(response).nextValue();
if (object instanceof JSONArray) {
JSONArray jsonArray = (JSONArray) object;
ArrayList<StorylineData> storylineData = new ArrayList<StorylineData>();
if (jsonArray != null) {
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject storylineJsonObject = jsonArray.optJSONObject(i);
if (storylineJsonObject != null) {
storylineData.add(StorylineData.parse(storylineJsonObject));
}
}
}
handler.onSuccess(storylineData);
} else {
handler.onFailure(MovesStatus.INVALID_RESPONSE, "Expected a JSONArray from server, but failed");
}
} catch (Exception ex) {
ex.printStackTrace();
handler.onFailure(MovesStatus.UNEXPECTED_ERROR, "An unexpected error occured, please check logcat");
}
}
}).start();
}
MovesHandler
public interface MovesHandler<T> {//T stands for generic type
/**
* Implement this method to get success notifications along with the result
* #param result : Result of the operation completed with this handler
*/
public void onSuccess(ProfileData result);
/**
* Implement this method to get failure notifications along with the {#link MovesStatus} code and a brief message
* #param status : Status code of the failure
* #param message : A brief message about the reason behind failure
*/
public void onFailure(MovesStatus status, String message);
}
If you wanted to have one ArrayList to store both SummaryData and SegmentData, you could just created an ArrayList of Objects, ArrayList<Object>. This would be the more general solution.
The alternative would be having SummaryData and SegmentData inherit the same class or implement the same interface.
Using an extended class, you could have:
class Data {
}
class SegmentData extends Data {
}
class SummaryData extends Data {
}
You could then have an ArrayList that would be able to add both SegmentData and SummaryData objects.
If you wanted to show each item as a String you would need to loop through the list and call the toString() function of each item
ArrayList<Data> dataList;
for (Data d : dataList) {
Log.d("data", d.toString())
}
Just make sure to overwrite the toString() function in SegmentData and SummaryData
EDIT: Showing how to print JsonArray
If you wanted to just print for JsonArrays, you could:
public class StorylineData {
private static String date;
private JSONArray summary;
private JSONArray segments;
private String caloriesIdle;
private String lastUpdate;
public String getDate() {
return date;
}
public JSONArray getSummary() {
return summary;
}
public JSONArray getSegments() {
return segments;
}
public String getCaloriesIdle() {
return caloriesIdle;
}
public String getLastUpdate() {
return lastUpdate;
}
public void setDate(String date) {
this.date = date;
}
public void setSummary(JSONArray summary) {
this.summary = summary;
}
public void setSegments(JSONArray segments) {
this.segments = segments;
}
public void setCaloriesIdle(String caloriesIdle) {
this.caloriesIdle = caloriesIdle;
}
public void setLastUpdate(String lastUpdate) {
this.lastUpdate = lastUpdate;
}
public static StorylineData parse(JSONObject jsonObject) {
if (jsonObject != null) {
StorylineData storylineData = new StorylineData();
storylineData.date = jsonObject.optString("date");
storylineData.caloriesIdle = jsonObject.optString("caloriesIdle");
storylineData.lastUpdate = jsonObject.optString("lastUpdate");
storylineData.summary = jsonObject.optJSONArray("summary");
storylineData.segments = jsonObject.optJSONArray("segments");
return storylineData;
}
return null;
}
#Override
public String toString() {
JSONArray combined = new JSONArray(summary);
combined.put(segment);
return combined.toString();
}
}
In your MainActivity
private StorylineData storylineData;
private MovesHandler<JSONArray> storylineHandler = new MovesHandler<JSONArray>() {
#Override
public void onSuccess(JSONArray result) {
toggleProgress(false);
storylineData = StorylineData.parse(summaryJsonObject);
updateResponse(storylineData.toString()) //displays true to layout view
result.add(storylineData.getSummary());
Log.d("call result", result.toString());
Log.d("Log.d storylineHandler", storylineHandler.toString());
}
};

How to cache in a Blackberry BrowserField

I am creating a Blackberry application to display a full screen web view of a certain site. I have a working browserfield that displays properly but navigation from page to page is slower than that of the native browser. The browserfield does not seem to have a built in cache causing the load time to be slow. When I add the following code to manage the cache the site no longer displays properly.
BrowserFieldScreen.java:
import net.rim.device.api.browser.field2.*;
import net.rim.device.api.script.ScriptEngine;
import net.rim.device.api.system.*;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
import org.w3c.dom.Document;
class BrowserFieldScreen extends MainScreen
{
BrowserField browserField;
LoadingScreen load = new LoadingScreen();;
public BrowserFieldScreen()
{
browserField = new BrowserField();
browserField.getConfig().setProperty(
BrowserFieldConfig.JAVASCRIPT_ENABLED,
Boolean.TRUE);
browserField.getConfig().setProperty(
BrowserFieldConfig.NAVIGATION_MODE,
BrowserFieldConfig.NAVIGATION_MODE_POINTER);
browserField.getConfig().setProperty(
BrowserFieldConfig.CONTROLLER,
new CacheProtocolController(browserField));
browserField.requestContent("http://www.stackoverflow.com");
add(browserField);
}
}
CacheProtocolController.java:
import javax.microedition.io.HttpConnection;
import javax.microedition.io.InputConnection;
import net.rim.device.api.browser.field2.BrowserField;
import net.rim.device.api.browser.field2.BrowserFieldRequest;
import net.rim.device.api.browser.field2.ProtocolController;
public class CacheProtocolController extends ProtocolController{
// The BrowserField instance
private BrowserField browserField;
// CacheManager will take care of cached resources
private CacheManager cacheManager;
public CacheProtocolController(BrowserField browserField) {
super(browserField);
this.browserField = browserField;
}
private CacheManager getCacheManager() {
if ( cacheManager == null ) {
cacheManager = new CacheManagerImpl();
}
return cacheManager;
}
/**
* Handle navigation requests (e.g., link clicks)
*/
public void handleNavigationRequest(BrowserFieldRequest request)
throws Exception
{
InputConnection ic = handleResourceRequest(request);
browserField.displayContent(ic, request.getURL());
}
/**
* Handle resource request
* (e.g., images, external css/javascript resources)
*/
public InputConnection handleResourceRequest(BrowserFieldRequest request)
throws Exception
{
// if requested resource is cacheable (e.g., an "http" resource),
// use the cache
if (getCacheManager() != null
&& getCacheManager().isRequestCacheable(request))
{
InputConnection ic = null;
// if requested resource is cached, retrieve it from cache
if (getCacheManager().hasCache(request.getURL())
&& !getCacheManager().hasCacheExpired(request.getURL()))
{
ic = getCacheManager().getCache(request.getURL());
}
// if requested resource is not cached yet, cache it
else
{
ic = super.handleResourceRequest(request);
if (ic instanceof HttpConnection)
{
HttpConnection response = (HttpConnection) ic;
if (getCacheManager().isResponseCacheable(response))
{
ic = getCacheManager().createCache(request.getURL(),
response);
}
}
}
return ic;
}
// if requested resource is not cacheable, load it as usual
return super.handleResourceRequest(request);
}
}
CacheManager.java:
import javax.microedition.io.HttpConnection;
import javax.microedition.io.InputConnection;
import net.rim.device.api.browser.field2.BrowserFieldRequest;
public interface CacheManager {
public boolean isRequestCacheable(BrowserFieldRequest request);
public boolean isResponseCacheable(HttpConnection response);
public boolean hasCache(String url);
public boolean hasCacheExpired(String url);
public InputConnection getCache(String url);
public InputConnection createCache(String url, HttpConnection response);
public void clearCache(String url);
}
CacheManagerImpl.java:
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.Hashtable;
import javax.microedition.io.HttpConnection;
import javax.microedition.io.InputConnection;
import net.rim.device.api.browser.field2.BrowserFieldRequest;
import net.rim.device.api.browser.field2.BrowserFieldResponse;
import net.rim.device.api.io.http.HttpHeaders;
public class CacheManagerImpl implements CacheManager {
private static final int MAX_STANDARD_CACHE_AGE = 2592000;
private Hashtable cacheTable;
public CacheManagerImpl() {
cacheTable = new Hashtable();
}
public boolean isRequestCacheable(BrowserFieldRequest request) {
// Only HTTP requests are cacheable
if (!request.getProtocol().equals("http")) {
return false;
}
// Don't cache the request whose method is not "GET".
if (request instanceof HttpConnection) {
if (!((HttpConnection) request).getRequestMethod().equals("GET"))
{
return false;
}
}
// Don't cache the request with post data.
if (request.getPostData() != null) {
return false;
}
// Don't cache authentication request.
if (request.getHeaders().getPropertyValue("Authorization") != null) {
return false;
}
return true;
}
public boolean isResponseCacheable(HttpConnection response) {
try {
if (response.getResponseCode() != 200) {
return false;
}
} catch (IOException ioe) {
return false;
}
if (!response.getRequestMethod().equals("GET")) {
return false;
}
if (containsPragmaNoCache(response)) {
return false;
}
if (isExpired(response)) {
return false;
}
if (containsCacheControlNoCache(response)) {
return false;
}
if ( response.getLength() <= 0 ) {
return false;
}
// additional checks can be implemented here to inspect
// the HTTP cache-related headers of the response object
return true;
}
private boolean isExpired(HttpConnection response) {
try
{
// getExpiration() returns 0 if not known
long expires = response.getExpiration();
if (expires > 0 && expires <= (new Date()).getTime()) {
return true;
}
return false;
} catch (IOException ioe) {
return true;
}
}
private boolean containsPragmaNoCache(HttpConnection response) {
try
{
if (response.getHeaderField("pragma") != null
&& response.getHeaderField("pragma")
.toLowerCase()
.indexOf("no-cache") >= 0)
{
return true;
}
return false;
} catch (IOException ioe) {
return true;
}
}
private boolean containsCacheControlNoCache(HttpConnection response) {
try {
String cacheControl = response.getHeaderField("cache-control");
if (cacheControl != null) {
cacheControl = removeSpace(cacheControl.toLowerCase());
if (cacheControl.indexOf("no-cache") >= 0
|| cacheControl.indexOf("no-store") >= 0
|| cacheControl.indexOf("private") >= 0
|| cacheControl.indexOf("max-age=0") >= 0) {
return true;
}
long maxAge = parseMaxAge(cacheControl);
if (maxAge > 0 && response.getDate() > 0) {
long date = response.getDate();
long now = (new Date()).getTime();
if (now > date + maxAge) {
// Already expired
return true;
}
}
}
return false;
} catch (IOException ioe) {
return true;
}
}
public InputConnection createCache(String url, HttpConnection response) {
byte[] data = null;
InputStream is = null;
try {
// Read data
int len = (int) response.getLength();
if (len > 0) {
is = response.openInputStream();
int actual = 0;
int bytesread = 0 ;
data = new byte[len];
while ((bytesread != len) && (actual != -1)) {
actual = is.read(data, bytesread, len - bytesread);
bytesread += actual;
}
}
} catch (IOException ioe) {
data = null;
} finally {
if (is != null) {
try {
is.close();
} catch (IOException ioe) {
}
}
if (response != null) {
try {
response.close();
} catch (IOException ioe) {
}
}
}
if (data == null) {
return null;
}
// Calculate expires
long expires = calculateCacheExpires(response);
// Copy headers
HttpHeaders headers = copyResponseHeaders(response);
// add item to cache
cacheTable.put(url, new CacheItem(url, expires, data, headers));
return new BrowserFieldResponse(url, data, headers);
}
private long calculateCacheExpires(HttpConnection response) {
long date = 0;
try {
date = response.getDate();
} catch (IOException ioe) {
}
if (date == 0) {
date = (new Date()).getTime();
}
long expires = getResponseExpires(response);
// If an expire date has not been specified assumes the maximum time
if ( expires == 0 ) {
return date + (MAX_STANDARD_CACHE_AGE * 1000L);
}
return expires;
}
private long getResponseExpires(HttpConnection response) {
try {
// Calculate expires from "expires"
long expires = response.getExpiration();
if (expires > 0) {
return expires;
}
// Calculate expires from "max-age" and "date"
if (response.getHeaderField("cache-control") != null) {
String cacheControl = removeSpace(response
.getHeaderField("cache-control")
.toLowerCase());
long maxAge = parseMaxAge(cacheControl);
long date = response.getDate();
if (maxAge > 0 && date > 0) {
return (date + maxAge);
}
}
} catch (IOException ioe) {
}
return 0;
}
private long parseMaxAge(String cacheControl) {
if (cacheControl == null) {
return 0;
}
long maxAge = 0;
if (cacheControl.indexOf("max-age=") >= 0) {
int maxAgeStart = cacheControl.indexOf("max-age=") + 8;
int maxAgeEnd = cacheControl.indexOf(',', maxAgeStart);
if (maxAgeEnd < 0) {
maxAgeEnd = cacheControl.length();
}
try {
maxAge = Long.parseLong(cacheControl.substring(maxAgeStart,
maxAgeEnd));
} catch (NumberFormatException nfe) {
}
}
// Multiply maxAge by 1000 to convert seconds to milliseconds
maxAge *= 1000L;
return maxAge;
}
private static String removeSpace(String s) {
StringBuffer result= new StringBuffer();
int count = s.length();
for (int i = 0; i < count; i++) {
char c = s.charAt(i);
if (c != ' ') {
result.append(c);
}
}
return result.toString();
}
private HttpHeaders copyResponseHeaders(HttpConnection response) {
HttpHeaders headers = new HttpHeaders();
try {
int index = 0;
while (response.getHeaderFieldKey(index) != null) {
headers.addProperty(response.getHeaderFieldKey(index),
response.getHeaderField(index));
index++;
}
} catch (IOException ioe) {
}
return headers;
}
public boolean hasCache(String url) {
return cacheTable.containsKey(url);
}
public boolean hasCacheExpired(String url) {
Object o = cacheTable.get(url);
if (o instanceof CacheItem) {
CacheItem ci = (CacheItem) o;
long date = (new Date()).getTime();
if (ci.getExpires() > date) {
return false;
} else {
// Remove the expired cache item
clearCache(url);
}
}
return true;
}
public void clearCache(String url) {
cacheTable.remove(url);
}
public InputConnection getCache(String url) {
Object o = cacheTable.get(url);
if (o instanceof CacheItem) {
CacheItem ci = (CacheItem) o;
return new BrowserFieldResponse(url,
ci.getData(),
ci.getHttpHeaders());
}
return null;
}
}
CacheItem.java:
import net.rim.device.api.io.http.HttpHeaders;
public class CacheItem {
private String url;
private long expires;
private byte[] data;
private HttpHeaders httpHeaders;
public CacheItem(String url,
long expires,
byte[] data,
HttpHeaders httpHeaders)
{
this.url = url;
this.expires = expires;
this.data = data;
this.httpHeaders = httpHeaders;
}
public String getUrl() {
return url;
}
public long getExpires() {
return expires;
}
public byte[] getData() {
return data;
}
public HttpHeaders getHttpHeaders() {
return httpHeaders;
}
}
Any help that can be giving towards this will be greatly appreciated. This really has me stumped. Thanks.
UPDATE: It looks like the caching only works at a certain level of the Blackberry libraries. I have added logic to check the current Software level and turn on the caching if it is supported by the device's current software level. This provides me with a good work around, but i would still like to know if there is a better way for the caching to work with all devices.
UPDATE 2 Based on comments: The site no longer displaying properly pertains to site not displaying the proper layout, images and text. It basically give a white background with links and text displaying as a bulleted list, all formatting removed.
I've been looking at your code, and the only thing I've found there's wrong with it, is you are completely ignoring the possibility of response.getLength(); returning less than zero (in CacheManagerImpl.createCache()). Although this didn't happen to me at the stackoverflow.com page, some pages use Transfer-Encoding: chunked, which means Content-Length is not present. This is, however, well handled, and should not cause the cache to fail (it would only be less effective).
I suggest testing your code on smaller problems, one step at a time. First, create cacheable page that only contains some text (like "hello") without any HTML tags. That should work pretty well, and in case it does not, it shouldn't be hard to determine where the data are getting lost. Or try to manually create cache item that does not expire and contains a webpage with no (external) stylesheet nor images, and see if it's even possible to pass it to BrowserField the way you do it. Then build on, add an image, add a style sheet so you can corner the problem.
The code is written very nicely, but at this point, it is not possible to help you because there are no evident flaws in the code and you are not explaining yourself very well, it is not clear how the error manifests itself, if it is every time or random, ... If I had a Blackberry device, I could probably try running the code for myself, but i don't.

Categories